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

Dev > Main #4

Merged
merged 9 commits into from
Sep 30, 2020
Merged
105 changes: 105 additions & 0 deletions src/NuGetizer.Tasks/InferImplicitPackageReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace NuGetizer.Tasks
{
public class InferImplicitPackageReference : Task
{
[Required]
public ITaskItem[] PackageReferences { get; set; } = Array.Empty<ITaskItem>();

[Required]
public ITaskItem[] PackageDependencies { get; set; } = Array.Empty<ITaskItem>();

[Output]
public ITaskItem[] ImplicitPackageReferences { get; set; } = Array.Empty<ITaskItem>();

public override bool Execute()
{
var packages = new ConcurrentDictionary<PackageIdentity, List<PackageIdentity>>();
Func<string, PackageIdentity> parse = value =>
{
var parts = value.Split('/');
return new PackageIdentity(parts[0], parts[1]);
};

// Build the list of parent>child relationships.
foreach (var dependency in PackageDependencies.Where(x => x.ItemSpec.Contains('/')))
{
var identity = parse(dependency.ItemSpec);
var parent = dependency.GetMetadata("ParentPackage");
if (!string.IsNullOrEmpty(parent))
{
packages.GetOrAdd(parse(parent), _ => new List<PackageIdentity>())
.Add(identity);
}
}

var inferred = new HashSet<PackageIdentity>();

foreach (var reference in PackageReferences.Where(x =>
"all".Equals(x.GetMetadata("PrivateAssets"), StringComparison.OrdinalIgnoreCase) &&
// Unless explicitly set to Pack=false
(!x.TryGetBoolMetadata("Pack", out var pack) || pack != false) &&
// NETCore/NETStandard are implicitly defined, we never need to bring them as deps.
!(bool.TryParse(x.GetMetadata("IsImplicitlyDefined"), out var isImplicit) && isImplicit)))
{
var identity = new PackageIdentity(reference.ItemSpec, reference.GetMetadata("Version"));
foreach (var dependency in FindDependencies(identity, packages))
{
inferred.Add(dependency);
}
}

ImplicitPackageReferences = inferred
.Select(x => new TaskItem(
x.Id,
new Dictionary<string, string>
{
{ "Version", x.Version } ,
{ "PrivateAssets", "all" },
}))
.ToArray();

return true;
}

IEnumerable<PackageIdentity> FindDependencies(PackageIdentity identity, IDictionary<PackageIdentity, List<PackageIdentity>> packages)
{
if (packages.TryGetValue(identity, out var dependencies))
{
foreach (var dependency in dependencies)
{
yield return dependency;
foreach (var child in FindDependencies(dependency, packages))
{
yield return child;
}
}
}
}

class PackageIdentity
{
public PackageIdentity(string id, string version)
=> (Id, Version)
= (id, version);

public string Id { get; }
public string Version { get; }

public override bool Equals(object obj)
=> obj is PackageIdentity dependency &&
dependency.Id == Id &&
dependency.Version == Version;

public override int GetHashCode() => Tuple.Create(Id, Version).GetHashCode();

public override string ToString() => Id + "/" + Version;
}
}
}
48 changes: 37 additions & 11 deletions src/NuGetizer.Tasks/NuGetizer.Inference.targets
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="NuGetizer.Tasks.InferImplicitPackageReference" AssemblyFile="NuGetizer.Tasks.dll" />

<PropertyGroup>
<!-- The Kind of primary output (build, symbols and doc) set if PackBuildOutput = true -->
Expand All @@ -36,6 +37,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<GetPackageContentsDependsOn>
$(GetPackageContentsDependsOn);
_BuildOutputFrameworkSpecific;
_SetDefaultPackageReferencePack;
InferPackageContents
</GetPackageContentsDependsOn>
</PropertyGroup>
Expand All @@ -59,15 +61,24 @@ Copyright (c) .NET Foundation. All rights reserved.
</_ReferenceRelatedPaths>
</ItemDefinitionGroup>

<Target Name="_BuildOutputFrameworkSpecific" Returns="$(BuildOutputFrameworkSpecific)">
<Target Name="_BuildOutputFrameworkSpecific" Condition="'$(BuildOutputFrameworkSpecific)' == ''" Returns="$(BuildOutputFrameworkSpecific)">
<!-- Determine whether primary output is framework specific -->
<ItemGroup Condition="'$(BuildOutputFrameworkSpecific)' == ''">
<ItemGroup>
<_BuildOutputKindFrameworkSpecific Include="@(PackageItemKind->'%(FrameworkSpecific)')" Condition="'%(Identity)' == '$(BuildOutputKind)'" />
</ItemGroup>
<PropertyGroup Condition="'$(BuildOutputFrameworkSpecific)' == ''">
<PropertyGroup>
<BuildOutputFrameworkSpecific>@(_BuildOutputKindFrameworkSpecific)</BuildOutputFrameworkSpecific>
</PropertyGroup>
</Target>

