This repository has been archived by the owner on Jan 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 241
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exclude:compile for implementation dependencies
- Loading branch information
Showing
3 changed files
with
121 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
src/Microsoft.DotNet.Build.Tasks.Packaging/src/PromoteReferenceDependencies.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} | ||
} | ||
} |