Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
L’une des tâches les plus importantes dans MSBuild et le processus de génération .NET consiste à résoudre les références d’assembly, qui se produisent dans la ResolveAssemblyReference tâche. Cet article explique quelques détails sur le fonctionnement de ResolveAssemblyReference, et comment résoudre les échecs de build qui peuvent survenir lorsque ResolveAssemblyReference n’est pas en mesure de résoudre une référence. Pour examiner les échecs de référence d’assembly, vous pouvez installer Structured Log Viewer pour afficher les journaux MSBuild. Les captures d’écran de cet article sont extraites de la visionneuse de journaux structurés.
L’objectif de ResolveAssemblyReference est de prendre toutes les références spécifiées dans les fichiers .csproj (ou ailleurs) via l’élément <Reference> et de les associer aux chemins des fichiers d'assemblage dans le système de fichiers.
Les compilateurs ne peuvent accepter qu’un .dll chemin d’accès sur le système de fichiers en tant que référence, par conséquent ResolveAssemblyReference convertit des chaînes comme mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 qui apparaissent dans les fichiers projet en chemins comme C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll, qui sont ensuite passés au compilateur via l'option /r.
Détermine également ResolveAssemblyReference l’ensemble complet (en fait la fermeture transitive en termes de théorie du graphique) de toutes .dll et .exe de références récursives, et pour chacun d’eux détermine s’il doit être copié dans le répertoire de sortie de build ou non. Il ne fait pas la copie réelle (qui est gérée ultérieurement, après l’étape de compilation réelle), mais elle prépare une liste d’éléments de fichiers à copier.
ResolveAssemblyReference est appelé à partir de la ResolveAssemblyReferences cible :
Si vous remarquez l’ordre, ResolveAssemblyReferences se produit avant Compile, et bien sûr, CopyFilesToOutputDirectory se produit après Compile.
Note
ResolveAssemblyReferencela tâche est appelée dans le fichier .targets standard Microsoft.Common.CurrentVersion.targets dans les dossiers d’installation MSBuild. Vous pouvez également parcourir les cibles MSBuild du SDK .NET en ligne à l’adresse https://github.com/dotnet/msbuild/blob/a936b97e30679dcea4d99c362efa6f732c9d3587/src/Tasks/Microsoft.Common.CurrentVersion.targets#L1991-L2140. Ce lien indique exactement où la ResolveAssemblyReference tâche est appelée dans le .targets fichier.
Entrées de ResolveAssemblyReference
ResolveAssemblyReference est complet dans la journalisation de ses entrées.
Le Parameters nœud est standard pour toutes les tâches, mais enregistre en outre ResolveAssemblyReference son propre ensemble d’informations sous Entrées (qui est essentiellement identique à sous Parameters, mais structuré différemment).
Les entrées les plus importantes sont Assemblies et AssemblyFiles:
<ResolveAssemblyReference
Assemblies="@(Reference)"
AssemblyFiles="@(_ResolvedProjectReferencePaths);@(_ExplicitReference)"
Assemblies utilise le contenu de l’élément Reference MSBuild au moment où il ResolveAssemblyReference est appelé pour le projet. Toutes les références de métadonnées et d’assembly, y compris vos références NuGet, doivent être contenues dans cet élément. Chaque référence a un ensemble complet de métadonnées attachées à celui-ci :
AssemblyFiles provient de l’élément appelé _ResolvedProjectReferencePaths, qui est la sortie de la cible ResolveProjectReference.
ResolveProjectReference s’exécute avant ResolveAssemblyReference et convertit les éléments en chemins d’assemblys générés <ProjectReference> sur le disque. Par conséquent, le AssemblyFiles contiendra les assemblies générés par tous les projets référencés du projet actuel.
Une autre entrée utile est le paramètre booléen FindDependencies , qui prend sa valeur à partir de la _FindDependencies propriété :
FindDependencies="$(_FindDependencies)"
Vous pouvez définir cette propriété false sur dans votre build pour désactiver l’analyse des assemblys de dépendance transitive.
Algorithme ResolveAssemblyReference
L’algorithme simplifié pour la ResolveAssemblyReference tâche est le suivant :
- Journalisation des entrées.
- Vérifiez la variable d’environnement
MSBUILDLOGVERBOSERARSEARCHRESULTS. Définissez cette variable sur n’importe quelle valeur pour obtenir des journaux plus détaillés. - Initialisez l'objet table des références.
- Lisez le fichier cache à partir du
objrépertoire (le cas échéant). - Calculez la fermeture des dépendances.
- Générez les tables de sortie.
- Écrivez le fichier cache dans le
objrépertoire. - Journaliser les résultats.
L’algorithme prend la liste d’entrée d’assemblys (à la fois à partir de métadonnées et de références de projet), récupère la liste des références pour chaque assembly qu’il traite (en lisant les métadonnées) et génère un ensemble complet (fermeture transitive) de tous les assemblys référencés et les résout à partir de différents emplacements (y compris le GAC, AssemblyFoldersEx, etc.).
Les assemblages référencés sont ajoutés à la liste de façon itérative jusqu'à ce qu'aucune nouvelle référence ne soit ajoutée. L’algorithme s’arrête ensuite.
Les références directes que vous avez fournies à la tâche sont appelées références principales. Les assemblages indirects ajoutés à l'ensemble en raison d'une référence transitive sont appelés dépendances. L'enregistrement de chaque assembly indirect consigne tous les éléments primaires (« racine ») qui ont conduit à son inclusion ainsi que leurs métadonnées correspondantes.
Résultats de la tâche ResolveAssemblyReference
ResolveAssemblyReference fournit une journalisation détaillée des résultats :
Les assemblages résolus sont divisés en deux catégories : les références principales et les dépendances. Les références principales ont été spécifiées explicitement en tant que références du projet en cours de génération. Les dépendances ont été déduites des références de références transitivement.
Important
ResolveAssemblyReference lit les métadonnées d’assembly pour déterminer les références d’un assembly donné. Lorsque le compilateur C# émet un assembly, il ajoute uniquement des références aux assemblys qui sont réellement nécessaires. Par conséquent, il peut arriver que lorsque vous compilez un certain projet, le projet peut spécifier une référence inutiles qui ne sera pas intégrée à l’assembly. Il est ok d’ajouter des références au projet qui ne sont pas nécessaires ; ils sont ignorés.
Métadonnées de l’élément CopyLocal
Les références peuvent également avoir les CopyLocal métadonnées ou non. Si la référence a CopyLocal = true, elle sera copiée ultérieurement dans le répertoire de sortie par la CopyFilesToOutputDirectory cible. Dans cet exemple, DataFlow a CopyLocal défini sur vrai, tandis que Immutable ne l'est pas :
Si les CopyLocal métadonnées sont manquantes entièrement, elles sont supposées être vraies par défaut. Ainsi, ResolveAssemblyReference essaie par défaut de copier les dépendances en sortie, sauf s'il trouve une raison de ne pas le faire.
ResolveAssemblyReference enregistre les raisons pour lesquelles cela a choisi une référence particulière pour être CopyLocal ou non.
Toutes les raisons possibles justifiant CopyLocal la décision sont énumérées dans le tableau suivant. Il est utile de connaître ces chaînes pour pouvoir les rechercher dans les journaux de compilation.
| État CopyLocal | Descriptif |
|---|---|
Undecided |
L’état local de copie est indécis pour le moment. |
YesBecauseOfHeuristic |
La référence devrait avoir CopyLocal='true' parce qu’elle n’était pas « non » pour une raison quelconque. |
YesBecauseReferenceItemHadMetadata |
La référence doit avoir CopyLocal='true' parce que son élément source a Private='true' |
NoBecauseFrameworkFile |
La référence doit avoir CopyLocal='false' parce qu’il s’agit d’un fichier framework. |
NoBecausePrerequisite |
La référence doit avoir CopyLocal='false' , car il s’agit d’un fichier requis. |
NoBecauseReferenceItemHadMetadata |
La référence doit avoir CopyLocal='false' , car l’attribut Private est défini sur « false » dans le projet. |
NoBecauseReferenceResolvedFromGAC |
La référence doit avoir CopyLocal='false' parce qu’elle a été résolue à partir du GAC. |
NoBecauseReferenceFoundInGAC |
Comportement hérité, CopyLocal='false' lorsque l’assembly est trouvé dans le GAC (même lorsqu’il a été résolu ailleurs). |
NoBecauseConflictVictim |
La référence doit avoir CopyLocal='false' parce qu’elle a perdu le conflit avec un fichier d’assembly ayant le même nom. |
NoBecauseUnresolved |
La référence n’a pas été résolue. Elle ne peut pas être copiée dans le répertoire bin, car elle n’a pas été trouvée. |
NoBecauseEmbedded |
La référence a été incorporée. Il ne doit pas être copié dans le répertoire bin, car il ne sera pas chargé au moment de l’exécution. |
NoBecauseParentReferencesFoundInGAC |
La propriété copyLocalDependenciesWhenParentReferenceInGac est définie sur false et tous les éléments sources parents ont été trouvés dans le GAC. |
NoBecauseBadImage |
Le fichier d’assembly fourni ne doit pas être copié, car il s’agit d’une image incorrecte, éventuellement pas gérée, éventuellement pas d’un assembly du tout. |
Métadonnées d’élément privé
Une partie importante de la détermination CopyLocal sont les Private métadonnées sur toutes les références principales. Chaque référence (principale ou de dépendance) inclut une liste de toutes les références principales (éléments source) qui ont contribué à l'ajout de cette référence à l'ensemble fermé.
- Si aucun des éléments sources ne spécifie les
Privatemétadonnées,CopyLocalest réglé surTrue(ou n'est pas défini, auquel cas il prend par défaut la valeurTrue) - Si l’un des éléments sources spécifie
Private=true,CopyLocalest défini surTrue. - Si aucun des assemblys sources ne spécifie
Private=trueet qu’au moins un spécifiePrivate=false,CopyLocalest défini surFalse
Quelle référence a défini Private sur false ?
Le dernier point est une raison souvent utilisée pour CopyLocal avoir la valeur 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".
MSBuild ne nous dit pas quelle référence a défini Private sur false, mais la visionneuse du journal structuré ajoute des métadonnées Private aux éléments pour lesquels cela a été spécifié ci-dessus :
Cela simplifie les enquêtes et vous indique exactement quelle référence a provoqué la définition de la dépendance en question CopyLocal=false.
Cache d'assemblage global
Le Global Assembly Cache (GAC) joue un rôle important dans la décision de copier ou non les références vers la sortie. Cela est malheureux, car le contenu du GAC est spécifique à l’ordinateur et cela entraîne des problèmes pour les builds reproductibles (où le comportement diffère sur une autre machine dépendante de l’état de l’ordinateur, tel que le GAC).
Des correctifs récents ont été apportés à ResolveAssemblyReference pour atténuer la situation. Vous pouvez contrôler le comportement par ces deux nouvelles entrées pour ResolveAssemblyReference:
CopyLocalDependenciesWhenParentReferenceInGac="$(CopyLocalDependenciesWhenParentReferenceInGac)"
DoNotCopyLocalIfInGac="$(DoNotCopyLocalIfInGac)"
AssemblySearchPaths
Il existe deux façons de personnaliser la liste de recherche des chemins d’accès ResolveAssemblyReference lors du processus de localisation d’un assembly. Pour personnaliser entièrement la liste, la propriété AssemblySearchPaths peut être définie à l’avance. L’ordre est important ; si un assemblage se trouve à deux emplacements, ResolveAssemblyReference s’arrête après l'avoir trouvé au premier emplacement.
AssemblySearchPaths est une liste délimitée par des points-virgules. Il prend en charge un ensemble d’espaces réservés intégrés (par exemple, {HintPathFromItem} et {GAC}) qui s’étendent vers des emplacements réels pendant la résolution.
Par défaut, les projets de style non SDK utilisent l’ordre de chemin de recherche suivant :
- Fichiers d’assembly candidats (
{CandidateAssemblyFiles}) - Propriété
ReferencePath($(ReferencePath)) - Chemins des indices provenant des éléments
<Reference>({HintPathFromItem}) - Répertoire du framework cible (
{TargetFrameworkDirectory}) - Dossiers d’assembly de
AssemblyFolders.config($(AssemblyFoldersConfigFileSearchPath)) - Registre (
{Registry:...}) - Dossiers d'assemblage hérités enregistrés (
{AssemblyFolders}) - Cache d’assemblages global (Global Assembly Cache, GAC) (
{GAC}) - Traitez la
<Reference Include="...">valeur comme un nom de fichier réel ({RawFileName}) - Répertoire de sortie (
$(OutDir))
Le Kit de développement logiciel (SDK) .NET définit une valeur par défaut AssemblySearchPaths plus petite (à l’exception du GAC/du Registre/des recherches de répertoires de sortie par défaut) :
{CandidateAssemblyFiles}{HintPathFromItem}{TargetFrameworkDirectory}{RawFileName}
Pour afficher la valeur effective de votre build, inspectez l’entrée SearchPaths enregistrée par ResolveAssemblyReference (par exemple, dans MSBuild Structured Log Viewer) ou prétraitez le projet avec msbuild /pp.
Chaque entrée peut être désactivée en définissant l’indicateur approprié sur false:
- La recherche de fichiers à partir du projet actuel est désactivée en définissant la
AssemblySearchPath_UseCandidateAssemblyFilespropriété sur false. - La recherche de la propriété de chemin d’accès de référence (à partir d’un
.userfichier) est désactivée en définissant laAssemblySearchPath_UseReferencePathpropriété sur false. - L'utilisation du chemin d'indication de l'élément est désactivée en définissant la propriété
AssemblySearchPath_UseHintPathFromItemsur false. - L’utilisation du répertoire avec le runtime cible de MSBuild est désactivée en définissant la
AssemblySearchPath_UseTargetFrameworkDirectorypropriété sur false. - La recherche de dossiers d’assembly à partir de AssemblyFolders.config est désactivée en définissant la
AssemblySearchPath_UseAssemblyFoldersConfigFileSearchPathpropriété sur false. - La recherche dans le Registre est désactivée en définissant la
AssemblySearchPath_UseRegistrypropriété sur false. - La recherche dans les dossiers de bibliothèques enregistrés anciens est désactivée en définissant la propriété
AssemblySearchPath_UseAssemblyFolderssur false. - La recherche dans le GAC est désactivée en définissant la
AssemblySearchPath_UseGACpropriété sur false. - Le traitement de l’élément Include de la référence en tant que nom de fichier réel est désactivé en définissant la valeur de la propriété
AssemblySearchPath_UseRawFileNamesur false. - La vérification du dossier de sortie de l’application est désactivée en définissant la
AssemblySearchPath_UseOutDirpropriété sur false.
Il y avait un conflit
Une situation courante est que MSBuild fournit un avertissement sur différentes versions du même assembly utilisées par différentes références. La solution implique souvent l’ajout d’une redirection de liaison au fichier app.config.
Un moyen utile d’examiner ces conflits consiste à rechercher dans MSBuild Structured Log Viewer pour « Il y a eu un conflit ». Il vous montre des informations détaillées sur les références indiquant quelles versions spécifiques de l’assembly sont nécessaires.