Skip to content

Commit

Permalink
Load projects one by one. Allows filtering on projects.
Browse files Browse the repository at this point in the history
  • Loading branch information
Octokit committed Aug 11, 2022
1 parent 08738df commit 27ea8ad
Showing 1 changed file with 67 additions and 20 deletions.
87 changes: 67 additions & 20 deletions SecurityCodeScan.Tool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ void SplitBy<T>(bool toUpper, char separator, ICollection<T> outContainer, strin

internal class Program
{
private delegate bool TryGetAbsoluteSolutionPath(string path, string baseDirectory, int reportingMode, out string? absolutePath);

private static TryGetAbsoluteSolutionPath? tryGetAbsoluteSolutionPath;

private static async Task<int> Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
Expand Down Expand Up @@ -273,10 +277,46 @@ private static async Task<int> Main(string[] args)
MSBuildLocator.RegisterInstance(instance);
}

var msCodeAnalyisBuildAssembly = typeof(MSBuildProjectLoader).Assembly;
var msBuildAssembly = Assembly.Load("Microsoft.Build");

var pathResolverType = msCodeAnalyisBuildAssembly.GetType("Microsoft.CodeAnalysis.MSBuild.PathResolver");
ConstructorInfo pathResolverCtor = pathResolverType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public,
null,
new[] { msCodeAnalyisBuildAssembly.GetType("Microsoft.CodeAnalysis.MSBuild.DiagnosticReporter") },
null);
var reporterField = typeof(MSBuildWorkspace).GetField("_reporter", BindingFlags.Instance | BindingFlags.NonPublic);
var solutionFileType = msBuildAssembly.GetType("Microsoft.Build.Construction.SolutionFile");
var constructionParseMethod = solutionFileType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public);
var pathResolverTryGetAbsoluteSolutionPathMethod = pathResolverType.GetMethod("TryGetAbsoluteSolutionPath", BindingFlags.Public | BindingFlags.Instance);
var projectsInOrderProperty = solutionFileType.GetProperty("ProjectsInOrder", BindingFlags.Instance | BindingFlags.Public);
var projectInSolutionType = msBuildAssembly.GetType("Microsoft.Build.Construction.ProjectInSolution");
var absolutePathProperty = projectInSolutionType.GetProperty("AbsolutePath", BindingFlags.Instance | BindingFlags.Public);

var properties = new Dictionary<string, string>() { { "AdditionalFileItemNames", "$(AdditionalFileItemNames);Content" } };

using (var workspace = MSBuildWorkspace.Create(properties))
{
var pathResolver = pathResolverCtor.Invoke( new object[] { reporterField.GetValue(workspace) });
tryGetAbsoluteSolutionPath = (TryGetAbsoluteSolutionPath)Delegate.CreateDelegate(
typeof(TryGetAbsoluteSolutionPath),
pathResolver,
pathResolverTryGetAbsoluteSolutionPathMethod);
if (tryGetAbsoluteSolutionPath == null)
return -1;

Console.WriteLine($"Loading solution '{parsedOptions.solutionPath}'");
if (!tryGetAbsoluteSolutionPath(parsedOptions.solutionPath, Directory.GetCurrentDirectory(), 0, out var absoluteSolutionPath))
{
// TryGetAbsoluteSolutionPath should throw before we get here.
return -1;
}

var parsedSolution = constructionParseMethod.Invoke(null, new object[] { absoluteSolutionPath });
var projectsInOrder = (IReadOnlyList<object>)projectsInOrderProperty.GetValue(parsedSolution);
var projectPaths = projectsInOrder.Select(x => (string)absolutePathProperty.GetValue(x));

// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) =>
{
Expand All @@ -291,17 +331,38 @@ private static async Task<int> Main(string[] args)
returnCode = 2;
};

Console.WriteLine($"Loading solution '{parsedOptions.solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(parsedOptions.solutionPath, new ConsoleProgressReporter(parsedOptions.verbose)).ConfigureAwait(false);
var solutionDirectory = Path.GetDirectoryName(absoluteSolutionPath) + Path.DirectorySeparatorChar;
List<Project> projects = new List<Project>(projectPaths.Count());
foreach (var projectPath in projectPaths)
{
if (projectPath.EndsWith(".shproj") || projectPath.EndsWith(".sqlproj") || projectPath.EndsWith(".fsproj"))
{
Console.WriteLine($"Skipped: {projectPath} excluded from analysis");
continue;
}

var path = projectPath;
if (projectPath.StartsWith(solutionDirectory))
path = projectPath.Remove(0, solutionDirectory.Length);

if ((parsedOptions.includeProjects.Any() && !parsedOptions.includeProjects.Any(x => x.IsMatch(path))) ||
parsedOptions.excludeProjects.Any(x => x.IsMatch(path)))
{
Console.WriteLine($"Skipped: {projectPath} excluded from analysis");
continue;
}

// Attach progress reporter so we print projects as they are loaded.
projects.Add(await workspace.OpenProjectAsync(projectPath, new ConsoleProgressReporter(parsedOptions.verbose)).ConfigureAwait(false));
}
Console.WriteLine($"Finished loading solution '{parsedOptions.solutionPath}'");
if (returnCode != 0)
return returnCode;

var analyzers = new List<DiagnosticAnalyzer>();
LoadAnalyzers(parsedOptions, analyzers);

var count = await GetDiagnostics(parsedOptions, versionString, solution, analyzers).ConfigureAwait(false);
var count = await GetDiagnostics(parsedOptions, versionString, projects, analyzers).ConfigureAwait(false);

var elapsed = DateTime.Now - startTime;
Console.WriteLine($@"Completed in {elapsed:hh\:mm\:ss}");
Expand Down Expand Up @@ -332,7 +393,7 @@ private static void LogError(bool error, string msg)
private static async Task<int> GetDiagnostics(
ParsedOptions parsedOptions,
string versionString,
Solution solution,
IEnumerable<Project> projects,
List<DiagnosticAnalyzer> analyzers)
{
Stream stream = null;
Expand Down Expand Up @@ -367,22 +428,8 @@ private static void LogError(bool error, string msg)
runner = new SingleThreadRunner(parsedOptions.verbose, analyzers, LogDiagnostics, parsedOptions, descriptors, logger);
}

var solutionPath = Path.GetDirectoryName(solution.FilePath) + Path.DirectorySeparatorChar;
foreach (var project in solution.Projects)
foreach (var project in projects)
{
var projectPath = project.FilePath;
if (projectPath.StartsWith(solutionPath))
projectPath = projectPath.Remove(0, solutionPath.Length);



if ((parsedOptions.includeProjects.Any() && !parsedOptions.includeProjects.Any(x => x.IsMatch(projectPath))) ||
parsedOptions.excludeProjects.Any(x => x.IsMatch(projectPath)))
{
Console.WriteLine($"Skipped: {project.FilePath} excluded from analysis");
continue;
}

await runner.Run(project).ConfigureAwait(false);
}

Expand Down

0 comments on commit 27ea8ad

Please sign in to comment.