解決方案層級 --output 選項不再對組建相關的命令有效

在 7.0.200 SDK 中,當搭配下列命令使用解決方案檔時,變更為不再接受 --output/-o 選項:

  • build
  • clean
  • pack
  • publish
  • store
  • test
  • vstest

這是因為 OutputPath 屬性的語意 (由 [--output/-o] 選項控制) 並未妥善定義解決方案。 以這種方式建置的專案會將其輸出放在相同的目錄中,這會造成不一致,並導致一些使用者回報的問題。

這項變更已在 7.0.201 SDK 中將嚴重性縮減為警告層級,pack 已從受影響的命令清單中移除。

導入的版本

.NET 7.0.200 SDK,只在 7.0.201 SDK 中縮減為警告。

先前的行為

之前,如果您在使用解決方案檔時指定 --output/-o ,所有建置專案的輸出都會以未定義且不一致的順序放置在指定目錄中。

新的行為

如果 --output/-o 選項與解決方案檔搭配使用,dotnet CLI 將會發生錯誤。 從 7.0.201 SDK 開始,將會改為發出警告,而且 dotnet pack 不會產生任何警告或錯誤。

中斷性變更的類型

這項重大變更可能需要修改,才能建置指令碼和持續整合管線。 因此,這會影響二進位和來源相容性。

變更原因

之所以進行這項變更,是因為 OutputPath 屬性的語意 (由 [--output/-o] 選項控制) 並未妥善定義解決方案。 以這種方式建置的專案會將其輸出放在相同的目錄中,這會造成不一致,並導致一些使用者回報的問題。

使用 [--output] 選項建置方案時, OutputPath 屬性會設定為所有專案的相同值,這表示所有專案的輸出都會放在相同目錄中。 視方案中專案的複雜度而定,可能會發生不同且不一致的結果。 讓我們看看不同解決方案圖形的一些範例,以及這些範例如何受到共用 OutputPath 的影響。

單一專案、單一 TargetFramework

想像一個解決方案,其中包含以單一 TargetFramework 為目標的單一專案 net7.0。 在此情況下,提供 --output 選項相當於在專案檔中設定 OutputPath 屬性。 在建置期間 (或其他命令期間,讓我們將討論範圍設定為目前建置),專案的所有輸出都會放在指定目錄中。

單一專案,多個 TargetFrameworks

現在想像一個解決方案,其中包含具有多個 TargetFrameworksnet6.0net7.0 的單一專案。 由於多重目標,專案會建置兩次,每個 TargetFramework 都會建置一次。 對於這些「內部」組建,OutputPath 會設定為相同的值,因此每個內部組建的輸出都會放在相同目錄中。 這表示,每當組建完成時,都會覆寫上一個組建的輸出,而且在 MSBuild 等平行建置系統中,依預設「上一個」是不確定的。

程式庫 = > 主控台 = > 測試,單一 TargetFramework

現在想像一個包含程式庫專案的解決方案、參考程式庫專案的主控台專案,以及參考主控台專案的測試專案。 所有這些專案都會以單一 TargetFrameworknet7.0 為目標。 在此情況下,會先建置程式庫專案,然後建置主控台專案。 測試專案會最後建置,並參考主控台專案。 針對每個建置專案,每個組建的輸出都會複製到 OutputPath 所指定目錄中,因此最終目錄會包含來自這三個專案的資產。 這適用於測試,但發佈可能會導致測試資產傳送至生產環境。

程式庫 = > 主控台 = > 測試,多個 TargetFrameworks

現在,除了 net7.0 組建之外,也採用相同的專案鏈結,並將 net6.0TargetFramework 組建新增至這些專案。 發生與單一專案、多目標群組建相同的問題 - 將 TFM 特定資產複製到指定的目錄不一致。

多個應用程式

到目前為止,我們已探討線性相依性關係圖的案例,但許多解決方案可能包含多個相關應用程式。 這表示多個應用程式可能同時建置至相同的輸出檔案夾。 如果應用程式包含具有相同名稱的相依性檔案,當多個專案嘗試同時寫入輸出路徑中的檔案時,建置可能會間歇性失敗。

如果多個應用程式相依於不同的檔案版本,則即使組建成功,將哪個版本的檔案複製到輸出路徑可能不具決定性。 當專案相依於不同版本的 NuGet 套件 (可能可轉移) 時,就會發生這種情況。 在單一專案中,NuGet 有助於確保其相依性 (包括透過 NuGet 套件和/或專案參考的任何可轉移相依性) 與相同的版本整合。 由於統一是在單一專案及其相依專案的內容內完成,這表示可以在建置兩個不同最上層專案時,解析不同版本的封裝。 如果相依於較高版本的專案最後複製相依性,則應用程式通常會成功執行。 不過,如果最後複製較低版本,則針對較高版本編譯的應用程式將無法在執行階段載入組件。 因為複製的版本可能不具決定性,所以可能會導致零星、不可靠的組建,使問題難以診斷。

其他範例

如需此基礎錯誤在實務上如何呈現的更多範例,請參閱 dotnet/sdk#15607 的討論。

一般建議是執行您先前「未」使用 [--output/-o] 選項採取的動作,然後在命令完成之後將輸出移至所需的位置。 您也可以在特定專案上執行動作,但仍套用 [--output/-o] 選項,因為該選項具有更妥善定義的語意。

如果您想要完全維護現有的行為,可以使用 --property 旗標將 MSBuild 屬性設定為所需的目錄。 要使用的屬性會根據命令而有所不同:

Command 屬性 範例
build OutputPath dotnet build --property:OutputPath=DESIRED_PATH
clean OutputPath dotnet clean --property:OutputPath=DESIRED_PATH
pack PackageOutputPath dotnet pack --property:PackageOutputPath=DESIRED_PATH
publish PublishDir dotnet publish --property:PublishDir=DESIRED_PATH
store OutputPath dotnet store --property:OutputPath=DESIRED_PATH
test TestResultsDirectory dotnet test --property:OutputPath=DESIRED_PATH

注意:為了獲得最佳結果,DESIRED_PATH 應該是絕對路徑。 相對路徑可能以您未預期的方式進行「錨定」(亦即絕對路徑),而且可能無法與所有命令相同。