Пример #1
0
        /// <summary>
        /// Initializes this object.
        /// </summary>
        ///
        /// <param name="host"> The host. </param>
        public void Initialize(IHost host)
        {
            this.host = host;

            switch (host.GitPaths.Count)
            {
            case 0:
                host.AddResult(Severity.Warning, false, $"Git.exe not found on PATH.");
                return;

            case 1:
                break;

            default:
                foreach (String fn in host.GitPaths)
                {
                    host.AddResult(Severity.Warning, true, $"Git.exe found at: '{fn}'.");
                }
                break;
            }

            if (!Directory.Exists(Utils.workdir))
            {
                Directory.CreateDirectory(Utils.workdir);
            }
        }
Пример #2
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <remarks>
        /// Detects if the solution contains a correctly named asset pair (normal/portable) and the
        /// PORTABLE define is correctly set (for both portable and .net core projects).
        /// </remarks>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Building '{Path.GetFileNameWithoutExtension(job.Parm)}' Project]");

            // See https://stackoverflow.com/questions/26034558/how-to-force-msbuild-to-run-code-analysis-without-recompiling
            // See https://social.msdn.microsoft.com/forums/en-US/9e77b76c-3ca5-42b4-b946-5c9d71f27ab3/invoke-code-metrics-calculation-programmatically
            //
            // Analyze.CalculateCodeMetricsforSolution
            //
            Directory.SetCurrentDirectory(Path.GetDirectoryName(job.Parm));

            return(Utils.ExecutePsi(new ProcessStartInfo
            {
                WorkingDirectory = Path.GetDirectoryName(job.Parm),
                Arguments = $"{Path.GetFileName(job.Parm)} /target:Clean;Build /p:RunCodeAnalysis=false",
                FileName = host.MSBuildPath,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
                UseShellExecute = false,
            }, host, out StringBuilder sb, false, true) == 0);

#warning warning MSB3061: Unable to delete file "C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\bin\Debug\RageAssetManager.dll". The process cannot access the file '<path>RageAssetManager.dll'

            //return p.ExitCode == 0;
        }
        public bool Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{job.GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Examining Output of '{Path.GetFileNameWithoutExtension(job.Parm)}' Project for Interfaces]");

            if (File.Exists(job.Parm))
            {
                Disassemble(job.Parm);
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Assembly.");

                return(false);
            }

            return(true);
        }
Пример #4
0
        /// <summary>
        /// Executes the psi operation.
        /// </summary>
        ///
        /// <param name="psi">   The psi. </param>
        /// <param name="host">  The host. </param>
        /// <param name="sb">    [out] The sb. </param>
        /// <param name="logstd">   (Optional) True to log. </param>
        /// <param name="logerr">  (Optional) True to log errors. </param>
        /// <param name="error"> (Optional) The error. </param>
        ///
        /// <returns>
        /// An Int32.
        /// </returns>
        public static Int32 ExecutePsi(ProcessStartInfo psi, IHost host, out StringBuilder sb, Boolean logstd = true, Boolean logerr = true, Severity error = Severity.Error)
        {
            host.AddResult(Severity.Info, true, $"{Path.GetFileNameWithoutExtension(psi.FileName)}{(String.IsNullOrEmpty(psi.Arguments) ? String.Empty : " " + psi.Arguments)} Output:");

            Process p = Process.Start(psi);

            sb = new StringBuilder();

            //! Standard Output
            //
            while (psi.RedirectStandardOutput && !p.StandardOutput.EndOfStream)
            {
                String line = p.StandardOutput.ReadLine();
                sb.AppendLine(line);

                if (logstd && !String.IsNullOrEmpty(line))
                {
                    host.AddResult(Severity.Info, true, line, 1);
                }
            }

            //! Error Output
            //
            while (psi.RedirectStandardError && !p.StandardError.EndOfStream)
            {
                String line = p.StandardError.ReadLine();
                sb.AppendLine(line);

                if (logstd && !String.IsNullOrEmpty(line))
                {
                    //! Git has the habit to emit a number of messages in the error stream.
                    //
                    host.AddResult(error, (error == Severity.Info), line, 1);
                }
            }

            p.WaitForExit();

            host.AddResult(p.ExitCode == 0 ? Severity.Info : Severity.Warning, p.ExitCode == 0, $"{Path.GetFileNameWithoutExtension(psi.FileName)} Job {(p.ExitCode == 0 ? "Success" : "Failure")} with (ExitCode: {p.ExitCode}).");

            return(p.ExitCode);
        }
Пример #5
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;String,Boolean&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            if (File.Exists(job.Parm) && TryAnalyzeProject(job, out OutputType outputType, out String outputFile))
            {
                switch (outputType)
                {
                case OutputType.Exe:
                    host.AddResult(Severity.Warning, true, $"{outputType} Project Output not analysed.");
                    break;

                case OutputType.Library:
#warning RCSAA Specific Code (Skip AssetManager from processing if present).
                    if (Utils.IsAssetManager(job.Parm, host))
                    {
                        //break;
                    }
                    else if (Utils.IsUnitTest(job.Parm))
                    {
                        host.RecurseForTypes(job, FileType.UnitTest, outputFile);
                    }
                    else
                    {
                        host.RecurseForTypes(job, FileType.Assembly, outputFile);
                    }
                    break;

                case OutputType.WinExe:
                    host.AddResult(Severity.Warning, true, $"{outputType} Project Output not analysed.");
                    break;

                case OutputType.Unknown:
                    host.AddResult(Severity.Warning, true, $"{outputType} Project Output not analysed.");
                    break;
                }
            }
Пример #6
0
        /// <summary>
        /// Query if 'csproj' is asset manager.
        /// </summary>
        ///
        /// <param name="csproj"> The csproj. </param>
        ///
        /// <returns>
        /// True if asset manager, false if not.
        /// </returns>
        public static Boolean IsAssetManager(String csproj, IHost host)
        {
            if (Utils.Load(csproj, out XDocument doc, out XmlNamespaceManager namespaces))
            {
                String  AM   = doc.Root.XPathSelectElement("ns:PropertyGroup/ns:AssemblyName", namespaces).Value;
                Boolean isAM = AM.StartsWith("RageAssetManager");

                if (isAM)
                {
                    host.AddResult(Severity.Warning, true, $"The {AM} Project should not be part of the repository.", 1);
                }

                return(isAM);
            }

            return(false);
        }
Пример #7
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Calculating Lines of Code of '{Path.GetFileNameWithoutExtension(job.Parm)}' Project]");

            if (File.Exists(job.Parm))
            {
                List <String> lines = Decompile(job.Parm)
                                      .Split(new char[] { '\r', '\n' }).ToList();
                Int32 LOC = lines.Count;

                //! See: https://dwheeler.com/sloccount/sloccount.html
                //! See: http://cloc.sourceforge.net/
                //
                List <String> nonemptylines = lines
                                              .Where(p => !String.IsNullOrEmpty(p.Trim()))
                                              .ToList();
                Int32 SLOC = nonemptylines.Count;

                List <String> trimmed = nonemptylines
                                        .Where(p => !(new String[] { "{", "}" }.Contains(p.Trim())))
                                        .ToList();
                Int32 LLOC = trimmed.Count;

                host.AddResult(Severity.Info, true, $"~ {LOC} Lines of Code (LOC) counted in {Path.GetFileName(job.Parm)}.");
                host.AddResult(Severity.Info, true, $"~ {SLOC} Source Lines of Code (SLOC) counted in {Path.GetFileName(job.Parm)}.");
                host.AddResult(Severity.Info, true, $"~ {LLOC} Logical Lines of Code (LLOC) counted in {Path.GetFileName(job.Parm)}.");
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Assembly.");

                return(false);
            }

            return(true);
        }
