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.
I spoke with a couple people about this and got some info for you and anyone else having this problem:
Based on this my personal suggestion would be to try setting a default RID so that in cases where you build without setting an RID, an RID gets set and you can always just conditionally check based on the RID. I think that would look like this, but I haven't tried it:
If you want to get fancy you can change the default based on the host OS, which you already know how to do. This may still not work right for the publish scenario.
Someone else has also mentioned that properties being missing can mean that your itemgroups are being evaluated 'too early', and the answer for that is to put your items in a Target that runs later in the build process, like mentioned in the second quote. This should be somewhat clear if you capture a binlog of the case where properties are missing.
EDIT: For me, a target running before
BeforeBuildalways has a RuntimeIdentifier set, even if I don't specify one on the command line.If you continue having issues, please capture a binlog of your build misbehaving (you can do this by passing the
-blswitch to the build command) and send it to me and we can look into it!