Opcja na poziomie --output rozwiązania nie jest już prawidłowa dla poleceń związanych z kompilacją

W zestawie SDK w wersji 7.0.200 wprowadzono zmianę , aby nie akceptowała --output/-o już opcji w przypadku używania pliku rozwiązania z następującymi poleceniami:

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

Dzieje się tak, ponieważ semantyka OutputPath właściwości, która jest kontrolowana przez --output/-o tę opcję, nie jest dobrze zdefiniowana dla rozwiązań. Projekty utworzone w ten sposób będą miały dane wyjściowe umieszczone w tym samym katalogu, co jest niespójne i doprowadziło do wielu problemów zgłaszanych przez użytkownika.

Ta zmiana została zmniejszona do poziomu ważności ostrzeżenia w zestawie SDK w wersji 7.0.201 i pack została usunięta z listy poleceń, których dotyczy problem.

Wprowadzona wersja

Zestaw SDK platformy .NET 7.0.200 został zredukowany do ostrzeżenia tylko w zestawie SDK 7.0.201.

Poprzednie zachowanie

Wcześniej, jeśli określono --output/-o podczas korzystania z pliku rozwiązania, dane wyjściowe dla wszystkich utworzonych projektów zostaną umieszczone w określonym katalogu w niezdefiniowanej i niespójnej kolejności.

Nowe zachowanie

Interfejs dotnet wiersza polecenia zostanie wyświetlony błąd, --output/-o jeśli opcja jest używana z plikiem rozwiązania. Począwszy od zestawu SDK w wersji 7.0.201, zamiast tego zostanie wyemitowane ostrzeżenie, a w przypadku dotnet pack braku ostrzeżenia lub błędu zostanie wygenerowane.

Typ zmiany powodującej niezgodność

Ta zmiana powodująca niezgodność może wymagać modyfikacji skryptów kompilacji i potoków ciągłej integracji. W rezultacie ma to wpływ zarówno na zgodność danych binarnych, jak i źródłowych.

Przyczyna wprowadzenia zmiany

Ta zmiana została wprowadzona, ponieważ semantyka OutputPath właściwości, która jest kontrolowana przez --output/-o tę opcję, nie jest dobrze zdefiniowana dla rozwiązań. Projekty utworzone w ten sposób będą miały dane wyjściowe umieszczone w tym samym katalogu, co jest niespójne i doprowadziło do wielu problemów zgłaszanych przez użytkownika.

Gdy rozwiązanie zostanie skompilowane przy użyciu --output opcji, OutputPath właściwość jest ustawiona na tę samą wartość dla wszystkich projektów, co oznacza, że wszystkie projekty będą miały dane wyjściowe umieszczone w tym samym katalogu. W zależności od złożoności projektów w rozwiązaniu mogą wystąpić różne i niespójne wyniki. Przyjrzyjmy się niektórym przykładom różnych kształtów rozwiązań i sposobom ich wpływu na udostępniony OutputPathelement .

Pojedynczy projekt, pojedynczy element TargetFramework

Wyobraź sobie rozwiązanie, które zawiera pojedynczy projekt przeznaczony dla pojedynczego TargetFrameworkelementu , net7.0. W takim przypadku podanie --output opcji jest równoważne ustawieniu OutputPath właściwości w pliku projektu. Podczas kompilacji (lub innych poleceń, ale określmy zakres dyskusji, aby skompilować na razie), wszystkie dane wyjściowe projektu zostaną umieszczone w określonym katalogu.

Pojedynczy projekt, wiele elementów TargetFrameworks

Teraz wyobraź sobie rozwiązanie, które zawiera jeden projekt z wieloma TargetFrameworkselementami i net6.0net7.0. Ze względu na wielowersyjność projekt będzie kompilować dwa razy, raz dla każdego TargetFrameworkelementu . Dla każdego z tych "wewnętrznych" kompilacji OutputPath zostanie ustawiona ta sama wartość, więc dane wyjściowe dla każdej kompilacji wewnętrznej zostaną umieszczone w tym samym katalogu. Oznacza to, że w zależności od tego, która kompilacja zakończy się ostatnio, zastąpi dane wyjściowe innej kompilacji, a w systemie równoległej kompilacji, takim jak MSBuild, domyślnie "ostatni" jest nieokreślony.

Biblioteka = Console =>> Test, single TargetFramework

