Skip to content

Commit

Permalink
PackageVersion property for .NET Core projects
Browse files Browse the repository at this point in the history
Resolves NuGet/Home#3901.

Acquire package version property for a .NET Core project from the
`IVsProjectRestoreInfo` as provided via the Nominate API call.

Although the `$(PackageVersion)` property shouldn't differ between
different TFMs current API design assumes all project properties will be
evaluated per each TFM.

As a result `SolutionRestoreService` expects to get the
same value of package version defined in each TFM property bag.
Otherwise `InvalidOperationException` will be thrown.
  • Loading branch information
alpaix committed Jan 25, 2017
1 parent 258cffb commit 367ae05
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace NuGet.SolutionRestoreManager
[Export(typeof(IVsSolutionRestoreService))]
public sealed class VsSolutionRestoreService : IVsSolutionRestoreService
{
private const string PackageVersion = nameof(PackageVersion);
private const string Version = nameof(Version);
private const string IncludeAssets = "IncludeAssets";
private const string ExcludeAssets = "ExcludeAssets";
private const string PrivateAssets = "PrivateAssets";
Expand Down Expand Up @@ -196,6 +198,7 @@ private static PackageSpec ToPackageSpec(ProjectNames projectNames, IVsProjectRe
var packageSpec = new PackageSpec(tfis)
{
Name = projectNames.ShortName,
Version = GetPackageVersion(projectRestoreInfo.TargetFrameworks),
FilePath = projectFullPath,
RestoreMetadata = new ProjectRestoreMetadata
{
Expand All @@ -220,6 +223,29 @@ private static PackageSpec ToPackageSpec(ProjectNames projectNames, IVsProjectRe
return packageSpec;
}

private static NuGetVersion GetPackageVersion(IVsTargetFrameworks tfms)
{
// $(PackageVersion) property if set overrides the $(Version)
var versionPropertyValue =
GetNonEvaluatedPropertyOrNull(tfms, PackageVersion)
?? GetNonEvaluatedPropertyOrNull(tfms, Version);

return versionPropertyValue != null
? NuGetVersion.Parse(versionPropertyValue)
: PackageSpec.DefaultVersion;
}

// Trying to fetch a property value from tfm property bags.
// If defined the property should have identical values in all of the occurances.
private static string GetNonEvaluatedPropertyOrNull(IVsTargetFrameworks tfms, string propertyName)
{
return tfms
.Cast<IVsTargetFrameworkInfo>()
.Select(tfm => GetPropertyValueOrNull(tfm.Properties, propertyName))
.Distinct()
.SingleOrDefault();
}

private static RuntimeGraph GetRuntimeGraph(IVsProjectRestoreInfo projectRestoreInfo)
{
var runtimes = projectRestoreInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,24 @@ namespace NuGet.SolutionRestoreManager.Test
/// Helper class providing a method of building <see cref="IVsProjectRestoreInfo"/>
/// out of <see cref="PackageSpec"/>.
/// </summary>
internal static class ProjectRestoreInfoBuilder
internal class ProjectRestoreInfoBuilder
{
private readonly VsProjectRestoreInfo _pri;

private ProjectRestoreInfoBuilder(VsProjectRestoreInfo pri)
{
_pri = pri;
}

/// <summary>
/// Creates project restore info object to be consumed by <see cref="IVsSolutionRestoreService"/>.
/// </summary>
/// <param name="packageSpec">Source project restore object</param>
/// <returns>Desired project restore object</returns>
public static VsProjectRestoreInfo Build(
public static ProjectRestoreInfoBuilder FromPackageSpec(
PackageSpec packageSpec,
string baseIntermediatePath,
bool crossTargeting,
IEnumerable<LibraryRange> tools)
bool crossTargeting)
{
if (packageSpec == null)
{
Expand All @@ -36,18 +42,24 @@ public static VsProjectRestoreInfo Build(
return null;
}

var projectProperties = new VsProjectProperties { };

if (packageSpec.Version != null)
{
projectProperties = new VsProjectProperties
{
{ "PackageVersion", packageSpec.Version.ToString() }
};
}

var targetFrameworks = new VsTargetFrameworks(
packageSpec
.TargetFrameworks
.Select(ToTargetFrameworkInfo));
.Select(tfm => ToTargetFrameworkInfo(tfm, projectProperties)));

var pri = new VsProjectRestoreInfo(
baseIntermediatePath,
targetFrameworks)
{
ToolReferences = new VsReferenceItems(
(tools ?? Enumerable.Empty<LibraryRange>()).Select(ToToolReference))
};
targetFrameworks);

if (crossTargeting)
{
Expand All @@ -57,32 +69,61 @@ public static VsProjectRestoreInfo Build(
.Select(tfm => tfm.FrameworkName.GetShortFolderName()));
}

return pri;
return new ProjectRestoreInfoBuilder(pri);
}

private static VsTargetFrameworkInfo ToTargetFrameworkInfo(TargetFrameworkInformation tfm)
public ProjectRestoreInfoBuilder WithTool(string name, string version)
{
var packageReferences = new VsReferenceItems(
tfm.Dependencies
.Where(d => d.LibraryRange.TypeConstraint == LibraryDependencyTarget.Package)
.Select(ToPackageReference));

var projectReferences = new VsReferenceItems(
tfm.Dependencies
.Where(d => d.LibraryRange.TypeConstraint == LibraryDependencyTarget.ExternalProject)
.Select(ToProjectReference));

var projectProperties = new VsProjectProperties(
new VsProjectProperty(
var properties = new VsReferenceProperties
{
{ "Version", version }
};

_pri.ToolReferences = new VsReferenceItems
{
new VsReferenceItem(name, properties)
};

return this;
}

public ProjectRestoreInfoBuilder WithTargetFrameworkInfo(
IVsTargetFrameworkInfo tfi)
{
(_pri.TargetFrameworks as VsTargetFrameworks).Add(tfi);

return this;
}

public VsProjectRestoreInfo Build() => _pri;

private static VsTargetFrameworkInfo ToTargetFrameworkInfo(
TargetFrameworkInformation tfm,
IEnumerable<IVsProjectProperty> globalProperties)
{
var packageReferences = tfm
.Dependencies
.Where(d => d.LibraryRange.TypeConstraint == LibraryDependencyTarget.Package)
.Select(ToPackageReference);

var projectReferences = tfm
.Dependencies
.Where(d => d.LibraryRange.TypeConstraint == LibraryDependencyTarget.ExternalProject)
.Select(ToProjectReference);

var projectProperties = new VsProjectProperties
{
{
"PackageTargetFallback",
string.Join(";", tfm.Imports.Select(x => x.GetShortFolderName())))
);
string.Join(";", tfm.Imports.Select(x => x.GetShortFolderName()))
}
};

return new VsTargetFrameworkInfo(
tfm.FrameworkName.ToString(),
packageReferences,
projectReferences,
projectProperties);
projectProperties.Concat(globalProperties));
}

private static IVsReferenceItem ToPackageReference(LibraryDependency library)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,10 @@ public VsProjectProperties(IEnumerable<IVsProjectProperty> collection) : base(co
public VsProjectProperties(params IVsProjectProperty[] collection) : base(collection) { }

protected override string GetKeyForItem(IVsProjectProperty value) => value.Name;

public void Add(string propertyName, string propertyValue)
{
Add(new VsProjectProperty(propertyName, propertyValue));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace NuGet.SolutionRestoreManager.Test
/// </summary>
internal class VsProjectRestoreInfo : IVsProjectRestoreInfo
{
public String BaseIntermediatePath { get; }
public string BaseIntermediatePath { get; }

public string OriginalTargetFrameworks { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@ public VsReferenceProperties() : base() { }
public VsReferenceProperties(IEnumerable<IVsReferenceProperty> collection) : base(collection) { }

protected override String GetKeyForItem(IVsReferenceProperty value) => value.Name;

public void Add(string propertyName, string propertyValue)
{
Add(new VsReferenceProperty(propertyName, propertyValue));
}
}
}
Loading

0 comments on commit 367ae05

Please sign in to comment.