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

Validate binding redirects #7153

Merged

Conversation

Forgind
Copy link
Member

@Forgind Forgind commented Dec 15, 2021

Fixes #7078

Context

If our binding redirects are off from the packages we actually ship, that can cause problems downstream when we try to integrate with anything other than MSBuild.

Changes Made

Added a post-build (and tests) validation step.

Testing

Built

Notes

How do I convert from a 3-part version to a 4-part version or vice versa? I imagine 3 -> 4 is easier if I can find the relevant assembly, but that would mean looking in our artifacts folder for something that matches its name and seeing if its version matches?
Worked around by looking for shipped assemblies with versions not matching our binding redirects.

I was timeboxing this with little bash experience. Sorry it's a mess. Please help fix 🙂
Has been fixed.

@Forgind Forgind changed the title Hacky, untested binding redirect validation Validate binding redirects Dec 16, 2021
@Forgind Forgind marked this pull request as ready for review December 16, 2021 01:20
</Task>
</UsingTask>

<Target Name="ValidateMSBuildPackageDependencyVersions" BeforeTargets="AfterBuild">
Copy link
Member

Choose a reason for hiding this comment

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

Only run this target on .NET Framework builds (since that's the only case where the binding redirects apply).

src/MSBuild/ValidateMSBuildPackageDependencyVersions.cs Outdated Show resolved Hide resolved
Comment on lines 44 to 50
foreach (var assemblyIdentityAttribute in dependentAssemblyXmlElement.Attributes)
{
if (assemblyIdentityAttribute is XmlAttribute assemblyIdentityAttributeXmlElement && assemblyIdentityAttributeXmlElement.Name.Equals("name", StringComparison.OrdinalIgnoreCase))
{
name = assemblyIdentityAttributeXmlElement.Value;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

You can simplify this as

Suggested change
foreach (var assemblyIdentityAttribute in dependentAssemblyXmlElement.Attributes)
{
if (assemblyIdentityAttribute is XmlAttribute assemblyIdentityAttributeXmlElement && assemblyIdentityAttributeXmlElement.Name.Equals("name", StringComparison.OrdinalIgnoreCase))
{
name = assemblyIdentityAttributeXmlElement.Value;
}
}
name = dependentAssemblyXmlElement.GetAttribute("name");

if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(version))
{
string path = Path.Combine(AssemblyPath, name + ".dll");
string assemblyVersion = File.Exists(path) ? Assembly.LoadFile(path).GetName().Version.ToString() : version;
Copy link
Member

Choose a reason for hiding this comment

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

Calling LoadFile will load the assemblies into the running MSBuild process in a way that's not unloadable, potentially causing problems on subsequent builds (the same problem you're tackling for task assemblies in #7132).

Fortunately since we just need the assembly identity, we don't need to use System.Reflection.Metadata here.

Suggested change
string assemblyVersion = File.Exists(path) ? Assembly.LoadFile(path).GetName().Version.ToString() : version;
string assemblyVersion = AssemblyName.GetAssemblyName(path).Version.ToString();

I don't think the File.Exists default is right--we want to guarantee that we ship the file if we have a binding redirect for it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've found a number of cases that seem pretty reasonable that we should ignore like M.B.Conversion.Core, M.B.Engine, and M.NET.StringTools.net35. I can make a list of assemblies to ignore, but it's a bit messy.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll ignore all of them, then let you comment on which to keep. (So far, I'd say those three and none of the others.)

src/MSBuild/ValidateMSBuildPackageDependencyVersions.cs Outdated Show resolved Hide resolved
{
if (!(String.Equals(name, "System.ValueTuple", StringComparison.OrdinalIgnoreCase) && String.Equals(version, "4.0.0.0") && String.Equals(assemblyVersion, "4.0.3.0")))
{
Log.LogError($"Binding redirect for '{name}' redirects to a different version ({version}) than MSBuild ships ({assemblyVersion}).");
Copy link
Member

Choose a reason for hiding this comment

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

It would be really nice if you could log the line number in the app.config in this error--then GitHub can put a nice annotation up indicating "you should edit here!"

This appears to be possible but nontrivial: https://stackoverflow.com/a/45182162

Copy link
Member Author

Choose a reason for hiding this comment

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

Slightly more complicated—this could be an issue in Packages.props or app.config, so if I specify a specific line (in app.config), I may be completely off. I think the current version is roughly as informative (gives the assembly name, so you can find it quickly) but without that possibility of erring.

Copy link
Member

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 agree with this reasoning--why not point explicitly to one of the locations? But since it's fiddly to get the line number you can leave it if you prefer.

Copy link
Member

@rainersigwald rainersigwald left a comment

Choose a reason for hiding this comment

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

Much cleaner!

src/MSBuild/MSBuild.csproj Show resolved Hide resolved
src/MSBuild/MSBuild.csproj Outdated Show resolved Hide resolved
{
if (!(String.Equals(name, "System.ValueTuple", StringComparison.OrdinalIgnoreCase) && String.Equals(version, "4.0.0.0") && String.Equals(assemblyVersion, "4.0.3.0")))
{
Log.LogError($"Binding redirect for '{name}' redirects to a different version ({version}) than MSBuild ships ({assemblyVersion}).");
Copy link
Member

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 agree with this reasoning--why not point explicitly to one of the locations? But since it's fiddly to get the line number you can leave it if you prefer.

src/MSBuild/ValidateMSBuildPackageDependencyVersions.cs Outdated Show resolved Hide resolved
[Required]
public string AssemblyPath { get; set; }

// Microsoft.Build.Conversion.Core and Microsoft.Build.Engine are deprecated. We don't ship them, but they're still used in VS for now. We should ensure they're the right version if present.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Microsoft.Build.Conversion.Core and Microsoft.Build.Engine are deprecated. We don't ship them, but they're still used in VS for now. We should ensure they're the right version if present.
// Microsoft.Build.Conversion.Core and Microsoft.Build.Engine are deprecated. We don't ship them, but they're still used in VS for now. We should ensure they're the right version if present.

Wait, sorry, I didn't notice this before. We do ship these assemblies. Why does this say we don't? Are they maybe not present in this project's output because they're not direct projectreferences?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call. Don't know what I was thinking here.

@Forgind Forgind merged commit 0006147 into dotnet:main Jan 7, 2022
@Forgind Forgind deleted the validate-packages-match-binding-redirects branch January 7, 2022 17:21
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.

Postbuild step to validate binding redirects to MSBuild VSIX assemblies
3 participants