Ř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:
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ů:
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:
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:
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í:
- Vstupy protokolu.
- Zkontrolujte proměnnou
MSBUILDLOGVERBOSERARSEARCHRESULTS
prostředí. Nastavte tuto proměnnou na libovolnou hodnotu, abyste získali podrobnější protokoly. - Inicializuje tabulku odkazů objektu.
- Čtení souboru mezipaměti z
obj
adresáře (pokud je k dispozici). - Vypočítá uzavření závislostí.
- Sestavte výstupní tabulky.
- Zapište soubor mezipaměti do
obj
adresáře. - 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ů:
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:
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 naTrue
(nebo není nastavena, což je výchozí hodnotaTrue
) - Pokud zadáte některou ze zdrojových položek
Private=true
,CopyLocal
nastaví se naTrue
- Pokud žádná ze zdrojových sestavení neurčí
Private=true
a alespoň jedno určujePrivate=false
,CopyLocal
je nastavena na hodnotuFalse
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:
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ímAssemblySearchPath_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í.