I want to copy OS-specific native runtime libraries to the bin directory. This will copy them, but will copy ALL runtimes (win-x64, linux-x64, osx, etc).
<ItemGroup>
<Content Include="libs/**/*.*" CopyToOutputDirectory="PreserveNewest" Link="%(Filename)%(Extension)" />
</ItemGroup>OK so we can just check the current platform, and only copy those:
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
<Content Include="libs/win-x64/**" CopyToOutputDirectory="PreserveNewest" Link="%(Filename)%(Extension)" />
</ItemGroup>Except this will now copy the host OS runtimes when you specify a runtime identifier (ex. dotnet build -r linux-x64 will still copy Windows libs)...
OK, so we can just use the runtime identifer instead:
<ItemGroup Condition="$(RuntimeIdentifier) == 'win-x64'">
<Content Include="libs/win-x64/**" CopyToOutputDirectory="PreserveNewest" Link="%(Filename)%(Extension)" />
</ItemGroup>Except now when you do a standard dotnet build, the runtime identifier is never set, so this breaks simple builds...
OK, so we can check if the runtime identifier is empty and fallback to the current OS:
<ItemGroup Condition="($(RuntimeIdentifier) == '' and $([MSBuild]::IsOSPlatform('Windows'))) or $(RuntimeIdentifier) == 'win-x64'">
<Content Include="libs/win-x64/**" CopyToOutputDirectory="PreserveNewest" Link="%(Filename)%(Extension)" />
</ItemGroup>Except now, when you do ex. dotnet publish -r linux-x64, msbuild runs in multiple steps, and during some of those steps $(RuntimeIdentifer) will not be assigned,
so you will end up with both the host OS runtimes and the target runtimes in the bin directory...
So now what? I genuinely do not know how to simply copy native libs to the bin directory such that dotnet build and dotnet publish -r [target] both work. I could just always copy all the native libraries, regardless of platform, and use NativeLibrary.SetDllImportResolver to find the correct ones at runtime, but that seems kind of ridiculous.
OK, after a lot of debugging with Kate I have come to a solution:
I needed to specify that the csproj was not RID agnostic, and would use the current RID so that
dotnet buildstill worked:This would ensure that an RID always exists, regardless of whether it's passed in through
-ror not, for bothdotnet buildanddotnet publish.After that, it was fairly trivial to copy the libraries needed to the bin:
The only other thing that had to happen was to disable default items as otherwise a modern csproj will include all of the libraries anyway. And then it worked!
A few other notes:
<Copy>because this is a Library project, included several layers deep.<Copy>wouldn't put it in the Exe bin dir, just the Library bin.<IsRidAgnostic>set to false.Ultimately a lot of this would be avoided if you just copied all of the native libs and accepted they'd all be there, but I wanted to try and figure this out. Appreciate the help from everyone who reached out!