Пример #8
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;String,Boolean&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Examining '{Path.GetFileNameWithoutExtension(job.Parm)}' Project]");

            if (File.Exists(job.Parm))
            {
                if (Utils.Load(job.Parm, out XDocument doc, out XmlNamespaceManager namespaces))
                {
                    OutputType outputType = (OutputType)Enum.Parse(typeof(OutputType), doc.Root.XPathSelectElement("ns:PropertyGroup/ns:OutputType", namespaces).Value);

                    XElement firstPropertyGroup = doc.Root.XPathSelectElement("ns:PropertyGroup", namespaces);

                    String nameSpace              = firstPropertyGroup.XPathSelectElement("ns:RootNamespace", namespaces)?.Value;
                    String assemblyName           = firstPropertyGroup.XPathSelectElement("ns:AssemblyName", namespaces)?.Value;
                    String targetFrameworkVersion = firstPropertyGroup.XPathSelectElement("ns:TargetFrameworkVersion", namespaces)?.Value;
                    String targetFrameworkProfile = firstPropertyGroup.XPathSelectElement("ns:TargetFrameworkProfile", namespaces)?.Value;
                    //String defineConstants = firstPropertyGroup.XPathSelectElement("ns:DefineConstants", namespaces)?.Value;

                    String projectDir = Path.GetDirectoryName(job.Parm);

                    foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:PropertyGroup", namespaces))
                    {
                        XAttribute condition = propertyGroup.Attribute("Condition");

                        if (condition != null)
                        {
                            // TODO Decode this better (AnyCPU) for example.
                            //
                            String value = condition.Value;
                            if (value.Equals($" '$(Configuration)|$(Platform)' == {Utils.debug} "))
                            {
                                host.AddResult(Severity.Info, true, $"Found {Utils.debug} Condition.");

                                // DONE <OutputPath>bin\Debug\</OutputPath>                         Check
                                // DONE <DocumentationFile>bin\Debug\RageAssetManager.XML</DocumentationFile>   Check
                                // TODO <DefineConstants>TRACE</DefineConstants>                    Check
                                // DONE <DebugSymbols>true</DebugSymbols>                           Check
                                // TODO <Prefer32Bit>false</Prefer32Bit>                            Check
                                //
                                String outputPath        = propertyGroup.XPathSelectElement("ns:OutputPath", namespaces)?.Value;
                                String debugSymbols      = propertyGroup.XPathSelectElement("ns:DebugSymbols", namespaces)?.Value;
                                String documentationFile = propertyGroup.XPathSelectElement("ns:DocumentationFile", namespaces)?.Value;
                                String output            = Path.GetFullPath(Path.Combine(projectDir, outputPath));

                                switch (outputType)
                                {
                                //! Console Applications.
                                //
                                case OutputType.Exe:
                                    output = Path.GetFullPath(Path.Combine(output, Path.ChangeExtension(assemblyName, ".exe")));
                                    break;

                                //! Assemblies
                                //
                                case OutputType.Library:
                                    output = Path.GetFullPath(Path.Combine(output, Path.ChangeExtension(assemblyName, ".dll")));
                                    if (File.Exists(output))
                                    {
                                        host.AddResult(Severity.Info, true, $"Located '{output}'.");

                                        if (Utils.IsUnitTest(job.Parm))
                                        {
                                            host.RecurseForTypes(job, FileType.UnitTest, output);
                                        }
                                        else
                                        {
                                            host.RecurseForTypes(job, FileType.Assembly, output);
                                        }
                                    }
                                    break;

                                //! Windows GUI Applications
                                //
                                case OutputType.WinExe:
                                    output = Path.GetFullPath(Path.Combine(output, Path.ChangeExtension(assemblyName, ".exe")));
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Project.");
            }

            return(true);
        }
Пример #9
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <remarks>
        /// Detects if the solution contains a correctly named asset pair (normal/portable).
        /// </remarks>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            String nasset = String.Empty;
            String passet = String.Empty;
            String casset = String.Empty;

#warning TODO Add Support for Test Suite and other exe (demo) project).

            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Examnining '{Path.GetFileNameWithoutExtension(job.Parm)}' Solution]");

            if (File.Exists(job.Parm))
            {
                Solution solution = new Solution();

                if (solution.Load(job.Parm))
                {
                    foreach (SolutionProject project in solution.Projects)
                    {
                        if (project.ProjectTypeGuid.Equals(new Guid(Lookups.ProjectTypeGuids["C#"])))
                        {
                            String csproj = Path.Combine(Path.GetDirectoryName(solution.SolutionPath), project.RelativePath);

                            if (File.Exists(csproj))
                            {
                                //! Skip Test Projects.
                                //! Skip AssetManager from processing if present.
                                if (Utils.ProjectOutputType(csproj) == Utils.OutputType.Library &&
                                    !Utils.IsUnitTest(csproj) &&
                                    !Utils.IsAssetManager(csproj, host))
                                {
                                    String output = Utils.ProjectOutput(csproj);

                                    if (File.Exists(output))
                                    {
#warning TODO IsAsset may fail a second time or just on loading.
                                        // System.IO.FileLoadException: 'Could not load file or assembly 'ICSharpCode.Decompiler,
                                        // Version = 3.1.0.3652, Culture = neutral, PublicKeyToken = d4bfe873e7598c49' or one of its dependencies.
                                        // The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)'

                                        if (Utils.IsAsset(output))
                                        {
                                            host.AddResult(Severity.Debug, true, $"Asset Detected: '{Path.GetFileNameWithoutExtension(output)}'.");

                                            nasset = Path.GetFileNameWithoutExtension(output);
                                        }
                                        else if (Utils.IsPortableAsset(output))
                                        {
                                            host.AddResult(Severity.Debug, true, $"Portable Asset Detected: '{Path.GetFileNameWithoutExtension(output)}'.");

                                            passet = Path.GetFileNameWithoutExtension(output);
                                        }
                                        else if (Utils.IsCoreAsset(output))
                                        {
                                            host.AddResult(Severity.Debug, true, $".Net Core Asset Detected: '{Path.GetFileNameWithoutExtension(output)}'.");

                                            casset = Path.GetFileNameWithoutExtension(output);
                                        }
                                    }
                                    else
                                    {
                                        if (String.IsNullOrEmpty(output))
                                        {
                                            host.AddResult(Severity.Warning, false, $"Failed to Locate Output.");
                                        }
                                        else
                                        {
                                            host.AddResult(Severity.Warning, false, $"Failed to Load Output: '{Path.GetFileName(output)}'.");
                                        }
                                    }
                                }
                            }
                            else
                            {
                                host.AddResult(Severity.Warning, false, $"Failed to Locate Project.");
                            }
                        }
                    }
                }
                else
                {
                    host.AddResult(Severity.Info, false, $"Failed to Load or Parse Solution {job.Parm}.");

                    return(false);
                }
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Solution.");

                return(false);
            }

            //Boolean misnamed = false;

            // No assets found.
            //
            if (nasset.Length + passet.Length + casset.Length == 0)
            {
                host.AddResult(Severity.Warning, false, $"No Asset assembly detected.");

                return(false);
            }

            //! Main asset not found.
            //
            if (String.IsNullOrEmpty(nasset))
            {
                host.AddResult(Severity.Warning, false, $"Main Asset assembly not detected.");
            }

            //! Portable asset naming.
            //
            if (String.IsNullOrEmpty(passet))
            {
                host.AddResult(Severity.Warning, false, $"Portable Asset assembly not detected.");
            }
            else
            {
                //! Portable asset naming convention, '_Portable' suffix.
                //
                if (Path.GetFileNameWithoutExtension(passet).Equals(Path.GetFileNameWithoutExtension(nasset) + "_Portable"))
                {
                    host.AddResult(Severity.Info, true, $"Portable Asset assembly correctly named: '{Path.GetFileNameWithoutExtension(passet)}'.");
                }
                else
                {
                    host.AddResult(Severity.Warning, false, $"Portable Asset assembly misnamed, got '{Path.GetFileNameWithoutExtension(passet)}' expected '{Path.GetFileNameWithoutExtension(nasset) + "_Portable"}'.");

                    //misnamed = true;
                }
            }

            //! Core asset naming.
            //
            if (String.IsNullOrEmpty(casset))
            {
                host.AddResult(Severity.Warning, false, $".Net Core Asset assembly not detected.");
            }
            else
            {
                //! Core asset naming convention, '_Core' suffix.
                //
                if (Path.GetFileNameWithoutExtension(passet).Equals(Path.GetFileNameWithoutExtension(casset) + "_Core"))
                {
                    host.AddResult(Severity.Info, true, $".Net Core Asset assembly correctly named: '{Path.GetFileNameWithoutExtension(casset)}'.");
                }
                else
                {
                    host.AddResult(Severity.Warning, false, $".Net Core Asset assembly misnamed, got '{Path.GetFileNameWithoutExtension(passet)}' expected '{Path.GetFileNameWithoutExtension(nasset) + "_Portable"}'.");

                    //misnamed = true;
                }
            }

            return(true);
        }
Пример #10
0
        /// <summary>
        /// Fixup assembly references.
        /// </summary>
        ///
        /// <param name="csproj"> The csproj. </param>
        public static void FixupAssemblyReferences(IHost host, String csproj)
        {
            //! Implemented
            //< Reference Include="RageAssetManager">
            //  <HintPath>..\..\AssetManager\RageAssetManager\bin\Debug\RageAssetManager.dll</HintPath>
            //</Reference>

            //! Fails (Needs a nuget restore?)
            //
            // <HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net35\Newtonsoft.Json.dll</HintPath>

            //! see https://stackoverflow.com/questions/837488/how-can-i-get-the-applications-path-in-a-net-console-application
            String path = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);

            Dictionary <String, String> Fixups = Directory.EnumerateFiles(Path.Combine(path, @"Fixups"), "*.dll")
                                                 .ToDictionary(p => Path.GetFileName(p), p => p);

            String projectpath = Path.GetDirectoryName(csproj);

            Boolean modified = false;

            if (Utils.Load(csproj, out XDocument doc, out XmlNamespaceManager namespaces))
            {
                foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:ItemGroup", namespaces))
                {
                    foreach (XElement reference in propertyGroup.XPathSelectElements(@"ns:Reference", namespaces))
                    {
                        if (reference.HasAttributes && reference.Attribute("Include") != null)
                        {
                            //<Reference Include="RageAssetManager" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
                            //  <HintPath>..\..\AssetManager\RageAssetManager\bin\Debug\RageAssetManager.dll</HintPath>
                            //</Reference>
                            //
                            String s = reference.Attribute("Include").Value;
                            if (reference.XPathSelectElement(@"ns:HintPath", namespaces) != null)
                            {
                                XElement hint = reference.XPathSelectElement(@"ns:HintPath", namespaces);

                                if (!(hint == null || String.IsNullOrEmpty(hint.Value)))
                                {
                                    String hintpath = Path.GetFullPath(Path.Combine(projectpath, hint.Value));

                                    if (!File.Exists(hintpath))
                                    {
                                        String am = Path.GetFileName(hint.Value);

                                        if (Fixups.ContainsKey(am))
                                        {
                                            host.AddResult(Severity.Info, true, $"Patching Hint Location of '{am}' in '{Path.GetFileName(csproj)}'.");

                                            hint.Value = Fixups[am];

                                            modified = true;
                                        }
                                        else
                                        {
                                            host.AddResult(Severity.Warning, false, $"Failed Patching Hint Location of '{am}' in '{Path.GetFileName(csproj)}'.");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                if (modified)
                {
                    doc.Save(csproj);
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;String,Boolean&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Building '{Path.GetFileNameWithoutExtension(job.Parm)}' Project]");

            // /p:NoWarn="CS1570;CS1572;CS1587;CS1591"
            String NoXmlWarns = String.Join(";", Utils.XMLComments.Select(q => $"{q.Key}"));

            Directory.SetCurrentDirectory(Path.GetDirectoryName(job.Parm));

            // See https://stackoverflow.com/questions/26034558/how-to-force-msbuild-to-run-code-analysis-without-recompiling
            // See https://social.msdn.microsoft.com/forums/en-US/9e77b76c-3ca5-42b4-b946-5c9d71f27ab3/invoke-code-metrics-calculation-programmatically
            //
            Int32 ExitCode = Utils.ExecutePsi(new ProcessStartInfo
            {
                WorkingDirectory       = Path.GetDirectoryName(job.Parm),
                Arguments              = $"{Path.GetFileName(job.Parm)} /target:Clean;Build /detailedsummary /p:NoWarn=\"{NoXmlWarns}\" /p:RunCodeAnalysis=true",
                FileName               = host.MSBuildPath,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                CreateNoWindow         = true,
                UseShellExecute        = false,
            }, host, out StringBuilder sb, false, true);

            //C:\Program Files(x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets(4365, 5): error MSB3027: Could not copy "C:\Users\Wim van der Vegt\AppData\Local\Temp\RQAT_h2ac2d32.w0e\ClientSideGameStorageAsset\GameStorageClientAsset\bin\Debug\GameStorageClientAsset.dll" to "bin\Debug\GameStorageClientAsset.dll".Exceeded retry count of 10.Failed.The file is locked by: "RQAT (19764)"[C: \Users\Wim van der Vegt\AppData\Local\Temp\RQAT_h2ac2d32.w0e\ClientSideGameStorageAsset\GameStorageUnitTests\GameStorageUnitTests.csproj]

            List <String> errors = new List <String>();

            //! Select All MSBuild Errors (MSB).
            //
            foreach (String line in sb.ToString()
                     .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
                     .Where(q => q.Contains("error MSB"))
                     .ToList())
            {
                String msg = line.Substring(line.IndexOf($"error MSB") + "error ".Length);
                msg = msg.Substring(0, msg.IndexOf("["));

                if (!errors.Contains(msg))
                {
                    errors.Add(msg);
                }
            }

            foreach (String msg in errors)
            {
                host.AddResult(Severity.Error, true, $"{msg}", 1);
            }

            //! CS codes to exclude.
            //
            List <String> csCodes = Utils.XMLComments.Keys.ToList();

            //! Select All Warnings (CAnnnn and CSnnnn).
            //
            List <String> lines = sb.ToString()
                                  .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
                                  .Where(q => q.Contains("warning C"))
                                  .ToList();

            //! This seems to work.
            //del / s *.lastcodeanalysissucceeded
            //msbuild DesktopBuild.proj /p:RunCodeAnalysis=true

            //! Code Analysis Output.
            List <String> cas = lines
                                .Where(q => q.Contains("warning CA"))
                                .Select(q => q.Trim())
                                .Distinct()
                                .ToList();

            //! Build Output.
            lines = lines
                    .Where(q => !q.Contains("warning CA"))
                    .Select(q => q.Trim())
                    .Distinct()
                    .ToList();

            //! Remove project name.
            //! Change full filename to filename only.
            //
            for (Int32 i = 0; i < lines.Count; i++)
            {
                Match m1 = Utils.rg_project.Match(lines[i]);
                Match m2 = Utils.rg_file.Match(lines[i]);

                if (m1.Success && m2.Success)
                {
                    lines[i] = lines[i].Replace(m1.Value, String.Empty).Trim();
                    lines[i] = lines[i].Replace(m2.Groups[1].Value, $"{Path.GetFileName(m2.Groups[1].Value)}");
                }
            }

            //! Compress data by taking distinct items and group on the filename.
            //
            IEnumerable <IGrouping <String, String> > warnings = lines
                                                                 .Distinct()
                                                                 .GroupBy(q => Path.GetFileName(Utils.rg_file.Match(q).Value.Trim('(')));

            Boolean issues = false;

            foreach (IGrouping <String, String> item in warnings)
            {
                host.AddResult(Severity.Info, true, $"{item.Key}");

                foreach (String line in item)
                {
                    if (Utils.rg_csa.IsMatch(line))
                    {
                        String cs = Utils.rg_csa.Match(line).Value.Trim(':');

                        // Skip Xml Comment Issues.
                        //
                        if (csCodes.Contains(cs))
                        {
                            continue;
                        }

                        issues = true;

                        String msg = line.Substring(line.IndexOf($"{cs}: ") + $"{cs}: ".Length);

                        if (msg.Contains(" -- "))
                        {
                            msg = msg.Substring(msg.IndexOf(" -- ") + " -- ".Length);
                            msg = msg.Trim(new Char[] { '\'' });
                        }

                        String row = $"0000";

                        if (line.IndexOf("(") != -1 && line.IndexOf(")") != -1)
                        {
                            row = line.Substring(line.IndexOf("(") + 1);
                            row = row.Substring(0, row.IndexOf(")"));

                            if (row.IndexOf(",") != -1)
                            {
                                row = row.Substring(0, row.IndexOf(","));
                            }

                            if (Int32.TryParse(row, out Int32 r))
                            {
                                row = $"{r:0000}";
                            }
                        }

                        host.AddResult(Severity.Warning, true, $"line: {row} - {msg}", 1);
                    }
                }
            }

            if (cas.Count != 0)
            {
                host.AddResult(Severity.Info, true, $"Code Analysis");

                foreach (String line in cas)
                {
                    String cs = Utils.rg_csa.Match(line).Value.Trim(':');

                    issues = true;

                    String msg = line.Substring(line.IndexOf($"{cs}: ") + $"{cs}: ".Length);

                    String row = $"0000";

                    if (line.IndexOf("(") != -1 && line.IndexOf(")") != -1)
                    {
                        row = line.Substring(line.IndexOf("(") + 1);
                        row = row.Substring(0, row.IndexOf(")"));

                        if (row.IndexOf(",") != -1)
                        {
                            row = row.Substring(0, row.IndexOf(","));
                        }

                        if (Int32.TryParse(row, out Int32 r))
                        {
                            row = $"{r:0000}";
                        }
                    }

                    host.AddResult(Severity.Warning, true, $"line: {row} - {msg}", 1);
                }
            }

            if (!issues)
            {
                host.AddResult(Severity.Info, true, $"No warnings found."); // - {Utils.XMLComments[cs]}
            }

#warning warning MSB3061: Unable to delete file "C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\bin\Debug\RageAssetManager.dll". The process cannot access the file '<path>RageAssetManager.dll'

            return(ExitCode == 0);
        }
Пример #12
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            List <String> csprojs = new List <String>();

            String root = Path.GetDirectoryName(job.Parm);

            if (!root.EndsWith(@"\"))
            {
                root += @"\";
            }

            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Checking '{Path.GetFileNameWithoutExtension(job.Parm)}' Solution]");

            if (File.Exists(job.Parm))
            {
                Solution solution = new Solution();

                if (solution.Load(job.Parm))
                {
                    XDocument  nuspec = new XDocument(new XDeclaration("1.0", "utf-8", null));
                    XNamespace ns     = XNamespace.Get("http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd");

                    nuspec.Add(new XElement(ns + "package"));

                    XElement metadata     = new XElement(ns + "metadata");
                    XElement files        = new XElement(ns + "files");
                    XElement dependencies = new XElement(ns + "dependencies");

                    host.AddResult(Severity.Info, true, "Nuspec Metadata");

                    AddMetaData("id", Path.GetFileNameWithoutExtension(job.Parm), ns, metadata);
                    AddMetaData("licenseUrl", "https://www.gamecomponents.eu", ns, metadata);
                    AddMetaData("requireLicenseAcceptance", "true", ns, metadata);

                    //! Where to get the release notes/readme ?
                    AddMetaData("releaseNotes", "This is the initial release.", ns, metadata);
                    AddMetaData("tags", "RCSAA component.", ns, metadata);

                    foreach (SolutionProject project in solution.Projects)
                    {
                        if (project.ProjectTypeGuid.Equals(new Guid(Lookups.ProjectTypeGuids["C#"])))
                        {
                            String csproj = Path.Combine(Path.GetDirectoryName(solution.SolutionPath), project.RelativePath);

                            XmlNamespaceManager namespaces = new XmlNamespaceManager(new NameTable());

                            XDocument doc = XDocument.Load(csproj);

                            namespaces.AddNamespace("ns", doc.Root.GetDefaultNamespace().NamespaceName);

                            OutputType outputType = (OutputType)Enum.Parse(typeof(OutputType), doc.Root.XPathSelectElement("ns:PropertyGroup/ns:OutputType", namespaces).Value);

                            switch (outputType)
                            {
                            case OutputType.Library:
                                //! Skip Test Projects
                                String testProjectType = doc.Root.XPathSelectElement("ns:PropertyGroup/ns:TestProjectType", namespaces)?.Value;
                                if (String.IsNullOrEmpty(testProjectType))
                                {
                                    csprojs.Add(csproj);
                                }
                                break;

                            case OutputType.WinExe:
                                //! Skip Executable Projects
                                break;

                            case OutputType.Exe:
                                //! Skip Console Projects
                                break;
                            }
                        }
                    }

                    metadata.Add(dependencies);

                    Boolean VersionAdded = false;

#warning There should ideally be only 2 projects left.
#warning Version info on both should match as the 2nd properties file is linked.

                    foreach (String csproj in csprojs)
                    {
                        //! 0) Examine *.csproj files containing assemblies only.
                        //
                        XmlNamespaceManager namespaces = new XmlNamespaceManager(new NameTable());

                        XDocument doc = XDocument.Load(csproj);

                        namespaces.AddNamespace("ns", doc.Root.GetDefaultNamespace().NamespaceName);

                        // TODO <RootNamespace>AssetManagerPackage</RootNamespace>      - Check Naming Convention
                        // TODO <AssemblyName>RageAssetManager</AssemblyName>           - Check Naming Convention
                        // DONE <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>   - Check Value & Convert into nuspec format
                        // TODO <DefineConstants>TRACE;DEBUG</DefineConstants>          - Check Values (presence of PORTABLE).

                        OutputType outputType = (OutputType)Enum.Parse(typeof(OutputType), doc.Root.XPathSelectElement("ns:PropertyGroup/ns:OutputType", namespaces).Value);

                        XElement firstPropertyGroup = doc.Root.XPathSelectElement("ns:PropertyGroup", namespaces);

                        String nameSpace              = firstPropertyGroup.XPathSelectElement("ns:RootNamespace", namespaces)?.Value;
                        String assemblyName           = firstPropertyGroup.XPathSelectElement("ns:AssemblyName", namespaces)?.Value;
                        String targetFrameworkVersion = firstPropertyGroup.XPathSelectElement("ns:TargetFrameworkVersion", namespaces)?.Value;
                        String targetFrameworkProfile = firstPropertyGroup.XPathSelectElement("ns:TargetFrameworkProfile", namespaces)?.Value;
                        String defineConstants        = firstPropertyGroup.XPathSelectElement("ns:DefineConstants", namespaces)?.Value;

                        String projectDir = Path.GetDirectoryName(csproj);

                        switch (outputType)
                        {
                        case OutputType.Library:

                            //! 1) Examine PropertyGroups related to Debug/Release output.
                            //
                            foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:PropertyGroup", namespaces))
                            {
                                XAttribute condition = propertyGroup.Attribute("Condition");

                                if (condition != null)
                                {
                                    // TODO Decode this better (AnyCPU) for example.
                                    //
                                    String value = condition.Value;
                                    if (value.Equals($" '$(Configuration)|$(Platform)' == {Utils.release} "))
                                    {
                                        Debug.Print($"FOUND {Utils.release} Condition");

                                        // DONE <OutputPath>bin\Debug\</OutputPath>                         Check
                                        // DONE <DocumentationFile>bin\Debug\RageAssetManager.XML</DocumentationFile>   Check
                                        // TODO <DefineConstants>TRACE</DefineConstants>                    Check
                                        // DONE <DebugSymbols>true</DebugSymbols>                           Check
                                        // TODO <Prefer32Bit>false</Prefer32Bit>                            Check
                                        //
                                        String outputPath        = propertyGroup.XPathSelectElement("ns:OutputPath", namespaces)?.Value;
                                        String debugSymbols      = propertyGroup.XPathSelectElement("ns:DebugSymbols", namespaces)?.Value;
                                        String documentationFile = propertyGroup.XPathSelectElement("ns:DocumentationFile", namespaces)?.Value;

                                        String output = Path.GetFullPath(Path.Combine(projectDir, outputPath, Path.ChangeExtension(assemblyName, ".dll")));

                                        if (File.Exists(output))
                                        {
                                            Debug.Print($"{output}");
                                        }

                                        if (!VersionAdded)
                                        {
                                            //! Add version only once. Info is located in the assemblies (AssemblyInfo.cs file).
                                            //
                                            FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(Path.Combine(projectDir, output));

                                            AddMetaData("version", fvi.FileVersion, ns, metadata);
                                            AddMetaData("authors", fvi.CompanyName, ns, metadata);

                                            //! Get URL from .git?
                                            AddMetaData("projectUrl", "https://www.gamecomponents.eu", ns, metadata);
                                            AddMetaData("copyright", fvi.LegalCopyright, ns, metadata);
                                            AddMetaData("description", fvi.FileDescription, ns, metadata);

                                            VersionAdded = true;
                                        }

                                        //! Add Assembly to Platform Files.
                                        //
                                        String asmfile = Path.GetFullPath(Path.Combine(projectDir, output)).Replace(root, String.Empty);
                                        if (File.Exists(output))
                                        {
                                            files.Add(CreatePlatformFile(ns, targetFrameworkVersion, targetFrameworkProfile, asmfile));
                                        }

                                        //! Check and add Debug Symbols to Platform Files.
                                        //
                                        if (!String.IsNullOrEmpty(debugSymbols) && debugSymbols.Equals("true"))
                                        {
                                            String symbols = Path.ChangeExtension(asmfile, ".pdb");

                                            if (File.Exists(Path.ChangeExtension(output, ".pdb")))
                                            {
                                                files.Add(CreatePlatformFile(ns, targetFrameworkVersion, targetFrameworkProfile, symbols));
                                            }
                                        }

                                        //! Check and add Xml Documentation to Platform Files.
                                        //
                                        if (!String.IsNullOrEmpty(documentationFile))
                                        {
                                            String documentation = Path.Combine(projectDir, documentationFile);

                                            if (File.Exists(documentation))
                                            {
                                                files.Add(CreatePlatformFile(ns, targetFrameworkVersion, targetFrameworkProfile, documentation.Replace(root, "")));
                                            }
                                        }
                                    }
                                    else if (value.Equals($" '$(Configuration)|$(Platform)' == {Utils.debug} "))
                                    {
                                        Debug.Print($"FOUND {Utils.debug} Condition");
                                    }
                                }
                            }

                            //! 2) Examine ItemGroup related to References.
                            //
                            foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:ItemGroup", namespaces))
                            {
                                foreach (XElement reference in propertyGroup.XPathSelectElements(@"ns:Reference", namespaces))
                                {
                                    Debug.Print($"Reference: {Path.GetFullPath(Path.Combine(projectDir, reference.Value))}");
                                }
                            }

                            //! 3) Examine ItemGroup related to Sources.
                            //
                            foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:ItemGroup", namespaces))
                            {
                                foreach (XElement compile in propertyGroup.XPathSelectElements(@"ns:Compile", namespaces))
                                {
                                    //! Check for linked sources and omit them.
                                    //
                                    if (compile.XPathSelectElements(@"ns:Link", namespaces).Count() == 0)
                                    {
#warning Make sure the portable project is 100% linked else generate a warning/error.

                                        String source = Path.GetFullPath(Path.Combine(projectDir, compile.Attribute("Include").Value)).Replace(root, String.Empty);
                                        String rel    = Path.GetDirectoryName(compile.Attribute("Include").Value);

                                        Debug.Print($"Source: {source}");

                                        files.Add(CreateSourceFile(ns, source, rel));
                                    }
                                }
                            }

                            //! 4) Examine ItemGroup related toEmbedded Resources
                            //
                            foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:ItemGroup", namespaces))
                            {
                                foreach (XElement embeddedResource in propertyGroup.XPathSelectElements(@"ns:EmbeddedResource", namespaces))
                                {
                                    if (embeddedResource.XPathSelectElements(@"ns:Link", namespaces).Count() == 0)
                                    {
                                        String resource = Path.GetFullPath(Path.Combine(projectDir, embeddedResource.Attribute("Include").Value)).Replace(root, String.Empty);
                                        String rel      = Path.GetDirectoryName(embeddedResource.Attribute("Include").Value);

                                        Debug.Print($"EmbeddedResource: {resource}");

                                        files.Add(CreateSourceFile(ns, resource, rel));
                                    }
                                }
                            }
                            break;

                        case OutputType.WinExe:
                            break;

                        case OutputType.Exe:
                            break;
                        }

                        //! 5) Check package file
                        //
                        //<?xml version="1.0" encoding="utf-8"?>
                        //<packages>
                        //  <package id="NuGet.Build.Packaging" version="0.2.2" targetFramework="net35" developmentDependency="true" />
                        //</packages>

                        String packageConfig = Path.Combine(Path.GetDirectoryName(csproj), "packages.config");

                        if (File.Exists(packageConfig))
                        {
                            XDocument packages = XDocument.Load(packageConfig);

                            foreach (XElement package in packages.Root.XPathSelectElements(@"package"))
                            {
                                XElement dependency = new XElement(ns + "dependency");
                                dependency.SetAttributeValue("id", package.Attribute("id").Value);
                                dependency.SetAttributeValue("version", package.Attribute("version").Value);

                                dependencies.Add(dependency);
                            }
                        }
                    }

                    nuspec.Root.Add(metadata);
                    nuspec.Root.Add(files);

                    //! 6) Save nuspec file.
                    //
                    nuspec.Save(Path.ChangeExtension(job.Parm, ".nuspec"));

                    host.AddResult(Severity.Info, true, "Building Nuget package");

                    //! 7) Pack nuspec file into a nupkg file.
                    //
                    return(Utils.ExecutePsi(new ProcessStartInfo
                    {
                        WorkingDirectory = root,
                        Arguments = "pack " + Path.Combine(root, Path.ChangeExtension(job.Parm, ".nuspec")),
                        FileName = host.NuGetPath,
                        RedirectStandardOutput = true,
                        RedirectStandardError = true,
                        CreateNoWindow = true,
                        UseShellExecute = false,
                    }, host, out StringBuilder sb, true) == 0);
                }
                else
                {
                    host.AddResult(Severity.Info, false, $"Failed to Load or Parse Solution {job.Parm}.");
                }
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Solution.");
            }

            return(false);
        }
Пример #13
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The level. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;String,Boolean&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            if (!File.Exists(host.GitPath))
            {
                return(false);
            }

            //! Fix for temporarily supporting problematic repositories.
            //
            String repo = job.Parm.Replace(".GIT", ".git");

            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{repo}')");

            host.AddResult(Severity.Info, true, $"[Cloning '{Path.GetFileNameWithoutExtension(job.Parm)}' Repository]");

            host.AddResult(Severity.Info, true, $"Using: '{host.GitPath}'.");
            host.AddResult(Severity.Info, true, $"Git version is: '{DoGitVersion(Utils.workdir)}'.");

            if (DoGitClone(job, new Uri(repo), Utils.workdir))
            {
                host.AddResult(Severity.Info, true, $"[Cloning Submodules]");

                String topofrepo = Path.Combine(Utils.workdir, Path.GetFileNameWithoutExtension(new Uri(repo).Segments.Last()));

                //! git submodule update --init --recursive
                //
                Utils.ExecutePsi(new ProcessStartInfo
                {
                    WorkingDirectory       = topofrepo,
                    Arguments              = "submodule update --init --recursive",
                    FileName               = host.GitPath,
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    CreateNoWindow         = true,
                    UseShellExecute        = false,
                }, host, out StringBuilder sb1, true, true, Severity.Info);

                //! Cache Solutions as NuGet Restore may add new ones;
                List <String> solutions = Directory.EnumerateFiles(Utils.workdir, "*.sln", SearchOption.AllDirectories).ToList();

                host.AddResult(Severity.Info, true, $"Detected {solutions.Count} Solutions.");

                host.AddResult(Severity.Info, true, $"[Restoring NuGet Packages]");

                foreach (String solution in solutions)
                {
                    //! nuget restore OpenConsole.sln
                    //
                    Utils.ExecutePsi(new ProcessStartInfo
                    {
                        WorkingDirectory       = Path.GetDirectoryName(solution), //topofrepo,
                        Arguments              = "restore",                       // \"" + file + "\"",
                        FileName               = host.NuGetPath,
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                        CreateNoWindow         = true,
                        UseShellExecute        = false,
                    }, host, out StringBuilder sb2, true);
                }

                //! Search for Solutions files and add them to the queue.
                //
                foreach (String solution in solutions)
                {
                    if (solution.Contains(@"\packages\"))
                    {
                        continue;
                    }

#warning Performing a Test build after checkout fails on references to AssetManager.

                    //host.AddResult(Severity.Info, true, $"[Performing a Test build of '{Path.GetFileNameWithoutExtension(solution)}' Solution]");

                    //!  msbuild OpenConsole.sln
                    //
                    //!  NOTE: We do not log the output as it will be recompiled later again.
                    //Utils.ExecutePsi(new ProcessStartInfo
                    //{
                    //    WorkingDirectory = Utils.workdir,
                    //    Arguments = $"\"{solution}\"",
                    //    FileName = host.MSBuildPath,
                    //    RedirectStandardOutput = true,
                    //    RedirectStandardError = true,
                    //    CreateNoWindow = true,
                    //    UseShellExecute = false,
                    //}, host, out StringBuilder sb3, false);

                    host.RecurseForTypes(job, FileType.Solution, solution);
                }

                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Decompiles.
        /// </summary>
        ///
        /// <param name="parm"> The parameter. </param>
        public void Decompile(String parm)
        {
            //Dictionary<String, String> code = new Dictionary<String, String>();

            host.AddResult(Severity.Info, true, $"[Decompiling '{Path.GetFileNameWithoutExtension(parm)}' Assembly to Check for Dead Code]");

            Version runtimeVersion = Utils.RuntimeVersion(parm);

            //! Asset Compiled with: .Net v2.0.50727
            //! Asset Compiled with: .Net v4.0.30319
            //
            Debug.WriteLine($"Asset Compiled with: .Net {runtimeVersion}");

            Boolean isAsset         = Utils.IsAsset(parm);
            Boolean isPortableAsset = Utils.IsPortableAsset(parm);

#warning Also check runtime version/.net profile here.

            Debug.WriteLine($"Is Asset: {isAsset}");
            Debug.WriteLine($"Is Portable Asset: {isPortableAsset}");


            //{
            CSharpDecompiler decompilerA = new CSharpDecompiler(parm, new DecompilerSettings()
            {
                AlwaysUseBraces     = true,
                LoadInMemory        = true,
                AutomaticProperties = false,
                RemoveDeadCode      = false,
            });

            String decompA = decompilerA.DecompileWholeModuleAsString();

            CSharpDecompiler decompilerB = new CSharpDecompiler(parm, new DecompilerSettings()
            {
                AlwaysUseBraces     = true,
                LoadInMemory        = true,
                AutomaticProperties = false,
                RemoveDeadCode      = true,
            });

            String decompB = decompilerB.DecompileWholeModuleAsString();

            //! Examples of failures that are NOT dead code (so we always will have some false alerts).
            //
            //!A:
            //          NodePath nodePath = new NodePath();
            //          nodePath = root.ToNodeStructure();
            //          string text = serializer.Serialize(nodePath, format);
            //!B:
            //          new NodePath();
            //          NodePath obj = root.ToNodeStructure();
            //          string text = serializer.Serialize(obj, format);

            //!A:		string empty = string.Empty;
            //			...
            //			empty = serializer.Serialize(nodeValue, format);
            //          if (jsonValue.IsMatch(empty))
            //
            //!B:		string empty = string.Empty;
            //			...
            //		    string text = serializer.Serialize(nodeValue, format);
            //          if (jsonValue.IsMatch(text))

            if (decompA != decompB)
            {
                host.AddResult(Severity.Warning, true, $"Possible Dead Code Detected in '{decompilerB.TypeSystem.MainModule.AssemblyName}'.");

                Debug.WriteLine(Utils.Diff(decompA, decompB));
            }
            else
            {
                host.AddResult(Severity.Info, true, $"No Dead Code Detected in '{decompilerB.TypeSystem.MainModule.AssemblyName}'.");
            }
        }
Пример #15
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{job.GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Decompiling '{Path.GetFileNameWithoutExtension(job.Parm)}' Assembly]");

            if (File.Exists(job.Parm))
            {
                //! See https://stackoverflow.com/questions/658498/how-to-load-an-assembly-to-appdomain-with-all-references-recursively
                //! See https://github.com/jduv/AppDomainToolkit
                //
                //! See https://msdn.microsoft.com/en-us/library/7hcs6az6(v=vs.110).aspx (this actually works as it does not lock Assemblies).
                //
                String dir = Path.GetDirectoryName(job.Parm);

                if (AppDomain.CurrentDomain.IsDefaultAppDomain())
                {
                    //Utils.SetDllDirectory(Path.GetDirectoryName(dir));

                    //ads = AppDomain.CurrentDomain.SetupInformation;
                    //ads.PrivateBinPath = dir;

                    //ad = AppDomain.CreateDomain("Mine", AppDomain.CurrentDomain.Evidence, ads);
                    //ad.Load(GetType().Assembly.FullName);
                    //ad.AssemblyLoad += Ad_AssemblyLoad;
                    //AppDomain.CurrentDomain.SetupInformation.SetPrivateBinPath(dir);

                    ad = AppDomain.CurrentDomain;

                    //if (parm.Contains("_Portable"))
                    //{
                    //    asm1 = AppDomain.CurrentDomain.Load(Utils.LoadFile(
                    //        Path.Combine(Path.GetDirectoryName(parm), "RageAssetManager_Portable.dll")
                    //        ));
                    //}
                    //

                    //! Fails 2nd time.
                    //Assembly x = Assembly.ReflectionOnlyLoad(Utils.LoadFile(parm));
                    //
                    //var asm = ad.CreateInstanceFrom(parm, "BaseAsset");

                    ad.Load(Utils.LoadFile(@"C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\bin\Debug\RageAssetManager.dll"));
                    ad.Load(Utils.LoadFile(@"C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset_Portable\bin\Debug\RageAssetManager_Portable.dll"));

                    Assembly asm = ad.Load(Utils.LoadFile(job.Parm));

                    host.AddResult(Severity.Info, true, $"Loaded Assembly.");

                    host.AddResult(Severity.Info, true, $"Assembly using .Net {asm.ImageRuntimeVersion}.");

                    Debug.Print(asm.Location);

                    Type ba  = null;
                    Type bas = null;

                    //var a = AppDomain.CurrentDomain.CreateInstance(parm, "BaseAsset");

                    //! For portable assembly ExportedTypes fails with:
                    //! Could not load type 'AssetPackage.BaseAsset' from assembly 'GameStorageClientAsset_Portable
                    //! Might be that base asset is identical between the .Net 3.5 & .Net Portable assemblies.
                    //
                    //
                    foreach (Type t in asm.ExportedTypes)
                    {
                        if (t.BaseType != null && t.BaseType.Name.Equals(typeof(BaseAsset).Name))
                        {
                            ba = t;
                        }
                        if (t.BaseType != null && t.BaseType.Name.Equals(typeof(BaseSettings).Name))
                        {
                            bas = t;
                        }

                        //GameStorageClientAsset,BaseAsset
                        //GameStorageClientAssetSettings, BaseSettings

                        //Debug.Print($"{t.Name},{(t.BaseType != null ? t.BaseType.Name : "<none>")}");
                    }

                    //Utils.SetDllDirectory(Path.GetDirectoryName(null));

                    BaseAsset asset    = (BaseAsset)Activator.CreateInstance(ba);
                    ISettings Settings = asset.Settings;

                    MethodInfo mi = asset.GetType().GetRuntimeMethod("CheckHealth", new Type[] { });

                    //! Fails as AssetManager is to old (no AssetPackage.RequestSetttings.hasBinaryResponse field).
                    //
                    //Debug.Print($"CheckHealth: {mi.Invoke(asset, new object[] { })})");
                }
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Assembly.");

                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            if (File.Exists(job.Parm))
            {
                host.AddResult(Severity.Info, true, $"[Examining Projects in '{Path.GetFileNameWithoutExtension(job.Parm)}' Solution]");

                Solution solution = new Solution();

                if (solution.Load(job.Parm))
                {
                    host.AddResult(Severity.Info, true, $"Loaded and Parsed Solution.");
                    host.AddResult(Severity.Info, true, $"Processing {solution.Projects.Count} Project Entries in Solution.", 1);

                    Int32 cnt     = 0;
                    Int32 skipped = 0;

                    foreach (SolutionProject project in solution.Projects)
                    {
                        String csproj = Path.Combine(Path.GetDirectoryName(solution.SolutionPath), project.RelativePath);

                        if (project.ProjectTypeGuid.Equals(new Guid(Lookups.ProjectTypeGuids["C#"])))
                        {
                            if (File.Exists(csproj))
                            {
#warning RCSAA Specific Code (Skip AssetManager from processing if present).

                                if (Utils.IsAssetManager(csproj, host))
                                {
                                    host.AddResult(Severity.Warning, false, $"{Path.GetFileName(csproj)} Project skipped.", 1);
                                    skipped++;
                                }
                                else if (Utils.IsUnitTest(csproj))
                                {
                                    Utils.FixupAssemblyReferences(host, csproj);

                                    //! Quickly Build the output as this is an enumerating plugin.
                                    //! We need assemblies for IsAsset() alike methods.
                                    //
                                    if (Utils.Build(csproj, host))
                                    {
                                        String output = Utils.ProjectOutput(csproj);

                                        //! For UnitTests we skip other FileType.Project related checks as they
                                        //! lead to failure to build due to locked RAGE assemblies.
                                        //
                                        host.RecurseForTypes(job, FileType.UnitTest, output);
                                    }
                                    else
                                    {
                                        skipped++;
                                    }

                                    cnt++;
                                }
                                else
                                {
                                    Utils.FixupAssemblyReferences(host, csproj);

                                    //! Quickly Build the output as this is an enumerating plugin.
                                    //! We need assemblies for IsAsset() alike methods.
                                    //
                                    if (Utils.Build(csproj, host))
                                    {
                                        //! Recurse for FileType.Project.
                                        //
                                        host.RecurseForTypes(job, FileType.Project, csproj);
                                    }
                                    else
                                    {
                                        skipped++;
                                    }

                                    cnt++;
                                }
                            }
                            else
                            {
                                host.AddResult(Severity.Warning, false, $"{Path.GetFileName(csproj)} Project not found.", 1);
                            }
                        }
                        else
                        {
                            host.AddResult(Severity.Warning, false, $"{Path.GetFileName(csproj)} Non C# project skipped.", 1);
                            skipped++;
                        }
                    }

                    host.AddResult(Severity.Info, solution.Projects.Count != 0, $"{solution.Projects.Count} Projects Detected.", 1);
                    if (skipped != 0)
                    {
                        host.AddResult(Severity.Warning, solution.Projects.Count != 0, $"{skipped} Projects Skipped.", 1);
                    }

                    if (solution.Projects.Count == cnt + skipped)
                    {
                        host.AddResult(Severity.Info, solution.Projects.Count == cnt + skipped, $"All Projects Located.");
                    }
                    else
                    {
                        host.AddResult(Severity.Warning, solution.Projects.Count == cnt + skipped, $"Some Projects could not be Located.");
                    }
                }
                else
                {
                    host.AddResult(Severity.Info, false, $"Failed to Load or Parse Solution {job.Parm}.");

                    return(false);
                }
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate Solution.");

                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <remarks>
        /// Detects if the solution contains a correctly named asset pair (normal/portable) and the
        /// PORTABLE define is correctly set.
        /// </remarks>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Examining '{Path.GetFileNameWithoutExtension(job.Parm)}' Project]");

            //! UnitTest Assemblies can/will return a false positive if they test an Asset.
            //
            if (!Utils.IsUnitTest(job.Parm))
            {
                String output = Utils.ProjectOutput(job.Parm);

                if (File.Exists(output))
                {
                    //! Non Portable Assets, no PORTABLE Symbol.
                    //
                    if (Utils.IsAsset(output))
                    {
                        if (Utils.DefinedSymbols(job.Parm).Contains(Utils.portable))
                        {
                            host.AddResult(Severity.Error, true, $"'{Utils.portable}' conditional compilation symbol is defined for Normal Asset.");
                        }
                        else
                        {
                            return(true);
                        }
                    }

                    //! Portable Assets, PORTABLE Symbol.
                    //
                    if (Utils.IsPortableAsset(output))
                    {
                        if (!Utils.DefinedSymbols(job.Parm).Contains(Utils.portable))
                        {
                            host.AddResult(Severity.Warning, true, $"'{Utils.portable}' conditional compilation symbol is not defined for Portable Asset.");
                        }
                        else
                        {
                            return(true);
                        }
                    }

                    //! .Net Core Assets, PORTABLE Symbol.
                    //
                    if (Utils.IsCoreAsset(output))
                    {
                        if (!Utils.DefinedSymbols(job.Parm).Contains(Utils.portable))
                        {
                            host.AddResult(Severity.Warning, true, $"'{Utils.portable}' conditional compilation symbol is not defined for .Net Core Asset.");
                        }
                        else
                        {
                            return(true);
                        }
                    }
                }
                else
                {
                    if (String.IsNullOrEmpty(output))
                    {
                        host.AddResult(Severity.Warning, false, $"Failed to Locate Output.");
                    }
                    else
                    {
                        host.AddResult(Severity.Warning, false, $"Failed to Load Output: '{Path.GetFileName(output)}'.");
                    }
                }

#warning Check as well symbol is used in properties.cs to enable shared compilation.
            }

            return(false);
        }
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;String,Boolean&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Examining '{Path.GetFileNameWithoutExtension(job.Parm)}' Project's Xml Documentation]");

            /// <remark>
            /// uses System.Diagnostics.CodeAnalysis;
            ///
            /// and the attribute:
            ///
            /// [SuppressMessage("Microsoft.Usage", "CS1591")]
            ///
            /// works much like:
            ///
            /// #pragma warning disable 1591
            /// #pragma warning restore 1591
            /// </remark>

            if (Utils.Load(job.Parm, out XDocument doc, out XmlNamespaceManager namespaces))
            {
                foreach (XElement propertyGroup in doc.Root.XPathSelectElements(@"ns:PropertyGroup", namespaces))
                {
                    XAttribute condition = propertyGroup.Attribute("Condition");

                    if (condition != null)
                    {
                        String value = condition.Value;
                        if (value.Equals($" '$(Configuration)|$(Platform)' == {Utils.debug} "))
                        {
                            host.AddResult(Severity.Info, true, $"Found {Utils.debug} Condition.");

                            String xml = propertyGroup.XPathSelectElement("ns:DocumentationFile", namespaces)?.Value;

                            if (!String.IsNullOrEmpty(xml))
                            {
                                String documentationFile = Path.Combine(Path.GetDirectoryName(job.Parm), xml);

                                if (!String.IsNullOrEmpty(documentationFile) && File.Exists(documentationFile))
                                {
                                    host.AddResult(Severity.Info, true, $"Found '{Path.GetFileName(documentationFile)}' XML Documentation.");
                                }
                                else
                                {
                                    host.AddResult(Severity.Warning, true, $"'Missing XML Documentation.");
                                }
                            }

                            break;
                        }
                    }
                }
            }

            Directory.SetCurrentDirectory(Path.GetDirectoryName(job.Parm));

            Int32 ExitCode = Utils.ExecutePsi(new ProcessStartInfo
            {
                WorkingDirectory       = Path.GetDirectoryName(job.Parm),
                Arguments              = $"{Path.GetFileName(job.Parm)} /target:Clean;Build",
                FileName               = host.MSBuildPath,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                CreateNoWindow         = true,
                UseShellExecute        = false,
            }, host, out StringBuilder sb, false, true);

            //! Print only the lines with one of the Xml Documentation Code Style codes in it.
            //
            //GameStorageClientAsset.cs(64,65): warning CS1570: XML comment has badly formed XML -- 'An identifier was expected.' [C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\GameStorageClientAsset.csproj]
            //ISerializer.cs(77,26): warning CS1572: XML comment has a param tag for 'type', but there is no parameter by that name [C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\GameStorageClientAsset.csproj]
            //Node.cs(178,58): warning CS1573: Parameter 'purpose' has no matching param tag in the XML comment for 'Node.Node(GameStorageClientAsset, string, StorageLocations)' (but other parameters do) [C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\GameStorageClientAsset.csproj]
            //NodeValue.cs(29,5): warning CS1587: XML comment is not placed on a valid language element [C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\GameStorageClientAsset.csproj]
            //NodeValue.cs(33,18): warning CS1591: Missing XML comment for publicly visible type or member 'NodePaths' [C:\Temp\NuGet\ClientSideGameStorageAsset\GameStorageClientAsset\GameStorageClientAsset.csproj]

            //! CS codes to include.
            //
            List <String> csCodes = Utils.XMLComments.Keys.ToList();

            //! Select Warnings.
            //
            List <String> lines = sb.ToString()
                                  .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
                                  .Where(q => q.Contains("warning CS"))
                                  //.Where((s, b) => csCodes.IndexOf(s) != -1)
                                  .ToList();

            //! Remove project name.
            //! Change full filename to filename only.
            //
            for (Int32 i = 0; i < lines.Count; i++)
            {
                Match m1 = Utils.rg_project.Match(lines[i]);
                Match m2 = Utils.rg_file.Match(lines[i]);

                if (m1.Success && m2.Success)
                {
                    lines[i] = lines[i].Replace(m1.Value, String.Empty).Trim();
                    lines[i] = lines[i].Replace(m2.Groups[1].Value, $"{Path.GetFileName(m2.Groups[1].Value)}");
                }
            }

            //! Compress data by taking distinct items and group on the filename.
            //
            IEnumerable <IGrouping <String, String> > warnings = lines
                                                                 .Distinct()
                                                                 .GroupBy(q => Path.GetFileName(Utils.rg_file.Match(q).Value.Trim('(')));

            Boolean issues = false;

            //! Group output per file and per CS code.
            //
            foreach (IGrouping <String, String> item in warnings)
            {
                foreach (String cs in csCodes)
                {
                    if (item.Count(r => r.Contains(cs)) == 0)
                    {
                        continue;
                    }

                    issues = true;

                    host.AddResult(Severity.Info, true, $"{cs} - {item.Key}"); // - {Utils.XMLComments[cs]}

                    foreach (String line in item)
                    {
                        if (line.Contains(cs))
                        {
                            String msg = line.Substring(line.IndexOf($"{cs}: ") + $"{cs}: ".Length);

                            //msg = msg.Substring(0, msg.IndexOf("[")).Trim();

                            if (msg.Contains(" -- "))
                            {
                                msg = msg.Substring(msg.IndexOf(" -- ") + " -- ".Length);
                                msg = msg.Trim(new Char[] { '\'' });
                            }

                            String row = line.Substring(line.IndexOf("(") + 1);
                            row = row.Substring(0, row.IndexOf(","));
                            if (Int32.TryParse(row, out Int32 r))
                            {
                                row = $"{r:0000}";
                            }

                            host.AddResult(Severity.Warning, true, $"line: {row} - {msg}", 1);
                        }
                    }
                }
            }

            if (!issues)
            {
                host.AddResult(Severity.Info, true, $"No XML Documentation issues found."); // - {Utils.XMLComments[cs]}
            }

            return(ExitCode == 0);
        }