NuGet / Home

Repo for NuGet Client issues
Other
1.49k stars 250 forks source link

allow dotnet pack to pack nuspec file without needing a csproj file #4254

Open natemcmaster opened 7 years ago

natemcmaster commented 7 years ago

Follow up to https://github.com/NuGet/Home/issues/3904#issuecomment-267671637

Scenario: I want to pack a nuspec file in an x-plat build system.

Inputs:

NB: I am not packing a csproj, just files.

In <= 4.0.0-rc2, this was available as dotnet nuget pack (nuspec) file. This was removed in 4.0.0-rc3 https://github.com/NuGet/NuGet.Client/pull/1065 without a suitable replacement.

More data to consider:

cc @emgarten @rohit21agrawal

Please πŸ‘ or πŸ‘Ž this comment to help us with the direction of this feature & leave as much feedback/questions/concerns as you'd like on this issue itself and we will get back to you shortly.

Thank You πŸŽ‰

rohit21agrawal commented 7 years ago

closing this as a duplicate of : https://github.com/NuGet/Home/issues/4250

natemcmaster commented 7 years ago

I don't think these are duplicates. #4250 is about issues packing with csproj + nuspec. This one is about packing nuspec without a csproj file.

rohit21agrawal commented 7 years ago

aah i see, i don't think this will be accommodated in 4.0-RTM as you need a way of getting the Pack targets imported so dotnet pack can work properly - which is why we have a requirement of having a dummy csproj right now

enricosada commented 7 years ago

Same issue there. But if possibile can be done like csproj instead of nuspec? One thing less to learn.

For me is not an issue use a csproj instead of nuspec who can dotnet pack, but without doing AND requing the build. If i use --no-build the dotnet pack anyway expect the built assemblies so fail.

Maybe we can use the csproj, but the dotnet pack --no-build --no-assemblies who doesnt build and doesnt expect built assemblies. A property inside csproj is ok too <PackageNoAssemblies>true</PackageNoAssemblies> to configure that.

rohit21agrawal commented 7 years ago

@enricosada you can use <IncludeBuildOutput>false</IncludeBuildOutput> to achieve the same.

rohit21agrawal commented 7 years ago

To see a list of all possible scenarios in dotnet pack, refer to : https://github.com/NuGet/Home/wiki/Adding-nuget-pack-as-a-msbuild-target

natemcmaster commented 7 years ago

For a little more context: the scenario I have in mind is that we produce some custom nuget packages with layouts that are hard to achieve using csproj, or even some packages that don't come from a .NET project at all.

Example: https://github.com/natemcmaster/libsqlite3-package/blob/ba16e2ede7768cf80cfe7a6023109e2c5a59e30e/SQLite.nuspec

enricosada commented 7 years ago

@natemcmaster using a csproj like

    <Content Include="files/SQLite.props">
      <PackagePath>build\net45\</PackagePath>
      <Pack>true</Pack>
    </Content>

doesnt work? i can set <PackagePath> to specify any directory inside package

natemcmaster commented 7 years ago

Yes, that would work for that specific example, but why should I need a csproj when I'm just packing things? I'm not compiling C#.

rohit21agrawal commented 7 years ago

You need a way to import the pack targets, and that is being achieved by having a csproj that does that for you.

enricosada commented 7 years ago

@natemcmaster

enricosada commented 7 years ago

thx @rohit21agrawal works, using

    <IncludeBuildOutput>false</IncludeBuildOutput>
    <DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>

and

dotnet restore
dotnet pack --no-build

generate the nuspec, with no compile and no dependencies (i dont need these) the dotnet restore is unfortunate, but better than nothing atm

karajas commented 7 years ago

Just wanted to follow up in this thread, I too would like a way to pass in the nuspec directly with the option to pass in command line nuspec properties. Is something like this in the pipeline?

dotnet pack /p:NuspecFile=myproject.nuspec /p:NuspecProperties=""

Could a default pack.targets file be imported if a nuspec file is passed on the command line? This would ensure that the right targets would be imported in the case of both csproj and nuspec files.

anangaur commented 6 years ago

We should allow dotnet pack with nuspec files. Currently this is a major dissatisfaction for pack on Linux and non Windows platforms. I think the following option would be better:

> dotnet pack <project> --nuspec-file=myproject.nuspec 
rohit21agrawal commented 6 years ago

you need a project that would import the targets that define the pack task. it's not as straightforward.

natemcmaster commented 6 years ago

dotnet pack --nuspec-file=myproject.nuspec

This still requires an MSBuild project, right? What I'm asking for is the ability to pack a nuspec without any MSBuild project file at all. Conceptually, this as a new dotnet gesture:

dotnet pack-nuspec MyPackage.nuspec

As an implementation detail, you could put this in dotnet-pack, or dotnet-nuget, but that piece is less important to me than the ability to pack without a *.csproj file.

anangaur commented 6 years ago

@rohit21agrawal I did not fully get what you meant here. Why shouldn't it just do what nuget pack does? Assuming everything is in place including the nuspec file.

@natemcmaster Agree.

For end user its confusing if there are two implementations of pack available under 2 different options. i.e. there shouldn't be dotnet pack as well as dotnet nuget pack both having different behavior.

natemcmaster commented 6 years ago

For end user its confusing if there are two implementations of pack available under 2 different options.

I know nuget.exe pack combines packing csproj and nuspec into one command. I don't know what kind of feedback you've had on this. My uninformed point-of-view is that having two modes of dotnet-pack can be just as confusing. There are command line options on dotnet-pack today that wouldn't apply if you were calling dotnet pack MyPackage.nuspec. For example, dotnet pack MyPackage.nuspec --no-restore --runtime win7-x64...what would those parameters mean in a nuspec-only pack context? The reverse would be true if you added options to dotnet pack that only apply when packing a nuspec file directly, but don't apply to csproj.

anangaur commented 6 years ago

I think we are on the same page. May be my wordings were ambiguous. I am against having another command dotnet nuget pack. I would rather have dotnet pack --nuspec-file mypackage.nuspec or just dotnet pack mypackage.nuspec.

natemcmaster commented 6 years ago

Thanks for clarifying. Yes, something like dotnet pack --nuspec-file mypackage.nuspec is the ask here.

I was just trying to point out that there may be a problem with command-line options that only apply to csproj or nuspec packing, but not both.

anangaur commented 6 years ago

