NuGet / Home

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

Feature : Allow project reference DLLs to be added to the parent nupkg for pack target like IncludeReferencedProjects in nuget.exe #3891

Open joelverhagen opened 7 years ago

joelverhagen commented 7 years ago

Steps

  1. dotnet new --type lib two .csproj class libraries: projectA and projectB.
  2. Change <TargetFramework> to <TargetFrameworks> if you don't have https://github.com/NuGet/Home/issues/3865.
  3. Make projectA have a ProjectReference to projectB.
  4. Add <Type>project</Type> to the <ProjectReference>.
  5. dotnet pack projectA
  6. Open the resulting .nupkg's lib folder

Expected

projectB.dll should be in the .nupkg along with projectA.dll

Actual

projectB is still a package reference, not a DLL included in the package.

Environment

Tried latest dev's pack target.

dotnet --info

.NET Command Line Tools (1.0.0-preview3-004056)

Product Information:
 Version:            1.0.0-preview3-004056
 Commit SHA-1 hash:  ccc4968bc3

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64

UPDATE: Please see workaround posted as comment to see how to add ProjectReferences as DLLs in the package dynamically.

rrelyea commented 7 years ago

Please check spec on wiki for planned behavior

joelverhagen commented 7 years ago

Spec: https://github.com/NuGet/Home/wiki/Adding-nuget-pack-as-a-msbuild-target

This doesn't work:

<TreatAsPackageReference>false</TreatAsPackageReference>

The output .nuspec still has projectB as a package reference and only one file: projectA.dll under lib.

emgarten commented 7 years ago

This is a non-trivial feature that hasn't been implemented for RC yet.

Supporting this will require potentially walking the entire project closure to determine all projects that need to be merged into the package, or reading the assets file and matching the project closure with both the project reference flags and the pack flags found in the project (since it can be set either way).

This is also impacted by build outputs not flowing transitively to parent projects yet.

joelverhagen commented 7 years ago

Plan of action:

  1. [x] Build out some automated tests for pack task to cover basic scenarios and detect regression.
  2. [x] Add the original PackageSpec to the lock/assets file.
  3. [ ] Add any missing properties to the assets file pack needs (e.g. path to child project output assemblies).
  4. [ ] Update restore (no, not pack) to collapse project references when <TreatAsPackageReference> is true.
  5. [ ] Move the PackTask away from looking at child projects. Look at restore's assets file instead.

This has repercussions on:

joelverhagen commented 7 years ago

@rohit21agrawal, I partially got through consuming the assets file from in the pack task: https://github.com/joelverhagen/NuGet.Client/tree/jver/3891

This also has some progress on getting the output DLLs of child projects (using @(ReferenceCopyLocalPaths) MSBuild items).

This branch is pretty rough so let me know if I can clarify.

rohit21agrawal commented 7 years ago

As per @rrelyea : https://github.com/NuGet/Home/issues/3893#issuecomment-265843267

"We don't plan to enable this in dotnet pack / msbuild /t:pack in 4.0 timeframe. We'll listen to customer feedback and consider in the future."

rohit21agrawal commented 7 years ago

moving to future as this is post-rtm work.

gulbanana commented 7 years ago

building a nupkg from multiple projects seems like a major feature to be missing :/

rohit21agrawal commented 7 years ago

@gulbanana thanks for the feedback. this is something that is not planned for the 4.0 RTM release, but this is something we will definitely address in a future release.

bbowman commented 7 years ago

@kzu 's nugetizer 3000 does this?

zvirja commented 7 years ago

Hi guys,

Just out of curiosity - are there any plans to proceed on this one? Currently, I need to use the dirty workaround that is MSBuild internals specifics:

<ItemGroup>
  <_PackageFiles Include="$(OutputPath)\ReferencedProjectDll.dll">
    <BuildAction>None</BuildAction>
    <PackagePath>lib\net45\</PackagePath>
  </_PackageFiles>
</ItemGroup>
kzu commented 7 years ago

Indeed @zvirja you could try install-package NuGet.Build.Packaging, which should be compatible with "sdk pack" and give you that behavior automatically.

zvirja commented 7 years ago

@kzu Thanks for the advice. I tried it for a couple of times, but cannot make it work. After I install this package to .NET Core project, it seems that SDK "Pack" is invoked rather than one from the package. As result it fails because you assign the NuspecFile property to non-existing file.

Package version I tried - 0.1.324.

Is there something that I'm missing? Is that suppose to work with .NET Core SDK projects?

Thanks!

P.S. BTW, was unable to find a bug tracker to ask this question there - is it present somewhere?

kzu commented 7 years ago

@zvirja you need to be on VS 15.3 at least

zvirja commented 7 years ago

@kzu Do you mean that MSBuild that is shipped with VS 2017.2 isn't compatible? Currently I run build from the command line, so it's unclear how VS version is involved..

Sent from my Samsung SM-T719 using FastHub

daviburg commented 7 years ago

+1 on also having a project with a reference project built in the same solution to include in a single NuGet package.

kzu commented 7 years ago

@zvirja in pre-15.3, the only way to override the built-in SDK 'Pack' target is to build with an explicitly garbage argument like https://github.com/xamarin/Xamarin.VSSDK/blob/master/build.proj#L8

Under MSBuild 15.3 (which == VS 15.3, since one doesn't ship without the other anymore), installing the nugetizer nuget package itself will perform the overriding as necessary.

niqhtei commented 7 years ago

+1. Can't stress enough how much this is needed for my project.

CraigN commented 7 years ago

+1

edjacob25 commented 6 years ago

+1

xenry commented 6 years ago

+1

UncleFirefox commented 6 years ago

+1 @zvirja do you have the workaround somewhere I can have a look at?

zvirja commented 6 years ago

@UncleFirefox ATM no, as I no longer need this feature. However, you can use smth like:

<ItemGroup>
  <Content Include="$(OutputPath)\ReferencedLib.dll">
    <Pack>true</Pack>
    <PackagePath>lib\$(TargetFramework)</PackagePath>
  </Content>
</ItemGroup>

If you target multiple frameworks, I'd look to smth like below (stolen from here):

<PropertyGroup>
  <TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);IncludeReferencedProjectInPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="IncludeReferencedProjectInPackage" Condition="'$(IncludeBuildOutput)' != 'false'">
  <ItemGroup>
    <TfmSpecificPackageFile Include="$(OutputPath)\ReferencedLib.dll" PackagePath="lib/$(TargetFramework)" />
  </ItemGroup>
</Target>

Hope that helps 😉

dasMulli commented 6 years ago

Setting the PackagePath can even be omitted when using $(TargetsForTfmSpecificBuildOutput):

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;netstandard1.6</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeP2POutput</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\testprivatelib\testprivatelib.csproj" PrivateAssets="All" />
  </ItemGroup>
  <Target Name="IncludeP2POutput">
    <ItemGroup>
      <BuildOutputInPackage Include="$(OutputPath)\testprivatelib.dll" />
    </ItemGroup>
  </Target>
</Project>
rohit21agrawal commented 6 years ago

@dasMulli 's approach is the recommended one when it comes to adding files that need to go into lib folder.

jbrinkle commented 6 years ago

Also wishing there was a way for project references to be auto-included in nuget packages...

jnm2 commented 6 years ago

I'm having the exact same issue with <PackageReference>. I need PrivateAssets="all" but I also need the referenced DLLs to end up packaged along with the build output. I guess the same approach is the best approach for this?

jnm2 commented 6 years ago

What would be really cool though is to not have PrivateAssets="all" because that way the test project doesn't need to duplicate the package references, but still suppress the nuspec dependency and include it as \lib build output.

pauldendulk commented 6 years ago

Just mentioning this feature is much wanted.

kzu commented 6 years ago

Nugetizer already does this ;) On Wed, Jan 10, 2018 at 4:22 PM Paul den Dulk notifications@github.com wrote:

Just mentioning this feature is much wanted.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/NuGet/Home/issues/3891#issuecomment-356708485, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKW6yfO1vqGTkwTiwTdmjNXimwLB2nLks5tJQ4CgaJpZM4Kr815 .

--

-- /kzu from mobile

jnm2 commented 6 years ago

Ran into another conversion where I could really use the capability to include the build output from a NuGet package rather than depending on it via nuspec.

jnm2 commented 6 years ago

Looks like the TargetsForTfmSpecificBuildOutput workaround above no longer works in newer SDKs.

Here's the current workaround for including copy-local references from NuGet packages:

  <!-- https://github.com/NuGet/Home/issues/3891 -->
  <Target Name="SomeUniqueName" BeforeTargets="_GetBuildOutputFilesWithTfm">
    <ItemGroup>
      <BuildOutputInPackage
        Include="@(ReferenceCopyLocalPaths)"
        Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' == 'SomeNuGetPackageId'" />
    </ItemGroup>
  </Target>

Would be good to get some of these arms races officially supported. 😜

dasMulli commented 6 years ago

