Repackage 3rd party libraries as NuGet package - .net-core

I am using a 3rd party library which supports .NET Core, but not .NET Standard. They provide separate binary SDKs for each platform they support; win-x86, linux-x64, etc. For each platform there is a CoolSdkDotNetCore.dll, and a coolsdk.dll/so/dylib. The CoolSdkDotNetCore.dll files are indeed different for each platform - they're all exactly the same size, but have different SHA hashes.
For my own sanity I'd like to repackage all this as a single .nupkg using runtime identifiers to pull in the right stuff for a given platform, so that I can build for Windows, docker, etc without having to swap binaries around. I have a project file like so:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>CoolSdk.NetStandard</PackageId>
<Version>1.2.3</Version>
<RootNamespace>coolsdk</RootNamespace>
<AssemblyName>coolsdk</AssemblyName>
</PropertyGroup>
<ItemGroup>
<None Pack="true" PackagePath="runtimes/win-x64/native/coolsdk.dll" Update="windows/x64/coolsdk.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/win-x64/lib/netcore/CoolSdkDotNetCore.dll" Update="windows/x64/CoolSdkDotNetCore.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/linux-x64/native/libcoolsdk.so" Update="linux/x64/libcoolsdk.so">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/linux-x64/lib/netcore/CoolSdkDotNetCore.dll" Update="linux/x64/CoolSdkDotNetCore.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
This produces a .nupkg that appears to have everything I need, but consuming projects don't actually get a reference to the CoolSdkDotNetCore assembly. In the bin folder, I see a runtimes folder with the native components, but no lib folders (the lib folders are definitely present in the actual nupkg).
What am I missing here? Do I need to go back and write a nuspec myself instead of trying to use a csproj to generate it all for me, or is there some other trick I can use?
edit:
Changing the library paths above from runtimes/<rid>/lib/netcore to runtimes/<rid>/netcoreapp allows the lib folders to be copied to the bin folder in a consuming project, but I'm still not able to actually reference the assembly. Manually adding a reference to CoolSdkDotNetCore like:
<Reference Include="CoolSdkDotNetCore">
<HintPath>CoolSdkDotNetCore.dll</HintPath>
</Reference>
Generates MSB3245 - Could not resolve this reference....

The answer is to include a single copy of the .NET Core assembly as a "ref". Which one doesn't really matter - this assembly will never be run, only loaded by the IDE and at compile time. At runtime, the correct assembly will be loaded from the runtimes folder. As a final gotcha, nuget will not pack the same file twice; if you have two or more items in the item group with the same value for the Update key, only the last one will actually get added to the final .nupkg. So, picking at random I made a copy of the windows/x64 dll in the root of my project, and updated my csproj thusly:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>CoolSdk.NetStandard</PackageId>
<Version>1.2.3</Version>
<RootNamespace>coolsdk</RootNamespace>
<AssemblyName>coolsdk</AssemblyName>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<None Pack="true" PackagePath="ref/netcoreapp2.1/CoolSdkDotNetCore.dll" Update="CoolSdkDotNetCore.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/win-x64/native/coolsdk.dll" Update="windows/x64/coolsdk.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/win-x64/lib/netcore/CoolSdkDotNetCore.dll" Update="windows/x64/CoolSdkDotNetCore.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/linux-x64/native/libcoolsdk.so" Update="linux/x64/libcoolsdk.so">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Pack="true" PackagePath="runtimes/linux-x64/lib/netcore/CoolSdkDotNetCore.dll" Update="linux/x64/CoolSdkDotNetCore.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Related

.target file is not applied to the .csproj file when nuget package is released

I'm making a nuget package which only includes references to a bunch of analyzers and a ruleset file.
I am struggling with getting it to add the <CodeAnalyzersRuleSet> tag to the .csproj file during package install.
After searching for a few hours I stumbled upon this 5 year old question which attempts to solve the same thing but I can't get it to work.
I've configured my NuGet project as follows:
Foo.csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Author, Description, ect removed for brevity -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsyncFixer" Version="1.6.0">
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.2.0">
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
build\Foo.targets file:
<Project>
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Foo.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
And then the build\Foo.ruleset itself.
I run dotnet pack (dotnet version 7.0.102) in order to create the nuget package.
During package install the only thing that happens is that the package is added like a normal <PackageReference>, and I get all the analyser warnings but it doesn't add the ruleset file, and it doesn't add the <CodeAnalyzerRuleSet> tag to the .csproj file.
I've inspected the nuget package and it doesn't include the .targets and .ruleset files unless I add the following to my .csproj file as well:
<ItemGroup>
<None Include="build\**" Pack="True" PackagePath="build\" />
</ItemGroup>
This adds the files to the .nupkg but they are still not added or applied to my project during package install.
Any help or pointers are greatly appreciated.

Missing static file from nuget package on install

I am trying to include a global configuration file in my nuget package. This is what I have so far:
In my csproj I have the following:
<ItemGroup>
<Content Include="appsettings.infrastructure.json">
<Pack>true</Pack>
<PackagePath>contentFiles;content</PackagePath>
<IncludeInPackage>true</IncludeInPackage>
<CopyToOutput>true</CopyToOutput>
<BuildAction>Content</BuildAction>
<copyToOutput>true</copyToOutput>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</Content>
</ItemGroup>
If I publish the package locally and unzip it I can see the file exists under contentFile folder.
The problem appears after I install my nuget package in a project. If I check bin/debug/netcoreapp3.1 I expected to see appsettings.infrastructure.json there but it is not there.
I have also tryed to do a file system search in the project folder but there does not seem to be any appsettings.infrastructure.json file saved anywhere.
Does anyone know what I can do to get this file in the bin/Debug/netcoreapp3.1 from a nuget package?
In the end I was able to achieve what I needed using the following configuration:
<ItemGroup>
<None Update="appsettings.infrastructure.json"
Pack="true"
PackagePath="contentFiles\any\any;content">
<PackageCopyToOutput>true</PackageCopyToOutput>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</None>
</ItemGroup>