We also need token replacement functionality in dotnet that exists today with nuget.exe. Typically I would want to use the version information from the assembly metadata to use as the package version (is this broken? https://github.com/NuGet/Home/issues/5807)

JamieKeeling commented 6 years ago

This is certainly something I would love to see happen.

I am leveraging the new templating functionality in the .NET CLI and I would like to make use of Nuget as a distribution vector instead of raw files.

The documentation states that you only need a .nuspec file and to then invoke nuget pack, but as @natemcmaster mentioned that involves using the mono equivalent for xplat.

Looking at the docs for dotnet pack it states

Packs the code into a NuGet package. As of NuGet 4.0, this runs the same code as nuget pack.

This implies that the documentation is incorrect as it doesn't operate on a like for like basis, nuget v4.4.1 allows the .nuspec only howevrer the nuget implementation bundled in with v2.0.3 of the .NET Core CLI does not.

rohit21agrawal commented 6 years ago

@JamiKeeling The documentation is indeed incorrect . Thanks for reporting that, I already have sent a PR to fix it.

We have taken your feedback into consideration, will keep you updated on progress

tannergooding commented 6 years ago

FYI. @jaredpar

jaredpar commented 6 years ago

As someone who just ran into this, have to say this is a major pain point in taking our repository cross platform. We have ~30 packages that are all driven by using nuget pack nuspec. Moving to dotnet pack is a non-trivial operation and when complete will be demonstrably slower than the old infrastructure.

In particular it's non-trivial because there is no comprehensive documentation available on how to approach this. There are a scattering of issues on GitHub but none that have a comprehensive solution.

rohit21agrawal commented 6 years ago

@jaredpar the documentation is here: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#packing-using-a-nuspec

Can i ask what were the issues you encountered when trying to pack using csproj + nuspec? You mentioned you actually were happy with the ability of being able to pass the nuspec file as a property through csproj.

natemcmaster commented 6 years ago

The point of this bug is that we often want to pack WITHOUT a .csproj file. Creating a dummy .csproj just so we can invoke pack seems like an additional hoop we shouldn't need to jump through.

By the way, in aspnet we decided to workaround this by creating an xplat tool to pack a nuspec directly. https://github.com/aspnet/BuildTools/blob/dev/modules/KoreBuild.Tasks/PackNuSpec.cs

rohit21agrawal commented 6 years ago

@natemcmaster yes I do understand what the point of the bug is , I am just trying to understand what difficulties @jaredpar faced in particular

jaredpar commented 6 years ago

@rohit21agrawal

That documentation is incomplete. Areas it does not cover:

  1. How do I avoid doing a build every time I pack? Or avoid builds at all. Can't see any good justification for having a new DLL in my output just so I can zip up my NuPkg.
  2. How should I handle the case where my NuSpec file has no relation to any single project in my tree? Think of tools which can't be expressed as a csproj but rather are defined by a Nuspec that grabs from various output directories.
  3. Given the case where I have to define an unused csproj what TF should it use? Must it be an SDK project? Is there a template somewhere I can draw from?

The second concern is probably the greatest. There are many nuspec files we have which don't have a project associated with them. Now though I have to create a project, restore and build it even though it's just an empty DLL that will never be shipped, tested or used in any way.

You mentioned you actually were happy with the ability of being able to pass the nuspec file as a property through csproj.

That's a bit misleading. My ideal case is dotnet pack blah.nuspec but that's not possible. I must have a csproj file. But once I'm forced into having a csproj file it's nice that I can define a smaller number and control the nuspec file they use with a parameter.

dasMulli commented 6 years ago

How do I avoid doing a build every time I pack? Or avoid builds at all.

This has come up before on the SDK repo not only for packing a NuSpec, but for creating "meta-packages" that don't have build output but only package references.

Since MSBuild 15.6 introduced a NuGet SDK resolver and https://github.com/Microsoft/MSBuildSdks contain SDKs for projects without assemblies (NoTargets and Traversal), they could maybe be extended with NuGet support so that project-file-integrated NuGet will work but won't see any assembly build output

rohit21agrawal commented 6 years ago

@jaredpar for now I have updated the docs to suggest a way of not building when using a nuspec to pack. ( https://github.com/NuGet/docs.microsoft.com-nuget/pull/755 ) and your question in 3) about what kind of project file and TF to use in the dummy csproj.

jaredpar commented 6 years ago

@rohit21agrawal commented on your updated docs. They are incorrect. There is no way I can find to avoid at least restoring the project.

Here are the files I'm using for context:

example.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <NuspecFile>example.nuspec</NuspecFile>
  </PropertyGroup>
</Project>

example.nuspec

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
  <metadata>
    <id>Microsoft.Net.Compilers</id>
    <summary> .NET Compilers package.  </summary>
    </description>
    <requireLicenseAcceptance>true</requireLicenseAcceptance>
    <version>1.0.0</version>
    <authors>Microsoft</authors>
    <licenseUrl>https://blah.com</licenseUrl>
    <projectUrl>https://blah.com</projectUrl>
    <tags></tags>
  </metadata>
  <files>
  </files>
</package>

The command lines I've tried:

> dotnet pack --no-build example.csproj
C:\Program Files\dotnet\sdk\2.2.0-preview1-007622\Sdks\Microsoft.NET.Sdk\build\Microsoft.PackageDependencyResolution.targets(327,5): error : Assets file 'C:\Users\jaredpar\temp\obj\project.assets.json' not found. Run a NuGet package restore to generate this file. [C:\Users\jaredpar\temp\example.csproj]
C:\Program Files\dotnet\sdk\2.2.0-preview1-007622\Sdks\Microsoft.NET.Sdk\build\Microsoft.PackageDependencyResolution.targets(167,5): error : Assets file 'C:\Users\jaredpar\temp\obj\project.assets.json' not found. Run a NuGet package restore to generate this file. [C:\Users\jaredpar\temp\example.csproj]

> dotnet restore example.csproj

C:\Users\jaredpar\temp> dotnet pack --no-build .\example.csproj
Microsoft (R) Build Engine version 15.5.178.35674 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

C:\Program Files\dotnet\sdk\2.2.0-preview1-007622\Sdks\Microsoft.NET.Sdk\build\Microsoft.PackageDependencyResolution.targets(327,5): error : Assets file 'C:\Users\jaredpar\temp\obj\project.assets.json' not found. Run a NuGet package restore to generate this file. [C:\Users\jaredpar\temp\example.csproj]
C:\Program Files\dotnet\sdk\2.2.0-preview1-007622\Sdks\Microsoft.NET.Sdk\build\Microsoft.PackageDependencyResolution.targets(167,5): error : Assets file 'C:\Users\jaredpar\temp\obj\project.assets.json' not found. Run a NuGet package restore to generate this file. [C:\Users\jaredpar\temp\example.csproj]
C:\Users\jaredpar\temp> dotnet restore .\example.csproj
  Restoring packages for C:\Users\jaredpar\temp\example.csproj...
  Generating MSBuild file C:\Users\jaredpar\temp\obj\example.csproj.nuget.g.props.
  Generating MSBuild file C:\Users\jaredpar\temp\obj\example.csproj.nuget.g.targets.
  Restore completed in 272.88 ms for C:\Users\jaredpar\temp\example.csproj.
> dotnet pack --no-build .\example.csproj
Microsoft (R) Build Engine version 15.5.178.35674 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
C:\Program Files\dotnet\sdk\2.2.0-preview1-007622\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(204,5): error : The file 'C:\Users\jaredpar\temp\bin\Debug\netcoreapp2.0\example.dll' to be packed was not found on disk. [C:\Users\jaredpar\temp\example.csproj]

This is basically where I spent hours of my life last night. The docs are incomplete and there is no comprehensive sample that gives you a guide here.

rohit21agrawal commented 6 years ago

you are right, I'll send out an update in a bit. Turns out you do need to restore atleast once, and if you don't want to build at all, you also need to pass /p:IncludeBuildOutput=false

jaredpar commented 6 years ago

@rohit21agrawal thanks for updating the docs.

Overall though this still seems like a less than ideal situation. Have to add and restore a project that has zero functioning code just to continue using our nuspec files cross platform or adopt the CLI.

brazilbean commented 6 years ago

We would also love to see this feature addressed. We often pack things that are are not associated with a project and creating wrapper project just to reference the nuspec file (while ensuring the proper incantation of tags is specified: no build, no build output, etc.) is very cumbersome.

Is there any ETA on this feature? If we knew this would be addressed in the near future (or not if that's the case) it would help us in some current decision making.

jaredpar commented 6 years ago

@rohit21agrawal when I use /p:IncludeBuildOutput=false the pack project still builds.

rohit21agrawal commented 6 years ago

right, I meant to say NoBuild

JamieKeeling commented 6 years ago

Are we any further in resolving the initial issue whereby you're forced to use a .csproj?

I need this as a Nuget package so I can distribute it easily.

JamieKeeling commented 6 years ago

It's looking like I have no choice but to use a dummy .CSPROJ to get this up and running.

Could somebody provide an example of the bare minimum content for a valid CSPROJ file?

jaurenq commented 5 years ago

Just wanted to point out here that project templates for use with "dotnet new" are a use case for this. If you follow the instructions here...

https://docs.microsoft.com/en-us/dotnet/core/tutorials/create-custom-template

...they mostly make use of the dotnet cli, but the template is packaged up for nuget as content, rather than being built to create content, and the instructions to do that are to make a .nuspec file and then use 'nuget pack'. It's the only place nuget.exe is used in that workflow; the other steps all use dotnet.exe.

It's a pain point for me b/c it's the only reason I need a command-line nuget to be available to build my project xplat -- I can use the dotnet cli for everything else -- which means I can't just tell users "Get the .NET Core 2.x SDK and it'll 'just work'".

klemmchr commented 5 years ago

Is there any progress about this issue? Needing to pack dotnet templates with nuget cli or with a dummy project is a PITA.

geekyed commented 5 years ago

Is there any progress about this issue? Needing to pack dotnet templates with nuget cli or with a dummy project is a PITA.

Do you have a workaround for templating, I am really struggling to get anything together.

klemmchr commented 5 years ago

Do you have a workaround for templating, I am really struggling to get anything together.

My current solution is a dummy project that contains a reference to my nuspec file

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <NuspecFile>Sample.nuspec</NuspecFile>
  </PropertyGroup>
</Project>

My nuspec:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>Sample</id>
    <packageTypes>
      <packageType name="Template" />
    </packageTypes>
  </metadata>
  <files>
    <file src="content/**" target="" />
  </files>
</package>

My templates are located in the subfolder content, each in it's own folder. The version is missing from the nuspec as I'm setting it while packing:

dotnet pack Sample.csproj -p:NoBuild=true -p:PackageVersion=1.0.0 -p:NoDefaultExcludes=true -o publish

Note the different syntax for properties. Normally you would specify them with /p:Foo=true however with dotnet pack it's -p:Foo=true. The NuGet package will be located in the publish folder. I think you can skip the build with --no-build but you have to restore the project at least once to make the publish work. dotnet pack is doing that automatically for you unless you specify --no-restore.

tebeco commented 5 years ago

@chris579 do you end up with the csproj and the obj folder packaged as well in the nupkg ? it's kinda anoying ^^

i used somnething similar like :

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>XXXXXX</id>
    <version>XXXXXX</version>
    <title>XXXXXX</title>
    <authors>XXXXXX</authors>
    <owners>XXXXXX</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <projectUrl>XXXXXX</projectUrl>
    <description>XXXXXX</description>
    <copyright>XXXXXX</copyright>
    <language>en-US</language>
    <tags></tags>

    <packageTypes>
      <packageType name="Template" />
    </packageTypes>

    <references>
      <reference file="foo.dll" />
      <reference file="bar.dll" />
      <reference file="foobar.dll" />
    </references>

    <dependencies exclude="obj">
      <group targetFramework=".NETStandard2.0" />
    </dependencies>

  </metadata>
</package>
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <NuspecFile>Xxxxxx.nuspec</NuspecFile>

    <NoBuild>true</NoBuild>
    <NoRestore>true</NoRestore>
  </PropertyGroup>
</Project>

Same if i add <IncludeBuildOutput>false</IncludeBuildOutput> to the csproj or if i run

rm -rf bin
rm -rf obj
dotnet pack  /p:IncludeBuildOutput=false
Dan-True commented 4 years ago

Any progress on this? We're also keen to pack any sort of files, in a docker container built on linux. I will try the various .csproj workarounds that are being suggested.

klemmchr commented 4 years ago

Any progress on this? We're also keen to pack any sort of files, in a docker container built on linux. I will try the various .csproj workarounds that are being suggested.

It’s already possible. See the docs for reference. IMO this issue can be closed.

tebeco commented 4 years ago

It’s already possible. See the docs for reference. IMO this issue can be closed.

You might be right here, just point points though:

The docs does not mentions PublishBuildOutput as IncludeBuildOutput did not worked, especiallly if all you do is posting a link and "It's here, please close" => does not always work

Having to guess that the page about custom-template would help doing third part vendor packaging and dotnet pack without packing dotnet build output is nearly impossible to guess => missleading page => probably not the page expected

This issue might be closed if IncludeBuildOutput is properly addressed in dotnet pack page This is the real issue here, the docs had information but on pages that you wont take a look at. IMO we could move details to dotnet pack and custom template would mention IncludeBuildOutput with a link / full explanations to the dotnet pack page

The not funny part is that this issue was open a long time ago and last year ~August 2019 even though I looked in many many many places I did not found that out git blame indicate at least May 2019 => knowledge lost by both community and team

And I finally managed to get that working with something else (that might be wrong if I just follow the given page about custom template while trying to pack a third part):

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

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <PublishBuildOutput>false</PublishBuildOutput> <=============== the docs indicates IncludeBuildOutput, what's the difference with it ? not in that docs page :s
    <Version>1.0.0</Version>
  </PropertyGroup>

  <ItemGroup>
    <None Include="../binaries/native_one.dll" Pack="True" PackagePath="runtimes/win-x64/native" />
    <None Include="../binaries/native_two.dll" Pack="True" PackagePath="runtimes/win-x64/native" />

    <None Include="../binaries/dotnet/managed_wrapper.dll" Pack="True" PackagePath="runtimes/win-x64/lib/netcoreapp3.1" />
    <None Include="../binaries/dotnet/managed_wrapper.xml" Pack="True" PackagePath="runtimes/win-x64/lib/netcoreapp3.1" />
    </ItemGroup>
</Project>
dasMulli commented 4 years ago

The issue still is about packing a .nuspec file without any project files. Currently you need a project file that uses the NuGet.Build.Tasks.Pack package (either as nuget or via .NET SDK).

So this is issue is more specifically about allowing for dotnet nuget pack some.nuspec which does have some advantages over a project file (for example, not needing any restore/build/* semantics) - if you know what you are doing ^^.

sayedihashimi commented 3 years ago

I believe that this is important for packing templates in a .nupkg file when running on an OS that is not windows.

Steinblock commented 3 years ago

Is there a solution for this?

I have done this to pack external dependencies (which might not necessarily have to be assemblies) into nuget packages.

I have done this for years with nuget.exe and the 4.x framework.

Now I need to do the same on a linux based ci server with dotnet core 3.1 (408) and I can't figure out how to do this.