Skip to content

Commit

Permalink
Merge pull request #21 from kzu/dev
Browse files Browse the repository at this point in the history
TargetPath and Compile pack improvements
  • Loading branch information
kzu authored Oct 21, 2020
2 parents e9b43e4 + 8c0869d commit 5ae7fa4
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 20 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ If the item does **not** provide a *PackagePath*, and *Pack* is not *false*, the

a. **PackFolder**: typically one of the [built-in package folders](https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/PackagingConstants.cs#L19), such as *build*, *lib*, etc.
b. **FrameworkSpecific**: *true*/*false*, determines whether the project's target framework is used when building the final *PackagePath*.
c. **TargetPath**: optional PackFolder-relative path for the item. If not provided, the relative path of the item in the project (or its *Link* metadata) is used.


When an item specifies *FrameworkSpecific=true*, the project's target framework is added to the final package path, such as `lib\netstandard2.0\My.dll`. Since the package folder itself typically determines whether it contains framework-specific files or not, the *FrameworkSpecific* value has sensible defaults so you don't have to specify it unless you wnat to override it. The [default values from NuGetizer.props](src/NuGetizer.Tasks/NuGetizer.props) are:

Expand Down Expand Up @@ -123,6 +125,32 @@ Whether items are packed by default or not is controlled by properties named aft
\* Back in the day, PDBs were Windows-only and fat files. Nowadays, portable PDBs
(the new default) are lightweight and can even be embedded. Combined with [SourceLink](https://github.com/dotnet/sourcelink), including them in the package (either standalone or embeded) provides the best experience for your users, so it's the default.

The various supported item inference are surfaced as `<PackInference Include="Compile;Content;None;..." />` items, which are ultimately evaluated together with the metadata for the individual items. These make the package inference candidates. You can also provide an exclude expression for that evaluation so that certain items are excluded by default, even if every other item of the same type is included. For example, to pack all `Content` items, except those in the `docs` folder, you can simply update the inference item like so:

```xml
<ItemGroup>
<PackInference Update="Content" PackExclude="docs/**/*.*" />
</ItemGroup>
```

Of course you could have achieved a similar effect by updating the Content items themselves too instead:

```xml
<ItemGroup>
<Content Update="docs/**/*.*" Pack="false" />
</ItemGroup>
```

By default (see [NuGetizer.Inference.props](src/NuGetizer.Tasks/NuGetizer.Inference.props)), `Compile` has the following exclude expression, so generated intermediate compile files aren't packed:

```xml
<ItemGroup>
<PackInference Include="Compile"
PackExclude="$(IntermediateOutputPath)/**/*$(DefaultLanguageSourceExtension)" />
</ItemGroup>
```


### CopyToOutputDirectory

There is a common metadata item that's used quite frequently: *CopyToOutputDirectory*, which is typically set to *PreserveNewest* to change it from its default behavior (when empty or set to *Never*).
Expand Down
21 changes: 21 additions & 0 deletions src/NuGetizer.Tasks/NuGetizer.Inference.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--
***********************************************************************************************
NuGetizer.Inference.props
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<ItemGroup>
<PackInference Include="Compile" PackExclude="$(IntermediateOutputPath)/**/*$(DefaultLanguageSourceExtension)" />
<PackInference Include="Content;EmbeddedResource;None;ApplicationDefinition;Page;
Resource;SplashScreen;DesignData;DesignDataWithDesignTimeCreatableTypes;
CodeAnalysisDictionary;AndroidAsset;AndroidResource;BundleResource" />
</ItemGroup>

</Project>
19 changes: 10 additions & 9 deletions src/NuGetizer.Tasks/NuGetizer.Inference.targets
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,17 @@ Copyright (c) .NET Foundation. All rights reserved.
<Pack Condition="'$(PackBundleResource)' == true">true</Pack>
<BuildAction>BundleResource</BuildAction>
</BundleResource>


<PackInference>
<PackExclude />
</PackInference>
<InferenceCandidate>
<DefaultPackFolder />
<Pack />
<PackagePath />
<PackFolder />
<ShouldPack />
<TargetPath />
</InferenceCandidate>
</ItemDefinitionGroup>

Expand Down Expand Up @@ -163,23 +167,20 @@ Copyright (c) .NET Foundation. All rights reserved.
Pack="false" />
</ItemGroup>
</Target>

<ItemGroup>
<PackInference Include="Compile;Content;EmbeddedResource;None;ApplicationDefinition;Page;
Resource;SplashScreen;DesignData;DesignDataWithDesignTimeCreatableTypes;
CodeAnalysisDictionary;AndroidAsset;AndroidResource;BundleResource" />
</ItemGroup>

<Target Name="InferPackageContents" DependsOnTargets="$(InferPackageContentsDependsOn)" Returns="@(PackageFile)">
<ItemGroup>
<InferenceCandidate Include="@(%(PackInference.Identity))" />
<InferenceCandidate Include="@(%(PackInference.Identity))" Exclude="@(%(PackInference.Identity) -> '%(PackExclude)')"/>
<InferenceCandidate>
<ShouldPack Condition="('%(Pack)' == 'true' or '%(PackagePath)' != '' or '%(PackFolder)' != '') and '%(Pack)' != 'false'">true</ShouldPack>
</InferenceCandidate>
<!-- No need to re-assign a target path if the item already provides one. We do this because otherwise the built-in AssignTargetPath
task unconditionally re-sets it. See https://github.com/dotnet/msbuild/blob/master/src/Tasks/AssignTargetPath.cs -->
<_InferenceCandidateWithTargetPath Include="@(InferenceCandidate)" Condition="'%(ShouldPack)' == 'true' and '%(TargetPath)' != ''" />
</ItemGroup>

<AssignTargetPath Files="@(InferenceCandidate)" RootFolder="$(MSBuildProjectDirectory)"
Condition="'%(ShouldPack)' == 'true'">
Condition="'%(ShouldPack)' == 'true' and '%(TargetPath)' == ''">
<Output TaskParameter="AssignedFiles" ItemName="_InferenceCandidateWithTargetPath" />
</AssignTargetPath>

Expand Down
1 change: 1 addition & 0 deletions src/NuGetizer.Tasks/NuGetizer.props
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Copyright (c) .NET Foundation. All rights reserved.
</PropertyGroup>

<Import Project="NuGetizer.Version.props" />
<Import Project="NuGetizer.Inference.props" Condition="'$(EnablePackInference)' != 'false'" />
<Import Project="NuGetizer.Authoring.props" Condition="'$(IsPackagingProject)' == 'true'" />
<Import Project="dotnet-nugetize.props" Condition="'$(dotnet-nugetize)' != ''"/>
</Project>
13 changes: 6 additions & 7 deletions src/NuGetizer.Tests/Builder.NuGetizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging.StructuredLogger;
using NuGet.ProjectManagement;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
Expand Down Expand Up @@ -77,12 +76,12 @@ public static TargetResult BuildProjects(
}

public static TargetResult BuildScenario(
string scenarioName,
object properties = null,
string projectName = null,
string target = "GetPackageContents",
ITestOutputHelper output = null,
LoggerVerbosity? verbosity = null)
string scenarioName,
object properties = null,
string projectName = null,
string target = "GetPackageContents",
ITestOutputHelper output = null,
LoggerVerbosity? verbosity = null)
{
var scenarioDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scenarios", scenarioName);
if (projectName != null && !Path.HasExtension(projectName))
Expand Down
4 changes: 4 additions & 0 deletions src/NuGetizer.Tests/Scenarios/Scenario.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,9 @@
<NuGetizerTargets>$(NuGetTargetsPath)\NuGetizer.Shared.targets</NuGetizerTargets>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)_._" />
</ItemGroup>

<Import Project="$(NuGetTargetsPath)\NuGetizer.props" Condition="'$(NuGetizerPropsImported)' != 'true'" />
</Project>
4 changes: 0 additions & 4 deletions src/NuGetizer.Tests/Scenarios/Scenario.targets
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
<ScenarioTargetsImported>true</ScenarioTargetsImported>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)_._" />
</ItemGroup>

<Target Name="Report" DependsOnTargets="GetPackageContents">
<Message Importance="high"
Text="%(_PackageContent.RelativeDir)%(_PackageContent.Filename)%(_PackageContent.Extension)
Expand Down
Empty file.
Empty file.
18 changes: 18 additions & 0 deletions src/NuGetizer.Tests/Scenarios/given_a_library/library.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$([MSBuild]::GetPathOfFileAbove(Scenario.props, $(MSBuildThisFileDirectory)))" />
<PropertyGroup>
<AssemblyName>library</AssemblyName>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
<EnableDefaultItems>true</EnableDefaultItems>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>true</GenerateTargetFrameworkAttribute>
</PropertyGroup>
<ItemGroup>
<!-- The _._ is added by the scenario targets so that projects aren't entirely empty of compile items -->
<Compile Remove="@(Compile -> WithMetadataValue('Extension', '._'))" />
</ItemGroup>
<ItemGroup Condition="'$(PackOnlyApi)' == 'true'">
<PackInference Update="Compile" PackExclude="%(PackExclude);*.cs" />
</ItemGroup>
</Project>
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<Content Include="relative\content-copy.txt" CopyToOutputDirectory="PreserveNewest" />
<None Include="none-with-packagepath.txt" PackagePath="build\%(Filename)%(Extension)" />
<Content Include="content-with-packagepath.txt" PackagePath="build\%(Filename)%(Extension)" />
<Content Include="content-with-targetpath.txt" Pack="true" TargetPath="relative\docs\%(Filename)%(Extension)" />
<None Include="non-existent-file.txt" Pack="true" />
</ItemGroup>
<Target Name="RemoveContent" Condition="'$(RemoveContent)' == 'true'" BeforeTargets="GetPackageContents">
Expand Down
58 changes: 58 additions & 0 deletions src/NuGetizer.Tests/given_a_library.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.IO;
using System.Linq;
using System.ServiceModel.Configuration;
using Microsoft.Build.Execution;
using Xunit;
using Xunit.Abstractions;

namespace NuGetizer
{
public class given_a_library
{
ITestOutputHelper output;

public given_a_library(ITestOutputHelper output)
{
this.output = output;
using var disable = OpenBuildLogAttribute.Disable();
Builder.BuildScenario(nameof(given_a_library), target: "Restore")
.AssertSuccess(output);
}

[Fact]
public void when_pack_compile_then_excludes_generated_files()
{
var result = Builder.BuildScenario(nameof(given_a_library),
new { PackCompile = "true" },
target: "Build,GetPackageContents,Pack");

Assert.True(result.BuildResult.HasResultsForTarget("GetPackageContents"));

var items = result.BuildResult.ResultsByTarget["GetPackageContents"];
var compile = items.Items.Where(item => item.Matches(new
{
BuildAction = "Compile",
})).ToArray();

Assert.Equal(2, compile.Length);
}

[Fact]
public void when_pack_excludes_additional_items_then_contains_only_matching_files()
{
var result = Builder.BuildScenario(nameof(given_a_library),
new { PackCompile = "true", PackOnlyApi = "true" },
target: "Build,GetPackageContents,Pack");

Assert.True(result.BuildResult.HasResultsForTarget("GetPackageContents"));

var items = result.BuildResult.ResultsByTarget["GetPackageContents"];
var compile = items.Items.Where(item => item.Matches(new
{
BuildAction = "Compile",
})).ToArray();

Assert.Single(compile);
}
}
}
17 changes: 17 additions & 0 deletions src/NuGetizer.Tests/given_a_library_with_content.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,23 @@ public void content_with_package_path_is_included_even_with_pack_content_false()
}));
}

[Fact]
public void content_with_target_path_is_included_relative_to_pack_folder()
{
var result = Builder.BuildScenario(nameof(given_a_library_with_content), new
{
PackageId = "ContentPackage",
PackContent = "false"
});

result.AssertSuccess(output);

Assert.Contains(result.Items, item => item.Matches(new
{
PackagePath = @"contentFiles\any\monoandroid51\relative\docs\content-with-targetpath.txt",
}));
}

#endregion

#region None scenarios
Expand Down

0 comments on commit 5ae7fa4

Please sign in to comment.