Open fizxmike opened 6 years ago
@wli3 you were looking at something right this. Can you comment?
give me some time i need to find the related issue
Sorry for the delay. It has similar root cause with https://github.com/dotnet/sdk/issues/1458
@fizxmike there are some work around in dotnet/sdk#1458 a solution is still work in progress. cc @nguerrera
This limitation is really painfull when the xml documentation are the one describing the model packed with NuGET and used on the WebAPI contracts where something like swagger is used to self document the WebApi themselves, in such a case there's no possibility to auto-describe the contracts (since it is autodiscovered using the xml documentations)
怎么解决?
The following solution helped me. Add it in you .csproj file for project which is building:
<ItemGroup>
<PackageReference Include="{PackageName}" Version="{PackageVersion}" GeneratePathProperty="true"/>
</ItemGroup>
<Target Name="CopyXMLFromPackages" AfterTargets="Build">
<Copy SourceFiles="$(PkgPackage_Name)\lib\netcoreapp3.1\{PackageName}.xml" DestinationFolder="$(OutDir)" />
</Target>
Any timeframe when this is getting fixed? Its already been 2 years ago this issue was opened and is a breaking change compared to the old system.
I really want my csproj to just copy all documentation xml files and pdb files to the output directory just like it used to do.
The workarounds that were mentioned do not work. Tried to do this with msbuild but failed since it only includes 1 of the xml files...:
<PackageReferenceFiles Include="$(NugetPackageRoot)\%(PackageReference.FileName)\%(PackageReference.Version)\**\*.xml" />
Any timeframe when this is getting fixed? Its already been 2 years ago this issue was opened and is a breaking change compared to the old system.
Try this:
<ItemGroup>
<PackageReference Include="MyNuget" Version="0.0.2526">
<CopyToOutputDirectory>lib/netcoreapp3.1/*.xml</CopyToOutputDirectory>
</PackageReference>
</ItemGroup>
<Target Name="CopyXMLFromPackagesForBuild" AfterTargets="Build">
<ItemGroup>
<PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory)" />
</ItemGroup>
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" />
</Target>
<Target Name="CopyXMLFromPackagesForPublish" BeforeTargets="PrepareForPublish">
<ItemGroup>
<PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory)" />
</ItemGroup>
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" />
</Target>
That means I have to do that for every nuget package and need to know if said nuget package has a netcoreapp3.1 folder... This means my customers (other developers) cannot use the package manager and have to manually edit the csproj for packages they add. Not a very good solution in my case.
To give a bit more context this is for a game engine, currently we have a integrated nuget (2.14) package manager that lets you install plugins (which are nuget packages) but in order to support the newer netstandard and netcore we want to upgrade nuget. Since the newer nuget contains many breaking changes that make this hard to do we are now looking how far we could get if we just used the default tooling (thus having all dependencies in a csproj).
Also this approach doesn't work on Linux or docker containers.
Any news on this? Does anyone know how to transfer the csproj
hack to support Linux or docker environments as well?
What we're currently doing is basically running a script during deployment to extract the NuGet XML content and put it next to the publish output. This solution is !!!terrible!!! but we don't have another one for now...
@JoFrMueller you can do this to copy the xml and/or pdb files to the build output if it exists:
<Target Name="CopyReferenceFiles" BeforeTargets="Build">
<ItemGroup>
<ReferenceFiles Include="%(Reference.RelativeDir)%(Reference.Filename).xml;%(Reference.RelativeDir)%(Reference.Filename).pdb" />
</ItemGroup>
<Message Text="Copying reference files to $(OutputPath)" Importance="High" />
<Copy SourceFiles="@(ReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" />
</Target>
Also see my blog about this https://the-photographing-programmer.com/packagereferences-and-xml-documentation/
Granted it would be nice if there was build in support for this common use case but this seems to work pretty well and takes into account transitive dependencies as well and doesn't lock your project into a specific targetframework.
We use this in our new project template for our game engine https://github.com/Barsonax/DualityProjectTemplate/blob/master/Source/Duality/Directory.Build.props#L12
@Barsonax @JoFrMueller the solution can do the trick!
just for sharing infos, into build stage i have set:
ENV NUGET_XMLDOC_MODE=none
into linux image the dotnet restore
wasn't able to pick up all xml files.
Any timeframe when this is getting fixed? Its already been 2 years ago this issue was opened and is a breaking change compared to the old system.
Try this:
<ItemGroup> <PackageReference Include="MyNuget" Version="0.0.2526"> <CopyToOutputDirectory>lib/netcoreapp3.1/*.xml</CopyToOutputDirectory> </PackageReference> </ItemGroup> <Target Name="CopyXMLFromPackagesForBuild" AfterTargets="Build"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory)" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" /> </Target> <Target Name="CopyXMLFromPackagesForPublish" BeforeTargets="PrepareForPublish"> <ItemGroup> <PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)$([MSBuild]::Escape('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory)" /> </ItemGroup> <Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" /> </Target>
it worked for me! ty ❤️
@JoFrMueller you can do this to copy the xml and/or pdb files to the build output if it exists:
<Target Name="CopyReferenceFiles" BeforeTargets="Build"> <ItemGroup> <ReferenceFiles Include="%(Reference.RelativeDir)%(Reference.Filename).xml;%(Reference.RelativeDir)%(Reference.Filename).pdb" /> </ItemGroup> <Message Text="Copying reference files to $(OutputPath)" Importance="High" /> <Copy SourceFiles="@(ReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" /> </Target>
Also see my blog about this https://the-photographing-programmer.com/packagereferences-and-xml-documentation/
Granted it would be nice if there was build in support for this common use case but this seems to work pretty well and takes into account transitive dependencies as well and doesn't lock your project into a specific targetframework.
We use this in our new project template for our game engine https://github.com/Barsonax/DualityProjectTemplate/blob/master/Source/Duality/Directory.Build.props#L12
how to filter xml? I just want to copy file start with xxx (xxx.*.xml), like xxx.Application.xml
@LGinC like this, just replace the placeholders:
<Target Name="CopyXmlDocs" BeforeTargets="Build">
<ItemGroup>
<XmlDocs Include="%(Reference.RelativeDir)your_filename_1.xml" />
<XmlDocs Include="%(Reference.RelativeDir)*.your_partial_filename_2.xml" />
</ItemGroup>
<Message Text="Copying XML docs to $(OutputPath)" Importance="High" />
<Copy SourceFiles="@(XmlDocs)" DestinationFolder="$(OutputPath)" Condition="Exists(%(FullPath))" />
</Target>
@LGinC like this, just replace the placeholders:
<Target Name="CopyXmlDocs" BeforeTargets="Build"> <ItemGroup> <XmlDocs Include="%(Reference.RelativeDir)your_filename_1.xml" /> <XmlDocs Include="%(Reference.RelativeDir)*.your_partial_filename_2.xml" /> </ItemGroup> <Message Text="Copying XML docs to $(OutputPath)" Importance="High" /> <Copy SourceFiles="@(XmlDocs)" DestinationFolder="$(OutputPath)" Condition="Exists(%(FullPath))" /> </Target>
dotnet publish -c Release
not copy xml to /bin/Release/net5.0/publish folder, just copy to /bin/Release/net5.0 folder. change$(OutputPath)
to$(PublishDir)
, and it work @nkoudelia thx<Target Name="CopyXmlDocs" BeforeTargets="Build"> <ItemGroup> <XmlDocs Include="%(Reference.RelativeDir)My.*.xml" /> </ItemGroup> <Message Text="Copying XML docs to $(OutputPath)" Importance="High" /> <Copy SourceFiles="@(XmlDocs)" DestinationFolder="$(PublishDir)" Condition="Exists(%(FullPath))" /> </Target>
copy My.*.xml to publish folder
In addition to already promoted workarounds: A generic copy for all xml files for all assemblies in build directory
<Target Name="CopyReferenceFiles" BeforeTargets="Build">
<ItemGroup>
<XmlReferenceFiles Condition="Exists('$(OutputPath)%(Filename).dll')" Include="%(Reference.RelativeDir)%(Reference.Filename).xml" />
</ItemGroup>
<Message Text="Copying reference files to $(OutputPath)" Importance="High" />
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" />
</Target>
More or less all workaround I've tried nothing worked in the Azure Pipeline. Is there any other workaround that works in the Azure Pipeline?
More or less all workaround I've tried nothing worked in the Azure Pipeline. Is there any other workaround that works in the Azure Pipeline?
I've used CopyFile (CopyFiles@2) task
In addition to already promoted workarounds: A generic copy for all xml files for all assemblies in build directory
<Target Name="CopyReferenceFiles" BeforeTargets="Build"> <ItemGroup> <XmlReferenceFiles Condition="Exists('$(OutputPath)%(Filename).dll')" Include="%(Reference.RelativeDir)%(Reference.Filename).xml" /> </ItemGroup> <Message Text="Copying reference files to $(OutputPath)" Importance="High" /> <Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" /> </Target>
I am bad at ms build, so I just want to ask you, It is worked like charm but it copies from ALL DLLS , what I need just get xml where xml name contains "magic string" is it possible?
It is possible, in this case you would add more logic to the Condition
on the XmlReferenceFiles
element on the third line. Something like Condition="Exists('$(OutputPath)%(Filename).dll') and %(Filename.Contains('magic string'))"
would get you I believe.
No, you cannot use String instance methods directly on metadata like %(Filename.Contains('magic string'))
; https://github.com/dotnet/msbuild/issues/1155 is still open.
Instead, use $([System.String]::Copy('%(Filename)').Contains('magic string'))
as in https://github.com/dotnet/msbuild/issues/4615.
No, you cannot use String instance methods directly on metadata like
%(Filename.Contains('magic string'))
; dotnet/msbuild#1155 is still open.Instead, use
$([System.String]::Copy('%(Filename)').Contains('magic string'))
as in dotnet/msbuild#4615.
it worked like charm thanks.
Make records:
<Target Name="CopyReferenceFiles" BeforeTargets="Build">
<ItemGroup>
<XmlReferenceFiles Condition="Exists('$(OutputPath)%(Filename).dll')" Include="%(Reference.RelativeDir)%(Reference.Filename).xml" />
</ItemGroup>
<Message Text="Copying reference files to $(OutputPath)" Importance="High" />
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" />
</Target>
<Target Name="CopyReferenceFilesToPublish" BeforeTargets="PrepareForPublish">
<ItemGroup>
<XmlReferenceFiles Condition="Exists('$(OutputPath)%(Filename).dll')" Include="%(Reference.RelativeDir)%(Reference.Filename).xml" />
</ItemGroup>
<Message Text="Copying reference files to $(OutputPath)" Importance="High" />
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(PublishDir)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" />
</Target>
So far, everything seems to be working normally。
@lichti81
In addition to already promoted workarounds: A generic copy for all xml files for all assemblies in build directory
<Target Name="CopyReferenceFiles" BeforeTargets="Build"> <ItemGroup> <XmlReferenceFiles Condition="Exists('$(OutputPath)%(Filename).dll')" Include="%(Reference.RelativeDir)%(Reference.Filename).xml" /> </ItemGroup> <Message Text="Copying reference files to $(OutputPath)" Importance="High" /> <Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')" /> </Target>
Thank you so much.
The fact that this is not default behaviour is beyond me.
Here's an easier way for folks still on .NET 6 SDK:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="6.4.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="6.4.0" />
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.5.1">
<!-- Only references with this metadata will be included -->
<CopyToOutputDirectory>xmldoc</CopyToOutputDirectory>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0">
<CopyToOutputDirectory>xmldoc</CopyToOutputDirectory>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
</ItemGroup>
<!-- This target adds our XML files to the @(ReferenceCopyLocalPaths) list. -->
<!-- It uses MSBuild's batching by executing the target once per entry in -->
<!-- @(PackageReferenceWithCopyToOutputDirectory) thanks to the "Inputs" and -->
<!-- "Outputs" attrs. We also assume the XML doc has the same name as the DLL. -->
<Target Name="_ResolveReferenceCopyLocalPaths" AfterTargets="ResolveReferences" DependsOnTargets="_ResolveCopyToOutputDirectory" Inputs="@(PackageReferenceWithCopyToOutputDirectory)" Outputs="%(PackageReferenceWithCopyToOutputDirectory.Identity)">
<PropertyGroup>
<PackageIdentity>%(PackageReferenceWithCopyToOutputDirectory.Identity)</PackageIdentity>
</PropertyGroup>
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' == '$(PackageIdentity)' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
<!-- This target filters all <PackageReference> entries to those with the given condition -->
<Target Name="_ResolveCopyToOutputDirectory">
<ItemGroup>
<PackageReferenceWithCopyToOutputDirectory Include="@(PackageReference)" Condition="'%(PackageReference.CopyToOutputDirectory)' == 'xmldoc'" />
</ItemGroup>
</Target>
</Project>
Seems to work well with Build and Publish, one or more packages to copy, etc...
This has worked for me (slightly tweaked from examples above) for a Docker environment in Azure pipelines:
<Target Name="CopyReferenceFiles" BeforeTargets="Build">
<ItemGroup>
<XmlReferenceFiles Condition="Exists('$(OutputPath)%(Filename).dll')" Include="%(Reference.RelativeDir)%(Reference.Filename).xml" />
</ItemGroup>
<Message Text="Found XML doc for copy: %(XmlReferenceFiles.FullPath)" Importance="normal"/>
<!-- In local build need to copy to output folder, ex: bin/debug/net8.0/-->
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)') AND $([System.String]::Copy('%(Filename)').StartsWith('<my.assembly.name>'))"/>
<!-- In Azure CI/CD pipeline need to copy to final publish dir-->
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(PublishDir)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)') AND $([System.String]::Copy('%(Filename)').StartsWith('<my.assembly.name>'))"/>
</Target>
It extended condition will make sure to copy only the file i need.
Also a super important piece to have in the docker file before dotnet restore
ENV NUGET_XMLDOC_MODE=none
By default that is set to 'skip' in a docker environment to skip xml files from the nuget package to save time/space when unpacking
This has worked for me (slightly tweaked from examples above) for a Docker environment in Azure pipelines: ... By default that is set to 'skip' in a docker environment to skip xml files from the nuget package to save time/space when unpacking
Works like a charm, thank you! And yep, that env var is critical.
And here's an implementation for defining multiple assemblies to copy docs for:
<PropertyGroup>
<!-- Define the external assemblies (; separated) that you want to include in your openapi specs -->
<AssemblyNames>MyAssembly;OtherAssembly</AssemblyNames>
</PropertyGroup>
<ItemGroup>
<XmlReferenceFiles
Condition="Exists('$(OutputPath)%(Filename).dll') AND $(AssemblyNames.Contains(%(Filename)))"
Include="%(Reference.RelativeDir)%(Reference.Filename).xml"/>
</ItemGroup>
<Message Text="Copy XML files for external dependencies to $(OutputPath)" Importance="High"/>
<!-- In local build need to copy to output folder, ex: bin/debug/net8.0/-->
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(OutputPath)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')"/>
<!-- In Azure CI/CD pipeline need to copy to final publish dir-->
<Copy SourceFiles="@(XmlReferenceFiles)" DestinationFolder="$(PublishDir)" Condition="Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')"/>
I've dug around for a solution to this problem, but I only find issues related to
dotnet publish
not including a project's xml documentation. My issue is that XML documentation files in NuGet packages are not copied to my project's build folder. I hope the distinction is clear to a human (google couldn't get it).Steps to reproduce
Add some_other_library nuget package which includes xml documentation to you project. Do
dotnet build
Expected behavior
The some_other_library.xml is included in the build (output) folder like so: (screenshot from output of VS 2017)
Actual behavior
some_other_library.xml file is not present in the build (output) folder: (screenshot of output of dotnet cli)
Environment data
dotnet --info
output: