솔루션 수준 --output 옵션이 빌드 관련 명령에 더 이상 유효하지 않음

7.0.200 SDK에 다음 명령과 함께 솔루션 파일을 사용할 때 더 이상 --output/-o 옵션을 허용하지 않게 된 변경이 있었습니다.

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

이는 --output/-o 옵션으로 제어되는 OutputPath 속성의 의미 체계가 솔루션에 대해 제대로 정의되지 않았기 때문입니다. 이러한 방식으로 빌드된 프로젝트에서는 동일한 디렉터리에 출력이 배치되어 일관성이 없고 사용자가 보고하는 문제가 많아집니다.

이 변경은 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의 경우에는 경고나 오류가 발생하지 않습니다.

호환성이 손상되는 변경의 형식

이 호환성이 손상되는 변경에는 스크립트 및 연속 통합 파이프라인을 빌드하기 위해 수정이 필요할 수 있습니다. 결과적으로 이진 호환성과 원본 호환성 모두 영향을 받습니다.

변경 이유

이 변경은 --output/-o 옵션으로 제어되는 OutputPath 속성의 의미 체계가 솔루션에 대해 제대로 정의되지 않았기 때문에 이루어졌습니다. 이러한 방식으로 빌드된 프로젝트에서는 동일한 디렉터리에 출력이 배치되어 일관성이 없고 사용자가 보고하는 문제가 많아집니다.

--output 옵션으로 솔루션을 빌드하면 OutputPath 속성이 모든 프로젝트에 대해 동일한 값으로 설정됩니다. 즉, 모든 프로젝트의 출력이 동일한 디렉터리에 배치됩니다. 솔루션에 있는 프로젝트의 복잡성에 따라 서로 다르고 일관되지 않은 결과가 발생할 수 있습니다. 다양한 솔루션 형태의 몇 가지 예와 이것들이 공유 OutputPath에 의해 어떻게 영향을 받는지 살펴보겠습니다.

단일 프로젝트, 단일 TargetFramework

단일 TargetFramework, net7.0을(를) 대상으로 하는 단일 프로젝트가 포함된 솔루션을 가정합니다. 이 경우 --output 옵션을 제공하는 것은 프로젝트 파일에서 OutputPath 속성을 설정하는 것과 같습니다. 빌드(또는 다른 명령일 수 있지만, 지금은 빌드에 논의를 집중하겠습니다) 중에 프로젝트의 모든 출력이 지정된 디렉터리에 배치됩니다.

단일 프로젝트, 다중 TargetFrameworks

이제는 여러 TargetFrameworks, net6.0, net7.0이(가) 있는 단일 프로젝트가 포함된 솔루션을 가정합니다. 다중 대상 지정로 인해 프로젝트는 각 TargetFramework에 한 번씩으로, 두 번 빌드됩니다. 이러한 각 '내부' 빌드에 대해 OutputPath이(가) 동일한 값으로 설정되고, 따라서 각 내부 빌드의 출력이 동일한 디렉터리에 배치됩니다. 즉, 마지막으로 완료된 빌드가 다른 빌드의 출력을 덮어쓰고, MSBuild가 기본적으로 작동하는 병렬 빌드 시스템에서 'last'가 확정되지 않습니다.

라이브러리 => 콘솔 => 테스트, 단일 TargetFramework

이제 라이브러리 프로젝트, 라이브러리 프로젝트를 참조하는 콘솔 프로젝트, 콘솔 프로젝트를 참조하는 테스트 프로젝트가 포함된 솔루션을 가정합니다. 이 모든 프로젝트는 단일 TargetFramework, net7.0을(를) 대상으로 합니다. 이 경우 라이브러리 프로젝트가 먼저 빌드된 다음 콘솔 프로젝트가 빌드됩니다. 테스트 프로젝트는 마지막으로 빌드되며 콘솔 프로젝트를 참조합니다. 빌드된 각 프로젝트에 대해 각 빌드의 출력이 OutputPath에 의해 지정된 디렉터리에 복사되므로 최종 디렉터리에는 세 프로젝트 모두의 자산이 포함됩니다. 테스트에서는 이렇게 되지만, 게시에서는 테스트 자산이 프로덕션으로 전송될 수도 있습니다.

라이브러리 => 콘솔 => 테스트, 다중 TargetFrameworks

이제 동일한 프로젝트 체인을 가져와서 net7.0 빌드 위에 net6.0TargetFramework 빌드를 추가합니다. 다중 대상 지정 빌드도 단일 프로젝트와 마찬가지로 TFM 관련 자산을 지정된 디렉터리에 일관되지 않게 복사하는 동일한 문제가 발생합니다.

다중 앱

지금까지 선형 종속성 그래프가 있는 시나리오를 살펴보았지만, 많은 솔루션에는 관련 애플리케이션이 여러 개 있습니다. 이는 동시에 여러 앱을 동일한 출력 폴더에 빌드할 수도 있음을 의미입니다. 여러 앱에 동일한 이름의 종속성 파일이 있는 경우 여러 프로젝트가 출력 경로에서 동시에 해당 파일에 쓰려고 할 때 빌드가 간헐적으로 실패할 수 있습니다.

여러 앱이 서로 다른 버전의 파일에 종속된 경우 빌드가 성공하더라도 출력 경로에 복사되는 파일의 버전은 비결정적일 수 있습니다. 이 문제는 프로젝트가 다른 버전의 NuGet 패키지에 종속(아마도 전이적으로)될 때 발생할 수 있습니다. 단일 프로젝트 내에서 NuGet을 사용하면 해당 종속성(NuGet 패키지 및/또는 프로젝트 참조를 통한 전이적 종속성 포함)을 동일한 버전으로 통합할 수 있습니다. 통합은 단일 프로젝트와 종속 프로젝트의 컨텍스트 내에서 이루어지므로 두 개의 개별 최상위 프로젝트를 빌드할 때 서로 다른 버전의 패키지를 확인할 수 있습니다. 상위 버전에 종속된 프로젝트가 종속성을 마지막으로 복사하는 경우 앱이 성공적으로 실행되는 경우가 많습니다. 그러나 하위 버전이 마지막으로 복사된 경우 상위 버전에 대해 컴파일된 앱은 런타임에 어셈블리를 로드하지 못합니다. 복사된 버전은 비결정적일 수 있으므로 문제를 진단하기가 매우 어려운 산발적이고 신뢰할 수 없는 빌드가 발생할 수 있습니다.

다른 예제

이 기본 오류가 실제로 어떻게 표시되는지에 대한 자세한 예제는 dotnet/sdk#15607에 대한 설명을 참조하세요.

일반적인 권장 사항은 이전에 수행한 작업을 --output/-o 옵션 없이 수행한 다음, 명령이 완료된 후 원하는 위치로 출력을 이동하는 것입니다. 특정 프로젝트에서 작업을 수행하고 여전히 --output/-o 옵션을 적용할 수도 있으며, 이 방법은 더 잘 정의된 의미 체계를 가질 수 있습니다.

기존 동작을 정확하게 유지하려는 경우 --property 플래그를 사용하여 MSBuild 속성을 원하는 디렉터리로 설정할 수 있습니다. 사용할 속성은 명령에 따라 달라집니다.

명령 속성 예시
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는 절대 경로여야 합니다. 상대 경로는 예상할 수 없는 방식으로 '고정'(즉, 절대 경로)되며 모든 명령에서 동일하게 작동하지 않을 수도 있습니다.