Git Product home page Git Product logo

nugetizer's Introduction

Icon nugetizer

Simple, flexible, intuitive and powerful NuGet packaging.

Version Downloads License GitHub

CI Version CI Status

Why

The .NET SDK has built-in support for packing. The design of its targets, property and item names it not very consistent, however. When packing non-trivial solutions with multiple projects, it's quite hard to actually get it to pack exactly the way you want it to.

An alternative clean and clear design was proposed and I got to implement the initial spec, but it never got traction with the NuGet team.

How

You must install the NuGetizer package on all projects that are directly or indirectly being packed, since NuGetizer relies heavily on MSBuild to provide discovery of contributed package content from projects and their project references.

Package Manager:

Install-Package NuGetizer

CLI:

dotnet add package NuGetizer

MSBuild:

<PackageReference Include="NuGetizer" Version="..." />

You don't need to set PrivateAssets=all for NuGetizer: it will automatically exclude itself from your packed dependencies.

What

With the learnings from years of building and shipping packages of different levels of complexity, as well as significant use of the SDK Pack functionality and its various extension points, NuGetizer takes a fresh look and exposes a clean set of primitives so that you never have to create .nuspec files again.

All the built-in properties are supported.

A key difference is that adding arbitrary content to the package is supported with the first-class PackageFile item for absolute control of the package contents.

<ItemGroup>
    <PackageFile Include=".." PackagePath="..." />
</ItemGroup>

Another key design choice is that any package content inference should be trivial to turn off wholesale in case the heuristics don't do exactly what you need. Just set EnablePackInference=false and you will only get explicit PackageFile items in your package. This gives you ultimate control without having to understand any of the inference rules explained below.

All inference rules are laid out in a single .targets file that's easy to inspect them to learn more, and the file is not imported at all when EnablePackInference=false.

Package Readme

Since the introduction of package readme on nuget.org, more and more packages are leveraging this feature to make a package more discoverable and user friendly. One common need that arises is reusing existing documentation content that exists elsewhere in the project repository, such as on the root readme for the project (which typically contains additional information beyond user facing documentation, such as how to clone, build and contribute to the repository). In order to maximize reuse for these documentation files, NuGetizer supports includes in the package readme, such as:

This is the package readme.
<!-- include ../../../readme.md#usage -->

<!-- include ../../../footer.md -->

This readme includes a specific section of the repository root readme (via #usage), which is defined as follows:

# Project Foo
This is a general section on cloning, contributing, CI badges, etc.

<!-- #usage -->
# Usage
Here we explain our awesome API...
<!-- #usage -->
...

By defining both starting and closing #usage markup, the package readme can include a specific section. The footer, by contrast, is included wholesale.

When the .nupkg is created, these includes are resolved automatically so you keep content duplication to a minimum. Nested includes are also supported (i.e. footer.md might in turn include a sponsors.md file or a fragment of it).

dotnet-nugetize

Carefully tweaking your packages until they look exactly the way you want them should not be a tedious and slow process. Even requiring your project to be built between changes can be costly and reduce the speed at which you can iterate on the packaging aspects of the project. Also, generating the final .nupkg, opening it in a tool and inspecting its content, is also not ideal for rapid iteration.

For this reason, NuGetizer provides a dotnet global tool to make this process straightforward and quick. Installation is just like for any other dotnet tool:

> dotnet tool install -g dotnet-nugetize

After installation, you can just run nugetize from the project directory to quickly get a report of the package that would be generated. This is done in the fastest possible way without compromising your customizations to the build process. They way this is achieved is by a combination of a simulated design-time build that skips the compiler invocation and avoids the output file copying entirely, and built-in support in NuGetizer to emit the entire contents of the package as MSBuild items with full metadata, that the tool can use to render an accurate report that contains exactly the same information that would be used to emit the final .nupkg without actually generating it.

Here's a sample output screenshot:

nugetize screenshot

Inner Devloop

Authoring, testing and iterating on your nuget packages should be easy and straightforward. NuGetizer makes it trivial to consume your locally-built packages from a sample test project to exercise its features, by automatically performing the following cleanups whenever you build a new version of a package:

a. Clean previous versions of the same package in the package output path b. Clean NuGet cache folder for the package id (i.e. %userprofile%.nuget\packages\mypackage) c. Clean the NuGet HTTP cache: this avoids a subsequent restore from a consuming project from getting a cached older version, in case you build locally the same version number that was previously restored.

This means that to iterate quickly, these are the only needed steps:

  1. Build/Pack a new version
  2. Run Restore/Build on the sample project

To make the process smoother, consider the following tweaks:

  • Use single PackageOutputPath: if you create multiple packages, it's helpful to place them all in a single output directory. This can be achieved easily by adding the property to a Directory.Build.props file and place it at your repository root (or your src folder).:

    <PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)..\bin</PackageOutputPath>
  • Use <RestoreSources> in your consuming/test projects: this allows you to point to that common folder and even do it selectively only if the folder exists (i.e. use local packages if you just built them, use regular feed otherwise). You can place this too in a Directory.Build.props for all your consuming sample/test projects to use:

    <RestoreSources>https://api.nuget.org/v3/index.json;$(RestoreSources)</RestoreSources>
    <RestoreSources Condition="Exists('$(MSBuildThisFileDirectory)..\..\bin\')">
      $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\bin'));$(RestoreSources)
    </RestoreSources>

Package Contents Inference

Package content inference provides some built-in heuristics for common scenarios so you don't have to customize the project much and can instead let the rules build up the contents of your package by interpreting your existing project elements. It works by transforming various built-in items into corresponding PackageFile items, much as if you had added them by hand.

For example, if you create a readme.md file alongside the project, it will (by default) be automatically included in the package and set as the Readme metadata. Likewise, if you provide the $(PackageReadmeFile) property pointing to a different filename (say, readme.txt), it will also be automatically added to the package, without you having to add an explicit PackageFile or update the item with <None Update='readme.txt' Pack='true' /> so it packs properly.

NOTE: package readme inference can be turned off with the PackReadme=false project property.

Inference can be turned off for specific items by just adding Pack="false" item metadata. It can also be turned off by default for all items of a given type with an item definition group:

<ItemDefinitionGroup>
  <PackageReference>
    <Pack>false</Pack>
  </PackageReference>
</ItemDefinitionGroup>

The basic item metadata that drive pack inference are:

  1. Pack: true/false, determines whether inference applies to the item at all.
  2. PackagePath: final path within the package. Can be a directory path ending in \ and in that case the item's RelativeDir, Filename and Extension will be appended automatically. Linked files are also supported automatically.

If the item does not provide a PackagePath, and Pack is not false, the inference targets wil try to determine the right value, based on the following additional metadata:

  • PackFolder: typically one of the built-in package folders, such as build, lib, etc.
  • FrameworkSpecific: true/false, determines whether the project's target framework is used when building the final PackagePath.
  • 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 want to override it. The default values from NuGetizer.props are:

PackFolder FrameworkSpecific
content (*) true
lib true
dependency (**) true
frameworkReference (**) true
build false
all others (***) false

* Since the plain content folder is deprecated as of NuGet v3+, we use content to mean contentFiles throughout the docs, targets and implementation. They are interchangeable in NuGetizer and always mean the latter.

** dependency and frameworkReference are pseudo folders containing the package references and framework (<Reference ...) references.

** tool(s), native, runtime(s), ref, analyzer(s), source/src, any custom folder.

The PackFolder property (at the project level) determines the PackFolder metadata value for the build outputs of the project (and its xml docs, pdb and other related files like satellite assemblies). It defaults to lib.

