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

Add AssignReferenceProperties task for MSBuild #1737

Merged
merged 6 commits into from
Oct 11, 2017
Merged

Add AssignReferenceProperties task for MSBuild #1737

merged 6 commits into from
Oct 11, 2017

Conversation

panopticoncentral
Copy link
Contributor

Bug

Link: dotnet/msbuild#1276
Regression: No

Fix

Details: This adds a new task, AssignReferenceProperties, that allows MSBuild to specify a project's target framework and that project's references, and have NuGet resolve what target frameworks should be used for each reference. This avoids having to add logic into MSBuild to figure out the compatibility matrix.

Testing/Validation

Tests Added: Yes
Validation done: Tests + been validated against MSBuild modified to use the task.

@dnfclas
Copy link

dnfclas commented Oct 6, 2017

@panopticoncentral,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by .NET Foundation. We will now review your pull request.
Thanks,
.NET Foundation Pull Request Bot


var possibleTargetFrameworks = MSBuildStringUtility.Split(targetFramework);
var possibleNuGetFrameworks = possibleTargetFrameworks.Select(ParseFramework).ToList();
var nearestNuGetFramework = NuGetFrameworkUtility.GetNearest(possibleTargetFrameworks, currentProjectTargetFramework, NuGetFramework.Parse);

Choose a reason for hiding this comment

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

Does this preserve the spelling of the target framework in possibleTargetFrameworks: e.g. net4.0 vs net40?

Copy link
Member

Choose a reason for hiding this comment

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

yes, this utility returns the original object back that is associated with the nearest framework, in this case it is the original string

{
var itemWithProperties = new TaskItem(project);

var targetFramework = project.GetMetadata("TargetFrameworks");

Choose a reason for hiding this comment

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

nit: This should be named targetFrameworks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.


In reply to: 143530143 [](ancestors = 143530143)

itemWithProperties.SetMetadata("UndefineProperties",
string.IsNullOrEmpty(undefineProperties)
? "TargetFramework"
: undefineProperties + ";TargetFramework");

Choose a reason for hiding this comment

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

IMHO, this is moving too much build logic in to NuGet. I was expecting the nuget task to be responsible for calculating nearest target framework and nothing else.

Choose a reason for hiding this comment

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

I suspect some handling of Rid-agnostic is missing here, which adds to my concern that we're handling this sort of thing here in nuget.

Copy link

@nguerrera nguerrera left a comment

Choose a reason for hiding this comment

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

See above

Copy link
Member

@emgarten emgarten left a comment

Choose a reason for hiding this comment

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

minor questions/comments


var possibleTargetFrameworks = MSBuildStringUtility.Split(targetFramework);
var possibleNuGetFrameworks = possibleTargetFrameworks.Select(ParseFramework).ToList();
var nearestNuGetFramework = NuGetFrameworkUtility.GetNearest(possibleTargetFrameworks, currentProjectTargetFramework, NuGetFramework.Parse);
Copy link
Member

Choose a reason for hiding this comment

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

yes, this utility returns the original object back that is associated with the nearest framework, in this case it is the original string

}

var possibleTargetFrameworks = MSBuildStringUtility.Split(targetFramework);
var possibleNuGetFrameworks = possibleTargetFrameworks.Select(ParseFramework).ToList();
Copy link
Member

Choose a reason for hiding this comment

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

Is this trying to validate all frameworks up front? Should this be put in the else block where it is used instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, I don't know that there's value in doing this at all, to be honest.


In reply to: 143551005 [](ancestors = 143551005)

{
var framework = NuGetFramework.Parse(name);

if (framework == null)
Copy link
Member

Choose a reason for hiding this comment

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

.Parse will return NuGetFramework.UnsupportedFramework for unknown frameworks, it will never return null.


if (nearestNuGetFramework != null)
{
if (TryConvertItemMetadataToBool(project, "HasSingleTargetFramework", out var singleTargetFramework) && singleTargetFramework)
Copy link
Member

Choose a reason for hiding this comment

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

if (MSBuildStringUtility.IsTrue(project.GetMetadata("HasSingleTargetFramework")))

I think this could be simplified to the above, it should only need to check if this value is true otherwise is false. I might be missing something here though, I don't see where the 2nd bool to see if the metadata was there at all comes into play here.

Log.LogError(string.Format(Strings.NoCompatibleTargetFramework, CurrentProjectTargetFramework, string.Join("; ", possibleNuGetFrameworks)));
}

return itemWithProperties;
Copy link
Member

Choose a reason for hiding this comment

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

nit: log output params?

@@ -81,6 +81,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<UsingTask TaskName="NuGet.Build.Tasks.GetRestoreSolutionProjectsTask" AssemblyFile="$(RestoreTaskAssemblyFile)" />
<UsingTask TaskName="NuGet.Build.Tasks.GetRestoreSettingsTask" AssemblyFile="$(RestoreTaskAssemblyFile)" />
<UsingTask TaskName="NuGet.Build.Tasks.WarnForInvalidProjectsTask" AssemblyFile="$(RestoreTaskAssemblyFile)" />
<UsingTask TaskName="NuGet.Build.Tasks.AssignReferenceProperties" AssemblyFile="$(RestoreTaskAssemblyFile)" />

Choose a reason for hiding this comment

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

This needs to be updated to the new task name with Task on the end.

return false;
}

AssignedProjects = AnnotatedProjectReferences.Select(project => AssignPropertiesForSingleReference(project, frameworkToMatch)).ToArray();
Copy link
Member

Choose a reason for hiding this comment

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

Allocations?

@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="NoCompatibleTargetFramework" xml:space="preserve">
<value>Cannot find a compatible framework for '{0}' in the following frameworks: {1}.</value>

Choose a reason for hiding this comment

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

@emgarten
Copy link
Member

emgarten commented Oct 10, 2017

@panopticoncentral Would it make more sense to have a task that only finds the nearest framework from a set of strings? Or would it impact perf to have to make multiple calls to this task instead?

I'm fine with this change overall, my thought is just that if there was a task that just found the nearest compatible framework it could save adding another task in the future.

@@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="NoCompatibleTargetFramework" xml:space="preserve">
<value>Cannot find a compatible framework for '{0}' in the following frameworks: {1}.</value>
<value>Project '{0}' targets '{2}'. It cannot be referenced by a project that targets '{1}'.</value>

Choose a reason for hiding this comment

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

Looks like the placeholder ordering is different here than the call site.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reordered the callsite, so the message is:
Project '{0}' targets '{2}'. It cannot be referenced by a project that targets '{1}'.
and the callsite is:
string.Format(Strings.NoCompatibleTargetFramework, project.ItemSpec, CurrentProjectTargetFramework, targetFrameworks)

It looked correct in the unit tests.

Choose a reason for hiding this comment

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

You're right, I read wrong.

@panopticoncentral
Copy link
Contributor Author

@emgarten We did talk about that but we felt it was better performance to do it all at once.

@AndyGerlicher
Copy link

Looks good. Verified works in my testing.

@emgarten emgarten merged commit dda0205 into NuGet:dev Oct 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants