PAYATHON 2026

如何用 PowerShell 批量构建不同 TargetFramework 的 .NET 配置?

数据小陈

可以。为不同的 Configuration 设置不同的 TargetFramework,再用 PowerShell 顺序构建即可。不需要模拟 Visual Studio 界面上的切换操作,也不用为了批量构建改用 TargetFrameworks。

你给出的循环没有明显的逻辑问题:dotnet build --configuration R2027 默认会执行隐式 restore。由于没有具体错误信息,还无法确定失败原因。建议检查 restore 和 build 是否使用相同配置、solution 中的项目配置映射是否正确,以及各次构建是否共用了不匹配的还原结果。

Visual Studio 切换配置时做了什么?

对于这种 SDK 风格项目,切换配置后,Visual Studio 会按新的 MSBuild 属性重新评估项目,更新 IntelliSense 等功能使用的设计时构建信息,并在需要时触发 NuGet restore。

例如,配置变成 R2027 后,下面的条件成立:

<PropertyGroup Condition="'$(Configuration)' == 'R2027'">
  <TargetFramework>net10.0-windows</TargetFramework>
  <OutputPath>..\..\Build2027\</OutputPath>
  <Optimize>true</Optimize>
</PropertyGroup>

随后,项目会按 net10.0-windows 解析框架引用、包依赖和相关构建属性。界面上的等待通常与这些后台操作有关,但仅凭现象无法确认是哪一步解决了当前配置的构建问题。

在命令行中,restore 和 build 都需要在正确的 Configuration 下评估项目。

用同一配置执行 restore 和 build

可以先把两个步骤分开,方便定位失败发生在哪里。下面的脚本假设它与 solution 位于同一目录,请将 MySolution.sln 替换为实际文件名。

$ErrorActionPreference = 'Stop'
$solution = Join-Path $PSScriptRoot 'MySolution.sln'

if (-not (Test-Path -LiteralPath $solution)) {
    throw "Solution not found: $solution"
}

for ($year = 2024; $year -le 2027; $year++) {
    $configuration = "R$year"
    Write-Host "Building for Revit $year ($configuration)"

    dotnet restore $solution "-p:Configuration=$configuration" --force
    if ($LASTEXITCODE -ne 0) {
        throw "Restore failed: $configuration"
    }

    dotnet build $solution `
        --configuration $configuration `
        --no-restore `
        --no-incremental

    if ($LASTEXITCODE -ne 0) {
        throw "Build failed: $configuration"
    }
}

脚本中的几个参数和检查值得注意:

  • dotnet restore 使用 -p:Configuration=...,确保条件属性组在还原阶段就生效。
  • 随后的 build 使用相同配置,并通过 --no-restore 使用刚生成的还原结果。
  • --force 强制重新解析依赖,适合排查切换配置时的还原状态问题;它不会清空全部 NuGet 缓存。
  • 检查 $LASTEXITCODE 可以防止某个配置失败后继续构建。对于这里调用的外部命令,不能只依赖 $ErrorActionPreference。

--no-incremental 影响增量构建,不能代替 restore,也不能修正错误的配置映射。

为什么单独执行 restore 仍可能失败?

如果执行的是:

dotnet restore MySolution.sln
dotnet build MySolution.sln --configuration R2027 --no-restore

restore 没有指定 R2027,可能会按默认配置评估项目。由于你的 TargetFramework 由配置决定,还原得到的目标框架可能与后续 build 不一致。

SDK 风格项目的还原结果通常保存在 obj/project.assets.json 中。如果该文件只包含前一个配置需要的目标框架,后续构建就可能报告找不到所需 target。需要实际错误与此相符,才能将问题归因于还原结果不匹配。

不过,你原来的 dotnet build --configuration ... 已经包含隐式 restore。显式拆分步骤主要是为了验证属性传递和定位故障,不能保证消除所有配置错误。

检查 solution 的配置映射

.csproj 中声明了 R2027,不代表 .sln 已经把所有项目正确映射到该配置。

在 Visual Studio 的 Configuration Manager 中,逐个检查 R2024 到 R2027:

  1. solution 中存在相应配置。
  2. 每个参与构建的项目映射到预期的项目配置。
  3. 需要生成的项目勾选了 Build。
  4. 如果使用自定义 Platform,restore 和 build 都传入相同的平台属性。

也可以直接构建一个项目,缩小排查范围:

dotnet build .\Path\MyProject.csproj --configuration R2027

如果直接构建项目成功,而构建 solution 失败,应优先检查 solution 配置映射和其他项目的配置。

OutputPath 不会隔离所有中间文件

为不同配置设置不同的 OutputPath 可以分开放置最终输出,但 NuGet 还原文件和其他中间产物不一定也完全分开。

上面的顺序脚本会先还原当前配置,再立即构建,通常可以避免直接沿用上一个配置的 assets 文件。排查期间应保持顺序执行,不要让多个配置同时写入同一套中间目录。

如果需要并行构建,或者日志显示存在中间文件冲突,再考虑按配置隔离相关中间路径。BaseIntermediateOutputPath 和 MSBuildProjectExtensionsPath 的设置时机有要求,通常应在较早导入的 Directory.Build.props 中处理,并保证 restore 与 build 使用一致的路径。

仍然失败时,保留第一个错误

先只运行一个失败的配置,分别为 restore 和 build 生成日志:

dotnet restore .\MySolution.sln `
    -p:Configuration=R2027 `
    --force `
    -bl:restore-R2027.binlog

dotnet build .\MySolution.sln `
    --configuration R2027 `
    --no-restore `
    -bl:build-R2027.binlog

如果 restore 已经失败,应先解决还原错误,再执行 build。根据第一个错误确定排查方向:

  • 提示 assets 文件缺少目标框架:检查 restore 使用的配置,以及 assets 文件是否被其他构建覆盖。
  • 提示 SDK 不支持目标框架:检查实际使用的 SDK,并查看 global.json 是否锁定了其他版本。
  • 提示找不到 .NET Framework 引用程序集:检查 net48 构建需要的 targeting pack 或引用程序集是否可用。
  • 提示 solution 配置无效或项目未生成:检查 .sln 中的配置和项目映射。
  • 提示 Revit API 引用或类型不匹配:检查各配置引用的宿主程序集是否对应预期版本。

示例中的 Revit 版本与目标框架的对应关系应按项目要求配置,不能仅凭年份推导。只要各配置的框架、引用和构建环境正确,就可以继续使用现有的条件 TargetFramework 方案。

备注:内容仅供参考。