For files that end up mapping to content, you can also specify BuildAction, CopyToOutput and Flatten item metadata, as supported by NuGet v4+. In addition to those, NuGetizer also supports CodeLanguage and TargetFramework to control the subfolders too.

Since it wouldn't be much fun having to annotate everything with either PackFolder or PackagePath (and also the additional content file metadata as needed), most common item types have sensible defaults too, defined in NuGetizer.Inference.targets.

ItemType Default Metadata
Content
EmbeddedResource
ApplicationDefinition
Page
Resource
SplashScreen
DesignData
DesignDataWithDesignTimeCreatableTypes
CodeAnalysisDictionary
AndroidAsset
AndroidResource
BundleResource
PackFolder="content"
BuildAction="[ItemType]"
None PackFolder=""
BuildAction="None"
Compile PackFolder="content"
BuildAction="Compile"
CodeLanguage="$(DefaultLanguageSourceExtension)"

None is sort of special in that the package folder is root of the package by default.

Whether items are packed by default or not is controlled by properties named after the item type (such as PackEmbeddedResource, PackNone and so on). Except for the ones below, they all default to false (or more precisely, empty, so, not true).

Property Default Value
PackBuildOutput true
PackReadme true
PackSymbols true if PackBuildOutput=true (*)
PackSatelliteDlls true if PackBuildOutput=true (**)
PackDependencies empty (***)
PackFrameworkReferences true if PackFolder=lib, false if PackDependencies=false
PackProjectReferences true

* 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, including them in the package (either standalone or embeded) provides the best experience for your users, so it's the default.

** Satellite resources can come from the main project or from dependencies, if those PackageReferences have PrivateAssets=all.

*** In some scenarios, you might want to turn off packing behavior for all PackageReference and FrameworkReferences alike. Setting PackDependencies=false achieves that.

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:

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

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

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

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

<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).

NOTE: if you're using Always, you're likely ruining your build performance for no reason.

When copying items to the output directory, you're implicitly saying that those items are needed in order to run/execute the built output. For example, if you have build targets/props in a build-only project (i.e. the one that builds the tasks), then those files are needed alongside the built output when packaging.

Given this common scenario, NuGetizer changes the default PackFolder metadata for packable items (i.e. those with explicit Pack=true metadata or defaulted to true, such as Content items) to match the PackFolder property defined for the project's built output, whenever CopyToOutputDirectory is not empty or Never.

Like other default inference behaviors, you can always opt out of it by specifying an explicit PackFolder item metadata.

In addition, the resulting PackageFile items for these items point to the location in the project's output folder, rather than the source location. This makes it easier to have custom behavior that might modify the item after copying to the output directory.

PackageReference

Package references are turned into package dependencies by default (essentially converting <PackageReference> to <PackageFile ... PackFolder="Dependency">), unless PackDependencies property is false. If the package reference specifies PrivateAssets="all", however, it's not added as a dependency. Instead, in that case, all the files contributed to the compilation (more precisely: all copy-local runtime dependencies) are placed in the same PackFolder as the project's build output (if packable, depending on PackBuildOutput property).

Build-only dependencies that don't contribute assemblies to the output (i.e. analyzers or things like GitInfo or ThisAssembly won't cause any extra items.

This even works transitively, so if you use PrivateAssets=all on package reference A, which in turn has a package dependency on B and B in turn depends on C, all of A, B and C assets will be packed. You can opt out of the transitive packing with PackTransitive=false metadata on the PackageReference.

As usual, you can change this default behavior by using Pack=false metadata.

You can also control precisely what assets are surfaced from your dependencies, by using PackInclude and PackExclude metadata on the PackageReference. This will result in the corresponding include/exclude attributes as documented in the nuspec reference. If not defined, both are defaulted to the package reference IncludeAssets and ExcludeAssets metadata.

ProjectReference

Unlike SDK Pack that considers project references as package references by default, NuGetizer has an explicit contract between projects: the GetPackageContents target. This target is invoked when packing project references, and it returns whatever the referenced project exposes as package contents (including the inference rules above). If the project is packable (that is, it produces a package, denoted by the presence of a PackageId property or IsPackable=true, for compatibility with SDK Pack), it will be packed as a dependency/package reference instead.

This means that by default, things Just Work: if you reference a library with no PackageId, it becomes part of whatever output your main project produces (analyzer, tools, plain lib). The moment you decide you want to make it a package on its own, you add the required metadata properties to that project and it automatically becomes a dependency instead.

This works flawlessly even when multi-targeting: if the main (packable) project multitargets net472;netcoreapp3.1, say, and it references a netstandard2.0 (non-packable) library, the package contents will be:

  /lib/
    net472/
      library.dll
      library.pdb
      sample.dll
      sample.pdb
    netcoreapp3.1/
      library.dll
      library.pdb
      sample.dll
      sample.pdb

If the packaging metadata is added to the library, it automatically turns to:

Package: Sample.1.0.0.nupkg
         ...\Sample.nuspec
    Authors                 : sample
    Description             : Sample
    Version                 : 1.0.0
  Dependencies:
    net472
      Library, 1.0.0
    netcoreapp3.1
      Library, 1.0.0
  Contents:
    /lib/
      net472/
        sample.dll
        sample.pdb
      netcoreapp3.1/
        sample.dll
        sample.pdb

If you need to tweak target folder of a referenced project, you can also do so via the PackFolder attribute on the ProjectReference itself:

   <ProjectReference Include="..\MyDesktopLibrary\MyDesktopLibrary.csproj" 
                     PackFolder="lib\net6.0\SpecificFolder" />

NOTE: this is a convenience shortcut since you can already pass additional project properties for project references using the built-in AdditionalProperties attribute.

Finally, you can focedly turn a project reference build output into a private asset even if it defines a PackageId by adding PrivateAssets=all. This is very useful for build and analyzer packages, which typically reference the main library project too, but need its output as private, since neither can use dependencies at run-time.

Packaging Projects

Typically, when creating a package involves more than one project (i.e. main library, some build tasks + targets, some other runtime tools), you will want to create a separate packaging project that is not a typical class library. For that purpose, you can create an .msbuildproj which has built-in support in Visual Studio. It can use the Microsoft.Build.NoTargets SDK as follows:

<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.Build.NoTargets/3.7.0">
  <PropertyGroup>
    <PackageId>MyPackage</PackageId>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
</Project>

NOTE: the requirement of a TargetFramework comes from the underlying SDK and the .NET SDK targets themselves, but this kind of project will not build any output. Running the nugetize on this project (after a dotnet restore) would render:

nugetize authoring screenshot

If you add a project reference to a build tasks project like the following:

<Project Sdk='Microsoft.NET.Sdk'>
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <PackFolder>buildTransitive</PackFolder>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include='Microsoft.Build.Tasks.Core' Version='16.6.0' />
  </ItemGroup>
</Project>

NOTE: this project would contain MSBuild tasks, and likely a [PackageId].targets alongside so that it's automatically imported in consuming projects.

The packaging project would now look as follows:

<Project Sdk='Microsoft.Build.NoTargets/3.7.0'>
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <PackageId>MyPackage</PackageId>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include='..\Tasks\Tasks.csproj' />
  </ItemGroup>
</Project>

And nugetize would show the following package structure:

nugetize authoring screenshot

Note that the targets file was automatically added to the package as expected. Packaging projects can reference other packaging projects in turn for complex packing scenarios too.

If the packaging project references both a build-targeting project (such as the one above) and also a regular library project, the package contents becomes the aggregation of the contents contributed by each referenced project automatically. For example, if you add a project reference from the packaging project to the following class library project:

<Project Sdk='Microsoft.NET.Sdk'>
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include='System.Text.Json' Version='6.0.0' />
  </ItemGroup>
</Project>

The content would now be:

nugetize authoring screenshot

You can also add a reference to a CLI tools program like the following:

<Project Sdk='Microsoft.NET.Sdk'>
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <OutputType>Exe</OutputType>
    <PackFolder>tools</PackFolder>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include='System.CommandLine' Version='2.0.0-beta3.22114.1' />
  </ItemGroup>
</Project>

nugetize authoring screenshot

As you can see, it's quite trivial to build fairly complex packages using very intuitive defaults and content inference.

Advanced Features

This section contains miscellaneous useful features that are typically used in advanced scenarios and are not necessarily mainstream.

PackAsPublish for CLI tools

When a project's output type is Exe and it's not set to PackAsTool=true (used specifically for .NET tools), it will default to be use the Publish output for packing. This is typically what you want for a CLI project, since dependencies are included in the publish directory automatically without having to annotate any references with PrivateAssets=all.

This can be turned off by setting PackAsPublish=false on the project, which will cause the project to be packed as a regular class library, with the dependencies inference rules applied (such as PrivateAssets=all for package reference and CopyLocal=true for references).

When packing as publish, the output won't be framework-specific by default, and will just contribute the published contents to the specified PackFolder.

Dynamically Extending Package Contents

If you need to calculate additional items to inject into the package dynamically, you can run a target before GetPackageContents, which is the target NuGetizer uses before packing to determine what needs to be included. At this point you can add arbitrary <PackageFile ... PackagePath=... /> items laying out precisely what it is you want to inject into the .nupkg. For example:

<Target Name="AddPackageContents" BeforeTargets="GetPackageContents">
    <ItemGroup>
        <PackageFile Include="$(MSBuildProjectDirectory)\..\docs\**\*.md" PackagePath="docs\%(RelativeDir)%(Filename)%(Extension)" />
    </ItemGroup>
</Target>

This example will add all markdown files in a docs folder one level above the current project, and place them all under the docs folder in the .nupkg, preserving their original folder structure.

Packing arbitrary files from referenced packages

If you want to pack files from referenced packages, you can simply add PackageReference attribute to PackageFile. Say we want to reuse the awesome icon from the ThisAssembly package, we can just bring it in with:

<ItemGroup>
  <PackageFile Include="icon-128.png" PackagePath="icon.png" PackageReference="ThisAssembly" />
</ItemGroup>

The project will need to reference that package too, of course:

<ItemGroup>
  <PackageReference Include="ThisAssembly" Version="1.0.0" GeneratePathProperty="true" Pack="false" />
</ItemGroup>

Note that we had to add the GeneratePathProperty to the reference, so that the package-relative path icon-128.png can be properly resolved to the package install location. You can also set that metadata for all your PackageReferences automatically by adding the following to your Directory.Build.props (or .targets):

  <ItemDefinitionGroup>
    <PackageReference>
      <!-- This enables referencing arbitrary files from any package by adding PackageReference="" to any packable item -->
      <GeneratePathProperty>true</GeneratePathProperty>
    </PackageReference>

Also note that in the scenario shown before, we don't want to pack the reference as a dependency (it's a build-only or development dependency package). That is, this feature does not require a package dependency for the referenced package content we're bringing in.

It even works for inferred content item types, such as None:

<ItemGroup>
  <None Include="icon-128.png" PackageReference="ThisAssembly" />
</ItemGroup>

Skip Build during Pack

