Sdílet prostřednictvím


Řešení potíží s odkazy na sestavení

Jednou z nejdůležitějších úloh v nástroji MSBuild a proces sestavení .NET je překlad odkazů na sestavení, ke kterým dochází v ResolveAssemblyReference úloze. Tento článek vysvětluje některé podrobnosti o tom, jak ResolveAssemblyReference funguje, a jak řešit potíže se selháními sestavení, ke kterým může dojít, když ResolveAssemblyReference se nedaří vyřešit odkaz. Pokud chcete prošetřit selhání odkazů na sestavení, můžete nainstalovat Prohlížeč strukturovaných protokolů pro zobrazení protokolů NÁSTROJE MSBuild. Snímky obrazovky v tomto článku pocházejí z prohlížeče strukturovaných protokolů.

Účelem ResolveAssemblyReference je vzít všechny odkazy zadané v .csproj souborech (nebo jinde) prostřednictvím <Reference> položky a mapovat je na cesty k souborům sestavení v systému souborů souborů.

Kompilátory mohou jako odkaz přijmout .dll pouze cestu v systému souborů, takže ResolveAssemblyReference převede řetězce, jako mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 jsou v souborech projektu, na cesty jako C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll, které se pak předají kompilátoru /r prostřednictvím přepínače.

ResolveAssemblyReference Dále určuje úplnou sadu (ve skutečnosti tranzitivní uzavření v teorii grafu) všech .dll a .exe odkazů rekurzivně, a pro každý z nich určuje, zda se má zkopírovat do výstupního adresáře sestavení, nebo ne. Neprovádí skutečné kopírování (které se zpracuje později po skutečném kroku kompilace), ale připraví seznam položek souborů ke kopírování.

ResolveAssemblyReference je vyvolána z ResolveAssemblyReferences cíle:

Snímek obrazovky prohlížeče protokolů, který znázorňuje, když se v procesu sestavení volá ResolveAssemblyReferences

Pokud si všimnete objednávání, ResolveAssemblyReferences děje se před Compile, a samozřejmě, CopyFilesToOutputDirectory stane se po Compile.

Poznámka:

ResolveAssemblyReference úloha je vyvolána ve standardním .targets souboru Microsoft.Common.CurrentVersion.targets v instalačních složkách NÁSTROJE MSBuild. Můžete také procházet cíle MSBuild sady .NET SDK online na adrese https://github.com/dotnet/msbuild/blob/a936b97e30679dcea4d99c362efa6f732c9d3587/src/Tasks/Microsoft.Common.CurrentVersion.targets#L1991-L2140. Tento odkaz ukazuje, kde ResolveAssemblyReference je úloha vyvolána v .targets souboru.

Řešení vstupů ResolveAssemblyReference

ResolveAssemblyReference je komplexní o protokolování svých vstupů:

Snímek obrazovky znázorňující vstupní parametry pro úlohu ResolveAssemblyReference

Uzel Parameters je standardní pro všechny úkoly, ale kromě toho ResolveAssemblyReference protokoluje svou vlastní sadu informací v části Vstupy (což je v podstatě stejné jako v Parametersčásti , ale strukturováno odlišně).

Nejdůležitější vstupy jsou Assemblies a AssemblyFiles:

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

Assemblies používá obsah Reference položky MSBuild v okamžiku, kdy ResolveAssemblyReference je vyvolán pro projekt. Všechna metadata a odkazy na sestavení, včetně odkazů NuGet, by měly být obsaženy v této položce. Každý odkaz má k němu připojenou bohatou sadu metadat:

Snímek obrazovky s metadaty v odkazu na sestavení

AssemblyFiles pochází z ResolveProjectReference výstupní položky cíle s názvem _ResolvedProjectReferencePaths. ResolveProjectReference spustí se dříve ResolveAssemblyReference a převede <ProjectReference> položky na cesty sestavených sestavení na disku. AssemblyFiles Proto bude obsahovat sestavení sestavená všemi odkazovanými projekty aktuálního projektu:

Snímek obrazovky zobrazující assemblyfiles

Dalším užitečným vstupem je logický FindDependencies parametr, který přebírá jeho hodnotu z _FindDependencies vlastnosti:

FindDependencies="$(_FindDependencies)"

Tuto vlastnost můžete v sestavení nastavit tak false , aby se vypnula analýza tranzitivních sestavení závislostí.

Algoritmus ResolveAssemblyReference

Zjednodušený algoritmus úkolu ResolveAssemblyReference je následující:

  1. Vstupy protokolu.
  2. Zkontrolujte proměnnou MSBUILDLOGVERBOSERARSEARCHRESULTS prostředí. Nastavte tuto proměnnou na libovolnou hodnotu, abyste získali podrobnější protokoly.
  3. Inicializuje tabulku odkazů objektu.
  4. Čtení souboru mezipaměti z obj adresáře (pokud je k dispozici).
  5. Vypočítá uzavření závislostí.
  6. Sestavte výstupní tabulky.
  7. Zapište soubor mezipaměti do obj adresáře.
  8. Zapíše výsledky.

Algoritmus přebírá vstupní seznam sestavení (jak z metadat, tak odkazů na projekt), načte seznam odkazů pro každé sestavení, které zpracovává (čtením metadat) a vytvoří úplnou sadu (tranzitivní uzavření) všech odkazovaných sestavení a přeloží je z různých umístění (včetně GAC, AssemblyFoldersEx atd.).

Odkazovaná sestavení jsou přidána do seznamu iterativním způsobem, dokud nebudou přidány žádné nové odkazy. Pak se algoritmus zastaví.

Přímé odkazy, které jste zadali úkolu, se nazývají primární odkazy. Nepřímá sestavení, která byla přidána do sady kvůli tranzitivnímu odkazu, se nazývají Závislost. Záznam pro každé nepřímé sestavení sleduje všechny primární ("kořenové") položky, které vedly k zahrnutí a jejich odpovídajícím metadatům.

Výsledky úlohy ResolveAssemblyReference

ResolveAssemblyReference poskytuje podrobné protokolování výsledků:

Snímek obrazovky zobrazující výsledky ResolveAssemblyReference ve strukturovaném prohlížeči protokolů

Vyřešená sestavení jsou rozdělena do dvou kategorií: primární odkazy a závislosti. Primární odkazy byly explicitně zadány jako odkazy na projekt, který se sestavuje. Závislosti byly odvozeny z odkazů na odkazy tranzitivně.

Důležité

ResolveAssemblyReference čte metadata sestavení k určení odkazů na dané sestavení. Když kompilátor jazyka C# vygeneruje sestavení, přidá pouze odkazy na sestavení, která jsou skutečně potřebná. Může se tedy stát, že při kompilaci určitého projektu může projekt zadat nepotřebný odkaz, který se do sestavení nezapečí. Je v pořádku přidat odkazy na projekt, které nejsou potřeba; jsou ignorovány.

Kopírovat metadata položkyLocal

Odkazy můžou mít CopyLocal také metadata nebo ne. Pokud odkaz obsahuje CopyLocal = true, bude později zkopírován do výstupního CopyFilesToOutputDirectory adresáře cílem. V tomto příkladu je DataFlow CopyLocal nastavená hodnota true, zatímco Immutable ne:

Snímek obrazovky s nastavením CopyLocal pro některé odkazy

CopyLocal Pokud metadata zcela chybí, předpokládá se, že jsou ve výchozím nastavení pravdivá. Ve výchozím nastavení se tedy ResolveAssemblyReference pokusí kopírovat závislosti do výstupu, pokud nenajde důvod, proč ne. ResolveAssemblyReference zaznamenává důvody, proč zvolil konkrétní odkaz, nebo CopyLocal ne.

Všechny možné důvody rozhodnutí CopyLocal jsou uvedené v následující tabulce. Je užitečné znát tyto řetězce, aby je bylo možné vyhledat v protokolech sestavení.

Kopírovat místní stav Popis
Undecided Místní stav kopírování je teď nedecidovaný.
YesBecauseOfHeuristic Odkaz by měl mít CopyLocal='true' , protože z nějakého důvodu nebyl "ne".
YesBecauseReferenceItemHadMetadata Odkaz by měl mít CopyLocal='true' , protože jeho zdrojová položka má private='true'
NoBecauseFrameworkFile Odkaz by měl mít CopyLocal='false' , protože se jedná o soubor architektury.
NoBecausePrerequisite Odkaz by měl mít CopyLocal='false' , protože se jedná o požadovaný soubor.
NoBecauseReferenceItemHadMetadata Odkaz by měl mít CopyLocal='false' , protože Private atribut je v projektu nastaven na false.
NoBecauseReferenceResolvedFromGAC Odkaz by měl mít CopyLocal='false' , protože byl vyřešen z GAC.
NoBecauseReferenceFoundInGAC Starší chování při CopyLocal='false' nalezení sestavení v GAC (i když bylo vyřešeno jinde).
NoBecauseConflictVictim Odkaz by měl mít CopyLocal='false' , protože došlo ke ztrátě konfliktu mezi souborem sestavení se stejným názvem.
NoBecauseUnresolved Odkaz se nevyřešil. Nejde ho zkopírovat do adresáře bin, protože se nenašel.
NoBecauseEmbedded Odkaz byl vložen. Nemělo by se kopírovat do adresáře bin, protože se nenačte za běhu.
NoBecauseParentReferencesFoundInGAC Vlastnost copyLocalDependenciesWhenParentReferenceInGac je nastavena na false a všechny nadřazené zdrojové položky byly nalezeny v GAC.
NoBecauseBadImage Zadaný soubor sestavení by se neměl kopírovat, protože se jedná o špatnou image, pravděpodobně nespravovanou, pravděpodobně vůbec ne sestavení.

