Compare commits

...

6 Commits

Author SHA1 Message Date
Michael B. Gale
2bce3879db C#: Add test for TargetFramework install 2023-11-21 13:35:19 +00:00
Michael B. Gale
a126e1dfae C#: Fall back to TargetFramework to install .NET 2023-11-21 13:35:19 +00:00
Michael B. Gale
ebc4aaa3db C#: Load TargetFramework from project files 2023-11-21 13:32:06 +00:00
Michael B. Gale
373eb0a57e C#: Refactor code to read global.json version 2023-11-21 13:32:05 +00:00
Michael B. Gale
adde95d7a1 C#: Tolerate missing solution files in tests 2023-11-21 13:32:05 +00:00
Michael B. Gale
098812b073 C#: Add channel to DownloadDotNetVersion 2023-11-21 13:32:05 +00:00
4 changed files with 115 additions and 24 deletions

View File

@@ -559,6 +559,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["test.sln"] = false;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln";
@@ -573,6 +574,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone"] = 10;
actions.FileExists["csharp.log"] = true;
actions.FileExists["test.sln"] = false;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln";
@@ -587,6 +589,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["foo.sln"] = false;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln";
@@ -629,6 +632,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess["./build.sh --skip-tests"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["test.sln"] = false;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln";
@@ -729,6 +733,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\codeql index --xml --extensions config"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\test.sln"] = false;
SkipVsWhere();
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -875,6 +880,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --skip-nuget"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["foo.sln"] = false;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln";
@@ -946,6 +952,40 @@ namespace Semmle.Autobuild.CSharp.Tests
TestAutobuilderScript(autobuilder, 0, 8);
}
[Fact]
public void TestDotnetVersionInstalledFromTargetFramework()
{
actions.RunProcess["dotnet --list-sdks"] = 0;
actions.RunProcessOut["dotnet --list-sdks"] = "6.0.404 [C:\\Program Files\\dotnet\\sdks]\n7.0.102 [C:\\Program Files\\dotnet\\sdks]";
actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0;
actions.RunProcess[@"./dotnet-install.sh --channel 8.0 --version latest --install-dir C:\Project/.dotnet"] = 0;
actions.RunProcess[@"rm dotnet-install.sh"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin";
actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj";
actions.EnumerateDirectories[@"C:\Project"] = "";
var xml = new XmlDocument();
xml.LoadXml(@"<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>");
actions.LoadXml[@"C:\Project/test.csproj"] = xml;
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
var autobuilder = CreateAutoBuilder(false);
TestAutobuilderScript(autobuilder, 0, 8);
}
[Fact]
public void TestDotnetVersionAlreadyInstalled()
{
@@ -1016,7 +1056,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
TestDotnetVersionWindows(() =>
{
actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0;
actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel release -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0;
},
6);
}
@@ -1026,8 +1066,8 @@ namespace Semmle.Autobuild.CSharp.Tests
{
TestDotnetVersionWindows(() =>
{
actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 1;
actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0;
actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel release -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 1;
actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel release -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0;
},
7);
}

View File

@@ -119,20 +119,13 @@ namespace Semmle.Autobuild.CSharp
=> WithDotNet(builder, (_1, env) => f(env));
/// <summary>
/// Returns a script for downloading relevant versions of the
/// .NET Core SDK. The SDK(s) will be installed at <code>installDir</code>
/// (provided that the script succeeds).
/// Tries to determine the version of .NET that's required for the project from a <code>global.json</code> file, if present.
/// </summary>
private static BuildScript DownloadDotNet(IAutobuilder<AutobuildOptionsShared> builder, string installDir)
/// <returns>
/// Returns the .NET version specified in <code>global.json<code> as a string or null if no version could be retrieved.
/// </returns>
private static string? SdkVersionFromGlobalJson(IAutobuilder<AutobuildOptionsShared> builder)
{
if (!string.IsNullOrEmpty(builder.Options.DotNetVersion))
// Specific version supplied in configuration: always use that
return DownloadDotNetVersion(builder, installDir, builder.Options.DotNetVersion);
// Download versions mentioned in `global.json` files
// See https://docs.microsoft.com/en-us/dotnet/core/tools/global-json
var installScript = BuildScript.Success;
var validGlobalJson = false;
foreach (var path in builder.Paths.Select(p => p.Item1).Where(p => p.EndsWith("global.json", StringComparison.Ordinal)))
{
string version;
@@ -147,11 +140,59 @@ namespace Semmle.Autobuild.CSharp
continue;
}
installScript &= DownloadDotNetVersion(builder, installDir, version);
validGlobalJson = true;
return version;
}
return validGlobalJson ? installScript : BuildScript.Failure;
return null;
}
/// <summary>
/// Returns a script for downloading relevant versions of the
/// .NET Core SDK. The SDK(s) will be installed at <code>installDir</code>
/// (provided that the script succeeds).
/// </summary>
private static BuildScript DownloadDotNet(IAutobuilder<AutobuildOptionsShared> builder, string installDir)
{
if (!string.IsNullOrEmpty(builder.Options.DotNetVersion))
// Specific version supplied in configuration: always use that
return DownloadDotNetVersion(builder, installDir, builder.Options.DotNetVersion);
// Download versions mentioned in `global.json` files
// See https://docs.microsoft.com/en-us/dotnet/core/tools/global-json
var installScript = BuildScript.Success;
var foundFrameworkVersion = false;
var globalJsonSdkVersion = SdkVersionFromGlobalJson(builder);
if (!string.IsNullOrEmpty(globalJsonSdkVersion))
{
installScript &= DownloadDotNetVersion(builder, installDir, globalJsonSdkVersion);
foundFrameworkVersion = true;
}
else
{
// If there is no `global.json` to retrieve the .NET version from, we find all the
// TargetFramework values in the project files and install the latest versions
// from the corresponding channels
var targetFrameworkVersions = builder.ProjectsOrSolutionsToBuild
.SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects))
.OfType<Project<CSharpAutobuildOptions>>()
.Select(p => p.TargetFramework)
.OfType<string>()
.Where(v => v.StartsWith("net"));
foreach (var targetFrameworkVersion in targetFrameworkVersions)
{
// For simplicity, we only accept target frameworks of the form "netX.Y"
Version? version = null;
if (Version.TryParse(targetFrameworkVersion[3..], out version))
{
installScript &= DownloadDotNetVersion(builder, installDir, "latest", channel: version.ToString());
foundFrameworkVersion = true;
}
}
}
return foundFrameworkVersion ? installScript : BuildScript.Failure;
}
/// <summary>
@@ -160,7 +201,7 @@ namespace Semmle.Autobuild.CSharp
///
/// See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script.
/// </summary>
private static BuildScript DownloadDotNetVersion(IAutobuilder<AutobuildOptionsShared> builder, string path, string version)
private static BuildScript DownloadDotNetVersion(IAutobuilder<AutobuildOptionsShared> builder, string path, string version, string? channel = "release")
{
return BuildScript.Bind(GetInstalledSdksScript(builder.Actions), (sdks, sdksRet) =>
{
@@ -174,7 +215,7 @@ namespace Semmle.Autobuild.CSharp
if (builder.Actions.IsWindows())
{
var psCommand = $"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version {version} -InstallDir {path}";
var psCommand = $"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel {channel} -Version {version} -InstallDir {path}";
BuildScript GetInstall(string pwsh) =>
new CommandBuilder(builder.Actions).
@@ -203,7 +244,7 @@ namespace Semmle.Autobuild.CSharp
var install = new CommandBuilder(builder.Actions).
RunCommand("./dotnet-install.sh").
Argument("--channel").
Argument("release").
Argument(channel).
Argument("--version").
Argument(version).
Argument("--install-dir").

View File

@@ -23,6 +23,8 @@ namespace Semmle.Autobuild.Shared
public Version ToolsVersion { get; private set; }
public string? TargetFramework { get; private set; }
private readonly Lazy<List<Project<TAutobuildOptions>>> includedProjectsLazy;
public override IEnumerable<IProjectOrSolution> IncludedProjects => includedProjectsLazy.Value;
@@ -51,7 +53,8 @@ namespace Semmle.Autobuild.Shared
{
if (root.HasAttribute("Sdk"))
{
DotNetProject = true;
this.DotNetProject = true;
this.TargetFramework = root.SelectSingleNode("//Project/PropertyGroup/TargetFramework/text()")?.Value;
return;
}
@@ -72,11 +75,12 @@ namespace Semmle.Autobuild.Shared
includedProjectsLazy = new Lazy<List<Project<TAutobuildOptions>>>(() =>
{
var mgr = new XmlNamespaceManager(projFile.NameTable);
mgr.AddNamespace("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003");
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);
mgr.AddNamespace("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003");
var projectFileIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFile/@Include", mgr)
?.OfType<XmlNode>() ?? Array.Empty<XmlNode>();
var projectFilesIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFiles/@Include", mgr)

View File

@@ -61,6 +61,12 @@ namespace Semmle.Autobuild.Shared
{
try
{
// SolutionFile.Parse won't throw a FileNotFoundException if it is given a Windows path on a non-Windows platform
if (!builder.Actions.FileExists(FullPath))
{
throw new FileNotFoundException(FullPath);
}
solution = SolutionFile.Parse(FullPath);
}
catch (Exception ex) when (ex is InvalidProjectFileException || ex is FileNotFoundException)