<Target Name="_SetDefaultPackageReferencePack" Condition="'$(BuildOutputKind)' == 'build'"
BeforeTargets="InferPrimaryOutputDependencies;InferPackageContents">
<ItemGroup>
<PackageReference Update="@(PackageReference)"
Condition="$([MSBuild]::ValueOrDefault('%(Identity)', '').StartsWith('Microsoft.Build')) and '%(Pack)' != 'true'"
Pack="false" />
</ItemGroup>
</Target>

<Target Name="InferPackageContents" DependsOnTargets="$(InferPackageContentsDependsOn)" Returns="@(PackageFile)">
<!-- Ensure TargetPath -->
Expand Down Expand Up @@ -132,7 +143,7 @@ Copyright (c) .NET Foundation. All rights reserved.
Condition="'%(_NoneToInfer.CopyToOutputDirectory)' == '' or '%(_NoneToInfer.CopyToOutputDirectory)' == 'Never'">
<Kind Condition="'%(_NoneToInfer.Kind)' == ''">None</Kind>
</_InferredPackageFile>

<_InferredPackageFile Include="@(PackageReference)"
Condition="'%(PackageReference.Identity)' != 'NuGetizer' and
'%(PackageReference.Identity)' != 'NETStandard.Library' and
Expand All @@ -145,7 +156,9 @@ Copyright (c) .NET Foundation. All rights reserved.
it also includes mscorlib which we don't need
TBD: maybe include ResolvedFrom=ImplicitlyExpandDesignTimeFacades too? -->
<_InferredPackageFile Include="@(ReferencePath->'%(OriginalItemSpec)')"
Condition="'$(PackFrameworkReferences)' == 'true' and '%(ReferencePath.ResolvedFrom)' == '{TargetFrameworkDirectory}' and '%(ReferencePath.Pack)' != 'false'">
Condition="'$(PackFrameworkReferences)' == 'true' and
'%(ReferencePath.ResolvedFrom)' == '{TargetFrameworkDirectory}' and
'%(ReferencePath.Pack)' != 'false'">
<Kind>FrameworkReference</Kind>
</_InferredPackageFile>
</ItemGroup>
Expand All @@ -165,47 +178,60 @@ Copyright (c) .NET Foundation. All rights reserved.
</ItemGroup>
</Target>

<Target Name="_CollectPrimaryOutputRelatedFiles" DependsOnTargets="BuildOnlySettings;ResolveReferences" Returns="@(_PrimaryOutputRelatedFile)">
<Target Name="_CollectPrimaryOutputDependencies" DependsOnTargets="BuildOnlySettings;ResolveReferences" Returns="@(ImplicitPackageReference)">
<ItemGroup>
<_PrimaryOutputRelatedFile Include="@(ReferencePath);@(_ReferenceRelatedPaths)"
Condition="'%(NuGetPackageId)' != 'NETStandard.Library' and
'%(Facade)' != 'true' and
'%(FrameworkFile)' != 'true' and
'%(Pack)' != 'false'"/>
<_PrivateAssetsPackageReference Include="@(PackageReference -> WithMetadataValue('PrivateAssets', 'all'))"
Condition="'%(PackageReference.IsImplicitlyDefined)' != 'true' and '%(PackageReference.Pack)' != 'false'"/>
</ItemGroup>
<InferImplicitPackageReference Condition="'@(_PrivateAssetsPackageReference)' != '' and '@(PackageDependencies)' != ''"
PackageReferences="@(_PrivateAssetsPackageReference)"
PackageDependencies="@(PackageDependencies)">
<Output TaskParameter="ImplicitPackageReferences" ItemName="ImplicitPackageReference" />
</InferImplicitPackageReference>
</Target>

<Target Name="_ResolvePackageDependencies" Condition="'$(UsingMicrosoftNETSdk)' == 'true'" DependsOnTargets="RunResolvePackageDependencies" />

<Target Name="InferPrimaryOutputDependencies"
Inputs="@(_PrimaryOutputRelatedFile)"
Outputs="%(_PrimaryOutputRelatedFile.NuGetPackageId)"
Returns="@(_InferredPackageFile)"
DependsOnTargets="_CollectPrimaryOutputRelatedFiles">
DependsOnTargets="_ResolvePackageDependencies;_CollectPrimaryOutputDependencies">

<ItemGroup>
<_NuGetPackageId Include="@(_PrimaryOutputRelatedFile -> '%(NuGetPackageId)')" Condition="'%(NuGetPackageId)' != 'NETStandard.Library'" />
</ItemGroup>
<PropertyGroup>
<_NuGetPackageId>@(_NuGetPackageId -> Distinct())</_NuGetPackageId>
</PropertyGroup>
<ItemGroup>
<_PrimaryPackageReference Include="@(PackageReference)" Condition="'$(_NuGetPackageId)' != '' and '%(Identity)' == '$(_NuGetPackageId)'" />
<_PrimaryPackageReference Include="@(PackageReference);@(ImplicitPackageReference)" Condition="'$(_NuGetPackageId)' != '' and '%(Identity)' == '$(_NuGetPackageId)'" />
</ItemGroup>

