csproj 根据操作系统复制文件

csproj copy files depending on operating system

我正在使用 .NET Core 构建跨平台 class 库。根据为使用 .csproj 文件构建 C# .NET Core 项目的操作系统,我需要将本机库复制到项目的输出目录。例如,对于 OS X 我想复制一个 .dylib 文件,对于 Windows 我想复制一个 .DLL 文件,对于 Linux 我想复制一个 .so 文件。

如何使用 .csproj ItemGroup 中的条件子句执行此操作?

  <ItemGroup>
    <Content Include="libNative.dylib" Condition=" '$(Configuration)|$(Platform)' == 'Debug|OSX' ">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>   

$(Platform) 好像不行。我可以使用其他变量吗?

为了区分 Windows 和 Mac/Linux,您可以使用 $(os) 属性:这为 Windows 和 Windows_NT UNIX 对于 Mac/Linux.

为了区分 Mac 和 Linux,至少在最新版本的 MSBuild 上,您可以使用 RuntimeInformation.IsOSPlatform.

所以,结合起来,你的 csproj 可以有这样的东西:

<ItemGroup>
    <Content Include="libNative.dll" Condition=" '$(OS)' == 'Windows_NT' ">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="libNative.so" Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' ">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="libNative.dylib" Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' ">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>

据我所知,这应该适用于所有最新版本的 .Net Core、Mono 和 .Net Framework。

在旧版本中,您需要使用邪恶的手段来区分 Mac 和 Linux:

对于Linux -> Condition=" '$(OS)' == 'Unix' and ! $([System.IO.File]::Exists('/usr/lib/libc.dylib')) "

对于Mac -> Condition=" '$(OS)' == 'Unix' and $([System.IO.File]::Exists('/usr/lib/libc.dylib')) "

来源:

我确实遇到过这样的问题,最终在库首次初始化时将 dll 保存在资源中并加载 X86 或 X64 的 dll。这对我来说是最干净的方式。

或者,当您在 nuget 中包含 dll 时,您还必须确保在 som 一个 remove/clean bin mapp 时重新复制相同的 dll。

因此您必须将文件添加到 nuger 并在构建文件上创建目标以在构建时重新复制 dll

这是我做的。

internal class EmbeddedDllClass
{
    public static void LoadAllDll()
    {
        Assembly assem = Assembly.GetExecutingAssembly();
        if (IntPtr.Size == 8)
        {
            var path = Path.Combine(string.Join("\", assem.Location.Split('\').Reverse().Skip(1).Reverse()), "x64");

            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            path = Path.Combine(path, "SQLite.Interop.dll");

            if (!File.Exists(path))
                File.WriteAllBytes(path, Properties.Resources.SQLite_Interop_64);
        }
        else if (IntPtr.Size == 4)
        {
            var path = Path.Combine(string.Join("\", assem.Location.Split('\').Reverse().Skip(1).Reverse()), "x86");

            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            path = Path.Combine(path, "SQLite.Interop.dll");

            if (!File.Exists(path))
                File.WriteAllBytes(path, Properties.Resources.SQLite_Interop_86);

        }
    }
}

然后直接调用它

EmbeddedDllClass.LoadAllDll();

也想为我的 Nuget 包创建跨平台。 在 .csproj.

中尝试了以下代码
<Content Include="libNative.win" Condition=" '$(OS)' == 'Windows_NT' ">
<Content Include="libNative.dylib" Condition=" '$(OS)' != 'Windows_NT' ">

因此创建了 .nupkg。

当引用相同的 .nupkg 时,在 linux 上,因为 $(OS) 已经设置为 Windows_NT,添加了 libNative.win 而不是 libNative.dylib(这是预期的).

所以这个 $(OS) 是在创建 nuget 时设置的,而不是在添加时设置的。 更多详细信息:您可以在<Description>中使用$(OS)或$(Platform),看看该值是如何设置的。

作为@tzachs 接受的答案的后续行动,自 msbuild 15.3(基本上是 Visual Studio 2017+ 或 .NET Core 2+)以来,您可以缩短它以使用 [MSBuild]::IsOSPlatform() 方法。 (this page 上的文档。)

它接受 OSPlatform struct 的值,例如Windows, Linux, OSX, FreeBSD.

<ItemGroup>
    <Content Include="libNative.dll" Condition="$([MSBuild]::IsOSPlatform('Windows'))">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="libNative.so" Condition="$([MSBuild]::IsOSPlatform('Linux'))">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="libNative.dylib" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>