@jnm2 what were you trying to pack there? "self-contained" tools, msbuild tasks etc? Posted a sample of how to include a publish output in an arbitrary location in a package at https://github.com/dotnet/sdk/issues/1846#issuecomment-356726385

btw, which version did the TargetsForTfmSpecificBuildOutput fail with? using an underscore-prefixed target is a bad practice (but then, that's why it's a workaround 🙃)

pauldendulk commented 6 years ago

Thanks @kzu, Nugetizer solved my problem. It took some trial and error though.

I would think my scenario is quite common. I have an existing solution, quite big, and start to convert the projects with the least dependencies to the new csproj format. This breaks the existing nuget publishing.

jnm2 commented 6 years ago

@dasMulli NUnit pulls NUnit.System.Linq as a NuGet package but prefers to bundle lib\net20\NUnit.System.Linq.dll rather than leaving it as a dependency. I forget offhand what the other times were that I needed this. I think Roslyn analyzers.

I tried to find TargetsForTfmSpecificBuildOutput using the MSBuild structured log viewer with /t:Pack and didn't find it. Maybe I made a mistake? I'll try again if that is preferable to the underscore version.

Troncek commented 6 years ago

Any update on this yet?

ericdunn commented 6 years ago

Adding my voice to this. We're running in to the same issue. Just feels dirty and wrong without it auto-including project references.

myclau commented 6 years ago

if the project reference are more than 2 levels , it is really mess that need to know what dll need to be included and add it to each level .csproj manually . really hope it have a solution soon.

Troncek commented 6 years ago

If you do dotnet publish on a project, it will include all the reference dlls in the publish folder. You can then package these into a nuget package manually somehow.

But it's not ideal.

AdamJamesNaylor commented 6 years ago

Also adding my +1. This is blocking a fairly simple project for me. I just need a .nuget package containing everything required to run the project.

gynet commented 6 years ago

Nuget team, this is a must-have, please

rohit21agrawal commented 6 years ago

One workaround that would work with dotnet.exe pack or msbuild /t:pack if you are packing a project with project references and want to include references as DLLs instead of dependencies in output nuspec is shown by the below example:

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" PrivateAssets="all" />
    <ProjectReference Include="..\ClassLibrary3\ClassLibrary3.csproj" Condition="'$(TargetFramework)' == 'net47'" PrivateAssets="all" />
  </ItemGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveProjectReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(_ResolvedProjectReferencePaths)" />
    </ItemGroup>
  </Target>
</Project>

Documentation for these extension points in the pack target can be found here: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#advanced-extension-points-to-create-customized-package

eerhardt commented 6 years ago

<BuildOutputInPackage Include="@(_ResolvedProjectReferencePaths)" />

From my understanding it is considered "bad form" to take a dependency on a property/item/target that starts with an underscore _. Everything in MSBuild is "public" and "global", and the convention of naming things starting with _ is to make consumers aware that this property/item/target can change in the future, and shouldn't be depended on.

So while this workaround may work now, I don't see that as a long term solution to this problem. And for people to aware when using this workaround that it might not work long term.

rohit21agrawal commented 6 years ago

Updated workaround:

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" PrivateAssets="all" />
    <ProjectReference Include="..\ClassLibrary3\ClassLibrary3.csproj" Condition="'$(TargetFramework)' == 'net47'" PrivateAssets="all" />
  </ItemGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" />
    </ItemGroup>
  </Target>
</Project>

Documentation for these extension points in the pack target can be found here: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#advanced-extension-points-to-create-customized-package

CC: @eerhardt

Tadimsky commented 6 years ago

Is there a good way for the NuGet package to import the dependencies that the Project had? Say Project A depends on Project B, but Project B depends on NuGet Package 1 and Nuget Package 2. How can I make Project A list the dependences of Project B as its own dependencies? This workaround is great at getting the DLL into the NuGet package, but I assume things will fail if they depend on something that doesn't exist?

bbowman commented 6 years ago

@Tadimsky, @kzu's https://github.com/NuGet/NuGet.Build.Packaging nugetizer project is probably what you are looking for.

rohit21agrawal commented 6 years ago

@Tadimsky right now there is no good way to do it, but that will be kept in mind when this feature is implemented. For now, your only option is to list it as a dependency in your top level project/

jiimaho commented 6 years ago

This seems like a very-much needed feature. Why would someone want a separate NuGet for each project dependency?

niwrA commented 6 years ago

Surprised (and disappointed) that this is sitll open ... can we do anything to help?

SidShetye commented 6 years ago

Open since 2016 ... how is this not yet fixed and released? Please hurry up folks ...