forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMsBuildRule.cs
175 lines (151 loc) · 7.82 KB
/
MsBuildRule.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System.Collections.Generic;
using System.Linq;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.Autobuild.Shared
{
internal static class MsBuildCommandExtensions
{
/// <summary>
/// Appends a call to msbuild.
/// </summary>
/// <returns></returns>
public static CommandBuilder MsBuildCommand(this CommandBuilder cmdBuilder, IAutobuilder<AutobuildOptionsShared> builder, bool preferDotnet)
{
// mono doesn't ship with `msbuild` on Arm-based Macs, but we can fall back to
// msbuild that ships with `dotnet` which can be invoked with `dotnet msbuild`
// perhaps we should do this on all platforms?
return builder.Actions.IsRunningOnAppleSilicon() || preferDotnet
? cmdBuilder.RunCommand("dotnet").Argument("msbuild")
: cmdBuilder.RunCommand("msbuild");
}
}
/// <summary>
/// A build rule using msbuild.
/// </summary>
public class MsBuildRule : IBuildRule<AutobuildOptionsShared>
{
/// <summary>
/// A list of solutions or projects which failed to build.
/// </summary>
public List<IProjectOrSolution> FailedProjectsOrSolutions { get; } = [];
public BuildScript Analyse(IAutobuilder<AutobuildOptionsShared> builder, bool auto)
{
if (!builder.ProjectsOrSolutionsToBuild.Any())
return BuildScript.Failure;
if (auto)
builder.Logger.LogInfo("Attempting to build using MSBuild");
VcVarsBatFile? vsTools = null;
if (builder.ProjectsOrSolutionsToBuild.Any())
{
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();
}
if (vsTools is null && builder.Actions.IsWindows())
{
builder.Logger.LogWarning("Could not find a suitable version of VsDevCmd.bat/vcvarsall.bat");
}
// Use `nuget.exe` from source code repo, if present, otherwise first attempt with global
// `nuget` command, and if that fails, attempt to download `nuget.exe` from nuget.org
var nuget = builder.GetFilename("nuget.exe").Select(t => t.Item1).FirstOrDefault() ?? "nuget";
var nugetDownloadPath = builder.Actions.PathCombine(FileUtils.GetTemporaryWorkingDirectory(builder.Actions.GetEnvironmentVariable, builder.Options.Language.UpperCaseName, out _), ".nuget", "nuget.exe");
var nugetDownloaded = false;
var ret = BuildScript.Success;
foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild)
{
BuildScript GetNugetRestoreScript() =>
new CommandBuilder(builder.Actions).
RunCommand(nuget).
Argument("restore").
QuoteArgument(projectOrSolution.FullPath).
Argument("-DisableParallelProcessing").
Script;
BuildScript GetMonoVersionScript() => new CommandBuilder(builder.Actions).
RunCommand("mono").
Argument("--version").
Script;
var preferDotnet = !builder.Actions.IsWindows() && GetMonoVersionScript().Run(builder.Actions, (_, _) => { }, (_, _, _) => { }) != 0;
var nugetRestore = GetNugetRestoreScript();
var msbuildRestoreCommand = new CommandBuilder(builder.Actions).
MsBuildCommand(builder, preferDotnet).
Argument("/t:restore").
QuoteArgument(projectOrSolution.FullPath);
if (builder.Actions.IsRunningOnAppleSilicon() || preferDotnet)
{
// On Apple Silicon, only try package restore with `dotnet msbuild /t:restore`
ret &= BuildScript.Try(msbuildRestoreCommand.Script);
}
else if (nugetDownloaded)
{
ret &= BuildScript.Try(nugetRestore | msbuildRestoreCommand.Script);
}
else
{
// If `nuget restore` fails, and we have not already attempted to download `nuget.exe`,
// download it and reattempt `nuget restore`.
var nugetDownloadAndRestore =
BuildScript.Bind(DownloadNugetExe(builder, nugetDownloadPath), exitCode =>
{
nugetDownloaded = true;
if (exitCode != 0)
return BuildScript.Failure;
nuget = nugetDownloadPath;
return GetNugetRestoreScript();
});
ret &= BuildScript.Try(nugetRestore | nugetDownloadAndRestore | msbuildRestoreCommand.Script);
}
var command = new CommandBuilder(builder.Actions);
if (vsTools is not null)
{
command.CallBatFile(vsTools.Path);
// `vcvarsall.bat` sets a default Platform environment variable,
// which may not be compatible with the supported platforms of the
// given project/solution. Unsetting it means that the default platform
// of the project/solution is used instead.
command.RunCommand("set Platform=&& type NUL", quoteExe: false);
}
command.MsBuildCommand(builder, preferDotnet);
command.QuoteArgument(projectOrSolution.FullPath);
var target = "rebuild";
var platform = projectOrSolution is ISolution s1 ? s1.DefaultPlatformName : null;
var configuration = projectOrSolution is ISolution s2 ? s2.DefaultConfigurationName : null;
command.Argument($"/t:{target}");
if (platform is not null)
command.Argument($"/p:Platform=\"{platform}\"");
if (configuration is not null)
command.Argument($"/p:Configuration=\"{configuration}\"");
// append the build script which invokes msbuild to the overall build script `ret`;
// we insert a check that building the current project or solution was successful:
// if it was not successful, we add it to `FailedProjectsOrSolutions`
ret &= BuildScript.OnFailure(command.Script, ret =>
{
FailedProjectsOrSolutions.Add(projectOrSolution);
});
}
return ret;
}
/// <summary>
/// Returns a script for downloading `nuget.exe` from nuget.org.
/// </summary>
private static BuildScript DownloadNugetExe<TAutobuildOptions>(IAutobuilder<TAutobuildOptions> builder, string path) where TAutobuildOptions : AutobuildOptionsShared =>
BuildScript.Create(_ =>
{
builder.Logger.LogInfo("Attempting to download nuget.exe");
return 0;
})
&
BuildScript.DownloadFile(
FileUtils.NugetExeUrl,
path,
e => builder.Logger.LogWarning($"Failed to download 'nuget.exe': {e.Message}"),
builder.Logger)
&
BuildScript.Create(_ =>
{
builder.Logger.LogInfo($"Successfully downloaded {path}");
return 0;
});
}
}