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 OutputPath
element .
Pojedynczy projekt, pojedynczy element TargetFramework
Wyobraź sobie rozwiązanie, które zawiera pojedynczy projekt przeznaczony dla pojedynczego TargetFramework
elementu , 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 TargetFrameworks
elementami i net6.0
net7.0
. Ze względu na wielowersyjność projekt będzie kompilować dwa razy, raz dla każdego TargetFramework
elementu . 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 TargetFramework
elementu , 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 OutputPath
przez 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.0
TargetFramework
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.
Zalecana akcja
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.