Teraz wyobraź sobie rozwiązanie, które zawiera projekt biblioteki, projekt konsoli odwołujący się do projektu biblioteki i projekt testowy odwołujący się do projektu konsoli. Wszystkie te projekty są przeznaczone dla pojedynczego TargetFrameworkelementu , net7.0. W takim przypadku projekt biblioteki zostanie skompilowany najpierw, a następnie zostanie skompilowany projekt konsoli. Projekt testowy zostanie skompilowany jako ostatni i będzie odwoływać się do projektu konsoli. Dla każdego utworzonego projektu dane wyjściowe każdej kompilacji zostaną skopiowane do katalogu określonego OutputPathprzez element , a więc końcowy katalog będzie zawierać zasoby ze wszystkich trzech projektów. Działa to w przypadku testowania, ale publikowanie może spowodować wysłanie zasobów testowych do środowiska produkcyjnego.

Biblioteka = Console =>> Test, multiple TargetFrameworks

Teraz przejmij ten sam łańcuch projektów i dodaj do nich kompilację net6.0TargetFramework oprócz kompilacji net7.0 . Ten sam problem co kompilacja jednoprojektowa, przeznaczona dla wielu osób występuje — niespójne kopiowanie zasobów specyficznych dla programu TFM do określonego katalogu.

Wiele aplikacji

Do tej pory przyjrzeliśmy się scenariuszom z wykresem zależności liniowych , ale wiele rozwiązań może zawierać wiele powiązanych aplikacji. Oznacza to, że wiele aplikacji może być kompilowanych jednocześnie w tym samym folderze wyjściowym. Jeśli aplikacje zawierają plik zależności o tej samej nazwie, kompilacja może sporadycznie zakończyć się niepowodzeniem, gdy wiele projektów próbuje zapisać w tym pliku w ścieżce wyjściowej jednocześnie.

Jeśli wiele aplikacji zależy od różnych wersji pliku, nawet jeśli kompilacja zakończy się pomyślnie, która wersja pliku zostanie skopiowana do ścieżki wyjściowej, może być niedeterministyczna. Może się to zdarzyć, gdy projekty zależą (prawdopodobnie przechodnio) od różnych wersji pakietu NuGet. W ramach pojedynczego projektu narzędzie NuGet pomaga zagwarantować, że jego zależności (w tym wszelkie przejściowe zależności za pośrednictwem pakietów NuGet i/lub odwołań projektu) są ujednolicone do tej samej wersji. Ponieważ zjednoczenie odbywa się w kontekście pojedynczego projektu i jego projektów zależnych, oznacza to, że można rozpoznać różne wersje pakietu, gdy tworzone są dwa oddzielne projekty najwyższego poziomu. Jeśli projekt, który zależy od nowszej wersji, kopiuje zależność ostatnio, często aplikacje będą działać pomyślnie. Jeśli jednak wersja niższa zostanie skopiowana jako ostatnia, aplikacja skompilowana na wyższą wersję nie będzie mogła załadować zestawu w czasie wykonywania. Ponieważ skopiowana wersja może być niedeterministyczna, może to prowadzić do sporadycznych, zawodnych kompilacji, w których bardzo trudno jest zdiagnozować problem.

Inne przykłady

Aby uzyskać więcej przykładów działania tego podstawowego błędu w praktyce, zobacz dyskusję na temat dotnet/sdk#15607.

Ogólną rekomendacją jest wykonanie wcześniej wykonanej akcji bez/--output-o opcji, a następnie przeniesienie danych wyjściowych do żądanej lokalizacji po zakończeniu polecenia. Można również wykonać akcję w określonym projekcie i nadal stosować --output/-o tę opcję, ponieważ ma ona bardziej dobrze zdefiniowaną semantyka.

Jeśli chcesz zachować istniejące zachowanie dokładnie, możesz użyć --property flagi , aby ustawić właściwość MSBuild na żądany katalog. Właściwość do użycia różni się w zależności od polecenia:

Polecenie Właściwości Przykład
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

UWAGA Aby uzyskać najlepsze wyniki, DESIRED_PATH powinna być ścieżką bezwzględną. Ścieżki względne będą "zakotwiczone" (tj. wykonane jako bezwzględne) w sposób, którego nie można oczekiwać, i mogą nie działać tak samo we wszystkich poleceniach.