Including a custom file in your build/publish for a .net core application

How do I include a custom file that is in the root of my project dir to be put into my build/publish folder?
If its a .json file it seems to come through automatically (probably because of .NET config being json)
I have a custom config file that is text based with a .txt extension.
I've tried:
<ItemGroup>
<DotnetPublishFiles Include="customfile.txt"></DotnetPublishFiles>
</ItemGroup>
And
<ItemGroup>
<Content Include="./*.txt" />
</ItemGroup>
As well as
<ItemGroup>
<Content Include="customfile.txt" />
</ItemGroup>
Also:
<_CustomFiles Include="$(MSBuildProjectDirectory)/customfile.txt" />
<DotNetPublishFiles Include="#(_CustomFiles)">
</DotNetPublishFiles>
</ItemGroup>
Nothing seems to work...
I'm using Visual Studio Code and .NET Core 3.1.
Any ideas?
<ItemGroup>
<None Update="customfile.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<ItemGroup>
JSON (as well as other suitable) files are treated the same way by SDK style projects, so for your own custom files you need to ask MSBuild explicitly.
For publishing there is another tag: CopyToPublishDirectory
<ItemGroup>
<None Update="customfile.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<ItemGroup>

"Duplicate compile items" error when generating code from "Add Web Reference" in Rider

I'm creating a .NET core API that consumes a web service with the Rider IDE.
I created a new csproj FooBar.Service, and added the web reference. The FooBar.Service.csproj file is as follow:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<WCFMetadata Include="Service References" />
</ItemGroup>
<ItemGroup>
<WCFMetadataStorage Include="Service References\FooBar" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Service References\FooBar\FooBar.svcmap">
<Generator>WCF Proxy Generator</Generator>
<LastGenOutput>FooBar.cs</LastGenOutput>
</None>
<None Include="Service References\FooBar\FooBar.webref" />
<None Include="Service References\FooBar\FooBar.wsdl" />
</ItemGroup>
<ItemGroup>
<Compile Include="Service References\FooBar\FooBar.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>FooBar.svcmap</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="System.ServiceModel" />
</ItemGroup>
</Project>
The generated code seems correct, but I have this error:
Duplicate 'Compile' items were included. The .NET SDK includes 'Compile' items from your project directory by default. You can either remove these items from your project file, or set the 'EnableDefaultCompileItems' property to 'false' if you want to explicitly include them in your project file. For more information, see https://aka.ms/sdkimplicititems. The duplicate items were: 'Service References\FooBar\FooBar.cs'
I've read the other question about this issue: the answer is that there are 2 compile items with the same name. If I understand the error message, the file is firstly added by default because it is in the folder of the .csproj, and it is added again by the <Compile Include="Service References\FooBar\FooBar.cs"> item.
I guess that this is a bug of the Rider web service code generation, but what could be a workaround without messing up with the automaticaly generated code? I know that I can deactivate the EnableDefaultCompileItems flag, but I'd prefer not to because I prefer this behavior.
I tried to replace the Include with an Update as seen in this answer, but then I have a bunch of compile error that says: The type or namespace name 'ServiceModel' does not exist in the namespace 'System' (are you missing an assembly reference?) although it is included.

NLog.config Transformation

I have an NLog.config file that I want transformed before publishing my site. Similar to how web.config is transformed. How do I accomplish this? I couldn't find any solid resources on how to do this.
I tried adding a transform to the csproj
<Target Name="BeforeBuild" Condition="exists('NLog.$(Configuration).config')">
<Message Text="Tranforming NLog..."/>
<TransformXml Source="NLog.config" Transform="NLog.$(Configuration).config" Destination="$(OutputPath)\NLog.config" />
</Target>
Also added the NLog to csproj:
<Content Include="NLog.config">
<SubType>Designer</SubType>
</Content>
<None Include="NLog.aws-prod.config">
<DependentUpon>NLog.config</DependentUpon>
</None>
<None Include="NLog.aws-test.config">
<DependentUpon>NLog.config</DependentUpon>
</None>
but this doesn't copy the transformed NLog.config to the package directory (or when deploying to AWS). The original NLog.config is copied and a copy in the /bin directory as well.
SlowCheetah seems to do what I want. I've tried it and I've made changes to my csproj to add:
<TransformOnBuild>true</TransformOnBuild>
and
<IsTransformFile>True</IsTransformFile>
so the final change looks like this:
<Content Include="NLog.config">
<TransformOnBuild>true</TransformOnBuild>
<SubType>Designer</SubType>
</Content>
<None Include="NLog.aws-prod.config">
<DependentUpon>NLog.config</DependentUpon>
<IsTransformFile>True</IsTransformFile>
<SubType>Designer</SubType>
</None>
<None Include="NLog.aws-test.config">
<DependentUpon>NLog.config</DependentUpon>
<IsTransformFile>True</IsTransformFile>
</None>
That's it and NLog.config is transformed!! This target below wasn't needed:
<Target Name="BeforeBuild" Condition="exists('NLog.$(Configuration).config')">
<Message Text="Tranforming NLog..."/>
<TransformXml Source="NLog.config" Transform="NLog.$(Configuration).config" Destination="$(OutputPath)\NLog.config" />
</Target>

Resources