Skip to content

Commit

Permalink
Merge pull request #3 from kzu/dev
Browse files Browse the repository at this point in the history
Dev > Main
  • Loading branch information
kzu authored Sep 29, 2020
2 parents e12a33c + db268db commit 0f5b883
Show file tree
Hide file tree
Showing 216 changed files with 4,173 additions and 2,894 deletions.
3 changes: 2 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ indent_style = space
indent_size = 4

# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj}]
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj,nuproj}]
indent_size = 2

# Xml config files
Expand All @@ -23,6 +23,7 @@ indent_size = 2
# YAML files
[*.{yaml,yml}]
indent_size = 2
indent_style = space

# JSON files
[*.json]
Expand Down
24 changes: 22 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
name: build
on: push

env:
DOTNET_NOLOGO: true
MSBUILDDISABLENODEREUSE: 1

jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.100-rc.1.20452.10
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 2.1.x
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.x
- run: dotnet tool update -g dotnet-vs
- name: set version
run: echo "::set-env name=VERSION_SUFFIX::$(git name-rev --name-only --refs=refs/heads/* HEAD).$($env:GITHUB_RUN_NUMBER)"
- run: echo "::set-env name=MSB::$(vs where preview --prop=InstallationPath)"
- run: vs install preview --quiet +Microsoft.VisualStudio.Component.ManagedDesktop.Core +Microsoft.NetCore.Component.DevelopmentTools
if: env.MSB == ''
- run: echo "::add-path::$(vs where preview --prop=InstallationPath)\MSBuild\Current\Bin"
- run: msbuild -r
- run: msbuild -t:test
- name: build
run: msbuild -r -p:versionsuffix=$($env:VERSION_SUFFIX)
- name: test
run: msbuild -t:test
- name: pack
run: msbuild -t:pack -p:nobuild=true -p:versionsuffix=$($env:VERSION_SUFFIX)
- name: sleet
run: |
dotnet tool install -g --version 2.3.33 sleet
sleet push bin --config none -f --verbose -p "SLEET_FEED_CONTAINER=nuget" -p "SLEET_FEED_CONNECTIONSTRING=${{ secrets.SLEET_CONNECTION }}" -p "SLEET_FEED_TYPE=azure"
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: release
on:
release:
types: [created]

env:
DOTNET_NOLOGO: true
MSBUILDDISABLENODEREUSE: 1

jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.100-rc.1.20452.10
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 2.1.x
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.x
- run: dotnet tool update -g dotnet-vs
- name: set version
run: echo "::set-env name=VERSION::${GITHUB_REF#refs/*/v}
- run: echo "::set-env name=MSB::$(vs where preview --prop=InstallationPath)"
- run: vs install preview --quiet +Microsoft.VisualStudio.Component.ManagedDesktop.Core +Microsoft.NetCore.Component.DevelopmentTools
if: env.MSB == ''
- run: echo "::add-path::$(vs where preview --prop=InstallationPath)\MSBuild\Current\Bin"
- name: build
run: msbuild -r -p:version=$($env:VERSION)
- name: test
run: msbuild -t:test
- name: pack
run: msbuild -t:pack -p:nobuild=true -p:version=$($env:VERSION)
- name: push
run: dotnet nuget push ./bin/**/*.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} --skip-duplicate
12 changes: 10 additions & 2 deletions NuGetizer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
src\Directory.Build.props = src\Directory.Build.props
src\Directory.Build.targets = src\Directory.Build.targets
src\NuGet.Config = src\NuGet.Config
README.md = README.md
.github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGet.Build.Packaging.Tasks", "src\Build\NuGet.Build.Packaging.Tasks\NuGet.Build.Packaging.Tasks.csproj", "{57F59BF6-9272-4B66-98A1-334B3FDA5721}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetizer.Tasks", "src\NuGetizer.Tasks\NuGetizer.Tasks.csproj", "{57F59BF6-9272-4B66-98A1-334B3FDA5721}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGet.Build.Packaging.Tests", "src\Build\NuGet.Build.Packaging.Tests\NuGet.Build.Packaging.Tests.csproj", "{50032C88-1B52-4DB1-BFC5-6BADE2CC58CC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetizer.Tests", "src\NuGetizer.Tests\NuGetizer.Tests.csproj", "{50032C88-1B52-4DB1-BFC5-6BADE2CC58CC}"
ProjectSection(ProjectDependencies) = postProject
{57F59BF6-9272-4B66-98A1-334B3FDA5721} = {57F59BF6-9272-4B66-98A1-334B3FDA5721}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-nugetize", "src\dotnet-nugetize\dotnet-nugetize.csproj", "{53B47B9E-212F-420D-9E9A-68EC3B44D39E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -34,6 +38,10 @@ Global
{50032C88-1B52-4DB1-BFC5-6BADE2CC58CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50032C88-1B52-4DB1-BFC5-6BADE2CC58CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50032C88-1B52-4DB1-BFC5-6BADE2CC58CC}.Release|Any CPU.Build.0 = Release|Any CPU
{53B47B9E-212F-420D-9E9A-68EC3B44D39E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53B47B9E-212F-420D-9E9A-68EC3B44D39E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53B47B9E-212F-420D-9E9A-68EC3B44D39E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53B47B9E-212F-420D-9E9A-68EC3B44D39E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
140 changes: 134 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,140 @@
![Nugetizer-3000 Logo](https://raw.githubusercontent.com/NuGet/NuGet.Build.Packaging/master/Nugetizer-3000.png)
![icon](img/nugetizer-32.png) nugetizer
===

# NuGetizer 3000

For the original design and intended goals and features, check out the [spec](https://github.com/NuGet/Home/wiki/NuGetizer-3000).
Simple, flexible, intuitive and powerful NuGet packaging.

## Why

I got sick of trying to modify the gazillion properties, weird item, property and target names and more from the built-in SDK Pack targets, and still never quite achieving the simple goals I had in mind. Also, the original clean and clear design from [NuGet.Build.Packaging](https://github.com/NuGet/NuGet.Build.Packaging) never got traction or support from the NuGet team, so the weirdness kept evolving in ever more incomprehensible ways ¯\_(ツ)_/¯.
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](https://github.com/NuGet/Home/wiki/NuGetizer-3000)
was proposed and I got to implement the initial spec, but it never got traction
with the NuGet team.

## 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](https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-target)
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.

```xml
<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.

### Package Inference

Package inference provides some built-in heuristics for common scenarios so you
don't have to customize the packing much. It works by transforming various built-in
items into corresponding `PackageFile` items, much as if you had added them by
hand.

Inference can be turned off for specific items by just adding `Pack="false"`
item metadata.

The default transformations are:

| Source | Inferred | Notes |
| --------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Project build output | `<PackageFile Kind="$(BuildOutputKind)"/>` | Kind defaults to `Lib`. Includes .xml and .pdb if they are generated. (1) |
| `<PackageReference ... />` | `<PackageFile ... Kind="Dependency" />` | `PrivateAssets=all` is present, it's not added as dependency. (2) |
| `<Content .../>` | `<PackageFile ... Kind="content" />` | Regular content that's not part of the build output |
| `<Content ... CopyToOutputDirectory="PreserveNewest\|Always"/>` | `<PackageFile Include="%(TargetPath)" Kind="$(BuildOutputKind)" ` | Content that's copied to the build output is part of the primary output so it uses its kind. (3) |
| `<None ... Pack="true" />` | `<PackageFile ... Kind="none" />` | |
| `<Reference ... />` | `<PackageFile ... Kind="frameworkReference"` | Only those resolved from the target framework directory are included. (4) |
| | | |

1. 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 by default (either standalone or embeded) provides the best experience for your users, so it's the default.
2. `PrivateAssets=all` in a `PackageReference` causes all the contributed files to the compilation to be packed together with the built output, unless `Pack=false` is also specified. Build-only dependencies that don't contribute assemblies to the output (i.e. analyzers or things like [GitInfo](https://github.com/kzu/GitInfo) or [ThisAssembly](https://github.com/kzu/ThisAssembly) won't cause any extra items).
3. This is the most intuitive default: things that you typically copy to the output in order to "run" your project, is stuff that will typically also be needed at run-time when users reference your package. This can be MSBuild props/targets for a build tasks package, for example, or additional files used by an analyzer or a tool.
4. `Reference` items are resolved by the `ResolveAssemblyReference` standard targets as `ReferencePath` items. Only those that have a `ResolvedFrom` metadata of `{TargetFrameworkDirectory}` are considered.

Inference control properties and metadata:

| Property | Description |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `PackContent=true\|false` | Turns on/off the default inference for `@(Content)`.  Items with `Pack=true` metadata are always included. Defaults to `true`. |
| `PackNone=true\|false` | Turns on/off the default inference for `@(None)`.  Items with `Pack=true` metadata are always included. Defaults to `false`. |
| `PackSymbols=true\|false` | Turns on/off inclusion of symbols (if generated). Defaults to `true`. |
| `PackFrameworkReferences=true\|false` | Turns on/off the default inference of `<Reference...` items |

All of these [inference rules are laid out in a single .targets](src/NuGetizer.Tasks/NuGetizer.Inference.targets] file that's easy to inspect to learn more.

### Project References

Unlike SDK Pack that [considers project references as package references by default](https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#project-to-project-references), NuGetizer has an explicit contract between projects: `GetPackageContents`. 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), 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 automatically it 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
```

### 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](https://github.com/dotnet/project-system/blob/master/docs/design-time-builds.md) 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 actually emit the final `.nupkg` without actually emitting it.

So, on to forking and shipping for real the simplified, understandable way of packing your libraries.
Here's a sample output screenshot:

![nugetize screenshot](img/dotnet-nugetize.png)
20 changes: 18 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
pool:
vmImage: 'windows-2019'

variables:
- name: DOTNET_NOLOGO
value: 'true'

steps:
- checkout: self

- task: UseDotNet@2
inputs:
packageType: sdk
version: 5.0.100-rc.1.20452.10
performMultiLevelLookup: true

- task: UseDotNet@2
inputs:
packageType: sdk
Expand All @@ -17,7 +28,12 @@ steps:
condition: eq(variables['MSB'], '')
- pwsh: echo "##vso[task.prependpath]$(vs where preview --prop=InstallationPath)\MSBuild\Current\Bin"
displayName: prepend MSBuild to %PATH%
- script: msbuild -r
- script: msbuild -r -bl:$(System.DefaultWorkingDirectory)/logs/build.binlog
displayName: msbuild -r
- script: msbuild -t:test
displayName: msbuild -t:test
displayName: msbuild -t:test
- publish: $(System.DefaultWorkingDirectory)/logs
artifact: logs
condition: always()
- script: msbuild -t:Pack -p:PackOnBuild=true
displayName: msbuild -t:pack
Binary file added img/dotnet-nugetize-xplat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/dotnet-nugetize.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nugetizer-100.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nugetizer-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nugetizer-256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nugetizer-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nugetizer-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions img/nugetizer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 0f5b883

Please sign in to comment.