Skip to content
This repository has been archived by the owner on Jan 11, 2024. It is now read-only.

Commit

Permalink
Exclude:compile for implementation dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
ericstj committed Mar 2, 2016
1 parent a49e1f3 commit d40435b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<Compile Include="PromoteReferenceDependencies.cs" />
<Compile Include="CreateTrimDependencyGroups.cs" />
<Compile Include="ApplyBaseLine.cs" />
<Compile Include="EnsureOOBFramework.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<UsingTask TaskName="GenerateNuSpec" AssemblyFile="$(PackagingTaskDir)Microsoft.DotNet.Build.Tasks.Packaging.dll"/>
<UsingTask TaskName="NuGetPack" AssemblyFile="$(PackagingTaskDir)Microsoft.DotNet.Build.Tasks.Packaging.dll"/>
<UsingTask TaskName="ApplyBaseLine" AssemblyFile="$(PackagingTaskDir)Microsoft.DotNet.Build.Tasks.Packaging.dll"/>
<UsingTask TaskName="PromoteReferenceDependencies" AssemblyFile="$(PackagingTaskDir)Microsoft.DotNet.Build.Tasks.Packaging.dll"/>

<!-- Determine if we actually need to build for this architecture -->
<!-- Packages can specifically control their architecture by specifying the PackagePlatforms
Expand Down Expand Up @@ -503,6 +504,26 @@

<Target Name="GetNuGetPackageDependencies"
DependsOnTargets="GetFilePackageReferences">
<PropertyGroup>
<!-- determine if we have any reference assets in the package (files in the ref folder) -->
<_containsReferenceAsset Condition="'%(File.IsReferenceAsset)' == 'true'">true</_containsReferenceAsset>
</PropertyGroup>

<ItemGroup Condition="'$(_containsReferenceAsset)' == 'true'">
<!-- If the package contains a reference asset, then make all non-reference dependencies
exclude compile assets, so as not to leak implementation dependencies into the compile
graph.-->
<FilePackageDependency>
<Exclude Condition="'%(FilePackageDependency.IsReferenceAsset)' != 'true'">Compile</Exclude>
</FilePackageDependency>
</ItemGroup>

<!-- Promote reference dependencies to implementation TargetFrameworks -->
<PromoteReferenceDependencies Dependencies="@(FilePackageDependency)" Condition="'@(FilePackageDependency)' != ''">
<Output TaskParameter="PromotedDependencies" ItemName="FilePackageDependency" />
</PromoteReferenceDependencies>


<!-- We can reduce the number of dependencies listed for any framework that has
inbox implementations since that framework doesn't need the packages for
compile/runtime. This reduces the noise when consuming our packages in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.Frameworks;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.DotNet.Build.Tasks.Packaging
{
/// <summary>
/// Promotes dependencies from reference (ref) assembly TargetFramework to the implementation (lib) assembly
/// TargetFramework. NuGet only ever chooses a single dependencyGroup from a package. Often the TFM of the
/// implementation and reference differ so in order to ensure the dependencies from the reference assembly are
/// also applicable in all circumstances that the implementation is applicable we need to copy the reference
/// dependencies to the implementation group. Usually this is redundant since the implementation must expose
/// the surface area of the reference, but it is needed in two circumstances:
/// 1. The implementation is a facade and doesn't reference the contracts.
/// 2. We supress implementation dependencies with exclude=compile, but persist the reference dependencies.
/// </summary>
public class PromoteReferenceDependencies : PackagingTask
{
private const string IsReferenceMetadataName = "IsReferenceAsset";
private const string TargetFrameworkMetadataName = "TargetFramework";

[Required]
public ITaskItem[] Dependencies { get; set; }

[Output]
public ITaskItem[] PromotedDependencies { get; set; }

public override bool Execute()
{
List<ITaskItem> promotedDependencies = new List<ITaskItem>();

var actualDependencies = Dependencies.Where(d => d.ItemSpec != "_._").Select(d => new Dependency(d)).ToArray();

var implementationFxs = actualDependencies.Where(d => !d.IsReference).Select(d => d.TargetFramework).Distinct();

var referenceSets = actualDependencies.Where(d => d.IsReference).GroupBy(d => d.TargetFramework).ToDictionary(g => NuGetFramework.Parse(g.Key), g => g.ToArray());
var candidateFxs = referenceSets.Keys.ToArray();

if (candidateFxs.Length != 0)
{
foreach (var implementationFx in implementationFxs)
{
NuGetFramework fx = NuGetFramework.Parse(implementationFx);
if (referenceSets.ContainsKey(fx))
{
// if this implementation assembly fx has a matching reference fx skip promotion
continue;
}

var nearestReferenceFx = FrameworkUtilities.GetNearest(fx, candidateFxs);

if (nearestReferenceFx == null)
{
// This should never happen and indicates a bug in the package. If a package contains references,
// all implementations should have an applicable reference assembly.
Log.LogError($"Could not find applicable reference assembly for implementation framework {implementationFx} from reference frameworks {string.Join(", ", referenceSets.Keys)}");
}

foreach (var reference in referenceSets[nearestReferenceFx])
{
var promotedDependency = new TaskItem(reference.OriginalItem);
promotedDependency.SetMetadata(TargetFrameworkMetadataName, implementationFx);

promotedDependencies.Add(promotedDependency);
}
}
}

PromotedDependencies = promotedDependencies.ToArray();

return !Log.HasLoggedErrors;
}

private class Dependency
{
public Dependency(ITaskItem item)
{
Id = item.ItemSpec;
bool isReference = false;
bool.TryParse(item.GetMetadata(IsReferenceMetadataName), out isReference);
IsReference = isReference;
TargetFramework = item.GetMetadata(TargetFrameworkMetadataName);
OriginalItem = item;
}

public string Id { get; }
public bool IsReference { get; }
public string TargetFramework { get; }

public ITaskItem OriginalItem { get; }
}
}
}

0 comments on commit d40435b

Please sign in to comment.