C#: Minimal update of relevant code to minimize project dependencies and hide some implementation details behind interfaces.

This commit is contained in:
Michael Nebel
2023-08-18 10:43:58 +02:00
parent f47e59dff1
commit c0d1179c8a
23 changed files with 181 additions and 147 deletions

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System;
using System.Linq;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Manages the set of assemblies.

View File

@@ -6,7 +6,7 @@ using System.Reflection;
using System.Security.Cryptography;
using System.Text;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Stores information about an assembly file (DLL).

View File

@@ -1,6 +1,6 @@
using System;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
public class AssemblyLoadException : Exception { }
}

View File

@@ -1,20 +1,20 @@
using Semmle.Util;
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Semmle.Extraction.CSharp.Standalone;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Text;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Main implementation of the build analysis.
/// </summary>
internal sealed class BuildAnalysis : IDisposable
public sealed class DependencyManager : IDisposable
{
private readonly AssemblyCache assemblyCache;
private readonly ProgressMonitor progressMonitor;
@@ -25,7 +25,7 @@ namespace Semmle.BuildAnalyser
private int succeededProjects;
private readonly string[] allSources;
private int conflictedReferences = 0;
private readonly Options options;
private readonly IDependencyOptions options;
private readonly DirectoryInfo sourceDir;
private readonly DotNet dotnet;
private readonly FileContent fileContent;
@@ -33,17 +33,17 @@ namespace Semmle.BuildAnalyser
/// <summary>
/// Performs a C# build analysis.
/// Performs a C# dependency fetching.
/// </summary>
/// <param name="options">Analysis options from the command line.</param>
/// <param name="progressMonitor">Display of analysis progress.</param>
public BuildAnalysis(Options options, ProgressMonitor progressMonitor)
/// <param name="options">Dependency fetching options</param>
/// <param name="logger">Logger for dependency fetching progress.</param>
public DependencyManager(string srcDir, IDependencyOptions options, ILogger logger)
{
var startTime = DateTime.Now;
this.options = options;
this.progressMonitor = progressMonitor;
this.sourceDir = new DirectoryInfo(options.SrcDir);
this.progressMonitor = new ProgressMonitor(logger);
this.sourceDir = new DirectoryInfo(srcDir);
try
{
@@ -55,7 +55,7 @@ namespace Semmle.BuildAnalyser
throw;
}
this.progressMonitor.FindingFiles(options.SrcDir);
this.progressMonitor.FindingFiles(srcDir);
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
@@ -285,7 +285,7 @@ namespace Semmle.BuildAnalyser
try
{
var csProj = new Extraction.CSharp.CsProjFile(project);
var csProj = new CsProjFile(project);
foreach (var @ref in csProj.References)
{
@@ -329,7 +329,6 @@ namespace Semmle.BuildAnalyser
}
}
private void DownloadMissingPackages()
{
var nugetConfigs = GetFiles("nuget.config", recurseSubdirectories: true).ToArray();

View File

@@ -0,0 +1,73 @@
using System.Linq;
using System.Collections.Generic;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Dependency fetching related options.
/// </summary>
public interface IDependencyOptions
{
/// <summary>
/// Directories to search DLLs in.
/// </summary>
IList<string> DllDirs { get; }
/// <summary>
/// Files/patterns to exclude.
/// </summary>
IList<string> Excludes { get; }
/// <summary>
/// Whether to analyse NuGet packages.
/// </summary>
bool UseNuGet { get; }
/// <summary>
/// The solution file to analyse, or null if not specified.
/// </summary>
string? SolutionFile { get; }
/// <summary>
/// Whether to use the packaged dotnet runtime.
/// </summary>
bool UseSelfContainedDotnet { get; }
/// <summary>
/// Whether to search the .Net framework directory.
/// </summary>
bool ScanNetFrameworkDlls { get; }
/// <summary>
/// Whether to use mscorlib as a reference.
/// </summary>
bool UseMscorlib { get; }
/// <summary>
/// Determine whether the given path should be excluded.
/// </summary>
/// <param name="path">The path to query.</param>
/// <returns>True iff the path matches an exclusion.</returns>
bool ExcludesFile(string path);
}
public class DependencyOptions : IDependencyOptions
{
public IList<string> DllDirs { get; set; } = new List<string>();
public IList<string> Excludes { get; set; } = new List<string>();
public bool UseNuGet { get; set; } = true;
public string? SolutionFile { get; set; }
public bool UseSelfContainedDotnet { get; set; } = false;
public bool ScanNetFrameworkDlls { get; set; } = true;
public bool UseMscorlib { get; set; } = true;
public bool ExcludesFile(string path) =>
Excludes.Any(path.Contains);
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using Semmle.Util;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal interface IDotNet
{

View File

@@ -1,11 +1,11 @@
using Semmle.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Semmle.Util;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
// <summary>
@@ -145,22 +145,22 @@ namespace Semmle.BuildAnalyser
[GeneratedRegex("<(.*\\s)?Project.*\\sSdk=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex ProjectSdk();
}
}
internal interface IUnsafeFileReader
{
IEnumerable<string> ReadLines(string file);
}
internal class UnsafeFileReader : IUnsafeFileReader
{
public IEnumerable<string> ReadLines(string file)
internal interface IUnsafeFileReader
{
using var sr = new StreamReader(file);
string? line;
while ((line = sr.ReadLine()) != null)
IEnumerable<string> ReadLines(string file);
}
internal class UnsafeFileReader : IUnsafeFileReader
{
public IEnumerable<string> ReadLines(string file)
{
yield return line;
using var sr = new StreamReader(file);
string? line;
while ((line = sr.ReadLine()) != null)
{
yield return line;
}
}
}
}

View File

@@ -1,11 +1,11 @@
using Semmle.Util;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Semmle.Util;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Manage the downloading of NuGet packages.

View File

@@ -1,7 +1,7 @@
using Semmle.Util.Logging;
using System;
using System;
using Semmle.Util.Logging;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal class ProgressMonitor
{

View File

@@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -21,6 +22,9 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8e902d1e-f639-4f9f-a6d2-71e8ade7c5a3")]
// Expose internals for testing purposes.
[assembly: InternalsVisibleTo("Semmle.Extraction.Tests")]
// Version information for an assembly consists of the following four values:
//
// Major Version

View File

@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Semmle.BuildAnalyser;
using Semmle.Util;
namespace Semmle.Extraction.CSharp.Standalone
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Locates .NET Runtimes.

View File

@@ -5,10 +5,16 @@
<AssemblyName>Semmle.Extraction.CSharp.DependencyFetching</AssemblyName>
<RootNamespace>Semmle.Extraction.CSharp.DependencyFetching</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
<ProjectReference Include="..\Semmle.Extraction\Semmle.Extraction.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>

View File

@@ -1,9 +1,9 @@
using Microsoft.Build.Construction;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Construction;
namespace Semmle.BuildAnalyser
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Access data in a .sln file.

View File

@@ -1,8 +1,7 @@
using Semmle.Util.Logging;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Semmle.Util;
using Semmle.Util.Logging;
using Semmle.Extraction.CSharp.DependencyFetching;
namespace Semmle.Extraction.CSharp.Standalone
{
@@ -25,19 +24,19 @@ namespace Semmle.Extraction.CSharp.Standalone
SkipExtraction = value;
return true;
case "skip-nuget":
UseNuGet = !value;
dependencies.UseNuGet = !value;
return true;
case "all-references":
AnalyseCsProjFiles = !value;
return true;
case "stdlib":
UseMscorlib = value;
dependencies.UseMscorlib = value;
return true;
case "skip-dotnet":
ScanNetFrameworkDlls = !value;
dependencies.ScanNetFrameworkDlls = !value;
return true;
case "self-contained-dotnet":
UseSelfContainedDotnet = value;
dependencies.UseSelfContainedDotnet = value;
return true;
default:
return base.HandleFlag(key, value);
@@ -49,10 +48,10 @@ namespace Semmle.Extraction.CSharp.Standalone
switch (key)
{
case "exclude":
Excludes.Add(value);
dependencies.Excludes.Add(value);
return true;
case "references":
DllDirs.Add(value);
dependencies.DllDirs.Add(value);
return true;
default:
return base.HandleOption(key, value);
@@ -61,8 +60,8 @@ namespace Semmle.Extraction.CSharp.Standalone
public override bool HandleArgument(string arg)
{
SolutionFile = arg;
var fi = new FileInfo(SolutionFile);
dependencies.SolutionFile = arg;
var fi = new FileInfo(dependencies.SolutionFile);
if (!fi.Exists)
{
System.Console.WriteLine("Error: The solution {0} does not exist", fi.FullName);
@@ -77,47 +76,22 @@ namespace Semmle.Extraction.CSharp.Standalone
Errors = true;
}
/// <summary>
/// Files/patterns to exclude.
/// </summary>
public IList<string> Excludes { get; } = new List<string>();
/// <summary>
/// The directory containing the source code;
/// </summary>
public string SrcDir { get; } = System.IO.Directory.GetCurrentDirectory();
private readonly DependencyOptions dependencies = new DependencyOptions();
/// <summary>
/// Whether to analyse NuGet packages.
/// Dependency fetching related options.
/// </summary>
public bool UseNuGet { get; private set; } = true;
/// <summary>
/// Directories to search DLLs in.
/// </summary>
public IList<string> DllDirs { get; } = new List<string>();
/// <summary>
/// Whether to search the .Net framework directory.
/// </summary>
public bool ScanNetFrameworkDlls { get; private set; } = true;
/// <summary>
/// Whether to use mscorlib as a reference.
/// </summary>
public bool UseMscorlib { get; private set; } = true;
public IDependencyOptions Dependencies => dependencies;
/// <summary>
/// Whether to search .csproj files.
/// </summary>
public bool AnalyseCsProjFiles { get; private set; } = true;
/// <summary>
/// The solution file to analyse, or null if not specified.
/// </summary>
public string? SolutionFile { get; private set; }
/// <summary>
/// Whether the extraction phase should be skipped (dry-run).
/// </summary>
@@ -133,21 +107,6 @@ namespace Semmle.Extraction.CSharp.Standalone
/// </summary>
public bool Help { get; private set; } = false;
/// <summary>
/// Whether to use the packaged dotnet runtime.
/// </summary>
public bool UseSelfContainedDotnet { get; private set; } = false;
/// <summary>
/// Determine whether the given path should be excluded.
/// </summary>
/// <param name="path">The path to query.</param>
/// <returns>True iff the path matches an exclusion.</returns>
public bool ExcludesFile(string path)
{
return Excludes.Any(ex => path.Contains(ex));
}
/// <summary>
/// Outputs the command line options to the console.
/// </summary>

View File

@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Semmle.BuildAnalyser;
using Semmle.Util.Logging;
using Semmle.Extraction.CSharp.DependencyFetching;
namespace Semmle.Extraction.CSharp.Standalone
{
@@ -27,11 +26,10 @@ namespace Semmle.Extraction.CSharp.Standalone
{
public Analysis(ILogger logger, Options options)
{
var progressMonitor = new ProgressMonitor(logger);
buildAnalysis = new BuildAnalysis(options, progressMonitor);
References = buildAnalysis.ReferenceFiles;
dependencyManager = new DependencyManager(options.SrcDir, options.Dependencies, logger);
References = dependencyManager.ReferenceFiles;
Extraction = new Extraction(options.SrcDir);
Extraction.Sources.AddRange(options.SolutionFile is null ? buildAnalysis.AllSourceFiles : buildAnalysis.ProjectSourceFiles);
Extraction.Sources.AddRange(options.Dependencies.SolutionFile is null ? dependencyManager.AllSourceFiles : dependencyManager.ProjectSourceFiles);
}
public IEnumerable<string> References { get; }
@@ -41,11 +39,11 @@ namespace Semmle.Extraction.CSharp.Standalone
/// </summary>
public Extraction Extraction { get; }
private readonly BuildAnalysis buildAnalysis;
private readonly DependencyManager dependencyManager;
public void Dispose()
{
buildAnalysis.Dispose();
dependencyManager.Dispose();
}
};

View File

@@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -14,9 +13,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Expose internals for testing purposes.
[assembly: InternalsVisibleTo("Semmle.Extraction.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.

View File

@@ -5,7 +5,6 @@
<AssemblyName>Semmle.Extraction.CSharp.Standalone</AssemblyName>
<RootNamespace>Semmle.Extraction.CSharp.Standalone</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsAsErrors/>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
@@ -13,6 +12,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj" />
<ProjectReference Include="..\Semmle.Extraction.CSharp.DependencyFetching\Semmle.Extraction.CSharp.DependencyFetching.csproj" />
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,18 +1,18 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Semmle.Util;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Semmle.Util.Logging;
using System.Collections.Concurrent;
using System.Globalization;
using System.Threading;
namespace Semmle.Extraction.CSharp
{

View File

@@ -1,7 +1,7 @@
using Xunit;
using Semmle.BuildAnalyser;
using Semmle.Util.Logging;
using System.Collections.Generic;
using Semmle.Util.Logging;
using Semmle.Extraction.CSharp.DependencyFetching;
namespace Semmle.Extraction.Tests
{

View File

@@ -1,9 +1,9 @@
using Xunit;
using Semmle.Util.Logging;
using System;
using System.IO;
using Semmle.Util;
using System.Text.RegularExpressions;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.Extraction.Tests
{
@@ -135,12 +135,12 @@ namespace Semmle.Extraction.Tests
public void StandaloneDefaults()
{
standaloneOptions = CSharp.Standalone.Options.Create(Array.Empty<string>());
Assert.Equal(0, standaloneOptions.DllDirs.Count);
Assert.True(standaloneOptions.UseNuGet);
Assert.True(standaloneOptions.UseMscorlib);
Assert.Equal(0, standaloneOptions.Dependencies.DllDirs.Count);
Assert.True(standaloneOptions.Dependencies.UseNuGet);
Assert.True(standaloneOptions.Dependencies.UseMscorlib);
Assert.False(standaloneOptions.SkipExtraction);
Assert.Null(standaloneOptions.SolutionFile);
Assert.True(standaloneOptions.ScanNetFrameworkDlls);
Assert.Null(standaloneOptions.Dependencies.SolutionFile);
Assert.True(standaloneOptions.Dependencies.ScanNetFrameworkDlls);
Assert.False(standaloneOptions.Errors);
}
@@ -148,12 +148,12 @@ namespace Semmle.Extraction.Tests
public void StandaloneOptions()
{
standaloneOptions = CSharp.Standalone.Options.Create(new string[] { "--references:foo", "--silent", "--skip-nuget", "--skip-dotnet", "--exclude", "bar", "--nostdlib" });
Assert.Equal("foo", standaloneOptions.DllDirs[0]);
Assert.Equal("bar", standaloneOptions.Excludes[0]);
Assert.Equal("foo", standaloneOptions.Dependencies.DllDirs[0]);
Assert.Equal("bar", standaloneOptions.Dependencies.Excludes[0]);
Assert.Equal(Verbosity.Off, standaloneOptions.Verbosity);
Assert.False(standaloneOptions.UseNuGet);
Assert.False(standaloneOptions.UseMscorlib);
Assert.False(standaloneOptions.ScanNetFrameworkDlls);
Assert.False(standaloneOptions.Dependencies.UseNuGet);
Assert.False(standaloneOptions.Dependencies.UseMscorlib);
Assert.False(standaloneOptions.Dependencies.ScanNetFrameworkDlls);
Assert.False(standaloneOptions.Errors);
Assert.False(standaloneOptions.Help);
}

View File

@@ -1,7 +1,6 @@
using Xunit;
using System.Collections.Generic;
using Semmle.BuildAnalyser;
using Semmle.Extraction.CSharp.Standalone;
using Semmle.Extraction.CSharp.DependencyFetching;
namespace Semmle.Extraction.Tests
{

View File

@@ -4,7 +4,7 @@ using System.IO;
using System.Linq;
using System.Xml;
namespace Semmle.Extraction.CSharp
namespace Semmle.Extraction
{
/// <summary>
/// Represents a .csproj file and reads information from it.
@@ -27,7 +27,7 @@ namespace Semmle.Extraction.CSharp
if (directoryName is null)
{
throw new Extraction.InternalError($"Directory of file '{Filename}' is null");
throw new InternalError($"Directory of file '{Filename}' is null");
}
Directory = directoryName;

View File

@@ -12,6 +12,7 @@
<DefineConstants>TRACE;DEBUG;DEBUG_LABELS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
<PackageReference Include="GitInfo" Version="2.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>