Metadata privátních položek

Důležitou součástí určení CopyLocal je Private metadata všech primárních odkazů. Každý odkaz (primární nebo závislost) obsahuje seznam všech primárních odkazů (zdrojových položek), které přispěly k přidání tohoto odkazu do uzavření.

  • Pokud žádná ze zdrojových položek nezadává Private metadata, CopyLocal je nastavena na True (nebo není nastavena, což je výchozí hodnota True)
  • Pokud zadáte některou ze zdrojových položek Private=true, CopyLocal nastaví se na True
  • Pokud žádná ze zdrojových sestavení neurčí Private=true a alespoň jedno určuje Private=false, CopyLocal je nastavena na hodnotu False

Který odkaz nastavil hodnotu Private na false?

Posledním bodem je často používaný důvod, proč CopyLocal je nastavená hodnota 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".

Nástroj MSBuild nám neřekne, který odkaz má nastavenou Private hodnotu false, ale prohlížeč strukturovaného protokolu přidává Private metadata k položkám, které byly zadány výše:

Snímek obrazovky znázorňující hodnotu Private set to false v prohlížeči strukturovaných protokolů

To zjednodušuje šetření a přesně vám řekne, který odkaz způsobil, že daná závislost byla nastavena CopyLocal=false.

Globální mezipaměť sestavení

Globální mezipaměť sestavení (GAC) hraje důležitou roli při určování, zda se mají kopírovat odkazy na výstup. Je to nešťastné, protože obsah GAC je specifický pro počítač a výsledkem jsou problémy s reprodukovatelnými buildy (kde se chování liší na různých počítačích závislých na stavu počítače, jako je GAC).

Došlo k nedávným opravám, které ResolveAssemblyReference by zmírnit situaci. Chování můžete řídit těmito dvěma novými vstupy:ResolveAssemblyReference

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

AssemblySearchPaths

Existují dva způsoby, jak přizpůsobit seznam cest ResolveAssemblyReference hledání při pokusu o vyhledání sestavení. Pokud chcete seznam plně přizpůsobit, můžete vlastnost AssemblySearchPaths nastavit předem. Pořadí záleží; pokud je sestavení ve dvou umístěních, zastaví se poté, ResolveAssemblyReference co ho najde na prvním místě.

Ve výchozím nastavení je vyhledávání v deseti umístěních ResolveAssemblyReference (čtyři, pokud používáte sadu .NET SDK) a každá z nich může být zakázána nastavením příslušného příznaku na false:

  • Vyhledávání souborů z aktuálního projektu je zakázáno nastavením AssemblySearchPath_UseCandidateAssemblyFiles vlastnosti na false.
  • Vyhledávání vlastnosti referenční cesty (ze .user souboru) je zakázáno nastavením AssemblySearchPath_UseReferencePath vlastnosti na false.
  • Použití cesty nápovědy z položky je zakázáno nastavením AssemblySearchPath_UseHintPathFromItem vlastnosti na false.
  • Použití adresáře s cílovým modulem runtime nástroje MSBuild je zakázáno nastavením AssemblySearchPath_UseTargetFrameworkDirectory vlastnosti na false.
  • Vyhledávání složek sestavení z AssemblyFolders.config je zakázáno nastavením AssemblySearchPath_UseAssemblyFoldersConfigFileSearchPath vlastnosti na false.
  • Vyhledávání v registru je zakázáno nastavením AssemblySearchPath_UseRegistry vlastnosti na false.
  • Vyhledávání starších registrovaných složek sestavení je zakázáno nastavením AssemblySearchPath_UseAssemblyFolders vlastnosti na false.
  • Zobrazení GAC je zakázáno nastavením AssemblySearchPath_UseGAC vlastnosti na false.
  • Zacházení s odkazem Include jako skutečným názvem souboru je zakázáno nastavením AssemblySearchPath_UseRawFileName vlastnosti na false.
  • Kontrola výstupní složky aplikace je zakázána nastavením AssemblySearchPath_UseOutDir vlastnosti na false.

Došlo ke konfliktu

Běžnou situací je, že nástroj MSBuild zobrazí upozornění na různé verze stejného sestavení, které používají různé odkazy. Řešení často zahrnuje přidání přesměrování vazby do souboru app.config.

Užitečný způsob, jak tyto konflikty prošetřit, je vyhledat ve strukturovaném prohlížeči protokolů NÁSTROJE MSBuild "Došlo ke konfliktu". Zobrazí podrobné informace o tom, které odkazy potřebovaly, které verze sestavení.