New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C#: Detect TargetFrameworks and install them if there is no global.json
#14821
base: main
Are you sure you want to change the base?
Conversation
09fc91e
to
9ab7c96
Compare
TargetFramework and install it if there is no global.jsonTargetFrameworks and install them if there is no global.json
9ab7c96
to
2bce387
Compare
| // TargetFramework values in the project files and install the latest versions | ||
| // from the corresponding channels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we only install the latest? Why not all? If there are multiple projects with different target framework settings in the repo, some of them will fail the build, won't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do install all! Maybe you had a look at the initial draft version of this PR, which only installed the latest, but now the review-ready version does install all the ones we find (in theory at least - pending changes we may have to make to DownloadDotNetVersion)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are multiple projects with different target framework settings in the repo, some of them will fail the build, won't it?
This is an interesting question. I would assume that to be the case yes, but if so that's been a pretty big oversight on our part with respect to C# projects generally.
Also relates to my question whether we should do this even if there is a global.json.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add an integration test to check this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's not possible to pin multiple versions of dotnet in global.json. So maybe whoever needs this would add a build script which takes care of the installation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the docs for global.json, the version specified there just controls which version of the CLI is used and is independent of the SDK versions that the projects target.
| @@ -51,7 +53,8 @@ public Project(Autobuilder<TAutobuildOptions> builder, string path) : base(build | |||
| { | |||
| if (root.HasAttribute("Sdk")) | |||
| { | |||
| DotNetProject = true; | |||
| this.DotNetProject = true; | |||
| this.TargetFramework = root.SelectSingleNode("//Project/PropertyGroup/TargetFramework/text()")?.Value; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also include the values from <TargetFrameworks> (plural).
| .OfType<Project<CSharpAutobuildOptions>>() | ||
| .Select(p => p.TargetFramework) | ||
| .OfType<string>() | ||
| .Where(v => v.StartsWith("net")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could be more specific about the versions. There's no point in issuing a web request for things that will obviously fail, such as netstandard.*, net48 and below, and maybe netcoreapp3.1 and below also doesn't work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
netstandard and netcoreapp should already get filtered out by the code further down (see the comment there). There was a unit test which initially failed for me (because it specified netcoreapp2.1 or something), but now passes since we no longer attempt to install those SDKs.
I can also filter out older net versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's as simple as filtering out anything that has a major version number below 5.
| Version? version = null; | ||
| if (Version.TryParse(targetFrameworkVersion[3..], out version)) | ||
| { | ||
| installScript &= DownloadDotNetVersion(builder, installDir, "latest", channel: version.ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the download logic work for both net7.0 and net70?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Version parsing here would interpret the latter as 70.0 essentially, so I doubt that the dotnet-install.sh script would be happy with that.
| // 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to declare version explicitly. You can just return directly within the try block.
try
{
var o = JObject.Parse(File.ReadAllText(path));
return (string)(o?["sdk"]?["version"]!);
}
catch // lgtm[cs/catch-of-all-exceptions]
{
// not a valid global.json file
continue;
}| { | ||
| // For simplicity, we only accept target frameworks of the form "netX.Y" | ||
| Version? version = null; | ||
| if (Version.TryParse(targetFrameworkVersion[3..], out version)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using out var version and avoid declaring version above.
What will happen in case the target framework is net70 and not net7.0 (I think it will be parsed as version 70, which probably will cause problems downstream).
Summary
For .NET projects, our builds may fail if a .NET version that is specified as a
TargetFrameworkis not available on the host. We have seen this recently in cases where e.g. a.csprojspecifies .NET 8 but .NET 8 was not available on a GHA runner yet by default.This PR:
TargetFrameworkvalues from the project files, where available.global.json, we look at theTargetFrameworkvalues and install those SDKs. (Should we do this even if there is aglobal.json?)