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.
Hey thanks for the detailed answer! :)
This sort of works but has the issue I mentioned in the original post where I believe during a
dotnet publish -r [target]it will copy content for both the host runtimes and the target runtimes ...Unfortunately I don't seem to be able to get this to work...
RuntimeIdentifierseems to always be empty, even inBeforeBuildandAfterBuild, unless I add<RuntimeIdentifiers>to the PropertyGroup, in which case it will perform both an empty one and then one with the correct value. I guess this makes sense as it does a generic build and then since it has a matching runtime identifier, it performs that build.I'm not using a nuget package in this case as this is just a local csproj (imagine I wrote a C dll and have it for various runtimes, and want my C# project to use it). I know nuget handles this more gracefully as I have set up public-facing nuget packages that do this before. If that's the intended route I could create a local nuget package I guess, but that also seems weird?
I'll do this and DM you shortly!
I've been investigating a bit more and I do think my project has some weirdness. Alongside this local project with custom libs, I am importing nuget packages with runtimes libs (ImGui), and so even when I don't include
-rin my build, it still puts everything into an RID folder - but all my reporting ($(OutputPath), $(PublishDir), $(OutDir), $(TargetDir)) don't include that in their path. I was briefly thinking I could just detect the runtime from the path but