Skip to content
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

Support .NET Core SDK 3.1.200+ #2221

Merged
merged 6 commits into from
Apr 6, 2020

Conversation

rainersigwald
Copy link
Contributor

Description

Support MSBuild 16.5+ by changing what gets loaded into the GitLoaderContext.

Related Issue

Fixes #2063.

Motivation and Context

MSBuild 16.5 changed (dotnet/msbuild#4916) how MSBuild loads task assemblies to make it more robust to assemblies with mismatched dependencies or different versions of a specific task.

This caused problems with GitVersion because it took an inadvertent dependency on having parts of itself loaded in the default AssemblyLoadContext.

This change modifies the GitLoaderContext to know about the task assembly in whatever context it was loaded in, and augments it to load the other assemblies shipped in the GitVersionTask package.

How Has This Been Tested?

I built a trivial sample project referencing GitVersionTask using 3.1.200 and 3.1.101 (controlled via global.json) to validate that this fixes the problem on new MSBuild/SDK and continues to work on old MSBuild/SDK.

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Copy link

@Forgind Forgind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I understand the root of the problem well enough to approve this. We can talk about it if you'd like, or I can approve it anyway, since I think I understand the problem, if not what caused it, and the test you did sounded reasonable.


protected override Assembly Load(AssemblyName assemblyName)
{
if (assemblies.Contains(assemblyName.Name))
if (assemblyName.Name == entryPointAssembly.GetName().Name)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (assemblyName.Name == entryPointAssembly.GetName().Name)
if (assemblyName.Name.Equals(entryPointAssembly.GetName().Name))

Also, shouldn't we take into account OS to know if we can be case insensitive?

return LoadFromAssemblyPath(path);
}

return Default.LoadFromAssemblyName(assemblyName);
return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It presumably would be messy to fully turn on nullable, since it would require modifying more code, but it would be nice to at least temporarily turn on nullable here, since you might return a null value. Then you could make sure every code path can handle null.

@rainersigwald
Copy link
Contributor Author

@arturcic No, and for some reason VS didn't run it on my machine. When I select it explicitly I see it failing there. Looking.

@arturcic
Copy link
Member

arturcic commented Apr 6, 2020

I'm using Rider and the net472 test are succeeding, only the 3.1 tests are failing.

@rainersigwald
Copy link
Contributor Author

@arturcic Figured it out. Details in the commit message, but my change to allow loading more types from the task's folder in the GitLoaderContext meant that it loaded Microsoft.Build.Framework there only in the unit test, causing that failure (on .NET Core runtimes).

@arturcic
Copy link
Member

arturcic commented Apr 6, 2020

Thanks a lot @rainersigwald, waiting for the build to finish and if green will merge it. Glad someone from the msbuild team helped us fixing this

rainersigwald and others added 6 commits April 6, 2020 11:55
This allows the runtime to handle attempting to load the file in another ALC,
rather than forcing it to attempt to load it in the Default context.
When the entry-point task is loaded in an AssemblyLoadContext (as it is
in MSBuild 16.5+ on .NET Core), the default ALC cannot load assemblies
from next to the task location, so assemblies that are redistributed with
the GitVersionTask package aren't automatically found. Extend the task's
private ALC to support loading any assembly from the task's folder.
When MSBuild invokes a task from GitVersionTask.MsBuild, it creates an
AssemblyLoadContext and loads GitVersionTask into it, then tries to load
the type GitVersion.MSBuildTask.GitVersionTasks. The resulting type is then
reflected over and a delegate is constructed to call the method inside the
loaded type.

However, if the task is loaded in its own AssemblyLoadContext, as it is in
MSBuild 16.5+ on .NET Core, the types do not match between:

1. The WriteVersionInfoToBuildLog loaded in the task ALC (used in the cast), and
2. The WriteVersionInfoToBuildLog loaded in the GitLoaderContext (returned by
   reflection).

This throws a runtime error. It worked in earlier versions of MSBuild because
GitVersionTask.MsBuild was loaded in the default ALC by the MSBuild engine and
found in the default ALC by the GetType() call.

Change GitLoaderContext so that it returns the currently-loaded entry-point assembly
when trying to load that assembly's identity. This works in older MSBuild,
because it'll return the default ALC's instance of the type, and in newer
MSBuild, because it'll return the task ALC's instance--in both cases
accurately reflecting the task's view of the world.
This reverts commit 3f5392e, but leaves
the SDK at 3.1.201.
Unit tests were failing because they load MSBuild assemblies from the
test output directory. Normally they're not available there, so
GitLoaderContext doesn't load them. But in tests, they were there, and
being loaded in both ALC.Default (from the main part of the test) and
a GitLoaderContext created a type mismatch

```
MissingMethodException: Method not found: 'Microsoft.Build.Framework.ITaskItem[] GitVersion.MSBuildTask.Tasks.UpdateAssemblyInfo.get_CompileFiles()'.
   at GitVersion.MSBuildTask.GitVersionTaskExecutor.UpdateAssemblyInfo(UpdateAssemblyInfo task)
   at GitVersion.MSBuildTask.GitVersionTasks.<>c__DisplayClass1_0.<UpdateAssemblyInfo>b__0(IGitVersionTaskExecutor executor) in D:\a\1\s\src\GitVersionTask\GitVersionTasks.cs:line 15
   at GitVersion.MSBuildTask.GitVersionTasks.ExecuteGitVersionTask[T](T task, Action`1 action) in D:\a\1\s\src\GitVersionTask\GitVersionTasks.cs:line 30
```

That was happening because the loaded `UpdateAssemblyInfo.CompileFiles`
was of type (`Microsoft.Build.Framework.ITaskItem[]` in the
`GitLoaderContext`) while it was looking for
(`Microsoft.Build.Framework.ITaskItem[]` in
`AssemblyLoadContext.Default`).
@arturcic
Copy link
Member

arturcic commented Apr 6, 2020

Btw, @rainersigwald being you here, can you suggest some resources/links to github repos I can look at for integration test for msbuild tasks? I want to create some integration tests that will run the msbuild as process (.net core and full framework)

@arturcic arturcic merged commit 8923b87 into GitTools:master Apr 6, 2020
@arturcic
Copy link
Member

arturcic commented Apr 6, 2020

@rainersigwald thank you so much for your contributions 👍

@SeppPenner
Copy link

This looks good. Now we just need a new version :)

@arturcic
Copy link
Member

arturcic commented Apr 6, 2020

the new version will be 5.3.0, but I still have to finish some stuff. Hopefully this week we can release it

@rainersigwald
Copy link
Contributor Author

can you suggest some resources/links to github repos I can look at for integration test for msbuild tasks? I want to create some integration tests that will run the msbuild as process (.net core and full framework)

We don't have any super easy way to do this, unfortunately. https://github.com/jeffkl/MSBuildProjectCreator can help.

We've talked about authoring some packages to make it easier to write a task (and test it), but it hasn't gotten to the top of the to-do list. dotnet/msbuild#3569 is the best work item I could find.

@arturcic
Copy link
Member

arturcic commented Apr 6, 2020

Thank you, I'll check them

@SeppPenner
Copy link

the new version will be 5.3.0, but I still have to finish some stuff. Hopefully this week we can release it

That would be awesome 👍

@rainersigwald rainersigwald deleted the assemblyloadcontext branch April 6, 2020 17:50
@admin-simeon
Copy link

any update?

@davidhjones
Copy link

davidhjones commented Apr 15, 2020

Update? Running into this issue trying to get a new build setup on sdk 3.1.201

@arturcic
Copy link
Member

Sorry, busy on other projects these days, hope to get back this week

@bonesoul
Copy link

when we should expect a nuget beta release containing this fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

the combination of VS 2019 Version 16.5.0 Preview 2.0 and .NET Core 3.1.200-preview-014883 fails dotnet build
7 participants