using System; using System.Collections.Generic; using System.Linq; using Semmle.Util; using Semmle.Autobuild.Shared; using Semmle.Extraction.CSharp.DependencyFetching; 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. /// internal class DotNetRule : IBuildRule { public List FailedProjectsOrSolutions { get; } = []; /// /// A list of projects which are incompatible with DotNet. /// public IEnumerable> NotDotNetProjects { get; private set; } public DotNetRule() => NotDotNetProjects = []; public BuildScript Analyse(IAutobuilder builder, bool auto) { if (!builder.ProjectsOrSolutionsToBuild.Any()) return BuildScript.Failure; if (auto) { NotDotNetProjects = builder.ProjectsOrSolutionsToBuild .SelectMany(p => new[] { p }.Concat(p.IncludedProjects)) .OfType>() .Where(p => !p.DotNetProject); var notDotNetProject = NotDotNetProjects.FirstOrDefault(); if (notDotNetProject is not null) { builder.Logger.LogInfo($"Not using .NET Core because of incompatible project {notDotNetProject}"); return BuildScript.Failure; } builder.Logger.LogInfo("Attempting to build using .NET Core"); } return WithDotNet(builder, ensureDotNetAvailable: false, (dotNetPath, environment) => { // When a custom .NET CLI has been installed, `dotnet --info` has already been executed // to verify the installation. var ret = dotNetPath is null ? DotNet.InfoScript(builder.Actions, DotNetCommand(builder.Actions, dotNetPath), environment, builder.Logger) : BuildScript.Success; foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild) { var cleanCommand = GetCleanCommand(builder.Actions, dotNetPath, environment); cleanCommand.QuoteArgument(projectOrSolution.FullPath); var clean = cleanCommand.Script; var restoreCommand = GetRestoreCommand(builder.Actions, dotNetPath, environment); restoreCommand.QuoteArgument(projectOrSolution.FullPath); var restore = restoreCommand.Script; var build = GetBuildScript(builder, dotNetPath, environment, projectOrSolution.FullPath); ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & BuildScript.OnFailure(build, ret => { FailedProjectsOrSolutions.Add(projectOrSolution); }); } return ret; }); } /// /// Returns a script that attempts to download relevant version(s) of the /// .NET Core SDK, followed by running the script generated by . /// /// The arguments to are the path to the directory in which the /// .NET Core SDK(s) were installed and any additional required environment /// variables needed by the installed .NET Core (null when no variables /// are needed). /// public static BuildScript WithDotNet(IAutobuilder builder, bool ensureDotNetAvailable, Func?, BuildScript> f) { var temp = FileUtils.GetTemporaryWorkingDirectory(builder.Actions.GetEnvironmentVariable, builder.Options.Language.UpperCaseName, out var shouldCleanUp); return DotNet.WithDotNet(builder.Actions, builder.Logger, builder.Paths.Select(x => x.Item1), temp, shouldCleanUp, ensureDotNetAvailable, builder.Options.DotNetVersion, installDir => { var env = DotNet.MinimalEnvironment.ToDictionary(); if (installDir is not null) { // The installation succeeded, so use the newly installed .NET var path = builder.Actions.GetEnvironmentVariable("PATH"); var delim = builder.Actions.IsWindows() ? ";" : ":"; env.Add("DOTNET_MULTILEVEL_LOOKUP", "false"); // prevent look up of other .NET SDKs env.Add("PATH", installDir + delim + path); } return f(installDir, env); }); } /// /// Returns a script that attempts to download relevant version(s) of the /// .NET Core SDK, followed by running the script generated by . /// /// The argument to is any additional required environment /// variables needed by the installed .NET Core (null when no variables /// are needed). /// public static BuildScript WithDotNet(IAutobuilder builder, Func?, BuildScript> f) => WithDotNet(builder, ensureDotNetAvailable: false, (_, env) => f(env)); private static string DotNetCommand(IBuildActions actions, string? dotNetPath) => dotNetPath is not null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet"; private static CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { var clean = new CommandBuilder(actions, null, environment). RunCommand(DotNetCommand(actions, dotNetPath)). Argument("clean"); return clean; } private static CommandBuilder GetRestoreCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { var restore = new CommandBuilder(actions, null, environment). RunCommand(DotNetCommand(actions, dotNetPath)). Argument("restore"); return restore; } /// /// Gets the `dotnet build` script. /// private static BuildScript GetBuildScript(IAutobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln) { var build = new CommandBuilder(builder.Actions, null, environment); var script = build.RunCommand(DotNetCommand(builder.Actions, dotNetPath)). Argument("build"). Argument("--no-incremental"); return script.QuoteArgument(projOrSln). Script; } } }