<PropertyGroup>
<_PrivateAssets>@(_PrimaryPackageReference -> '%(PrivateAssets)')</_PrivateAssets>
<_ShouldPack>@(_PrimaryPackageReference -> '%(Pack)')</_ShouldPack>
<_ShouldIncludeAssetsRegex>$(_NuGetPackageId)\\.+\\$(_PrivateAssets)\\.*</_ShouldIncludeAssetsRegex>
</PropertyGroup>

<ItemGroup Condition="'$(_PrivateAssets)' == 'all'">
<ItemGroup Condition="'$(_ShouldPack)' != 'false' and '$(_PrivateAssets)' == 'all'">
<_InferredPackageFile Include="@(_PrimaryOutputRelatedFile)" Condition="'%(_PrimaryOutputRelatedFile.FrameworkFile)' != 'true'">
<Kind>$(BuildOutputKind)</Kind>
<FrameworkSpecific>$(BuildOutputFrameworkSpecific)</FrameworkSpecific>
</_InferredPackageFile>
</ItemGroup>

<ItemGroup Condition="'$(_PrivateAssets)' != 'all' and '$(_PrivateAssets)' != 'none' and '$(_PrivateAssets)' != ''">
<ItemGroup Condition="'$(_ShouldPack)' != 'false' and '$(_PrivateAssets)' != 'all' and '$(_PrivateAssets)' != 'none' and '$(_PrivateAssets)' != ''">
<!-- In this case, we only add files that have a matching path to the private assets value.
i.e. for Mono.Options, PrivateAssets=lib, we'll include the file if its full path contains
'Mono.Options\*\lib\*', meaning the file is a lib. -->
<_InferredPackageFile Include="@(_PrimaryOutputRelatedFile)" Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(_PrimaryOutputRelatedFile.FullPath)', '$(_ShouldIncludeAssetsRegex)', 'RegexOptions.IgnoreCase')) == 'true'">
<_InferredPackageFile Include="@(_PrimaryOutputRelatedFile)"
Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(_PrimaryOutputRelatedFile.FullPath)', '$(_ShouldIncludeAssetsRegex)', 'RegexOptions.IgnoreCase')) == 'true'">
<Kind>$(BuildOutputKind)</Kind>
<FrameworkSpecific>$(BuildOutputFrameworkSpecific)</FrameworkSpecific>
</_InferredPackageFile>
Expand Down
7 changes: 1 addition & 6 deletions src/NuGetizer.Tasks/NuGetizer.Tasks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="15.9.20" PrivateAssets="all" />
<PackageReference Include="NuGet.Common" Version="5.7.0" PrivateAssets="all" />
<PackageReference Include="NuGet.Frameworks" Version="5.7.0" PrivateAssets="all" />
<PackageReference Include="NuGet.Packaging" Version="5.7.0" PrivateAssets="all" />
<PackageReference Include="NuGet.ProjectManagement" Version="4.2.0" PrivateAssets="all" />
<PackageReference Include="NuGet.Versioning" Version="5.7.0" PrivateAssets="all" />
<PackageReference Include="ThisAssembly" Version="0.10.6" PrivateAssets="all" />
</ItemGroup>

Expand All @@ -26,9 +23,7 @@
<None Update="@(None)" CopyToOutputDirectory="PreserveNewest" Pack="true" />
<None Update="NuGetizer.MultiTargeting.props" PackagePath="buildMultiTargeting\NuGetizer.props" />
<None Update="NuGetizer.MultiTargeting.targets" PackagePath="buildMultiTargeting\NuGetizer.targets" />
<None Include="NuGetizer.PackageMetadata.targets;dotnet-nugetize.props;dotnet-nugetize.targets"
PackagePath="buildMultiTargeting\%(Filename)%(Extension)"
Pack="true" />
<None Include="NuGetizer.PackageMetadata.targets;dotnet-nugetize.props;dotnet-nugetize.targets" PackagePath="buildMultiTargeting\%(Filename)%(Extension)" Pack="true" />
<None Include="..\..\img\nugetizer-256.png" Link="icon.png" PackagePath="icon.png" />
</ItemGroup>

Expand Down
4 changes: 2 additions & 2 deletions src/NuGetizer.Tasks/dotnet-nugetize.targets
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<PackageMetadata Update="@(PackageMetadata)" Nuspec="$(_AbsoluteNuspecFile)" NuPkg="@(PackageTargetPath)" />
<PackageContent Include="@(_PackageContent)" Exclude="@(PackageMetadata)" />
</ItemGroup>
<WriteItemsToFile Overwrite="false" Items="@(PackageMetadata)" ItemName="PackageMetadata" File="$(dotnet-nugetize)" />
<WriteItemsToFile Overwrite="false" Items="@(PackageContent)" ItemName="PackageContent" File="$(dotnet-nugetize)" />
<WriteItemsToFile Condition="'@(PackageMetadata)' != ''" Overwrite="false" Items="@(PackageMetadata)" ItemName="PackageMetadata" File="$(dotnet-nugetize)" />
<WriteItemsToFile Condition="'@(PackageContent)' != ''" Overwrite="false" Items="@(PackageContent)" ItemName="PackageContent" File="$(dotnet-nugetize)" />
</Target>

</Project>
Loading