Udostępnij za pośrednictwem


Rozwiązywanie problemów z odwołaniami do zestawów

Jednym z najważniejszych zadań w programie MSBuild i procesie kompilacji platformy .NET jest rozwiązywanie odwołań do zestawów, które są wykonywane w ResolveAssemblyReference zadaniu. W tym artykule opisano niektóre szczegóły dotyczące ResolveAssemblyReference sposobu działania oraz sposób rozwiązywania problemów z błędami kompilacji, które mogą wystąpić, gdy ResolveAssemblyReference nie można rozpoznać odwołania. Aby zbadać błędy odwołań do zestawów, możesz zainstalować przeglądarkę dzienników strukturalnych, aby wyświetlić dzienniki programu MSBuild. Zrzuty ekranu w tym artykule pochodzą z przeglądarki dzienników strukturalnych.

Celem programu ResolveAssemblyReference jest użycie wszystkich odwołań określonych w .csproj plikach (lub gdzie indziej) za pośrednictwem <Reference> elementu i mapowanie ich na ścieżki do plików zestawów w systemie plików.

Kompilatory mogą akceptować tylko ścieżkę .dll w systemie plików jako odwołanie, więc ResolveAssemblyReference konwertuje ciągi takie jak mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 w plikach projektu na ścieżki, takie jak C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll, które są następnie przekazywane do kompilatora za pośrednictwem przełącznika /r .

ResolveAssemblyReference Ponadto określa pełny zestaw (faktycznie przejściowe zamknięcie w kategoriach teorii grafu) wszystkich .dll i .exe odwołań cyklicznych, a dla każdego z nich określa, czy ma zostać skopiowany do katalogu wyjściowego kompilacji, czy nie. Nie wykonuje rzeczywistego kopiowania (obsługiwanego później, po rzeczywistym kroku kompilacji), ale przygotowuje listę elementów plików do skopiowania.

ResolveAssemblyReference jest wywoływany ResolveAssemblyReferences z obiektu docelowego:

Zrzut ekranu przedstawiający podgląd dzienników wyświetlany podczas wywoływania funkcji ResolveAssemblyReferences w procesie kompilacji.

Jeśli zauważysz, że kolejność ResolveAssemblyReferences jest wykonywana przed Compileelementem , i oczywiście CopyFilesToOutputDirectory następuje po Compile.

Uwaga

ResolveAssemblyReferencezadanie jest wywoływane w pliku Microsoft.Common.CurrentVersion.targets standardowym .targets w folderach instalacyjnych MSBuild. Możesz również przeglądać cele programu MSBuild dla zestawu .NET SDK w trybie online pod adresem https://github.com/dotnet/msbuild/blob/a936b97e30679dcea4d99c362efa6f732c9d3587/src/Tasks/Microsoft.Common.CurrentVersion.targets#L1991-L2140. Ten link pokazuje dokładnie, gdzie ResolveAssemblyReference zadanie jest wywoływane w .targets pliku.

ResolveAssemblyReference inputs

ResolveAssemblyReference usługa jest kompleksowa w zakresie rejestrowania danych wejściowych:

Zrzut ekranu przedstawiający parametry wejściowe zadania ResolveAssemblyReference.

Węzeł Parameters jest standardowy dla wszystkich zadań, ale dodatkowo ResolveAssemblyReference rejestruje własny zestaw informacji w obszarze Dane wejściowe (które są zasadniczo takie same jak w obszarze Parameters, ale inaczej ustrukturyzowane).

Najważniejsze dane wejściowe to Assemblies i AssemblyFiles:

    <ResolveAssemblyReference
        Assemblies="@(Reference)"
        AssemblyFiles="@(_ResolvedProjectReferencePaths);@(_ExplicitReference)"

Assemblies używa zawartości Reference elementu MSBuild w momencie, gdy ResolveAssemblyReference jest wywoływany dla projektu. Wszystkie odwołania do metadanych i zestawów, w tym odwołania nuGet, powinny być zawarte w tym elemencie. Każde odwołanie zawiera bogaty zestaw metadanych dołączonych do niego:

Zrzut ekranu przedstawiający metadane w dokumentacji zestawu.

AssemblyFiles pochodzi z ResolveProjectReference elementu wyjściowego elementu docelowego o nazwie _ResolvedProjectReferencePaths. ResolveProjectReference jest uruchamiany przed ResolveAssemblyReference i konwertuje <ProjectReference> elementy na ścieżki skompilowanych zestawów na dysku. W związku z tym AssemblyFiles będą zawierać zestawy utworzone przez wszystkie przywołyzowane projekty bieżącego projektu:

Zrzut ekranu przedstawiający pliki AssemblyFiles.

Innym przydatnym wejściem jest parametr logiczny FindDependencies , który pobiera jego wartość z _FindDependencies właściwości :

FindDependencies="$(_FindDependencies)"

Tę właściwość false można ustawić na w kompilacji, aby wyłączyć analizowanie zestawów zależności przechodnich.

ResolveAssemblyReference algorithm

Uproszczony algorytm zadania ResolveAssemblyReference jest następujący:

  1. Dane wejściowe dziennika.
  2. Sprawdź zmienną MSBUILDLOGVERBOSERARSEARCHRESULTS środowiskową. Ustaw tę zmienną na dowolną wartość, aby uzyskać bardziej szczegółowe dzienniki.
  3. Zainicjuj tabelę obiektów odwołań.
  4. Odczytaj plik pamięci podręcznej obj z katalogu (jeśli istnieje).
  5. Oblicz zamknięcie zależności.
  6. Skompiluj tabele wyjściowe.
  7. Zapisz plik pamięci podręcznej w obj katalogu.
  8. Zarejestruj wyniki.

Algorytm pobiera listę wejściowych zestawów (zarówno z metadanych, jak i odwołań do projektu), pobiera listę odwołań dla każdego zestawu, który przetwarza (odczytując metadane) i tworzy pełny zestaw (przejściowe zamknięcie) wszystkich zestawów, do których się odwołuje, i rozpoznaje je z różnych lokalizacji (w tym GAC, AssemblyFoldersEx itd.).

Przywoływany zestawy są dodawane do listy iteracyjnie, dopóki nie zostaną dodane żadne nowe odwołania. Następnie algorytm zatrzymuje się.

Bezpośrednie odwołania podane do zadania są nazywane odwołaniami podstawowymi. Zestawy pośrednie, które zostały dodane do zestawu z powodu odwołania przechodniego, są nazywane zależnością. Rekord dla każdego zestawu pośredniego śledzi wszystkie elementy podstawowe ("root"), które doprowadziły do jego dołączenia i odpowiednich metadanych.

Wyniki zadania ResolveAssemblyReference

ResolveAssemblyReference Zawiera szczegółowe rejestrowanie wyników:

Zrzut ekranu przedstawiający wyniki resolveAssemblyReference w przeglądarce dzienników strukturalnych.

Rozwiązane zestawy są podzielone na dwie kategorie: odwołania podstawowe i zależności. Odwołania podstawowe zostały określone jawnie jako odwołania do tworzonego projektu. Zależności zostały wywnioskowane z odwołań przechodnich.

Ważne

ResolveAssemblyReference odczytuje metadane zestawu w celu określenia odwołań do danego zestawu. Gdy kompilator języka C# emituje zestaw, dodaje tylko odwołania do zestawów, które są rzeczywiście potrzebne. Tak więc może się zdarzyć, że podczas kompilowania określonego projektu projekt może określić niepotrzebne odwołanie, które nie zostanie upieczone w zestawie. Możesz dodać odwołania do projektu, które nie są potrzebne. są ignorowane.

CopyLocal item metadata (Kopiowanie metadanych elementu lokalnego)

Odwołania mogą również zawierać CopyLocal metadane. Jeśli odwołanie ma CopyLocal = truewartość , zostanie później skopiowane do katalogu wyjściowego przez obiekt docelowy CopyFilesToOutputDirectory . W tym przykładzie DataFlow ustawiono CopyLocal wartość true, ale Immutable nie:

Zrzut ekranu przedstawiający ustawienia CopyLocal dla niektórych odwołań.