If you are building explicitly prior to running Pack (and you're not using PackOnBuild=true), you might want to optimize the process by skipping the automatic Build run that happens by default when you run Pack by setting BuildOnPack=false. Not building before Pack with BuildOnPack=false can cause the target run to fail since output files expected by the packaging might be missing (i.e. the primary output, content files, etc.).

This option is useful in combination with BuildProjectReferences=false when packing on CI, since at that point all that's run are the P2P protocol involving GetPackageContents.

Package Validation

Package validation is a new feature in .NET 6 that allows you to validate that your multi-targeting library packages offer consistent APIs across all targets. Since it's quite important to validate that your packages are consistent across all targets, NuGetizer turns this feature on by default for Release builds in multi-targeting projects (unlike the default which is strictly opt-in).

You can turn this off by setting the following property at the project level:

<PropertyGroup>
  <EnablePackageValidation>false</EnablePackageValidation>
</PropertyGroup>

Sponsors

Clarius Org Kirill Osenkov MFB Technologies, Inc. Stephen Shaw Torutek DRIVE.NET, Inc. Ashley Medway Keith Pickford Thomas Bolon Kori Francis Toni Wenzel Giorgi Dalakishvili Uno Platform Dan Siegel Reuben Swartz Jacob Foshee Eric Johnson Ix Technologies B.V. David JENNI Jonathan Oleg Kyrylchuk Charley Wu Jakob Tikjรธb Andersen Seann Alexander Tino Hager Mark Seemann Ken Bonny Simon Cropp agileworks-eu sorahex Zheyu Shen Vezel ChilliCream 4OTC

Sponsor this project ย 

Learn more about GitHub Sponsors

nugetizer's People

Contributors

adalon avatar antonc9018 avatar ap0llo avatar azeno avatar christophlindemann avatar denjmpr avatar dependabot[bot] avatar devlooped-bot avatar gpwen avatar iskiselev avatar kzu avatar mhutch avatar migueldeicaza avatar mrward avatar patridge avatar thorgeir avatar williamb1024 avatar wjk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

nugetizer's Issues

Populate RepositoryUrl/RepositoryCommit early

We currently only populate these properties when the GetPackageMetadata target is run, which is part of the Pack/GetPackageContents call chain. If you want to use these properties earlier, however (such as in assembly metadata or some ThisAssembly constants), you need to tweak the build to force it to run.

It's better if we also run our property inits whenever the InitializeSourceControlInformation target runs, which will typically be before assembly info/version generation is run, which is early enough for most usages.

Cannot place .NET Framework 4.7.2 project reference dll inside lib\net5.0\SpecificFolder

Describe the Bug

Firstly I want to thank you for implementing such great project! It really simplifies the way the NuGet packages can be created.

There are situations when we need to put .NET Framework 4.7.2 dll built from project reference inside lib\net5.0\SpecificFolder (or any other place under lib folder).
If it would be a .nuspec file I would want to generate such structure of files there:

  <files>
	<file src="ClassLibraryNET5.dll" target="lib\net5.0"/>
	<file src="ClassLibraryNET472.dll" target="lib\net5.0\SpecificFolder"/>
  </files>

Could you explain how to do it?

Steps to Reproduce

  1. Open NugetizerProjectReferenceTest.sln from attached file.
    NugetizerProjectReferenceTest.zip
  2. Pack NugetizerPack.msbuildproj project with Pack menu item from Visual Studio context menu.
  3. Unzip the result NugetizerPack.1.0.0.nupkg file.
  4. Observe - there is no ClassLibraryNET472.dll file under "lib\net5.0\SpecificFolder" folder.

Expected Behavior

The project reference dll can be placed anywhere under lib folder of result NuGet package.

Version Info

NuGetizer 0.7.1

Packaging project generates package improperly

Describe the Bug

When using a packaging project to pack both my MSBuild tasks/targets with the actual library references a netcore50 target is added to the lib outputs with a number of runtime references.

Additionally the net472 target framework from the MSBuild project is added while a 3rd MSBuild project is only added as a NuGet dependency for the net472 target.

Steps to Reproduce

See demo project...

  • Have a multi-targeting library project
  • Have a MSBuild targets project
  • Have another MSBuild targets project that generates it's own core nuget.
  • Have a msbuildproj packing project that packs each of these

Expected Behavior

The target frameworks should come from the multi-targeting library. The net472 target from the MSBuild project should be ignored... the netcore50 target should not exist. The "Core" project should be added as an all around reference.

Exception with Stack Trace

n/a

Version Info

0.7.1

Additional Info

PackingDemo.zip

Support multi-targeting with NoTargets and/or Traversal SDKs

Both NoTargets and Traversal SDKs are very convenient for creating packaging projects.

NoTargets, as a simple aggregator of content for a single-targeting (one TF) package. Traversal for multi-package building from a single entry point.

Once scenario that isn't working right now, is for multi-targeting packaging projects. Neither SDK will actually pack the "outer" packaging project itself.

We could also just introduce a NuGetizer SDK too, based off of the Traversal SDK most likely.

AfterPack task breaks nugetize command

Describe the Bug

When the project files has a Target with AfterTargets="Pack" , nugetize command (in dev console) gives an error
The project packs without problems.

Steps to Reproduce

add this to your project

<Target Name="AfterPack" AfterTargets="Pack">
      <Exec Command="dotnet nuget push $(PackageOutputPath)*.nupkg --source LocalNuGet" />
</Target>

Exception with Stack Trace

nugetize
EXEC : error : File does not exist (bin\Debug\*.nupkg). [xxxx.csproj]


  Usage: dotnet nuget push [arguments] [options]

  Arguments:
    [root]  Specify the path to the package and your API key to push the package to the server.

  Options:
    -h|--help                      Show help information
    --force-english-output         Forces the application to run using an invariant, English-based culture.
    -s|--source <source>           Package source (URL, UNC/folder path or package source name) to use. Defaults to DefaultPushSource if specified in NuGet.Config.
    -ss|--symbol-source <source>   Symbol server URL to use.
    -t|--timeout <timeout>         Timeout for pushing to a server in seconds. Defaults to 300 seconds (5 minutes).
    -k|--api-key <apiKey>          The API key for the server.
    -sk|--symbol-api-key <apiKey>  The API key for the symbol server.
    -d|--disable-buffering         Disable buffering when pushing to an HTTP(S) server to decrease memory usage.
    -n|--no-symbols                If a symbols package exists, it will not be pushed to a symbols server.
    --no-service-endpoint          Does not append "api/v2/package" to the source URL.
    --interactive                  Allow the command to block and require manual action for operations like authentication.
    --skip-duplicate               If a package and version already exists, skip it and continue with the next package in the push, if any.
xxxx.csproj(35,7): error MSB3073: The command "dotnet nuget push bin\Debug\*.nupkg --source LocalNuGet" exited with code 1.

Several issues when packing a project referencing a multi-target project

Describe the Bug

When packing a single-target project that references a multi-target project, I'm facing 3 different issues that I'll try to describe here.
I'm a bit lost and I'd really appreciate some help. Thanks

Steps to Reproduce

TestNugetizer.zip

Open the repro TestNugetizer.zip and extract it. It contains 3 projects: LibA (a single-target project, references LibB) ; LibB (a mutli-target project) ; LibPackaging (a multi-target project, references LibA, and is used to build both LibA and LibB properly).

Go to the build folder, run clean.bat first, then pack.bat. There are 4 steps (clean, restore, build, pack). It should build all dlls and produce a TestNugetizer.1.0.0.nupkg file.

First issue: open the nupkg file ; the lib\monoandroid11.0\ folder should contain both LibA.dll and LibB.dll, but it's missing LibB.dll. The lib\netstandard2.1\ folder contains both libs, which is expected.

Second issue: open the nupkg file, and then the TestNugetizer.nuspec file. The Microsoft.Extensions.Http dependency is missing in the MonoAndroid11.0 targetFramework.

Third issue: the pack.bat produces the following error, which is weird:

NuGetizer.Shared.targets(277,5): error NG0012: Duplicate package source files with distinct content detected. Duplicates are not allowed in the package. Please remove the conflict between these files: 'D:\Dev\Temp\TestNugetizer\LibB\bin\Debug\netstandard2.1\LibB.dll' > 'lib\monoandroid11.0\LibB.dll' [D:\Dev\Temp\TestNugetize
r\LibPackaging\LibPackaging.csproj]
NuGetizer.Shared.targets(277,5): error NG0012: 'D:\Dev\Temp\TestNugetizer\LibB\bin\Debug\monoandroid11.0\LibB.dll' > 'lib\monoandroid11.0\LibB.dll' [D:\Dev\Temp\TestNugetizer\LibPackaging\LibPackaging.csproj]
NuGetizer.Shared.targets(277,5): error NG0012: 'D:\Dev\Temp\TestNugetizer\LibB\bin\Debug\netstandard2.1\LibB.pdb' > 'lib\monoandroid11.0\LibB.pdb' [D:\Dev\Temp\TestNugetizer\LibPackaging\LibPackaging.csproj]
NuGetizer.Shared.targets(277,5): error NG0012: 'D:\Dev\Temp\TestNugetizer\LibB\bin\Debug\monoandroid11.0\LibB.pdb' > 'lib\monoandroid11.0\LibB.pdb' [D:\Dev\Temp\TestNugetizer\LibPackaging\LibPackaging.csproj]

Version Info

Nugetizer 0.7.0

Document support for packaging projects

When creating non-trivial nugets with multiple projects included, the simple instructions in the current docs typically fall short and you need a proper packaging project instead.

This is already supported by nugetizer, but not documented at all. We need to document how a simple packaging project like:

<Project Sdk="Microsoft.Build.NoTargets/2.0.1" DefaultTargets="Pack">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <PackageId>MyPackage</PackageId>
    <Description>...</Description>
    <PackageTags>dotnet proxy runtime dynamic</PackageTags>
  </PropertyGroup>
  <ItemGroup>
     <!--  PackageReference/ProjectReference/Content/None/etc. items just work here too -->
  </ItemGroup>
</Project>

can be used to get very granular control of what's packed and where. The project needs to have a .msbuildproj extension.

May need multi-targeting feature from #89 to be a comprehensive solution.

Automatically default PackageProjectUrl to RepositoryUrl from source control info

If SourceLink is used, the RepositoryUrl will be initialized from the PrivateRepositoryUrl (as long as PublishRepositoryUrl=true). This URL should also be the default for PackageProjectUrl since it's quite common for OSS projects to not have a separate site.

NOTE: neither value is defaulted to the URL unless PublishRepositoryUrl=true, so it's an explicit opt-in, to avoid leaking internal URLs

Packing with Microsoft.Build.NoTargets doesn't put dependency under proper target framework group when reference other Microsoft.Build.NoTargets project

Describe the Bug

Hi @kzu!
I want to create "Microsoft.Build.NoTargets" project which uses Library project as reference and another "Microsoft.Build.NoTargets" which uses another Library project and the first "Microsoft.Build.NoTargets" project as references.
In the generated package's nuspec file the first "Microsoft.Build.NoTargets" project should be in the proper target framework group dependency, but it is inside common group and target framework group dependency is empty:

<dependencies>
  <group>
    <dependency id="Common" version="1.0.0" />
  </group>
  <group targetFramework="net5.0" />
</dependencies>

Steps to Reproduce

  1. Open HierarchyTest.sln from attached file.
    HierarchyTest.zip
  2. Build HierarchyTest.sln solution with Visual Studio.
  3. Unzip the result Core.1.0.0.nupkg file.
  4. Open Core.nuspec file with some editor.
  5. Observe - the "Common" package is inside common group dependency and there is empty net5.0 target framework group.

Expected Behavior

The "Common" package is inside net5.0 target framework group dependency.

Version Info

NuGetizer 0.7.5

dotnet.exe nugetize My.sln errors if one project doesn't PackageReference Nugetizer

Describe the Bug

dotnet.exe nugetize My.sln errors if one project doesn't PackageReference Nugetizer

Steps to Reproduce

Create a solution with two projects, /A/A.csproj and /B/B.csproj and only A.csproj references Nugetizer:

<PackageReference Include="Nugetizer" Version="*" />

run dotnet.exe nugetize on the top-level folder containing the solution file.

Expected Behavior

Plain Old CSProj should not need to know about nugetizer.

Alternatively, or additionally, there should be a nugetize option to warn or silence warnings related to projects not having a PackageReference to Nugetizer.

Alternatively, it seems like there is some additional error checking that should happen on the csproj itself, such that it could check that PackageReference Include="Nugetizer" is present or not. However, that could have versioning issues depending on how dotnet-nugetize is built due to assembly version mismatches between Nugetizer and dotnet-nugetize.

Exception with Stack Trace

PS D:\Users\John.Zabroski\source\infrastructure2\master\Prototype> dotnet.exe nugetize
D:\Users\John.Zabroski\source\infrastructure2\master\Prototype\B.csproj : error MSB4057: The target "GetPackageContents" does not exist in the project.

Version Info

0.7

Additional Info

n/a

No longer able to generate separate snupkg symbol packages.

msbuild-nugetize.log
msbuild.log
Once you add nugetize as a package to your projects, it is no longer possible to generate a separate symbol packages as per the discussion here: https://docs.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg.

I am relying on setting the <IncludeSymbols> and <SymbolPackageFormat> to generate the separate files that are then pushed to a package server and a symbol server respectively.

I have a global target file that is included in all my projects that has the settings for <IncludeSymbols> and <SymbolPackageFormat> and other properties that control the source link and repository url.

This is my target file:

<Project>
  <PropertyGroup>
    <!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
 
    <!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
  
    <!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.Bitbucket.Git" Version="1.0.0-beta2-19367-01" PrivateAssets="All"/>
    <SourceLinkBitbucketGitHost Include="source.host.com" Version="3.7.1"/>
  </ItemGroup>
</Project>

Then as the last item in my .csproj file I import the symbol target file:

Import Project="$(SolutionDir)..\SymbolConfiguration.targets" />

I have provided MSBuild logs. The msbuild.log file works as expected and generates the separate symbol package. The msbuild-nugetize.log creates one package that contains the dlls and the symbols.

Is there a way to have a separate symbol package generated?

Thanks

-marc

Multitarget Issues

With the release of .NET5, I've been trying to do some multitargeting by having my libraries target both .NET5 and .net standard 2.1.

I have a solution with two libraries.
One is netstandard2.1. The other depends on the first and is netcore3.1.

Both compile and work fine as described above, and also if both are upgraded to net5.0.

The issue comes up when i multitarget /w <TargetFrameworks>net5.0;netstandard2.1</TargetFrameworks>, for example.

The first library works and compiles fine. The second fails with this error:

Some project references cannot be properly packaged. 
Please install the NuGetizer package on the following projects: 
..\TheParentProject\TheParentProject.csproj

Not 100% certain it's not user error on my side, but I figured I'd see if you saw similar results

Generate package with symbols?

In my project we have following configuration for our nuget packages

  <PropertyGroup>
    <!-- Embed source files that are not tracked by the source control manager - compiler-generated source, like AssemblyInfo, are included in the PDB.-->
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
    <!-- Source code embedded into pdb.-->
    <EmbedAllSources>true</EmbedAllSources>
    <!-- Defines the level of debug information that you want to be generated. Valid values are "full," "pdbonly," "portable", "embedded", and "none." -->
    <DebugType>portable</DebugType>
    <!-- A boolean value that indicates whether symbols are generated by the build. -->
    <DebugSymbols>true</DebugSymbols>
    <!-- This Boolean value indicates whether the package should create additional symbols package when the project is packed -->
    <IncludeSymbols>true</IncludeSymbols>
    <!-- If "snupkg", a snupkg symbol package is created containing the portable PDBs. -->
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  </PropertyGroup>

When configured project in that way we have two packages - one main package and package with format snupkg with symbols and source codes. Unfortunately when we add nugetizer to our project it generates only one package and symbols are integrated in same package. Can we generate snupkg when nugetizer is enabled?

Pack issues with multi targeting

When using the TargetFrameworks property in a project, a Build called "inner build" is run as part of the main Build target, for each defined target framework.
The issue is that the Pack target is also running for those inner builds, when it shouldn't. The reason why it shouldn't is because the Pack should run just once as part of the main Build target and include the outputs for all the target frameworks as sub folders inside the .nupkg. Running also for inner builds causes the .nupgk to be generated more than once and just for a single target framework, overriding the previous one. This ends up getting a .nupkg with only one target framework folder (the latest defined in the TargetFrameworks property).

If the Pack target is run separated from the Build target (/t:Pack), it runs correctly; which confirms the theory that what is causing the problem is the combination of Build + Inner Builds (multi targeting) + Pack.

A workaround has been found and consist of defining the following target in the .csproj or in the Directory.Build.targets file:

<Target Name="PackAfterInnerBuilds" AfterTargets="DispatchToInnerBuilds" DependsOnTargets="Pack" />

Note that DispatchToInnerBuilds is a target defined in Microsoft.Common.CrossTargeting.targets and it's the one responsible of running the inner builds for each target framework, so by running an extra Pack after this completes, we ensure that the malformed .nupgk is overriden with a correct one including all the target frameworks.

Of course this is not performant and it's just a workaround. This issue is reproducible by just creating a single .net Standard Class Library and editing the .csproj like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net472;netstandard2.0</TargetFrameworks>
  </PropertyGroup>

  <PropertyGroup>
    <PackageId>TestNuGetizer</PackageId>
    <Description>Test Package</Description>
    <PackOnBuild>True</PackOnBuild>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageReference Include="NuGetizer" Version="0.4.12" />
  </ItemGroup>
</Project>

Then build the project and observe the resulting .nupkg that contains just a single target framework folder in lib instead of two.

Cannot use PackageRequireLicenseAcceptance = true with Microsoft.Build.NoTargets sdk

Hello again!
I started to use your project widely - it is really helpful.
But I figured out a few issues which blocks me from making packages - I'm not sure - maybe I miss something.
I'll post them all as separate issues to your repository in the near future.

Describe the Bug

The current problem:
There is Microsoft.Build.NoTargets sdk project which references other Microsoft.NET.Sdk project.
In the Microsoft.Build.NoTargets project I'm trying to use PackageRequireLicenseAcceptance property, but getting Error "Enabling license acceptance requires a license or a licenseUrl to be specified. The licenseUrl will be deprecated, consider using the license metadata." I also use PackageLicenseFile property and placed "License.txt" file inside the package with PackageFile item.

Steps to Reproduce

  1. Open PackageLicenseFileExample.sln from attached file.
    PackageLicenseFileExample.zip
  2. Build PackageLicenseFileExample.sln solution with Visual Studio.
  3. Observe - Error "Enabling license acceptance requires a license or a licenseUrl to be specified. The licenseUrl will be deprecated, consider using the license metadata."

Expected Behavior

There is no error and during installing NuGet package dialog about license acceptance will appear.

Version Info

NuGetizer 0.7.4

Exclude assembly that is added by consumed package targets

Discussed in #120

Originally posted by quasarea June 29, 2021
Hi,

I have library that depends on Grpc.Core package. This package has a targets file that adds assemblies to output

...
  <ItemGroup Condition="'$(Grpc_SkipNativeLibsCopy)' != 'true'">
    <Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\grpc_csharp_ext.x86.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <Link>grpc_csharp_ext.x86.dll</Link>
      <Visible>false</Visible>
    </Content>
...

How can I exclude grpc_csharp_ext.x86.dll from being packaged? I have tried several combinations of

    <PackInference Update="Content" PackExclude="**/grpc_csharp_ext.x86.dll;**/grpc_csharp_ext.x64.dll" />
    <PackInference Update="Content" PackExclude="$(OutputPath)/**/grpc_csharp_ext.x64.dll" />
    <PackInference Update="Content" PackExclude="$(OutputPath)/grpc_csharp_ext.x64.dll" />
    <PackInference Update="Content" PackExclude="**/grpc_csharp_ext.x64.dll" />

But nothing worked :(

Add PackageFolder support as project property

Scenario: analyzers\cs (for roslyn source generators, for example).
This would mean it's a custom (non-TFM specific) folder/PackageItemKind.
In this sense, PackageItemKind is basically a shorthand for PackageFolder+FrameworkSpecific really.

I don't think we need PackageFolder + FrameworkSpecific project property though. The framework-specific folders are well-known and built-in, so I don't foresee a need for that combination.

If short sha is available, use it for package metadata

The short SHA (9-char version) is sufficiently unique for use to retrieve the upstream commit/tree.

Whenever the short sha is available, we should use that instead. An opt-out property UseShortSourceRevisionId should allow turning off this behavior.

Allow specifying PackFolder in ProjectReference

Instead of having to use the less intuitive AdditionalProperties like so:

    <ProjectReference Include="..\ClassLibraryNET472\ClassLibraryNET472.csproj" 
                      AdditionalProperties="PackFolder=lib\net5.0\SpecificFolder" />

Make it possible to just place PackFolder on the ProjectReference itself too:

    <ProjectReference Include="..\ClassLibraryNET472\ClassLibraryNET472.csproj" 
                      PackFolder="lib\net5.0\SpecificFolder" />

Originally asked on #118 by @denjmpr.

Re-enable build/tests on macOS/linux

When bumping to NET6, the new dotnet (SDK? CLI?) fails to run tests that target net472 due to MSBuild internal implementation details/changes:

Can't find custom attr constructor image: /home/runner/.dotnet/sdk/6.0.300/Microsoft.Build.dll mtoken: 0x0a000007 due to: Could not resolve type with token 01000009 from typeref (expected class 'System.Resources.NeutralResourcesLanguageAttribute' in assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') assembly:System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a type:System.Resources.NeutralResourcesLanguageAttribute member:(null)

Bumping tests to net6.0 caused a never ending stream of issues, where MSBuild wouldn't even resolve properly anymore and ALL tests in ALL platforms were broken. Therefore, as a stop-gap measure, I disabled the builds on on non-win OS for now.

When inferring content, PackInference.PackExclude isn't properly evaluated

We're currently including items for inference with the following (somewhat esoteric) MSBuild:

<InferenceCandidate Include="@(%(PackInference.Identity))" Exclude="@(%(PackInference.Identity) -> '%(PackExclude)')"/>

What this does is evaluate the identity of each PackInference so that the Include contains, for example, @(Content). Since the Include attribute does not contain actual item specs, the Exclude isn't actually filtering out anything, causing this basic extensibility point in nugetizer to malfunction.

Discussed in #120

Originally posted by quasarea June 29, 2021
Hi,

I have library that depends on Grpc.Core package. This package has a targets file that adds assemblies to output

...
  <ItemGroup Condition="'$(Grpc_SkipNativeLibsCopy)' != 'true'">
    <Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\grpc_csharp_ext.x86.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <Link>grpc_csharp_ext.x86.dll</Link>
      <Visible>false</Visible>
    </Content>
...

How can I exclude grpc_csharp_ext.x86.dll from being packaged? I have tried several combinations of

    <PackInference Update="Content" PackExclude="**/grpc_csharp_ext.x86.dll;**/grpc_csharp_ext.x64.dll" />
    <PackInference Update="Content" PackExclude="$(OutputPath)/**/grpc_csharp_ext.x64.dll" />
    <PackInference Update="Content" PackExclude="$(OutputPath)/grpc_csharp_ext.x64.dll" />
    <PackInference Update="Content" PackExclude="**/grpc_csharp_ext.x64.dll" />

But nothing worked :(

Add PackDependencies property to opt-out of dependencies packing easily

NuGet provides a SuppressDependenciesWhenPacking property to opt-out wholesale of PackageReference/framework reference dependencies packing.

This is convenient in particular for tools that pack all their runtime dependencies alongside the primary executable.

The new synthetized property would behave just like the recently added support for SupressDependenciesWhenPacking property, so that SuppressDependenciesWhenPacking becomes the default value of PackDependencies directly.

Do BuildAction and CopyToOutput work?

I was trying to add a content file to a package I was creating, in my case an XML config file.
In my situation I want a user doing a PackageReference to see the file in Visual Studio and for the file to be copied to the output directory on a build.
So I added something like
<ItemGroup> <Content Include="Foo.config" PackFolder="contentFiles\any\any" BuildAction="None" CopyToOutput ="true" /> </ItemGroup>
When the resultant packege that is build is referenced I see the Foo.config appear. However it's properties are
BuildActrion = C# compiler
Copy to Output Directory = Do Not copy
Based on the readme stuff I have tried every variant of the packaging line I can think of and nothing seems to work and I can't find any good examples of this. So I am starting to wonder if I am missing something obvious or if this doesn't work.

Normalize all paths for consistency, use SDK Pack format

Currently, we perform no path normalization in the AssignPackagePath task, and this means we can easily end with mixed paths, making it difficult to tell apart which are the same, and when comparing via code, having to take into account the possibility that there might be a mixture.

To simplify things, we should normalize all paths to use the / forward slash which is more compatible for cross-platform projects, is already supported universally in MSBuild as well as NuGet, and entirely avoids having to do \\ double slashes for escaping (or @ literals) in code.

Floating version problem

I'm using nugetizer 0.6 and have following issue when using floating versions. In my project I have PackageReference like this

<PackageReference Include="packageName" Version="1.0.1-preview.*" />

on my artifactory I have this package in versions from preview.1 to preview.6 but package created from my project have dependency with version 1.0.1-preview.0

When nugetizer is removed from project package dependency have proper 1.0.1-preview.6 version which point to actual version. It looks like nugetizer is not handling floating versions properly?

When using PackOnBuild in multitargeting project, only one TFM is packed

Given a project:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net472;netstandard2.0</TargetFrameworks>
  </PropertyGroup>

  <PropertyGroup>
    <PackageId>TestNuGetizer</PackageId>
    <Description>Test Package</Description>
    <PackOnBuild>True</PackOnBuild>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageReference Include="NuGetizer" Version="*" />
  </ItemGroup>
</Project>

Running build will result in a package that only contains netstandard2.0 libs.

Reported by @mauroa

Can't get a project to pack correctly (multi-project, with analyzer)

Overview

Hello, I discovered this project yesterday and it looks super promising, so great work on this! ๐Ÿ˜„
I've been trying to use this in my own project, ComputeSharp, but so far I have not been able to figure out how to configure this correctly, and I'm wondering what I'm doing wrong. I looked at the docs and I couldn't find a solution there either.

I'm trying to build/pack this .csproj file in particular. The structure of my project is like this:

NuGet package structure (click to expand):

image

So essentially:

  • There will only be a single ComputeSharp package on NuGet, including all assemblies.
  • The .SourceGenerators assembly will need to automatically be added as an analyzer to consuming projects.

To do this, I did the following in the .csproj file:

  <ItemGroup>
    <ProjectReference Include="..\ComputeSharp.Core\ComputeSharp.Core.csproj" />
    <ProjectReference Include="..\ComputeSharp.Graphics\ComputeSharp.Graphics.csproj" />
    <ProjectReference Include="..\ComputeSharp.Shaders\ComputeSharp.Shaders.csproj" />
    <ProjectReference Include="..\ComputeSharp.SourceGenerators\ComputeSharp.SourceGenerators.csproj" PrivateAssets="All" PackagePath="analyzers\dotnet\cs" />
  </ItemGroup>

I have a few issues though:

  • When running the nugetize tool on my project, I get a message sayaing "the project is not packable", even though I did add all the packing metadata to it. I also tried setting Pack=True and PackOnBuild=True, same result.
  • Looking at the results for the nugetize tool, it seems the analyzer is not put in the correct folder. It seems the analyzers/dotnet/cs folder is created but just remains empty, and the analyzer is instead added as if I was using multi-targeting in my project. Am I doing something wrong here in the .csproj? I get the following output:
Nugetize result (click to expand):
Project  is not packable, rendering its contributed package contents.
  Dependencies:
    net5.0
      Microsoft.Toolkit, 7.0.0-preview4
      TerraFX.Interop.Windows, 10.0.19041-beta2-379749354
    netstandard2.0
      Microsoft.Toolkit, 7.0.0-preview4
  Contents:
    /icon.png
    /runtimes/
      win-x64/
        native/
          dxcompiler.dll
          dxil.dll
    /analyzers/
      dotnet/
        cs
    /lib/
      net5.0/
        ComputeSharp.Core.dll
        ComputeSharp.Core.pdb
        ComputeSharp.dll
        ComputeSharp.Graphics.dll
        ComputeSharp.Graphics.pdb
        ComputeSharp.pdb
        ComputeSharp.Shaders.dll
        ComputeSharp.Shaders.pdb
      netstandard2.0/
        ComputeSharp.Core.dll
        ComputeSharp.Core.pdb
        ComputeSharp.SourceGenerators.dll
        ComputeSharp.SourceGenerators.pdb
        Microsoft.CodeAnalysis.CSharp.dll
        Microsoft.CodeAnalysis.dll
        System.Buffers.dll
        System.Collections.Immutable.dll
        System.Memory.dll
        System.Numerics.Vectors.dll
        System.Reflection.Metadata.dll
        System.Runtime.CompilerServices.Unsafe.dll
        System.Text.Encoding.CodePages.dll
        System.Threading.Tasks.Extensions.dll
  • When running dotnet pack -c Release, I just get no package at all - the command runs with no errors but it just does nothing. I think this is caused by that "project is not packable" message from above, but I don't get why that is.

EDIT: just noticed that adding the ProjectReference to the .csproj like that also causes the SG to break when used in the sample projects. Not sure if this is expected or if it's just a thing that happens when used locally and not through NuGet. Would love to have a way to make this work in both cases though, and I'm also not sure that ProjectReference for the SG in the main project is correct at all, since the SG is not actually a dependency of that project ๐Ÿค”

I couldn't find any info on the docs/readme specifically regarding these "mixed" scenarios, with a single package containing both binaries to be used by consumers, as well as a SG that should also be added as an analyzer by consuming projects.

Steps to Reproduce

  • Clone ComputeSharp.
  • Checkout to nuget
  • Go to src\ComputeSharp
  • Run dotnet build -c Release and dotnet pack -c Release

Expected Behavior

The package would be created with the referenced projects in the correct location.
Of course, I'm very probably just missing something obvious here, but I can't figure out what ๐Ÿ˜…

Version Info

  • NuGetizer version 0.6.0

Files with absolute paths that do not specify a PackFolder are not included in the final nupkg

Describe the Bug

Specifying a PackageFile to an absolute path without specifying a PackFolder causes that file not to be included in the resulting nupkg

Steps to Reproduce

Specify a PackageFile with an absolute path as follows:

<PackageFile Include="$(BinaryLocation)\web\service\bin\Microsoft.Oasys.ServiceLogging.dll"/>

BinaryLocation is an MSBuild property that resolves to an absolute path ("D:\OM\Import\x64\debug\pkgnuget_oasys\x-none\x64\oasys\x-none" in this case).

The diag logging for "AssignPackagePath" shows the following Task Parameter:

  Files=
      D:\OM\Import\x64\debug\pkgnuget_oasys\x-none\x64\oasys\x-none\web\service\bin\Microsoft.Oasys.ServiceLogging.dll
              OriginalItemSpec=
              PackageId=EngTelemetryLogging
              PackageReference=
              PackageReferencePath=
              PackageReferencePathProperty=
              PackFolder=None
              Platform=AnyCPU
              Source=Explicit

Which results in the following Output Item:

  _PackageContent=
      D:\OM\Import\x64\debug\pkgnuget_oasys\x-none\x64\oasys\x-none\web\service\bin\Microsoft.Oasys.ServiceLogging.dll
              OriginalItemSpec=D:\OM\Import\x64\debug\pkgnuget_oasys\x-none\x64\oasys\x-none\web\service\bin\Microsoft.Oasys.ServiceLogging.dll
              PackageFolder=
              PackageId=EngTelemetryLogging
              PackagePath=D:/OM/Import/x64/debug/pkgnuget_oasys/x-none/x64/oasys/x-none/web/service/bin/Microsoft.Oasys.ServiceLogging.dll
              PackageReference=
              PackageReferencePath=
              PackageReferencePathProperty=
              PackFolder=None
              Platform=AnyCPU
              Source=Explicit
              TargetPath=D:/OM/Import/x64/debug/pkgnuget_oasys/x-none/x64/oasys/x-none/web/service/bin/Microsoft.Oasys.ServiceLogging.dll

The resulting nupkg then does not include that file in the package.

Expected Behavior

The expectation is that the file would be packed to the root of the package, which would likely mean stripping the absolute path to define the following:

  _PackageContent=
      D:\OM\Import\x64\debug\pkgnuget_oasys\x-none\x64\oasys\x-none\web\service\bin\Microsoft.Oasys.ServiceLogging.dll
              OriginalItemSpec=D:\OM\Import\x64\debug\pkgnuget_oasys\x-none\x64\oasys\x-none\web\service\bin\Microsoft.Oasys.ServiceLogging.dll
              PackageFolder=
              PackageId=EngTelemetryLogging
              PackagePath=Microsoft.Oasys.ServiceLogging.dll
              PackageReference=
              PackageReferencePath=
              PackageReferencePathProperty=
              PackFolder=None
              Platform=AnyCPU
              Source=Explicit
              TargetPath=Microsoft.Oasys.ServiceLogging.dll

Version Info

NuGetizer 0.7.5

Enable GeneratePathProperty by default

Since you can now include arbitrary files from any package with:

<ItemGroup>
   <PackageFile Include="tools/foo.exe" PackageReference="Foo" PackagePath="tools/%(Filename)%(Extension)" />
</ItemGroup>

It would be better to just turn on the GeneratePathProperty metadata for all PackageReference by default, since that only incurs minimal overhead in that it will create a new PkgPACKAGE_ID property on nuget restore, but that's about it.

Allow packing content from referenced packages

Say you want to bring into your package some files that are included in some other package (i.e. reuse an icon, some targets, etc.), without necessarily introducing a package dependency on it.

It would be handy to be able to annotate a PackageFile item with PackageReference="ID" and have that cause the file to be pulled from the install path for the referenced package automatically.

Proposed syntax:

<ItemGroup>
  <PackageFile Include="icon-128.png" PackagePath="icon.png" PackageReference="ThisAssembly" />
</ItemGroup>

Where ThisAssembly was a PackageReference for the project declaring the PackageFile. Ideally, it should also integrate with package content inference (i.e. using Content/None/etc. as the item name, instead of PackageFile).

IsPackable default

One thing that caught me off guard for a while was that the default for IsPackable is false? I believe without Nugetizer it's default true, so the lack of property definition means it's packed.

I eventually figured it out and added the property everywhere, but it was initially disorienting

Add support for SuppressDependenciesWhenPacking for compatibility with SDK pack

From NuGet/Home#6354, the SuppressDependenciesWhenPacking property can turn off package reference and framework reference dependencies inference in SDK pack.

NuGetizer offers more granularity for this, via:

  • PackFrameworkReferences: to ignore framework references
  • %(PackageReference.Pack): to ignore specific package references.

So perhaps a compatibility behavior would be to set the following if SuppressDependenciesWhenPacking==true:

  • If PackFrameworkReferences == '', set it to false
  • Set %(PackageReference.Pack) = false if empty

When package reference is direct but also indirect, pack fails to include it

Given a project like:

<Project Sdk='Microsoft.NET.Sdk'>
  <PropertyGroup>
    <PackageId>Library</PackageId>
    <TargetFramework>net472</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include='Microsoft.VisualStudio.Shell.Interop' Version='16.7.30328.74' PrivateAssets='all' />
    <PackageReference Include='Microsoft.VisualStudio.Shell.Interop.12.0' Version='16.7.30328.74' PrivateAssets='all' />        
  </ItemGroup>
</Project>

Which directly references a package that is also included transitively like the above (interop.12 indirectly references interop), the direct package reference is not packed.

Multitargeting => No auto nuget package creation

If I go to multiple target frameworks, it no longer produces a nuget package. I am able to get one with an explicit dotnet pack, but not the automatic one after building. If I knock my project back to a single framework target, it creates a nuget package on build as expected.

When packing transitive dependencies for PrivateAssets=all, pack lib, not ref

If a package reference with PrivateAssets=all has transitive dependencies and the project targets netstandard2.0, the transitive dependency is packed from the ref (reference assembly) folder instead of the lib folder, making the reference unusable at run-time.

Repro:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <PackageId>TransitiveRef</PackageId>
    <Description>TransitiveRef</Description>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="NuGetizer" Version="0.5.0" />
    <PackageReference Include="Scriban" Version="3.0.4" PrivateAssets="all" />
  </ItemGroup>

</Project>

Package contents seems to be correct:

โฏ nugetize
Package: TransitiveRef.1.0.0.nupkg
         C:\Temp\bin\TransitiveRef.nuspec
    Authors    : TransitiveRef
    Description: TransitiveRef
    Version    : 1.0.0
  Contents:
    /lib/
      netstandard2.0/
        Microsoft.CSharp.dll
        Scriban.dll
        System.Runtime.CompilerServices.Unsafe.dll
        System.Threading.Tasks.Extensions.dll
        TransitiveRef.dll
        TransitiveRef.pdb

However, opening the System.Runtime.CompilerServices.Unsafe.dll assembly reveals it to be a reference assembly with no implementations.

Allow opting out of transitive private assets package reference packing

Currently, you can pack an entire graph of transitive dependencies along with your package by simply adding PrivateAssets=all on the PackageReference:

  <ItemGroup>
    <PackageReference Include="NuGetizer" Version="0.5.0" />
    <PackageReference Include="Scriban" Version="3.0.4" PrivateAssets="all" />
  </ItemGroup>

There is currently no way of stopping the transitivity, however. You'd have to manually add each dependency and set Pack=false on it to stop this behavior.

We should support a new PackTransitive=false attribute to opt-out of this behavior. I'd still keep the transitive packing on by default since that's the most common approach when doing private assets for a dependency (in my experience, i.e. tools or msbuild tasks).

AppendTargetFrameworkToOutputPath not respected by PackageFile target

Describe the Bug

AppendTargetFrameworkToOutputPath not respected by PackageFile target, such that the output from dotnet nugetize custom.csproj is as follows:

Package: Custom.0.0.0.1-dev.nupkg
         D:\Users\John.Zabroski\source\infrastructure\trunk\Prototype\bin\Custom.nuspec
    Authors    : John.Zabroski
    Copyright  : Simmaculate c 2021
    Description: Package Description
    Icon       : icon.png
    Version    : 0.0.0.1-dev
  Contents:
    /contentFiles/
      any/
        net5.0/
          ThirdParty.OData

Steps to Reproduce

I don't know how to provide a unit test for MSBuild related stuff.

<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
  <!-- ReSharper disable UnknownProperty -->  
  <PropertyGroup>
    <Authors>John Zabroski</Authors>
    <Copyright>Simmaculate</Copyright>
    <TargetFramework>net5.0</TargetFramework> <!--netcoreapp2.1, netcoreapp3.1 also supported -->
    <EnablePackInference>false</EnablePackInference>
    <PackageIcon>icon.png</PackageIcon>
    <PackageId>Custom</PackageId>
    <AssemblyTitle>Custom Packaging</AssemblyTitle>
    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
    <OutputPath>bin</OutputPath>
  </PropertyGroup>

  <PropertyGroup>
    <PackDependencies>true</PackDependencies>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Nugetizer" Version="*" />
    <PackageReference Include="ThirdParty.OData" Version="*" >
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <PackageFile Include="ThirdParty.OData" Version="*" PackFolder="Content" Pack="true" />
  </ItemGroup>

</Project>

Expected Behavior

Based on AppendTargetFrameworkToOutputPath=false and PackDependencies=true and OutputPath=bin, I would expect the third party nuget package to be transcluded into the bin folder, and the /any/net5.0 sub-tree to be deforested so that its just "/contentFiles/".

Exception with Stack Trace

n/a

Version Info

dotnet-nugetize 0.7, Nugetizer 0.7

Additional Info

I figured I would use this prototype as research into @kzu suggesting I give Nugetizer a try to solve problems with SDK Pack, but so far I am not fully wrapping my head around how this is supposed to work, and I am most definitely not an MSBuild or Nuget expert.

Not Respecting "Generate NuGet package on build"

First off, just wanted to say thanks for the help over in issue devlooped/GitInfo#134

When swapping over to Nugetizer, it was able to solve the issue of parent library versioning stamps, but I'm now noticing that it doesn't seem to be respecting my Project Settings -> Package -> "Generate NuGet package on build" setting.

Is this something that Nugetizer could be adjusted to respect and fulfill?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.