C#: Split AutobuildOptions into C#/C++ specific classes

This commit is contained in:
Tom Hvitved
2022-11-23 10:57:21 +01:00
parent 1c17d854d8
commit 8f3731fd42
17 changed files with 163 additions and 83 deletions

View File

@@ -83,3 +83,4 @@ jobs:
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"

View File

@@ -257,11 +257,11 @@ namespace Semmle.Autobuild.Cpp.Tests
Actions.GetCurrentDirectory = cwd;
Actions.IsWindows = isWindows;
var options = new AutobuildOptions(Actions, Language.Cpp);
var options = new CppAutobuildOptions(Actions);
return new CppAutobuilder(Actions, options);
}
void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun)
void TestAutobuilderScript(CppAutobuilder autobuilder, int expectedOutput, int commandsRun)
{
Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback));
@@ -299,7 +299,7 @@ namespace Semmle.Autobuild.Cpp.Tests
{
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;

View File

@@ -2,9 +2,26 @@
namespace Semmle.Autobuild.Cpp
{
public class CppAutobuilder : Autobuilder
/// <summary>
/// Encapsulates C++ build options.
/// </summary>
public class CppAutobuildOptions : AutobuildOptionsShared
{
public CppAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { }
public override Language Language => Language.Cpp;
/// <summary>
/// Reads options from environment variables.
/// Throws ArgumentOutOfRangeException for invalid arguments.
/// </summary>
public CppAutobuildOptions(IBuildActions actions) : base(actions)
{
}
}
public class CppAutobuilder : Autobuilder<CppAutobuildOptions>
{
public CppAutobuilder(IBuildActions actions, CppAutobuildOptions options) : base(actions, options) { }
public override BuildScript GetBuildScript()
{

View File

@@ -11,14 +11,14 @@ namespace Semmle.Autobuild.Cpp
try
{
var actions = SystemBuildActions.Instance;
var options = new AutobuildOptions(actions, Language.Cpp);
var options = new CppAutobuildOptions(actions);
try
{
Console.WriteLine("CodeQL C++ autobuilder");
var builder = new CppAutobuilder(actions, options);
return builder.AttemptBuild();
}
catch(InvalidEnvironmentException ex)
catch (InvalidEnvironmentException ex)
{
Console.WriteLine("The environment is invalid: {0}", ex.Message);
}

View File

@@ -403,7 +403,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.GetCurrentDirectory = cwd;
actions.IsWindows = isWindows;
var options = new AutobuildOptions(actions, Language.CSharp);
var options = new CSharpAutobuildOptions(actions);
return new CSharpAutobuilder(actions, options);
}
@@ -576,7 +576,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = false;
}
private void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun)
private void TestAutobuilderScript(CSharpAutobuilder autobuilder, int expectedOutput, int commandsRun)
{
Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(actions, StartCallback, EndCallback));

View File

@@ -4,9 +4,32 @@ using Semmle.Autobuild.Shared;
namespace Semmle.Autobuild.CSharp
{
public class CSharpAutobuilder : Autobuilder
/// <summary>
/// Encapsulates C# build options.
/// </summary>
public class CSharpAutobuildOptions : AutobuildOptionsShared
{
public CSharpAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { }
private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
public bool Buildless { get; }
public override Language Language => Language.CSharp;
/// <summary>
/// Reads options from environment variables.
/// Throws ArgumentOutOfRangeException for invalid arguments.
/// </summary>
public CSharpAutobuildOptions(IBuildActions actions) : base(actions)
{
Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
}
}
public class CSharpAutobuilder : Autobuilder<CSharpAutobuildOptions>
{
public CSharpAutobuilder(IBuildActions actions, CSharpAutobuildOptions options) : base(actions, options) { }
public override BuildScript GetBuildScript()
{

View File

@@ -13,9 +13,9 @@ namespace Semmle.Autobuild.CSharp
/// A build rule where the build command is of the form "dotnet build".
/// Currently unused because the tracer does not work with dotnet.
/// </summary>
internal class DotNetRule : IBuildRule
internal class DotNetRule : IBuildRule<CSharpAutobuildOptions>
{
public BuildScript Analyse(Autobuilder builder, bool auto)
public BuildScript Analyse(IAutobuilder<CSharpAutobuildOptions> builder, bool auto)
{
if (!builder.ProjectsOrSolutionsToBuild.Any())
return BuildScript.Failure;
@@ -24,7 +24,7 @@ namespace Semmle.Autobuild.CSharp
{
var notDotNetProject = builder.ProjectsOrSolutionsToBuild
.SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects))
.OfType<Project>()
.OfType<Project<CSharpAutobuildOptions>>()
.FirstOrDefault(p => !p.DotNetProject);
if (notDotNetProject is not null)
{
@@ -56,7 +56,7 @@ namespace Semmle.Autobuild.CSharp
});
}
private static BuildScript WithDotNet(Autobuilder builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
private static BuildScript WithDotNet(IAutobuilder<AutobuildOptionsShared> builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
{
var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet");
var installScript = DownloadDotNet(builder, installDir);
@@ -92,7 +92,7 @@ namespace Semmle.Autobuild.CSharp
/// variables needed by the installed .NET Core (<code>null</code> when no variables
/// are needed).
/// </summary>
public static BuildScript WithDotNet(Autobuilder builder, Func<IDictionary<string, string>?, BuildScript> f)
public static BuildScript WithDotNet(IAutobuilder<AutobuildOptionsShared> builder, Func<IDictionary<string, string>?, BuildScript> f)
=> WithDotNet(builder, (_1, env) => f(env));
/// <summary>
@@ -100,7 +100,7 @@ namespace Semmle.Autobuild.CSharp
/// .NET Core SDK. The SDK(s) will be installed at <code>installDir</code>
/// (provided that the script succeeds).
/// </summary>
private static BuildScript DownloadDotNet(Autobuilder builder, string installDir)
private static BuildScript DownloadDotNet(IAutobuilder<AutobuildOptionsShared> builder, string installDir)
{
if (!string.IsNullOrEmpty(builder.Options.DotNetVersion))
// Specific version supplied in configuration: always use that
@@ -137,7 +137,7 @@ namespace Semmle.Autobuild.CSharp
///
/// See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script.
/// </summary>
private static BuildScript DownloadDotNetVersion(Autobuilder builder, string path, string version)
private static BuildScript DownloadDotNetVersion(IAutobuilder<AutobuildOptionsShared> builder, string path, string version)
{
return BuildScript.Bind(GetInstalledSdksScript(builder.Actions), (sdks, sdksRet) =>
{
@@ -233,7 +233,7 @@ namespace Semmle.Autobuild.CSharp
/// <summary>
/// Gets the `dotnet build` script.
/// </summary>
private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary<string, string>? environment, string projOrSln)
private static BuildScript GetBuildScript(IAutobuilder<CSharpAutobuildOptions> builder, string? dotNetPath, IDictionary<string, string>? environment, string projOrSln)
{
var build = new CommandBuilder(builder.Actions, null, environment);
var script = build.RunCommand(DotNetCommand(builder.Actions, dotNetPath)).

View File

@@ -11,7 +11,7 @@ namespace Semmle.Autobuild.CSharp
try
{
var actions = SystemBuildActions.Instance;
var options = new AutobuildOptions(actions, Language.CSharp);
var options = new CSharpAutobuildOptions(actions);
try
{
Console.WriteLine("CodeQL C# autobuilder");

View File

@@ -6,9 +6,9 @@ namespace Semmle.Autobuild.CSharp
/// <summary>
/// Build using standalone extraction.
/// </summary>
internal class StandaloneBuildRule : IBuildRule
internal class StandaloneBuildRule : IBuildRule<CSharpAutobuildOptions>
{
public BuildScript Analyse(Autobuilder builder, bool auto)
public BuildScript Analyse(IAutobuilder<CSharpAutobuildOptions> builder, bool auto)
{
BuildScript GetCommand(string? solution)
{

View File

@@ -6,12 +6,12 @@ using System.Text.RegularExpressions;
namespace Semmle.Autobuild.Shared
{
/// <summary>
/// Encapsulates build options.
/// Encapsulates build options shared between C# and C++.
/// </summary>
public class AutobuildOptions
public abstract class AutobuildOptionsShared
{
private const string lgtmPrefix = "LGTM_INDEX_";
private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
protected const string lgtmPrefix = "LGTM_INDEX_";
public int SearchDepth { get; } = 3;
public string RootDirectory { get; }
@@ -25,16 +25,16 @@ namespace Semmle.Autobuild.Shared
public string? BuildCommand { get; }
public IEnumerable<string> Solution { get; }
public bool IgnoreErrors { get; }
public bool Buildless { get; }
public bool AllSolutions { get; }
public bool NugetRestore { get; }
public Language Language { get; }
public abstract Language Language { get; }
/// <summary>
/// Reads options from environment variables.
/// Throws ArgumentOutOfRangeException for invalid arguments.
/// </summary>
public AutobuildOptions(IBuildActions actions, Language language)
public AutobuildOptionsShared(IBuildActions actions)
{
RootDirectory = actions.GetCurrentDirectory();
VsToolsVersion = actions.GetEnvironmentVariable(lgtmPrefix + "VSTOOLS_VERSION");
@@ -48,12 +48,8 @@ namespace Semmle.Autobuild.Shared
Solution = actions.GetEnvironmentVariable(lgtmPrefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty<string>());
IgnoreErrors = actions.GetEnvironmentVariable(lgtmPrefix + "IGNORE_ERRORS").AsBool("ignore_errors", false);
Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
AllSolutions = actions.GetEnvironmentVariable(lgtmPrefix + "ALL_SOLUTIONS").AsBool("all_solutions", false);
NugetRestore = actions.GetEnvironmentVariable(lgtmPrefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
Language = language;
}
}

View File

@@ -9,21 +9,21 @@ namespace Semmle.Autobuild.Shared
/// <summary>
/// A build rule analyses the files in "builder" and outputs a build script.
/// </summary>
public interface IBuildRule
public interface IBuildRule<TAutobuildOptions> where TAutobuildOptions : AutobuildOptionsShared
{
/// <summary>
/// Analyse the files and produce a build script.
/// </summary>
/// <param name="builder">The files and options relating to the build.</param>
/// <param name="auto">Whether this build rule is being automatically applied.</param>
BuildScript Analyse(Autobuilder builder, bool auto);
BuildScript Analyse(IAutobuilder<TAutobuildOptions> builder, bool auto);
}
/// <summary>
/// A delegate used to wrap a build script in an environment where an appropriate
/// version of .NET Core is automatically installed.
/// </summary>
public delegate BuildScript WithDotNet(Autobuilder builder, Func<IDictionary<string, string>?, BuildScript> f);
public delegate BuildScript WithDotNet<TAutobuildOptions>(IAutobuilder<TAutobuildOptions> builder, Func<IDictionary<string, string>?, BuildScript> f) where TAutobuildOptions : AutobuildOptionsShared;
/// <summary>
/// Exception indicating that environment variables are missing or invalid.
@@ -33,6 +33,59 @@ namespace Semmle.Autobuild.Shared
public InvalidEnvironmentException(string m) : base(m) { }
}
public interface IAutobuilder<out TAutobuildOptions> where TAutobuildOptions : AutobuildOptionsShared
{
/// <summary>
/// Full file paths of files found in the project directory, as well as
/// their distance from the project root folder. The list is sorted
/// by distance in ascending order.
/// </summary>
IEnumerable<(string, int)> Paths { get; }
/// <summary>
/// Gets all paths matching a particular filename, as well as
/// their distance from the project root folder. The list is sorted
/// by distance in ascending order.
/// </summary>
/// <param name="name">The filename to find.</param>
/// <returns>Possibly empty sequence of paths with the given filename.</returns>
IEnumerable<(string, int)> GetFilename(string name) =>
Paths.Where(p => Actions.GetFileName(p.Item1) == name);
/// <summary>
/// List of project/solution files to build.
/// </summary>
IList<IProjectOrSolution> ProjectsOrSolutionsToBuild { get; }
/// <summary>
/// Gets the supplied build configuration.
/// </summary>
TAutobuildOptions Options { get; }
/// <summary>
/// The set of build actions used during the autobuilder.
/// Could be real system operations, or a stub for testing.
/// </summary>
IBuildActions Actions { get; }
/// <summary>
/// Log a given build event to the console.
/// </summary>
/// <param name="format">The format string.</param>
/// <param name="args">Inserts to the format string.</param>
void Log(Severity severity, string format, params object[] args);
/// <summary>
/// Value of CODEQL_EXTRACTOR_<LANG>_ROOT environment variable.
/// </summary>
string? CodeQLExtractorLangRoot { get; }
/// <summary>
/// Value of CODEQL_PLATFORM environment variable.
/// </summary>
string? CodeQlPlatform { get; }
}
/// <summary>
/// Main application logic, containing all data
/// gathered from the project and filesystem.
@@ -40,7 +93,7 @@ namespace Semmle.Autobuild.Shared
/// The overall design is intended to be extensible so that in theory,
/// it should be possible to add new build rules without touching this code.
/// </summary>
public abstract class Autobuilder
public abstract class Autobuilder<TAutobuildOptions> : IAutobuilder<TAutobuildOptions> where TAutobuildOptions : AutobuildOptionsShared
{
/// <summary>
/// Full file paths of files found in the project directory, as well as
@@ -60,16 +113,6 @@ namespace Semmle.Autobuild.Shared
public IEnumerable<(string, int)> GetExtensions(params string[] extensions) =>
Paths.Where(p => extensions.Contains(Path.GetExtension(p.Item1)));
/// <summary>
/// Gets all paths matching a particular filename, as well as
/// their distance from the project root folder. The list is sorted
/// by distance in ascending order.
/// </summary>
/// <param name="name">The filename to find.</param>
/// <returns>Possibly empty sequence of paths with the given filename.</returns>
public IEnumerable<(string, int)> GetFilename(string name) =>
Paths.Where(p => Actions.GetFileName(p.Item1) == name);
/// <summary>
/// Holds if a given path, relative to the root of the source directory
/// was found.
@@ -115,7 +158,7 @@ namespace Semmle.Autobuild.Shared
/// <summary>
/// Gets the supplied build configuration.
/// </summary>
public AutobuildOptions Options { get; }
public TAutobuildOptions Options { get; }
/// <summary>
/// The set of build actions used during the autobuilder.
@@ -123,7 +166,7 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public IBuildActions Actions { get; }
private IEnumerable<IProjectOrSolution>? FindFiles(string extension, Func<string, ProjectOrSolution> create)
private IEnumerable<IProjectOrSolution>? FindFiles(string extension, Func<string, ProjectOrSolution<TAutobuildOptions>> create)
{
var matchingFiles = GetExtensions(extension)
.Select(p => (ProjectOrSolution: create(p.Item1), DistanceFromRoot: p.Item2))
@@ -146,7 +189,7 @@ namespace Semmle.Autobuild.Shared
/// solution file and tools.
/// </summary>
/// <param name="options">The command line options.</param>
protected Autobuilder(IBuildActions actions, AutobuildOptions options)
protected Autobuilder(IBuildActions actions, TAutobuildOptions options)
{
Actions = actions;
Options = options;
@@ -167,7 +210,7 @@ namespace Semmle.Autobuild.Shared
foreach (var solution in options.Solution)
{
if (actions.FileExists(solution))
ret.Add(new Solution(this, solution, true));
ret.Add(new Solution<TAutobuildOptions>(this, solution, true));
else
Log(Severity.Error, $"The specified project or solution file {solution} was not found");
}
@@ -175,17 +218,17 @@ namespace Semmle.Autobuild.Shared
}
// First look for `.proj` files
ret = FindFiles(".proj", f => new Project(this, f))?.ToList();
ret = FindFiles(".proj", f => new Project<TAutobuildOptions>(this, f))?.ToList();
if (ret is not null)
return ret;
// Then look for `.sln` files
ret = FindFiles(".sln", f => new Solution(this, f, false))?.ToList();
ret = FindFiles(".sln", f => new Solution<TAutobuildOptions>(this, f, false))?.ToList();
if (ret is not null)
return ret;
// Finally look for language specific project files, e.g. `.csproj` files
ret = FindFiles(this.Options.Language.ProjectExtension, f => new Project(this, f))?.ToList();
ret = FindFiles(this.Options.Language.ProjectExtension, f => new Project<TAutobuildOptions>(this, f))?.ToList();
return ret ?? new List<IProjectOrSolution>();
});

View File

@@ -7,11 +7,11 @@ namespace Semmle.Autobuild.Shared
/// <summary>
/// Auto-detection of build scripts.
/// </summary>
public class BuildCommandAutoRule : IBuildRule
public class BuildCommandAutoRule : IBuildRule<AutobuildOptionsShared>
{
private readonly WithDotNet withDotNet;
private readonly WithDotNet<AutobuildOptionsShared> withDotNet;
public BuildCommandAutoRule(WithDotNet withDotNet)
public BuildCommandAutoRule(WithDotNet<AutobuildOptionsShared> withDotNet)
{
this.withDotNet = withDotNet;
}
@@ -31,7 +31,7 @@ namespace Semmle.Autobuild.Shared
"build"
};
public BuildScript Analyse(Autobuilder builder, bool auto)
public BuildScript Analyse(IAutobuilder<AutobuildOptionsShared> builder, bool auto)
{
builder.Log(Severity.Info, "Attempting to locate build script");

View File

@@ -3,16 +3,16 @@
/// <summary>
/// Execute the build_command rule.
/// </summary>
public class BuildCommandRule : IBuildRule
public class BuildCommandRule : IBuildRule<AutobuildOptionsShared>
{
private readonly WithDotNet withDotNet;
private readonly WithDotNet<AutobuildOptionsShared> withDotNet;
public BuildCommandRule(WithDotNet withDotNet)
public BuildCommandRule(WithDotNet<AutobuildOptionsShared> withDotNet)
{
this.withDotNet = withDotNet;
}
public BuildScript Analyse(Autobuilder builder, bool auto)
public BuildScript Analyse(IAutobuilder<AutobuildOptionsShared> builder, bool auto)
{
if (builder.Options.BuildCommand is null)
return BuildScript.Failure;

View File

@@ -6,14 +6,14 @@ namespace Semmle.Autobuild.Shared
/// <summary>
/// A build rule using msbuild.
/// </summary>
public class MsBuildRule : IBuildRule
public class MsBuildRule : IBuildRule<AutobuildOptionsShared>
{
/// <summary>
/// The name of the msbuild command.
/// </summary>
private const string msBuild = "msbuild";
public BuildScript Analyse(Autobuilder builder, bool auto)
public BuildScript Analyse(IAutobuilder<AutobuildOptionsShared> builder, bool auto)
{
if (!builder.ProjectsOrSolutionsToBuild.Any())
return BuildScript.Failure;
@@ -27,8 +27,8 @@ namespace Semmle.Autobuild.Shared
{
var firstSolution = builder.ProjectsOrSolutionsToBuild.OfType<ISolution>().FirstOrDefault();
vsTools = firstSolution is not null
? BuildTools.FindCompatibleVcVars(builder.Actions, firstSolution)
: BuildTools.VcVarsAllBatFiles(builder.Actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault();
? BuildTools.FindCompatibleVcVars(builder.Actions, firstSolution)
: BuildTools.VcVarsAllBatFiles(builder.Actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault();
}
if (vsTools is null && builder.Actions.IsWindows())
@@ -123,7 +123,7 @@ namespace Semmle.Autobuild.Shared
///
/// Returns <code>null</code> when no version is specified.
/// </summary>
public static VcVarsBatFile? GetVcVarsBatFile(Autobuilder builder)
public static VcVarsBatFile? GetVcVarsBatFile<TAutobuildOptions>(IAutobuilder<TAutobuildOptions> builder) where TAutobuildOptions : AutobuildOptionsShared
{
VcVarsBatFile? vsTools = null;
@@ -154,7 +154,7 @@ namespace Semmle.Autobuild.Shared
/// <summary>
/// Returns a script for downloading `nuget.exe` from nuget.org.
/// </summary>
private static BuildScript DownloadNugetExe(Autobuilder builder, string path) =>
private static BuildScript DownloadNugetExe<TAutobuildOptions>(IAutobuilder<TAutobuildOptions> builder, string path) where TAutobuildOptions : AutobuildOptionsShared =>
BuildScript.Create(_ =>
{
builder.Log(Severity.Info, "Attempting to download nuget.exe");

View File

@@ -12,7 +12,7 @@ namespace Semmle.Autobuild.Shared
/// C# project files come in 2 flavours, .Net core and msbuild, but they
/// have the same file extension.
/// </summary>
public class Project : ProjectOrSolution
public class Project<TAutobuildOptions> : ProjectOrSolution<TAutobuildOptions> where TAutobuildOptions : AutobuildOptionsShared
{
/// <summary>
/// Holds if this project is for .Net core.
@@ -23,13 +23,13 @@ namespace Semmle.Autobuild.Shared
public Version ToolsVersion { get; private set; }
private readonly Lazy<List<Project>> includedProjectsLazy;
private readonly Lazy<List<Project<TAutobuildOptions>>> includedProjectsLazy;
public override IEnumerable<IProjectOrSolution> IncludedProjects => includedProjectsLazy.Value;
public Project(Autobuilder builder, string path) : base(builder, path)
public Project(Autobuilder<TAutobuildOptions> builder, string path) : base(builder, path)
{
ToolsVersion = new Version();
includedProjectsLazy = new Lazy<List<Project>>(() => new List<Project>());
includedProjectsLazy = new Lazy<List<Project<TAutobuildOptions>>>(() => new List<Project<TAutobuildOptions>>());
if (!builder.Actions.FileExists(FullPath))
return;
@@ -70,9 +70,9 @@ namespace Semmle.Autobuild.Shared
}
}
includedProjectsLazy = new Lazy<List<Project>>(() =>
includedProjectsLazy = new Lazy<List<Project<TAutobuildOptions>>>(() =>
{
var ret = new List<Project>();
var ret = new List<Project<TAutobuildOptions>>();
// The documentation on `.proj` files is very limited, but it appears that both
// `<ProjectFile Include="X"/>` and `<ProjectFiles Include="X"/>` is valid
var mgr = new XmlNamespaceManager(projFile.NameTable);
@@ -89,7 +89,7 @@ namespace Semmle.Autobuild.Shared
}
var includePath = builder.Actions.PathCombine(include.Value.Split('\\', StringSplitOptions.RemoveEmptyEntries));
ret.Add(new Project(builder, builder.Actions.PathCombine(DirectoryName, includePath)));
ret.Add(new Project<TAutobuildOptions>(builder, builder.Actions.PathCombine(DirectoryName, includePath)));
}
return ret;
});

View File

@@ -20,13 +20,13 @@ namespace Semmle.Autobuild.Shared
IEnumerable<IProjectOrSolution> IncludedProjects { get; }
}
public abstract class ProjectOrSolution : IProjectOrSolution
public abstract class ProjectOrSolution<TAutobuildOptions> : IProjectOrSolution where TAutobuildOptions : AutobuildOptionsShared
{
public string FullPath { get; }
public string DirectoryName { get; }
protected ProjectOrSolution(Autobuilder builder, string path)
protected ProjectOrSolution(Autobuilder<TAutobuildOptions> builder, string path)
{
FullPath = builder.Actions.GetFullPath(path);
DirectoryName = builder.Actions.GetDirectoryName(path) ?? "";

View File

@@ -40,11 +40,11 @@ namespace Semmle.Autobuild.Shared
/// <summary>
/// A solution file on the filesystem, read using Microsoft.Build.
/// </summary>
internal class Solution : ProjectOrSolution, ISolution
internal class Solution<TAutobuildOptions> : ProjectOrSolution<TAutobuildOptions>, ISolution where TAutobuildOptions : AutobuildOptionsShared
{
private readonly SolutionFile? solution;
private readonly IEnumerable<Project> includedProjects;
private readonly IEnumerable<Project<TAutobuildOptions>> includedProjects;
public override IEnumerable<IProjectOrSolution> IncludedProjects => includedProjects;
@@ -57,7 +57,7 @@ namespace Semmle.Autobuild.Shared
public string DefaultPlatformName =>
solution is null ? "" : solution.GetDefaultPlatformName();
public Solution(Autobuilder builder, string path, bool allowProject) : base(builder, path)
public Solution(Autobuilder<TAutobuildOptions> builder, string path, bool allowProject) : base(builder, path)
{
try
{
@@ -69,19 +69,19 @@ namespace Semmle.Autobuild.Shared
// that scenario as a solution with just that one project
if (allowProject)
{
includedProjects = new[] { new Project(builder, path) };
includedProjects = new[] { new Project<TAutobuildOptions>(builder, path) };
return;
}
builder.Log(Severity.Info, $"Unable to read solution file {path}.");
includedProjects = Array.Empty<Project>();
includedProjects = Array.Empty<Project<TAutobuildOptions>>();
return;
}
includedProjects = solution.ProjectsInOrder
.Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat)
.Select(p => builder.Actions.PathCombine(DirectoryName, builder.Actions.PathCombine(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries))))
.Select(p => new Project(builder, p))
.Select(p => new Project<TAutobuildOptions>(builder, p))
.ToArray();
}