CopyLocal Jeśli brakuje metadanych w całości, przyjmuje się, że domyślnie ma wartość true. Dlatego ResolveAssemblyReference domyślnie próbuje skopiować zależności do danych wyjściowych, chyba że znajdzie powód, aby nie. ResolveAssemblyReference rejestruje przyczyny, dla których wybrała konkretne odwołanie, które ma być CopyLocal lub nie.

Wszystkie możliwe przyczyny CopyLocal decyzji są wyliczane w poniższej tabeli. Warto znać te ciągi, aby móc wyszukiwać je w dziennikach kompilacji.

KopiujLokalny stan opis
Undecided Stan lokalny kopiowania jest teraz niezdecydowany.
YesBecauseOfHeuristic Odwołanie powinno mieć CopyLocal='true' , ponieważ nie było "nie" z jakiegokolwiek powodu.
YesBecauseReferenceItemHadMetadata Odwołanie powinno mieć CopyLocal='true' , ponieważ jego element źródłowy ma wartość Private='true'
NoBecauseFrameworkFile Odwołanie powinno mieć CopyLocal='false' , ponieważ jest to plik struktury.
NoBecausePrerequisite Odwołanie powinno mieć CopyLocal='false' , ponieważ jest to plik wymagań wstępnych.
NoBecauseReferenceItemHadMetadata Odwołanie powinno mieć CopyLocal='false' wartość , ponieważ Private atrybut jest ustawiony na wartość "false" w projekcie.
NoBecauseReferenceResolvedFromGAC Odwołanie powinno mieć CopyLocal='false' miejsce, ponieważ zostało ono rozwiązane z GAC.
NoBecauseReferenceFoundInGAC Starsze zachowanie, CopyLocal='false' gdy zestaw zostanie znaleziony w GAC (nawet wtedy, gdy został rozwiązany gdzie indziej).
NoBecauseConflictVictim Odwołanie powinno mieć CopyLocal='false' , ponieważ utraciło konflikt między plikiem zestawu o tej samej nazwie.
NoBecauseUnresolved Odwołanie zostało nierozwiązane. Nie można skopiować go do katalogu bin, ponieważ nie został znaleziony.
NoBecauseEmbedded Odwołanie zostało osadzone. Nie należy kopiować go do katalogu bin, ponieważ nie zostanie załadowany w czasie wykonywania.
NoBecauseParentReferencesFoundInGAC Właściwość copyLocalDependenciesWhenParentReferenceInGac jest ustawiona na false, a wszystkie nadrzędne elementy źródłowe zostały znalezione w GAC.
NoBecauseBadImage Podany plik zestawu nie powinien być kopiowany, ponieważ jest to zły obraz, prawdopodobnie nie zarządzany, prawdopodobnie nie zestaw w ogóle.

Metadane elementu prywatnego

Ważną częścią określania CopyLocal jest Private metadane we wszystkich podstawowych odwołaniach. Każde odwołanie (podstawowe lub zależność) zawiera listę wszystkich odwołań podstawowych (elementów źródłowych), które przyczyniły się do dodania tego odwołania do zamknięcia.

  • Jeśli żadna z elementów źródłowych nie określi Private metadanych, CopyLocal zostanie ustawiona wartość True (lub nie zostanie ustawiona wartość domyślna )True
  • Jeśli którykolwiek z elementów źródłowych określ Private=truewartość , jest ustawiona na wartość CopyLocalTrue
  • Jeśli żaden z zestawów źródłowych nie określi Private=true i co najmniej jeden określi Private=falseparametr , CopyLocal zostanie ustawiona wartość False

Które odwołanie ma wartość False?

Ostatni punkt jest często używanym powodem CopyLocal ustawiania wartości false: This reference is not "CopyLocal" because at least one source item had "Private" set to "false" and no source items had "Private" set to "true".

Program MSBuild nie informuje nas, które odwołanie ma ustawioną Private wartość false, ale ustrukturyzowana przeglądarka dzienników dodaje Private metadane do elementów, które zostały określone powyżej:

Zrzut ekranu przedstawiający ustawienie Prywatne ustawione na wartość false w przeglądarce dzienników strukturalnych.

Upraszcza to badanie i informuje o tym, które odwołanie spowodowało, że zależność, o której mowa, ma być ustawiona za pomocą polecenia CopyLocal=false.

Global Assembly Cache

Global Assembly Cache (GAC) odgrywa ważną rolę w określaniu, czy skopiować odwołania do danych wyjściowych. Jest to niefortunne, ponieważ zawartość GAC jest specyficzna dla maszyny i powoduje to problemy z powtarzalnymi kompilacjami (gdzie zachowanie różni się od innej maszyny zależnej od stanu komputera, na przykład GAC).

Wprowadzono ostatnie poprawki, aby ResolveAssemblyReference złagodzić sytuację. Możesz kontrolować zachowanie tych dwóch nowych danych wejściowych w usłudze ResolveAssemblyReference:

    CopyLocalDependenciesWhenParentReferenceInGac="$(CopyLocalDependenciesWhenParentReferenceInGac)"
    DoNotCopyLocalIfInGac="$(DoNotCopyLocalIfInGac)"

AssemblySearchPaths

Istnieją dwa sposoby dostosowywania listy wyszukiwań ścieżek ResolveAssemblyReference podczas próby zlokalizowania zestawu. Aby w pełni dostosować listę, właściwość AssemblySearchPaths można ustawić przed upływem czasu. Porządek ma znaczenie; jeśli zestaw znajduje się w dwóch lokalizacjach, ResolveAssemblyReference zatrzymuje się po znalezieniu go w pierwszej lokalizacji.

Domyślnie istnieją dziesięć wyszukiwań ResolveAssemblyReference lokalizacji (cztery, jeśli używasz zestawu .NET SDK), a każda z nich może zostać wyłączona, ustawiając odpowiednią flagę na false:

  • Wyszukiwanie plików z bieżącego projektu jest wyłączone, ustawiając AssemblySearchPath_UseCandidateAssemblyFiles właściwość na false.
  • Przeszukiwanie właściwości ścieżki odwołania (z .user pliku) jest wyłączone przez ustawienie AssemblySearchPath_UseReferencePath właściwości na false.
  • Użycie ścieżki wskazówek z elementu jest wyłączone przez ustawienie AssemblySearchPath_UseHintPathFromItem właściwości na false.
  • Użycie katalogu z docelowym środowiskiem uruchomieniowym msBuild jest wyłączone przez ustawienie AssemblySearchPath_UseTargetFrameworkDirectory właściwości na false.
  • Wyszukiwanie folderów zestawów z pliku AssemblyFolders.config jest wyłączone przez ustawienie AssemblySearchPath_UseAssemblyFoldersConfigFileSearchPath właściwości na false.
  • Przeszukiwanie rejestru jest wyłączone, ustawiając AssemblySearchPath_UseRegistry właściwość na false.
  • Wyszukiwanie starszych zarejestrowanych folderów zestawów jest wyłączone, ustawiając AssemblySearchPath_UseAssemblyFolders właściwość na false.
  • Szukanie w GAC jest wyłączone, ustawiając AssemblySearchPath_UseGAC właściwość na false.
  • Traktowanie odwołania Include jako prawdziwej nazwy pliku jest wyłączone przez ustawienie AssemblySearchPath_UseRawFileName właściwości false.
  • Sprawdzanie, czy folder wyjściowy aplikacji jest wyłączony, ustawiając AssemblySearchPath_UseOutDir właściwość na false.

Wystąpił konflikt

Typowa sytuacja polega na tym, że program MSBuild wyświetla ostrzeżenie dotyczące różnych wersji tego samego zestawu używanego przez różne odwołania. Rozwiązanie często polega na dodaniu przekierowania powiązania do pliku app.config.

Przydatnym sposobem zbadania tych konfliktów jest wyszukiwanie w przeglądarce dzienników strukturalnych MSBuild pod kątem "Wystąpił konflikt". Zawiera on szczegółowe informacje o tym, które odwołania są potrzebne do tego, które wersje danego zestawu.