You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过.targets文件让NuGet包按Runtime Identifier引用对应OS专属包

问题描述

  • 正在创建多个OS专属NuGet包,每个包包含体积较大的原生进程,无法合并为单个包
  • 包列表示例:
    • MyProject.win-x86-x64
    • MyProject.macos-x86-x64
    • MyProject.ubuntu1804-x86-x64
    • MyProject.ubuntu1804-arm64
    • MyProject.debian10-x86-x64
    • ...
  • 需求:创建一个netstandard2.0包,通过build/buildTransitive目录的.targets文件,根据当前OS的Runtime Identifier(RID)自动引用对应OS专属包
  • 现有问题:$([MSBuild]::IsOsPlatform())只能识别Windows/OSX/Linux,无法区分具体Linux发行版;不知道如何整合Microsoft.DotNet.PlatformAbstractions的RID检测逻辑到.targets中

解决方案

方法一:利用MSBuild内置命令检测Linux发行版(无需额外代码)

直接在.targets文件中执行系统命令读取OS信息,判断发行版和架构后添加对应包引用:

创建build/MyProject.targets文件,内容如下:

<Project>
  <!-- 构建时指定了RID的情况:直接用指定的RID引用包 -->
  <ItemGroup Condition="'$(RuntimeIdentifier)' != ''">
    <PackageReference Include="MyProject.$(RuntimeIdentifier)" Version="1.0.0" PrivateAssets="all" />
  </ItemGroup>

  <!-- Windows自动检测 -->
  <ItemGroup Condition="'$(RuntimeIdentifier)' == '' and $([MSBuild]::IsOsPlatform('Windows'))">
    <PackageReference Include="MyProject.win-x86-x64" Version="1.0.0" PrivateAssets="all" />
  </ItemGroup>

  <!-- macOS自动检测 -->
  <ItemGroup Condition="'$(RuntimeIdentifier)' == '' and $([MSBuild]::IsOsPlatform('OSX'))">
    <PackageReference Include="MyProject.macos-x86-x64" Version="1.0.0" PrivateAssets="all" />
  </ItemGroup>

  <!-- Linux发行版与架构检测 -->
  <Target Name="DetectLinuxRID" BeforeTargets="ResolvePackageReferences" Condition="$([MSBuild]::IsOsPlatform('Linux')) and '$(RuntimeIdentifier)' == ''">
    <!-- 判断是否为Ubuntu 18.04 -->
    <Exec Command="grep -qE '^ID=ubuntu|^ID_LIKE=ubuntu' /etc/os-release &amp;&amp; grep -qE '^VERSION_ID=&quot;18.04&quot;' /etc/os-release" ConsoleToMSBuild="true" IgnoreExitCode="true">
      <Output TaskParameter="ExitCode" PropertyName="IsUbuntu1804" />
    </Exec>

    <!-- 判断是否为Debian 10 -->
    <Exec Command="grep -qE '^ID=debian' /etc/os-release &amp;&amp; grep -qE '^VERSION_ID=&quot;10&quot;' /etc/os-release" ConsoleToMSBuild="true" IgnoreExitCode="true">
      <Output TaskParameter="ExitCode" PropertyName="IsDebian10" />
    </Exec>

    <!-- 获取系统架构 -->
    <Exec Command="uname -m" ConsoleToMSBuild="true">
      <Output TaskParameter="ConsoleOutput" PropertyName="LinuxArch" />
    </Exec>

    <!-- 设置对应RID并添加包引用 -->
    <PropertyGroup>
      <CurrentRID Condition="$(IsUbuntu1804) == 0 and $(LinuxArch.Trim()) == 'x86_64'">ubuntu1804-x86-x64</CurrentRID>
      <CurrentRID Condition="$(IsUbuntu1804) == 0 and $(LinuxArch.Trim()) == 'aarch64'">ubuntu1804-arm64</CurrentRID>
      <CurrentRID Condition="$(IsDebian10) == 0 and $(LinuxArch.Trim()) == 'x86_64'">debian10-x86-x64</CurrentRID>
    </PropertyGroup>

    <ItemGroup Condition="'$(CurrentRID)' != ''">
      <PackageReference Include="MyProject.$(CurrentRID)" Version="1.0.0" PrivateAssets="all" />
    </ItemGroup>
  </Target>
</Project>

方法二:自定义MSBuild任务实现RID检测(更可靠)

提取Microsoft.DotNet.PlatformAbstractions的RID检测逻辑,写成自定义MSBuild任务,避免依赖已弃用的包:

  1. 创建自定义MSBuild任务项目
    创建一个.NET类库项目,添加以下代码:
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
using System.IO;
using System.Runtime.InteropServices;

public class GetCurrentRID : Task
{
    [Output]
    public string CurrentRID { get; set; }

    public override bool Execute()
    {
        CurrentRID = ResolveCurrentRID();
        return !string.IsNullOrEmpty(CurrentRID);
    }

    private string ResolveCurrentRID()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            return "win-x86-x64";
        }
        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            return "macos-x86-x64";
        }
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            var distro = ResolveLinuxDistribution();
            var arch = RuntimeInformation.ProcessArchitecture switch
            {
                Architecture.X64 => "x86-x64",
                Architecture.Arm64 => "arm64",
                _ => null
            };

            if (distro != null && arch != null)
            {
                return $"{distro}-{arch}";
            }
        }
        return null;
    }

    private string ResolveLinuxDistribution()
    {
        if (File.Exists("/etc/os-release"))
        {
            var content = File.ReadAllText("/etc/os-release");
            var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
            
            string id = null;
            string versionId = null;

            foreach (var line in lines)
            {
                if (line.StartsWith("ID=", StringComparison.OrdinalIgnoreCase))
                {
                    id = line.Split('=')[1].Trim('"', '\'');
                }
                else if (line.StartsWith("VERSION_ID=", StringComparison.OrdinalIgnoreCase))
                {
                    versionId = line.Split('=')[1].Trim('"', '\'');
                }
            }

            if (id == "ubuntu" && versionId == "18.04")
            {
                return "ubuntu1804";
            }
            if (id == "debian" && versionId == "10")
            {
                return "debian10";
            }
        }
        return null;
    }
}
  1. 编译并引用任务到.targets
    编译该类库得到YourRIDTask.dll,将其放在NuGet包的tools目录下,然后在build/MyProject.targets中引用:
<Project>
  <UsingTask TaskName="GetCurrentRID" AssemblyFile="$(MSBuildThisFileDirectory)../tools/YourRIDTask.dll" />

  <Target Name="DetectRID" BeforeTargets="ResolvePackageReferences" Condition="'$(RuntimeIdentifier)' == ''">
    <GetCurrentRID>
      <Output TaskParameter="CurrentRID" PropertyName="CurrentRID" />
    </GetCurrentRID>

    <ItemGroup Condition="'$(CurrentRID)' != ''">
      <PackageReference Include="MyProject.$(CurrentRID)" Version="1.0.0" PrivateAssets="all" />
    </ItemGroup>
  </Target>

  <!-- 优先使用指定的RID -->
  <ItemGroup Condition="'$(RuntimeIdentifier)' != ''">
    <PackageReference Include="MyProject.$(RuntimeIdentifier)" Version="1.0.0" PrivateAssets="all" />
  </ItemGroup>
</Project>

关键注意事项

  • 设置PrivateAssets="all"可避免OS专属包被传递到依赖项目中
  • 若用户构建时通过-r参数指定了RID,会优先使用该RID对应的包,支持交叉编译
  • Linux发行版检测逻辑可根据实际支持的版本扩展,比如添加更多发行版的判断

内容的提问来源于stack exchange,提问作者Anthony Simmon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 19:48:44