1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using System;
5 using System.Collections;
6 using System.Collections.Concurrent;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using System.IO;
10 using System.Reflection;
11 using System.Runtime.Versioning;
12 
13 using Microsoft.Build.Framework;
14 using Microsoft.Build.Shared;
15 using Microsoft.Build.Tasks.AssemblyDependency;
16 using Microsoft.Build.Utilities;
17 #if (!STANDALONEBUILD)
18 using Microsoft.Internal.Performance;
19 #endif
20 using FrameworkNameVersioning = System.Runtime.Versioning.FrameworkName;
21 using SystemProcessorArchitecture = System.Reflection.ProcessorArchitecture;
22 
23 namespace Microsoft.Build.Tasks
24 {
25     /// <summary>
26     /// A table of references.
27     /// </summary>
28     sealed internal class ReferenceTable
29     {
30         /// <summary>version 4.0</summary>
31         private static readonly Version s_targetFrameworkVersion_40 = new Version("4.0");
32 
33         /// <summary>
34         /// A mapping of a framework identifier to the most current redist list on the system based on the target framework identifier on the moniker.
35         /// This is used to determine if an assembly is in a redist list for the framework targeted by the moniker.
36         /// </summary>
37         private static Dictionary<string, Tuple<RedistList, string>> s_monikerToHighestRedistList = new Dictionary<string, Tuple<RedistList, string>>(StringComparer.OrdinalIgnoreCase);
38 
39         /// <summary>
40         /// The table of references.
41         ///   Key is assemblyName;
42         ///   Value is Reference.
43         /// </summary>
44         private Dictionary<AssemblyNameExtension, Reference> _references = new Dictionary<AssemblyNameExtension, Reference>(AssemblyNameComparer.GenericComparer);
45 
46         /// <summary>
47         /// Reference simple names that were resolved by an external entity to RAR.
48         /// </summary>
49         private HashSet<string> _externallyResolvedPrimaryReferences = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
50 
51         /// <summary>The table of remapped assemblies. Used for Unification.</summary>
52         private DependentAssembly[] _remappedAssemblies = Array.Empty<DependentAssembly>();
53         /// <summary>If true, then search for dependencies.</summary>
54         private bool _findDependencies = true;
55         /// <summary>
56         ///  Should version be ignored for framework primary references
57         /// </summary>
58         private bool _ignoreVersionForFrameworkReferences = false;
59         /// <summary>If true, then search for satellite files.</summary>
60         private bool _findSatellites = true;
61         /// <summary>If true, then search for serialization assembly files.</summary>
62         private bool _findSerializationAssemblies = true;
63         /// <summary>If true, then search for related files.</summary>
64         private bool _findRelatedFiles = true;
65         /// <summary>
66         /// If true, then force framework assembly version check against the target framework version
67         /// If false, the default behavior is to disable version checks for target framework versions 4.5 and above.
68         /// </summary>
69         private bool _checkAssemblyVersionAgainstTargetFrameworkVersion = false;
70 
71         /// <summary>Path to the FX.</summary>
72         private string[] _frameworkPaths;
73         /// <summary>The allowed assembly extensions.</summary>
74         private string[] _allowedAssemblyExtensions;
75         /// <summary>These are companion files that typically travel with assemblies</summary>
76         private string[] _relatedFileExtensions;
77         /// <summary>
78         /// Locations where sdks are installed. K:SDKName v: Resolved Reference item
79         /// </summary>
80         private Dictionary<string, ITaskItem> _resolvedSDKReferences;
81         /// <summary>Path to installed assembly XML tables.</summary>
82         private InstalledAssemblies _installedAssemblies;
83         /// <summary>Like x86 or IA64\AMD64, the processor architecture being targetted.</summary>
84         private SystemProcessorArchitecture _targetProcessorArchitecture;
85         /// <summary>Delegate used for checking for the existence of a file.</summary>
86         private FileExists _fileExists;
87         /// <summary>Delegate used for checking for the existence of a directory.</summary>
88         private DirectoryExists _directoryExists;
89         /// <summary>Delegate used for getting directories.</summary>
90         private GetDirectories _getDirectories;
91         /// <summary>Delegate used for getting assembly names.</summary>
92         private GetAssemblyName _getAssemblyName;
93         /// <summary>Delegate used for finding dependencies of a file.</summary>
94         private GetAssemblyMetadata _getAssemblyMetadata;
95         /// <summary>Delegate used to get the image runtime version of a file</summary>
96         private GetAssemblyRuntimeVersion _getRuntimeVersion;
97 #if FEATURE_WIN32_REGISTRY
98         /// <summary> Delegate to get the base registry key for AssemblyFoldersEx</summary>
99         private OpenBaseKey _openBaseKey;
100 #endif
101         /// <summary>Version of the runtime we are targeting</summary>
102         private Version _targetedRuntimeVersion = null;
103 
104         /// <summary>
105         /// Delegate used to get the machineType from the PE header of the dll.
106         /// </summary>
107         private ReadMachineTypeFromPEHeader _readMachineTypeFromPEHeader;
108 
109         /// <summary>
110         /// Is the file a winMD file
111         /// </summary>
112         private IsWinMDFile _isWinMDFile;
113 
114         /// <summary>version of the framework targeted by this project</summary>
115         private Version _projectTargetFramework;
116 
117         /// <summary>
118         /// Target framework moniker we are targeting.
119         /// </summary>
120         private FrameworkNameVersioning _targetFrameworkMoniker;
121 
122         /// <summary>
123         /// Searchpaths compiled into an array of resolvers.
124         /// </summary>
125         private Resolver[] _compiledSearchPaths;
126 
127         /// <summary>
128         /// Logging helper to allow the logging of meessages from the Reference Table
129         /// </summary>
130         private TaskLoggingHelper _log;
131 
132         /// <summary>
133         /// List of framework directories which are the highest on the machine
134         /// </summary>
135         private string[] _latestTargetFrameworkDirectories;
136 
137         /// <summary>
138         /// List of assemblies which have been excluded from being referenced.
139         /// </summary>
140         private List<string> _listOfExcludedAssemblies = null;
141 
142         /// <summary>
143         /// Should dependencies be set to copy local if the parent reference is in the GAC
144         /// </summary>
145         private bool _copyLocalDependenciesWhenParentReferenceInGac;
146 
147         private bool _doNotCopyLocalIfInGac;
148 
149         /// <summary>
150         ///  Shoould the framework attribute version mismatch be ignored.
151         /// </summary>
152         private bool _ignoreFrameworkAttributeVersionMismatch;
153 
154         /// <summary>
155         /// Delegate to determine if an assembly name is in the GAC.
156         /// </summary>
157         private GetAssemblyPathInGac _getAssemblyPathInGac;
158 
159         /// <summary>
160         /// Build engine
161         /// </summary>
162         private IBuildEngine _buildEngine;
163 
164         /// <summary>
165         /// Should a warning or error be emitted on architecture mismatch
166         /// </summary>
167         private WarnOrErrorOnTargetArchitectureMismatchBehavior _warnOrErrorOnTargetArchitectureMismatch = WarnOrErrorOnTargetArchitectureMismatchBehavior.Warning;
168 
169         private readonly ConcurrentDictionary<string, AssemblyMetadata> _assemblyMetadataCache;
170 
171         /// <summary>
172         /// When we exclude an assembly from resolution because it is part of out exclusion list we need to let the user know why this is.
173         /// There can be a number of reasons each for un-resolving a reference, these reasons are encapsulated by a different black list. We need to log a specific message
174         /// depending on which black list we have found the offending assembly in. This delegate allows one to tie a set of logging messages to a black list so that when we
175         /// discover an assembly in the black list we can log the correct message.
176         /// </summary>
LogExclusionReason(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)177         internal delegate void LogExclusionReason(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework);
178 
179         // Offset to the PE header
180         private const int PEOFFSET = 0x3c;
181 
182         // PEHeader
183         private const int PEHEADER = 0x00004550;
184 
185         /// <summary>
186         /// Construct.
187         /// </summary>
188         /// <param name="findDependencies">If true, then search for dependencies.</param>
189         /// <param name="findSatellites">If true, then search for satellite files.</param>
190         /// <param name="findSerializationAssemblies">If true, then search for serialization assembly files.</param>
191         /// <param name="findRelatedFiles">If true, then search for related files.</param>
192         /// <param name="searchPaths">Paths to search for dependent assemblies on.</param>
193         /// <param name="candidateAssemblyFiles">List of literal assembly file names to be considered when SearchPaths has {CandidateAssemblyFiles}.</param>
194         /// <param name="resolvedSDKItems">Resolved sdk items</param>
195         /// <param name="frameworkPaths">Path to the FX.</param>
196         /// <param name="installedAssemblies">Installed assembly XML tables.</param>
197         /// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targetted.</param>
198         /// <param name="fileExists">Delegate used for checking for the existence of a file.</param>
199         /// <param name="directoryExists">Delegate used for files.</param>
200         /// <param name="getDirectories">Delegate used for getting directories.</param>
201         /// <param name="getAssemblyName">Delegate used for getting assembly names.</param>
202         /// <param name="getAssemblyMetadata">Delegate used for finding dependencies of a file.</param>
203         /// <param name="getRegistrySubKeyNames">Used to get registry subkey names.</param>
204         /// <param name="getRegistrySubKeyDefaultValue">Used to get registry default values.</param>
205         /// <param name="assemblyMetadataCache">Cache of metadata already read from paths.</param>
ReferenceTable( IBuildEngine buildEngine, bool findDependencies, bool findSatellites, bool findSerializationAssemblies, bool findRelatedFiles, string[] searchPaths, string[] allowedAssemblyExtensions, string[] relatedFileExtensions, string[] candidateAssemblyFiles, ITaskItem[] resolvedSDKItems, string[] frameworkPaths, InstalledAssemblies installedAssemblies, System.Reflection.ProcessorArchitecture targetProcessorArchitecture, FileExists fileExists, DirectoryExists directoryExists, GetDirectories getDirectories, GetAssemblyName getAssemblyName, GetAssemblyMetadata getAssemblyMetadata, GetRegistrySubKeyNames getRegistrySubKeyNames, GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue, OpenBaseKey openBaseKey, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVersion, Version projectTargetFramework, FrameworkNameVersioning targetFrameworkMoniker, TaskLoggingHelper log, string[] latestTargetFrameworkDirectories, bool copyLocalDependenciesWhenParentReferenceInGac, bool doNotCopyLocalIfInGac, GetAssemblyPathInGac getAssemblyPathInGac, IsWinMDFile isWinMDFile, bool ignoreVersionForFrameworkReferences, ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader, WarnOrErrorOnTargetArchitectureMismatchBehavior warnOrErrorOnTargetArchitectureMismatch, bool ignoreFrameworkAttributeVersionMismatch, bool unresolveFrameworkAssembliesFromHigherFrameworks, ConcurrentDictionary<string, AssemblyMetadata> assemblyMetadataCache)206         internal ReferenceTable
207         (
208             IBuildEngine buildEngine,
209             bool findDependencies,
210             bool findSatellites,
211             bool findSerializationAssemblies,
212             bool findRelatedFiles,
213             string[] searchPaths,
214             string[] allowedAssemblyExtensions,
215             string[] relatedFileExtensions,
216             string[] candidateAssemblyFiles,
217             ITaskItem[] resolvedSDKItems,
218             string[] frameworkPaths,
219             InstalledAssemblies installedAssemblies,
220             System.Reflection.ProcessorArchitecture targetProcessorArchitecture,
221             FileExists fileExists,
222             DirectoryExists directoryExists,
223             GetDirectories getDirectories,
224             GetAssemblyName getAssemblyName,
225             GetAssemblyMetadata getAssemblyMetadata,
226 #if FEATURE_WIN32_REGISTRY
227             GetRegistrySubKeyNames getRegistrySubKeyNames,
228             GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue,
229             OpenBaseKey openBaseKey,
230 #endif
231             GetAssemblyRuntimeVersion getRuntimeVersion,
232             Version targetedRuntimeVersion,
233             Version projectTargetFramework,
234             FrameworkNameVersioning targetFrameworkMoniker,
235             TaskLoggingHelper log,
236             string[] latestTargetFrameworkDirectories,
237             bool copyLocalDependenciesWhenParentReferenceInGac,
238             bool doNotCopyLocalIfInGac,
239             GetAssemblyPathInGac getAssemblyPathInGac,
240             IsWinMDFile isWinMDFile,
241             bool ignoreVersionForFrameworkReferences,
242             ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader,
243             WarnOrErrorOnTargetArchitectureMismatchBehavior warnOrErrorOnTargetArchitectureMismatch,
244             bool ignoreFrameworkAttributeVersionMismatch,
245             bool unresolveFrameworkAssembliesFromHigherFrameworks,
246             ConcurrentDictionary<string, AssemblyMetadata> assemblyMetadataCache)
247         {
248             _buildEngine = buildEngine;
249             _log = log;
250             _findDependencies = findDependencies;
251             _findSatellites = findSatellites;
252             _findSerializationAssemblies = findSerializationAssemblies;
253             _findRelatedFiles = findRelatedFiles;
254             _frameworkPaths = frameworkPaths;
255             _allowedAssemblyExtensions = allowedAssemblyExtensions;
256             _relatedFileExtensions = relatedFileExtensions;
257             _installedAssemblies = installedAssemblies;
258             _targetProcessorArchitecture = targetProcessorArchitecture;
259             _fileExists = fileExists;
260             _directoryExists = directoryExists;
261             _getDirectories = getDirectories;
262             _getAssemblyName = getAssemblyName;
263             _getAssemblyMetadata = getAssemblyMetadata;
264             _getRuntimeVersion = getRuntimeVersion;
265             _projectTargetFramework = projectTargetFramework;
266             _targetedRuntimeVersion = targetedRuntimeVersion;
267 #if FEATURE_WIN32_REGISTRY
268             _openBaseKey = openBaseKey;
269 #endif
270             _targetFrameworkMoniker = targetFrameworkMoniker;
271             _latestTargetFrameworkDirectories = latestTargetFrameworkDirectories;
272             _copyLocalDependenciesWhenParentReferenceInGac = copyLocalDependenciesWhenParentReferenceInGac;
273             _doNotCopyLocalIfInGac = doNotCopyLocalIfInGac;
274             _getAssemblyPathInGac = getAssemblyPathInGac;
275             _isWinMDFile = isWinMDFile;
276             _readMachineTypeFromPEHeader = readMachineTypeFromPEHeader;
277             _warnOrErrorOnTargetArchitectureMismatch = warnOrErrorOnTargetArchitectureMismatch;
278             _ignoreFrameworkAttributeVersionMismatch = ignoreFrameworkAttributeVersionMismatch;
279             _assemblyMetadataCache = assemblyMetadataCache;
280 
281             // Set condition for when to check assembly version against the target framework version
282             _checkAssemblyVersionAgainstTargetFrameworkVersion = unresolveFrameworkAssembliesFromHigherFrameworks || ((_projectTargetFramework ?? ReferenceTable.s_targetFrameworkVersion_40) <= ReferenceTable.s_targetFrameworkVersion_40);
283 
284             // Convert the list of installed SDK's to a dictionary for faster lookup
285             _resolvedSDKReferences = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
286             _ignoreVersionForFrameworkReferences = ignoreVersionForFrameworkReferences;
287 
288 
289             if (resolvedSDKItems != null)
290             {
291                 foreach (ITaskItem resolvedSDK in resolvedSDKItems)
292                 {
293                     string sdkName = resolvedSDK.GetMetadata("SDKName");
294 
295                     if (sdkName.Length > 0)
296                     {
297                         if (!_resolvedSDKReferences.ContainsKey(sdkName))
298                         {
299                             _resolvedSDKReferences.Add(sdkName, resolvedSDK);
300                         }
301                         else
302                         {
303                             _resolvedSDKReferences[sdkName] = resolvedSDK;
304                         }
305                     }
306                 }
307             }
308 
309             // Compile searchpaths into fast resolver array.
310             _compiledSearchPaths = AssemblyResolution.CompileSearchPaths
311                 (
312                     buildEngine,
313                     searchPaths,
314                     candidateAssemblyFiles,
315                     targetProcessorArchitecture,
316                     frameworkPaths,
317                     fileExists,
318                     getAssemblyName,
319 #if FEATURE_WIN32_REGISTRY
320                     getRegistrySubKeyNames,
321                     getRegistrySubKeyDefaultValue,
322                     openBaseKey,
323 #endif
324                     installedAssemblies,
325                     getRuntimeVersion,
326                     targetedRuntimeVersion,
327                     getAssemblyPathInGac,
328                     log
329                 );
330         }
331 
332         /// <summary>
333         /// Set of resolvers the reference table uses.
334         /// </summary>
335         internal Resolver[] Resolvers
336         {
337             get { return _compiledSearchPaths; }
338         }
339 
340         /// <summary>
341         /// Get a table of all vertices.
342         /// </summary>
343         /// <returns></returns>
344         internal Dictionary<AssemblyNameExtension, Reference> References
345         {
346             get
347             {
348                 return _references;
349             }
350         }
351 
352         /// <summary>
353         /// If assemblies have been marked for exclusion this contains the list of their full names
354         /// This may be null
355         /// </summary>
356         internal List<string> ListOfExcludedAssemblies
357         {
358             get
359             {
360                 return _listOfExcludedAssemblies;
361             }
362         }
363 
364         /// <summary>
365         /// Indicates that at least one reference was <see cref="Reference.ExternallyResolved"/> and
366         /// we skipped finding its dependencies as a result.
367         /// </summary>
368         /// <remarks>
369         /// This is currently used to perform a shallow search for System.Runtime/netstandard usage
370         /// within the externally resolved graph.
371         /// </remarks>
372         internal bool SkippedFindingExternallyResolvedDependencies { get; private set; }
373 
374         /// <summary>
375         /// Force dependencies to be walked even when a reference is marked with ExternallyResolved=true
376         /// metadata.
377         /// </summary>
378         /// <remarks>
379         /// This is currently used to ensure that we suggest appropriate binding redirects for
380         /// assembly version conflicts within an externally resolved graph.
381         /// </remarks>
382         internal bool FindDependenciesOfExternallyResolvedReferences { get; set; }
383 
384         /// <summary>
385         /// Adds a reference to the table.
386         /// </summary>
387         /// <param name="assemblyName">The assembly name to be used as a key.</param>
388         /// <param name="reference">The reference to add.</param>
AddReference(AssemblyNameExtension assemblyName, Reference reference)389         internal void AddReference(AssemblyNameExtension assemblyName, Reference reference)
390         {
391             ErrorUtilities.VerifyThrow(assemblyName.Name != null, "Got an empty assembly name.");
392             if (_references.ContainsKey(assemblyName))
393             {
394                 Reference referenceGoingToBeReplaced = _references[assemblyName];
395                 foreach (AssemblyRemapping pair in referenceGoingToBeReplaced.RemappedAssemblyNames())
396                 {
397                     reference.AddRemapping(pair.From, pair.To);
398                 }
399             }
400 
401             _references[assemblyName] = reference;
402         }
403 
404 
405         /// <summary>
406         /// Find the reference that corresponds to the given path.
407         /// </summary>
408         /// <param name="assemblyName">The assembly name  to find the reference for.</param>
409         /// <returns>'null' if no reference existed.</returns>
GetReference(AssemblyNameExtension assemblyName)410         internal Reference GetReference(AssemblyNameExtension assemblyName)
411         {
412             ErrorUtilities.VerifyThrow(assemblyName.Name != null, "Got an empty assembly name.");
413             Reference referenceToReturn = null;
414             _references.TryGetValue(assemblyName, out referenceToReturn);
415             return referenceToReturn;
416         }
417 
418         /// <summary>
419         /// Give an assembly file name, adjust a Reference to match it.
420         /// </summary>
421         /// <param name="reference">The reference to work on</param>
422         /// <param name="assemblyFileName">The path to the assembly file.</param>
423         /// <returns>The AssemblyName of assemblyFileName</returns>
NameAssemblyFileReference( Reference reference, string assemblyFileName )424         private AssemblyNameExtension NameAssemblyFileReference
425         (
426             Reference reference,
427             string assemblyFileName
428         )
429         {
430             AssemblyNameExtension assemblyName = null;
431 
432             if (!Path.IsPathRooted(assemblyFileName))
433             {
434                 reference.FullPath = Path.GetFullPath(assemblyFileName);
435             }
436             else
437             {
438                 reference.FullPath = assemblyFileName;
439             }
440 
441             try
442             {
443                 if (_directoryExists(assemblyFileName))
444                 {
445                     assemblyName = new AssemblyNameExtension("*directory*");
446 
447                     reference.AddError
448                     (
449                         new ReferenceResolutionException
450                         (
451                             ResourceUtilities.FormatResourceString("General.ExpectedFileGotDirectory", reference.FullPath),
452                             null
453                         )
454                     );
455                     reference.FullPath = String.Empty;
456                 }
457                 else
458                 {
459                     if (_fileExists(assemblyFileName))
460                     {
461                         assemblyName = _getAssemblyName(assemblyFileName);
462                         if (assemblyName != null)
463                         {
464                             reference.ResolvedSearchPath = assemblyFileName;
465                         }
466                     }
467 
468                     if (assemblyName == null)
469                     {
470                         reference.AddError
471                         (
472                             new DependencyResolutionException(ResourceUtilities.FormatResourceString("General.ExpectedFileMissing", reference.FullPath), null)
473                         );
474                     }
475                 }
476             }
477             catch (System.BadImageFormatException e)
478             {
479                 reference.AddError(new DependencyResolutionException(e.Message, e));
480             }
481             catch (UnauthorizedAccessException e)
482             {
483                 // If this isn't a valid assembly, then record the exception and continue on
484                 reference.AddError(new DependencyResolutionException(e.Message, e));
485             }
486 
487             // If couldn't resolve the assemly name then just use the simple name extracted from
488             // the file name.
489             if (assemblyName == null)
490             {
491                 string simpleName = Path.GetFileNameWithoutExtension(assemblyFileName);
492                 assemblyName = new AssemblyNameExtension(simpleName);
493             }
494 
495             return assemblyName;
496         }
497 
498         /// <summary>
499         /// Given a list of task items, add them all to this table and make them the only primary items.
500         /// </summary>
501         /// <param name="referenceAssemblyFiles">The task items which contain file names to add.</param>
502         /// <param name="referenceAssemblyNames">The task items which contain fusion names to add.</param>
503         /// <param name="exceptions">Exceptions encountered while setting primary items. Exceptions are logged, but it doesn't stop the resolution process.</param>
SetPrimaryItems( ITaskItem[] referenceAssemblyFiles, ITaskItem[] referenceAssemblyNames, ArrayList exceptions )504         private void SetPrimaryItems
505         (
506             ITaskItem[] referenceAssemblyFiles,
507             ITaskItem[] referenceAssemblyNames,
508             ArrayList exceptions
509         )
510         {
511             // Loop over the referenceAssemblyFiles provided and add each one that doesn't exist.
512             // Set the primary flag to 'true'.
513             if (referenceAssemblyFiles != null)
514             {
515                 for (int i = 0; i < referenceAssemblyFiles.Length; ++i)
516                 {
517                     SetPrimaryFileItem(referenceAssemblyFiles[i]);
518                 }
519             }
520 
521             // Loop over the referenceAssemblyNames provided and add each one that doesn't exist.
522             // Set the primary flag to 'true'.
523             if (referenceAssemblyNames != null)
524             {
525                 for (int i = 0; i < referenceAssemblyNames.Length; ++i)
526                 {
527                     Exception e = SetPrimaryAssemblyReferenceItem(referenceAssemblyNames[i]);
528 
529                     if (e != null)
530                     {
531                         exceptions.Add(e);
532                     }
533                 }
534             }
535         }
536 
537         /// <summary>
538         /// Given an item that refers to a assembly name, make it a primary reference.
539         /// </summary>
540         /// <param name="referenceAssemblyNames">The task item which contain fusion names to add.</param>
541         /// <returns>Resulting exception containing resolution failure details, if any: too costly to throw it.</returns>
SetPrimaryAssemblyReferenceItem( ITaskItem referenceAssemblyName )542         private Exception SetPrimaryAssemblyReferenceItem
543         (
544             ITaskItem referenceAssemblyName
545         )
546         {
547             // Get the desired executable extension.
548             string executableExtension = referenceAssemblyName.GetMetadata(ItemMetadataNames.executableExtension);
549 
550             // Get the assembly name, if possible.
551             string rawFileNameCandidate = referenceAssemblyName.ItemSpec;
552             AssemblyNameExtension assemblyName = null;
553             string itemSpec = referenceAssemblyName.ItemSpec;
554             string fusionName = referenceAssemblyName.GetMetadata(ItemMetadataNames.fusionName);
555             bool metadataFound = false;
556             bool result = MetadataConversionUtilities.TryConvertItemMetadataToBool(referenceAssemblyName, ItemMetadataNames.IgnoreVersionForFrameworkReference, out metadataFound);
557             bool ignoreVersionForFrameworkReference = false;
558 
559             if (metadataFound)
560             {
561                 ignoreVersionForFrameworkReference = result;
562             }
563             else
564             {
565                 ignoreVersionForFrameworkReference = _ignoreVersionForFrameworkReferences;
566             }
567 
568             TryConvertToAssemblyName(itemSpec, fusionName, ref assemblyName);
569 
570             // Figure out the specific version value.
571             bool foundSpecificVersionMetadata = false;
572             bool wantSpecificVersion = MetadataConversionUtilities.TryConvertItemMetadataToBool(referenceAssemblyName, ItemMetadataNames.specificVersion, out foundSpecificVersionMetadata);
573 
574             bool isSimpleName = (assemblyName != null && assemblyName.IsSimpleName);
575 
576             // Create the reference.
577             Reference reference = new Reference(_isWinMDFile, _fileExists, _getRuntimeVersion);
578             reference.MakePrimaryAssemblyReference(referenceAssemblyName, wantSpecificVersion, executableExtension);
579 
580             // Escape simple names.
581             // 1) If the itemSpec for the task is already a simple name
582             // 2) We have found the metadata and it is specifically set to false
583             if (assemblyName != null && (isSimpleName || (foundSpecificVersionMetadata && !wantSpecificVersion)))
584             {
585                 assemblyName = new AssemblyNameExtension
586                 (
587                     AssemblyNameExtension.EscapeDisplayNameCharacters(assemblyName.Name)
588                 );
589 
590                 isSimpleName = assemblyName.IsSimpleName;
591             }
592 
593             // Set the HintPath if there is one.
594             reference.HintPath = referenceAssemblyName.GetMetadata(ItemMetadataNames.hintPath);
595 
596             if (assemblyName != null && !wantSpecificVersion && !isSimpleName && reference.HintPath.Length == 0)
597             {
598                 // Check to see if the assemblyname is in the framework list just use that fusion name
599                 if (_installedAssemblies != null && ignoreVersionForFrameworkReference)
600                 {
601                     AssemblyEntry entry = _installedAssemblies.FindHighestVersionInRedistList(assemblyName);
602                     if (entry != null)
603                     {
604                         assemblyName = entry.AssemblyNameExtension.Clone();
605                     }
606                 }
607             }
608 
609             if (assemblyName != null && _installedAssemblies != null && !wantSpecificVersion && reference.HintPath.Length == 0)
610             {
611                 AssemblyNameExtension remappedExtension = _installedAssemblies.RemapAssemblyExtension(assemblyName);
612 
613                 if (remappedExtension != null)
614                 {
615                     reference.AddRemapping(assemblyName.CloneImmutable(), remappedExtension.CloneImmutable());
616                     assemblyName = remappedExtension;
617                 }
618             }
619 
620 
621             // Embed Interop Types aka "NOPIAs" support is not available for Fx < 4.0
622             // So, we just ignore this setting on down-level platforms
623             if (_projectTargetFramework != null && _projectTargetFramework >= s_targetFrameworkVersion_40)
624             {
625                 reference.EmbedInteropTypes = MetadataConversionUtilities.TryConvertItemMetadataToBool
626                     (
627                         referenceAssemblyName,
628                         ItemMetadataNames.embedInteropTypes
629                     );
630             }
631 
632             // Set the AssemblyFolderKey if there is one.
633             reference.AssemblyFolderKey = referenceAssemblyName.GetMetadata(ItemMetadataNames.assemblyFolderKey);
634 
635             // It's possible, especially in cases where the fusion name was passed in through the item
636             // that we'll have a better (more information) fusion name once we know the assembly path.
637             try
638             {
639                 ResolveReference(assemblyName, rawFileNameCandidate, reference);
640 
641                 if (reference.IsResolved)
642                 {
643                     AssemblyNameExtension possiblyBetterAssemblyName = null;
644 
645                     try
646                     {
647                         // This may throw if, for example, the culture embedded in the assembly's manifest
648                         // is not recognised by AssemblyName.GetAssemblyName
649                         possiblyBetterAssemblyName = _getAssemblyName(reference.FullPath);
650                     }
651                     catch (ArgumentException)
652                     {
653                         // Give up trying to get a better name
654                         possiblyBetterAssemblyName = null;
655                     }
656 
657                     // Use the better name if it exists.
658                     if (possiblyBetterAssemblyName != null && possiblyBetterAssemblyName.Name != null)
659                     {
660                         assemblyName = possiblyBetterAssemblyName;
661                     }
662                 }
663             }
664             catch (BadImageFormatException e)
665             {
666                 // If this isn't a valid assembly, then record the exception and continue on
667                 reference.AddError(new BadImageReferenceException(e.Message, e));
668             }
669             catch (FileNotFoundException e) // Why isn't this covered in NotExpectedException?
670             {
671                 reference.AddError(new BadImageReferenceException(e.Message, e));
672             }
673             catch (FileLoadException e)
674             {
675                 // Managed assembly was found but could not be loaded.
676                 reference.AddError(new BadImageReferenceException(e.Message, e));
677             }
678             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
679             {
680                 reference.AddError(new BadImageReferenceException(e.Message, e));
681             }
682 
683             // If there is still no assembly name then this is a case where the assembly metadata
684             // just doesn't contain an assembly name. We want to try to tolerate this because
685             // mscorlib.dll (sometimes?) doesn't contain an assembly name.
686             if (assemblyName == null)
687             {
688                 if (!reference.IsResolved)
689                 {
690                     // The file doesn't exist and the reference was unresolved, there's nothing we can do at this point.
691                     // Return, rather than throw, the exception, as in some situations it can happen thousands of times.
692                     return new InvalidReferenceAssemblyNameException(referenceAssemblyName.ItemSpec);
693                 }
694 
695                 assemblyName = new AssemblyNameExtension
696                 (
697                     AssemblyNameExtension.EscapeDisplayNameCharacters(reference.FileNameWithoutExtension)
698                 );
699             }
700 
701             // Check to see if this is a prereq assembly.
702             if (_installedAssemblies == null)
703             {
704                 reference.IsPrerequisite = false;
705             }
706             else
707             {
708                 Version unifiedVersion = null;
709                 bool isPrerequisite = false;
710                 bool? isRedistRoot = null;
711                 string redistName = null;
712 
713                 _installedAssemblies.GetInfo
714                 (
715                     assemblyName,
716                     out unifiedVersion,
717                     out isPrerequisite,
718                     out isRedistRoot,
719                     out redistName
720                 );
721 
722                 reference.IsPrerequisite = isPrerequisite;
723                 reference.IsRedistRoot = isRedistRoot;
724                 reference.RedistName = redistName;
725             }
726 
727             AddReference(assemblyName, reference);
728 
729             if (reference.ExternallyResolved)
730             {
731                 _externallyResolvedPrimaryReferences.Add(assemblyName.Name);
732             }
733 
734             return null;
735         }
736 
737         /// <summary>
738         /// Attempts to convert an itemSpec and fusionName into an assembly name.
739         /// AssemblyName is left unchanged if conversion wasn't possible.
740         /// </summary>
741         /// <param name="itemSpec"></param>
742         /// <param name="fusionName"></param>
743         /// <param name="assemblyName"></param>
TryConvertToAssemblyName(string itemSpec, string fusionName, ref AssemblyNameExtension assemblyName)744         private static void TryConvertToAssemblyName(string itemSpec, string fusionName, ref AssemblyNameExtension assemblyName)
745         {
746             // FusionName is used if available.
747             string finalName = fusionName;
748             if (finalName == null || finalName.Length == 0)
749             {
750                 // Otherwise, its itemSpec.
751                 finalName = itemSpec;
752             }
753 
754             bool pathRooted = false;
755             try
756             {
757                 pathRooted = Path.IsPathRooted(finalName);
758             }
759             catch (ArgumentException)
760             {
761                 /* Eat this because it has invalid chars in to and cannot be a path, maybe it can be parsed as a fusion name.*/
762             }
763 
764             if (!pathRooted)
765             {
766                 // Now try to convert to an AssemblyName.
767                 try
768                 {
769                     assemblyName = new AssemblyNameExtension(finalName, true /*throw if not valid*/);
770                 }
771                 catch (System.IO.FileLoadException)
772                 {
773                     // Not a valid AssemblyName. Maybe its a file name.
774                     TryGatherAssemblyNameEssentials(finalName, ref assemblyName);
775                     return;
776                 }
777             }
778             else
779             {
780                 // Maybe the string has a fusion name inside of it.
781                 TryGatherAssemblyNameEssentials(finalName, ref assemblyName);
782             }
783         }
784 
785         /// <summary>
786         /// Given a string that may be a fusion name, try to gather the four essential properties:
787         ///     Name
788         ///     Version
789         ///     PublicKeyToken
790         ///     Culture
791         /// </summary>
792         /// <param name="fusionName"></param>
793         /// <param name="assemblyName"></param>
TryGatherAssemblyNameEssentials(string fusionName, ref AssemblyNameExtension assemblyName)794         private static void TryGatherAssemblyNameEssentials(string fusionName, ref AssemblyNameExtension assemblyName)
795         {
796             int firstComma = fusionName.IndexOf(',');
797             if (firstComma == -1)
798             {
799                 return;
800             }
801             string name = fusionName.Substring(0, firstComma);
802 
803             string version = null;
804             string publicKeyToken = null;
805             string culture = null;
806             TryGetAssemblyNameComponent(fusionName, "Version", ref version);
807             TryGetAssemblyNameComponent(fusionName, "PublicKeyToken", ref publicKeyToken);
808             TryGetAssemblyNameComponent(fusionName, "Culture", ref culture);
809 
810             if (version == null || publicKeyToken == null || culture == null)
811             {
812                 return;
813             }
814 
815             string newFusionName = String.Format(CultureInfo.InvariantCulture,
816                 "{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
817                 name, version, culture, publicKeyToken);
818 
819             // Now try to convert to an AssemblyName.
820             try
821             {
822                 assemblyName = new AssemblyNameExtension(newFusionName, true /* throw if not valid */);
823             }
824             catch (System.IO.FileLoadException)
825             {
826                 // Not a valid AssemblyName. Maybe its a file name.
827                 // TryGatherAssemblyNameEssentials
828                 return;
829             }
830         }
831 
832         /// <summary>
833         /// Attempt to get one field out of an assembly name.
834         /// </summary>
835         /// <param name="fusionName"></param>
836         /// <param name="component"></param>
837         /// <param name="value"></param>
TryGetAssemblyNameComponent(string fusionName, string component, ref string value)838         private static void TryGetAssemblyNameComponent(string fusionName, string component, ref string value)
839         {
840             int position = fusionName.IndexOf(component + "=", StringComparison.Ordinal);
841             if (position == -1)
842             {
843                 return;
844             }
845             position += component.Length + 1;
846             int nextDelimiter = fusionName.IndexOfAny(new char[] { ',', ' ' }, position);
847             if (nextDelimiter == -1)
848             {
849                 value = fusionName.Substring(position);
850             }
851             else
852             {
853                 value = fusionName.Substring(position, nextDelimiter - position);
854             }
855         }
856 
857         /// <summary>
858         /// Given an item that refers to a file name, make it a primary reference.
859         /// </summary>
860         /// <param name="referenceAssemblyFile"></param>
SetPrimaryFileItem(ITaskItem referenceAssemblyFile)861         private void SetPrimaryFileItem(ITaskItem referenceAssemblyFile)
862         {
863             try
864             {
865                 // Create the reference.
866                 Reference reference = new Reference(_isWinMDFile, _fileExists, _getRuntimeVersion);
867 
868                 bool hasSpecificVersionMetadata = MetadataConversionUtilities.TryConvertItemMetadataToBool(referenceAssemblyFile, ItemMetadataNames.specificVersion);
869                 reference.MakePrimaryAssemblyReference
870                 (
871                     referenceAssemblyFile,
872                     hasSpecificVersionMetadata,
873                     Path.GetExtension(referenceAssemblyFile.ItemSpec)
874                 );
875 
876                 AssemblyNameExtension assemblyName = NameAssemblyFileReference
877                 (
878                     reference,
879                     referenceAssemblyFile.ItemSpec  // Contains the assembly file name.
880                 );
881 
882                 // Embed Interop Types aka "NOPIAs" support is not available for Fx < 4.0
883                 // So, we just ignore this setting on down-level platforms
884                 if (_projectTargetFramework >= s_targetFrameworkVersion_40)
885                 {
886                     reference.EmbedInteropTypes = MetadataConversionUtilities.TryConvertItemMetadataToBool
887                         (
888                             referenceAssemblyFile,
889                             ItemMetadataNames.embedInteropTypes
890                         );
891                 }
892 
893                 AddReference(assemblyName, reference);
894 
895                 if (reference.ExternallyResolved)
896                 {
897                     _externallyResolvedPrimaryReferences.Add(assemblyName.Name);
898                 }
899             }
900             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
901             {
902                 throw new InvalidParameterValueException("AssemblyFiles", referenceAssemblyFile.ItemSpec, e.Message);
903             }
904         }
905 
906         /// <summary>
907         /// Find related files like .pdbs and .xmls
908         /// </summary>
909         /// <param name="fullPath">Path to the parent assembly.</param>
910         /// <param name="reference">The reference to the parent assembly.</param>
FindRelatedFiles( Reference reference )911         private void FindRelatedFiles
912         (
913             Reference reference
914         )
915         {
916             string baseName = reference.FullPathWithoutExtension;
917 
918             // Look for companion files like pdbs and xmls that ride along with
919             // assemblies.
920             foreach (string companionExtension in _relatedFileExtensions)
921             {
922                 string companionFile = baseName + companionExtension;
923 
924                 if (_fileExists(companionFile))
925                 {
926                     reference.AddRelatedFileExtension(companionExtension);
927                 }
928             }
929 
930             // Native Winmd files may have a companion dll beside it.
931             // If this is not a primary reference or the implementation metadata is not set on the item we need to set the implmentation metadata.
932             if (reference.IsWinMDFile && (!reference.IsPrimary || String.IsNullOrEmpty(reference.PrimarySourceItem.GetMetadata(ItemMetadataNames.winmdImplmentationFile))) && !reference.IsManagedWinMDFile)
933             {
934                 string companionFile = baseName + ".dll";
935 
936                 if (_fileExists(companionFile))
937                 {
938                     reference.ImplementationAssembly = companionFile;
939                 }
940             }
941         }
942 
943         /// <summary>
944         /// Find satellite assemblies.
945         /// </summary>
946         /// <param name="directoryName">Directory of the parrent assembly.</param>
947         /// <param name="fullPath">Path to the parent assembly.</param>
948         /// <param name="reference">The reference to the parent assembly.</param>
FindSatellites( Reference reference )949         private void FindSatellites
950         (
951             Reference reference
952         )
953         {
954             try
955             {
956                 // If the directory doesn't exist (which is possible in the situation
957                 // where we were passed in a pre-resolved reference from a P2P reference
958                 // that hasn't actually been built yet), then GetDirectories will throw.
959                 // Avoid that by just short-circuiting here.
960                 if (!_directoryExists(reference.DirectoryName))
961                 {
962                     return;
963                 }
964 
965                 string[] subDirectories = _getDirectories(reference.DirectoryName, "*");
966                 string sateliteFilename = reference.FileNameWithoutExtension + ".resources.dll";
967 
968                 foreach (string subDirectory in subDirectories)
969                 {
970                     // Is there a candidate satellite in that folder?
971                     string cultureName = Path.GetFileName(subDirectory);
972 
973                     if (CultureInfoCache.IsValidCultureString(cultureName))
974                     {
975                         string satelliteAssembly = Path.Combine(subDirectory, sateliteFilename);
976                         if (_fileExists(satelliteAssembly))
977                         {
978                             // This is valid satellite assembly.
979                             reference.AddSatelliteFile(Path.Combine(cultureName, sateliteFilename));
980                         }
981                     }
982                 }
983             }
984             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
985             {
986                 if (_log != null)
987                 {
988                     _log.LogErrorFromResources("ResolveAssemblyReference.ProblemFindingSatelliteAssemblies", reference.FullPath, e.Message);
989                 }
990             }
991         }
992 
993         /// <summary>
994         /// Find serialization assemblies.
995         /// </summary>
996         /// <param name="directoryName">Directory of the parrent assembly.</param>
997         /// <param name="fullPath">Path to the parent assembly.</param>
998         /// <param name="reference">The reference to the parent assembly.</param>
FindSerializationAssemblies( Reference reference )999         private void FindSerializationAssemblies
1000         (
1001             Reference reference
1002         )
1003         {
1004             // If the directory doesn't exist (which is possible in the situation
1005             // where we were passed in a pre-resolved reference from a P2P reference
1006             // that hasn't actually been built yet), then GetDirectories will throw.
1007             // Avoid that by just short-circuiting here.
1008             if (!_directoryExists(reference.DirectoryName))
1009             {
1010                 return;
1011             }
1012 
1013             string serializationAssemblyFilename = reference.FileNameWithoutExtension + ".XmlSerializers.dll";
1014             string serializationAssemblyPath = Path.Combine(reference.DirectoryName, serializationAssemblyFilename);
1015             if (_fileExists(serializationAssemblyPath))
1016             {
1017                 // This is valid serialization assembly.
1018                 reference.AddSerializationAssemblyFile(serializationAssemblyFilename);
1019             }
1020         }
1021 
1022         /// <summary>
1023         /// Get unified dependencies and scatter files for a reference.
1024         /// </summary>
1025         /// <param name="reference"></param>
1026         /// <param name="unifiedDependencies"></param>
1027         /// <param name="scatterFiles"></param>
GetUnifiedAssemblyMetadata( Reference reference, out IEnumerable<UnifiedAssemblyName> unifiedDependencies, out string[] scatterFiles )1028         private void GetUnifiedAssemblyMetadata
1029             (
1030                 Reference reference,
1031                 out IEnumerable<UnifiedAssemblyName> unifiedDependencies,
1032                 out string[] scatterFiles
1033             )
1034         {
1035             // Shortcut if this is a prereq file--don't find dependencies.
1036             // We also don't want to look for dependencies if we already know
1037             // this assembly is a bad image.
1038             if (reference.IsPrerequisite || reference.IsBadImage)
1039             {
1040                 unifiedDependencies = null;
1041                 scatterFiles = null;
1042                 return;
1043             }
1044 
1045             AssemblyNameExtension[] dependentAssemblies = null;
1046             FrameworkName frameworkName = null;
1047             _getAssemblyMetadata
1048             (
1049                 reference.FullPath,
1050                 _assemblyMetadataCache,
1051                 out dependentAssemblies,
1052                 out scatterFiles,
1053                 out frameworkName
1054             );
1055 
1056             reference.FrameworkNameAttribute = frameworkName;
1057 
1058             List<AssemblyNameExtension> dependencies = new List<AssemblyNameExtension>(dependentAssemblies?.Length ?? 0);
1059 
1060             if (dependentAssemblies != null && dependentAssemblies.Length > 0)
1061             {
1062                 // Re-map immediately so that to the sytem we actually got the remapped version when reading the manifest.
1063                 for (int i = 0; i < dependentAssemblies.Length; i++)
1064                 {
1065                     // This will return a clone of the remapped assemblyNameExtension so its ok to party on it.
1066                     AssemblyNameExtension remappedExtension = _installedAssemblies?.RemapAssemblyExtension(dependentAssemblies[i]);
1067                     if (remappedExtension != null)
1068                     {
1069                         AssemblyNameExtension originalExtension = dependentAssemblies[i];
1070                         AssemblyNameExtension existingExtension = dependencies.Find(x => x.Equals(remappedExtension));
1071                         if (existingExtension != null)
1072                         {
1073                             existingExtension.AddRemappedAssemblyName(originalExtension.CloneImmutable());
1074                             continue;
1075                         }
1076                         else
1077                         {
1078                             dependentAssemblies[i] = remappedExtension;
1079                             dependentAssemblies[i].AddRemappedAssemblyName(originalExtension.CloneImmutable());
1080                         }
1081                     }
1082 
1083                     // Assemblies which reference WinMD files sometimes will have references to mscorlib version 255.255.255 which is invalid. For this reason
1084                     // We will remove the dependency to mscorlib from the list of dependencies so it is not used for resolution or unification.
1085                     bool isMscorlib = IsPseudoAssembly(dependentAssemblies[i].Name);
1086 
1087                     if (!isMscorlib || dependentAssemblies[i].Version.Major != 255)
1088                     {
1089                         dependencies.Add(dependentAssemblies[i]);
1090                     }
1091                 }
1092 
1093                 dependentAssemblies = dependencies.ToArray();
1094             }
1095 
1096             unifiedDependencies = GetUnifiedAssemblyNames(dependentAssemblies);
1097         }
1098 
1099         /// <summary>
1100         /// Given an enumerator of pre-unified assembly names, return an enumerator of unified
1101         /// assembly names.
1102         /// </summary>
1103         /// <param name="preUnificationAssemblyName"></param>
1104         /// <returns></returns>
GetUnifiedAssemblyNames( IEnumerable<AssemblyNameExtension> preUnificationAssemblyNames )1105         private IEnumerable<UnifiedAssemblyName> GetUnifiedAssemblyNames
1106         (
1107             IEnumerable<AssemblyNameExtension> preUnificationAssemblyNames
1108         )
1109         {
1110             foreach (AssemblyNameExtension preUnificationAssemblyName in preUnificationAssemblyNames)
1111             {
1112                 // First, unify the assembly name so that we're dealing with the right version.
1113                 // Not AssemblyNameExtension because we're going to write to it.
1114                 AssemblyNameExtension dependentAssembly = new AssemblyNameExtension(preUnificationAssemblyName.AssemblyName.CloneIfPossible());
1115 
1116                 Version unifiedVersion;
1117                 bool isPrerequisite;
1118                 bool? isRedistRoot;
1119                 string redistName;
1120 
1121                 UnificationReason unificationReason;
1122                 bool isUnified = UnifyAssemblyNameVersions(dependentAssembly, out unifiedVersion, out unificationReason, out isPrerequisite, out isRedistRoot, out redistName);
1123                 dependentAssembly.ReplaceVersion(unifiedVersion);
1124 
1125                 yield return new UnifiedAssemblyName(preUnificationAssemblyName, dependentAssembly, isUnified, unificationReason, isPrerequisite, isRedistRoot, redistName);
1126             }
1127         }
1128 
1129         /// <summary>
1130         /// Find references and scatter files defined for the given assembly.
1131         /// </summary>
1132         /// <param name="reference">The reference to the parent assembly.</param>
1133         /// <param name="newEntries">New references are added to this list.</param>
1134         /// <param name="removeEntries">Entries that should be removed from the list.</param>
FindDependenciesAndScatterFiles( Reference reference, ArrayList newEntries )1135         private void FindDependenciesAndScatterFiles
1136         (
1137             Reference reference,
1138             ArrayList newEntries
1139         )
1140         {
1141             // Before checking for dependencies check to see if the reference itself exists.
1142             // Even though to get to this point the reference must be resolved
1143             // the reference may not exist on disk if the reference is a project to project reference.
1144             if (!_fileExists(reference.FullPath))
1145             {
1146                 reference.AddError
1147                       (
1148                           new DependencyResolutionException(ResourceUtilities.FormatResourceString("General.ExpectedFileMissing", reference.FullPath), null)
1149                       );
1150 
1151                 return;
1152             }
1153 
1154             try
1155             {
1156                 IEnumerable<UnifiedAssemblyName> unifiedDependencies = null;
1157                 string[] scatterFiles = null;
1158                 GetUnifiedAssemblyMetadata(reference, out unifiedDependencies, out scatterFiles);
1159                 reference.AttachScatterFiles(scatterFiles);
1160 
1161                 // If no dependencies then fall out.
1162                 if (unifiedDependencies == null)
1163                 {
1164                     return;
1165                 }
1166 
1167                 foreach (UnifiedAssemblyName unifiedDependency in unifiedDependencies)
1168                 {
1169                     // Now, see if it has already been found.
1170                     Reference existingReference = GetReference(unifiedDependency.PostUnified);
1171 
1172                     if (existingReference == null)
1173                     {
1174                         // This is valid reference.
1175                         Reference newReference = new Reference(_isWinMDFile, _fileExists, _getRuntimeVersion);
1176 
1177                         newReference.MakeDependentAssemblyReference(reference);
1178                         if (unifiedDependency.IsUnified)
1179                         {
1180                             newReference.AddPreUnificationVersion(reference.FullPath, unifiedDependency.PreUnified.Version, unifiedDependency.UnificationReason);
1181                         }
1182 
1183                         foreach (AssemblyNameExtension remappedFromName in unifiedDependency.PreUnified.RemappedFromEnumerator)
1184                         {
1185                             newReference.AddRemapping(remappedFromName, unifiedDependency.PreUnified.CloneImmutable());
1186                         }
1187 
1188                         newReference.IsPrerequisite = unifiedDependency.IsPrerequisite;
1189 
1190                         DictionaryEntry newEntry = new DictionaryEntry(unifiedDependency.PostUnified, newReference);
1191 
1192                         newEntries.Add(newEntry);
1193                     }
1194                     else
1195                     {
1196                         // If it already existed then just append the source items.
1197                         if (existingReference == reference)
1198                         {
1199                             // This means the assembly depends on itself. This seems to be legal so we allow allow it.
1200                             // I don't think this rises to the level of a warning for the user because fusion handles
1201                             // this case gracefully.
1202                         }
1203                         else
1204                         {
1205                             // Now, add new information to the reference.
1206                             existingReference.AddSourceItems(reference.GetSourceItems());
1207                             existingReference.AddDependee(reference);
1208 
1209                             if (unifiedDependency.IsUnified)
1210                             {
1211                                 existingReference.AddPreUnificationVersion(reference.FullPath, unifiedDependency.PreUnified.Version, unifiedDependency.UnificationReason);
1212                             }
1213 
1214                             existingReference.IsPrerequisite = unifiedDependency.IsPrerequisite;
1215                         }
1216 
1217                         foreach (AssemblyNameExtension remappedFromName in unifiedDependency.PreUnified.RemappedFromEnumerator)
1218                         {
1219                             existingReference.AddRemapping(remappedFromName, unifiedDependency.PreUnified.CloneImmutable());
1220                         }
1221                     }
1222                 }
1223             }
1224             catch (FileNotFoundException e) // Why isn't this covered in NotExpectedException?
1225             {
1226                 reference.AddError(new DependencyResolutionException(e.Message, e));
1227             }
1228             catch (FileLoadException e)
1229             {
1230                 // Managed assembly was found but could not be loaded.
1231                 reference.AddError(new DependencyResolutionException(e.Message, e));
1232             }
1233             catch (BadImageFormatException e)
1234             {
1235                 reference.AddError(new DependencyResolutionException(e.Message, e));
1236             }
1237             catch (System.Runtime.InteropServices.COMException e)
1238             {
1239                 reference.AddError(new DependencyResolutionException(e.Message, e));
1240             }
1241             catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
1242             {
1243                 reference.AddError(new DependencyResolutionException(e.Message, e));
1244             }
1245         }
1246 
1247         /// <summary>
1248         /// Mscorlib is not a real managed assembly. It is seen both with and without metadata.
1249         /// We assume that the correct mscorlib is on the target platform.
1250         /// </summary>
1251         /// <param name="name"></param>
1252         /// <returns></returns>
IsPseudoAssembly(string name)1253         private static bool IsPseudoAssembly(string name)
1254         {
1255             return String.Compare(name, "mscorlib", StringComparison.OrdinalIgnoreCase) == 0;
1256         }
1257 
1258 
1259         /// <summary>
1260         /// Based on the set of parent assemblies we want to add their directories to the list of resolvers so that
1261         /// if the dependency is sitting beside the assembly which requires it then we will resolve the assembly from that location first.
1262         ///
1263         /// The only time we do not want to do this is if the parent assembly came from the GAC or AssemblyFoldersEx then we want the assembly
1264         /// to be found using those resolvers so that our GAC and AssemblyFolders checks later on will work on those assemblies.
1265         /// </summary>
CalculateParentAssemblyDirectories(Hashtable parentReferenceFolderHash, List<string> parentReferenceFolders, Reference parentReference)1266         internal static void CalculateParentAssemblyDirectories(Hashtable parentReferenceFolderHash, List<string> parentReferenceFolders, Reference parentReference)
1267         {
1268             string parentReferenceFolder = parentReference.DirectoryName;
1269             string parentReferenceResolvedSearchPath = parentReference.ResolvedSearchPath;
1270 
1271             bool parentReferenceResolvedFromGAC = false;
1272             bool parentReferenceResolvedFromAssemblyFolders = false;
1273             if (!String.IsNullOrEmpty(parentReferenceResolvedSearchPath))
1274             {
1275                 parentReferenceResolvedFromGAC = parentReferenceResolvedSearchPath.Equals(AssemblyResolutionConstants.gacSentinel, StringComparison.OrdinalIgnoreCase);
1276                 parentReferenceResolvedFromAssemblyFolders = parentReferenceResolvedSearchPath.Equals(AssemblyResolutionConstants.assemblyFoldersSentinel, StringComparison.OrdinalIgnoreCase);
1277             }
1278 
1279             // Only add the parent folder as a search location if we have not added it to the list yet and the parent reference has not been resolved from the GAC or AssemblyFolders
1280             // If the reference has been resolved from one of these locations we want the dependency to be found using the GAC or AssemblyFolder resolver rather than the directory resolver
1281             // This way the dependency is marked with the correct search path "GAC" or "AssemblyFolder"  rather than "c:\xxxxxx" which prevents our GAC/AssemblyFolder check from working
1282             if (!parentReferenceFolderHash.ContainsKey(parentReferenceFolder) && !parentReferenceResolvedFromGAC && !parentReferenceResolvedFromAssemblyFolders)
1283             {
1284                 parentReferenceFolderHash[parentReferenceFolder] = String.Empty;
1285                 parentReferenceFolders.Add(parentReferenceFolder);
1286             }
1287         }
1288 
1289         /// <summary>
1290         /// Given an unresolved reference (one that we don't know the full name for yet), figure out the
1291         /// full name. Should only be called on references that haven't been resolved yet--otherwise, its
1292         /// a perf problem.
1293         /// </summary>
1294         /// <param name="referenceFusionName">The fusion name for this reference.</param>
1295         /// <param name="rawFileNameCandidate">The file name to match if {RawFileName} is seen. (May be null).</param>
1296         /// <param name="reference">The reference object.</param>
ResolveReference( AssemblyNameExtension assemblyName, string rawFileNameCandidate, Reference reference )1297         private void ResolveReference
1298         (
1299             AssemblyNameExtension assemblyName,
1300             string rawFileNameCandidate,
1301             Reference reference
1302         )
1303         {
1304             // Now, resolve this reference.
1305             string resolvedPath = null;
1306             string resolvedSearchPath = String.Empty;
1307             bool userRequestedSpecificFile = false;
1308 
1309             // A list of assemblies that might have been matches but weren't
1310             ArrayList assembliesConsideredAndRejected = new ArrayList();
1311 
1312             // First, look for the dependency in the parents' directories. Unless they are resolved from the GAC or assemblyFoldersEx then
1313             // we should make sure we use the GAC and assemblyFolders resolvers themserves rather than a directory resolver to find the reference.\
1314             // this way we dont get assemblies pulled from the GAC or AssemblyFolders but dont have the marking that they were pulled form there.
1315             Hashtable parentReferenceFolderHash = new Hashtable(StringComparer.OrdinalIgnoreCase);
1316             List<string> parentReferenceFolders = new List<string>();
1317             foreach (Reference parentReference in reference.GetDependees())
1318             {
1319                 CalculateParentAssemblyDirectories(parentReferenceFolderHash, parentReferenceFolders, parentReference);
1320             }
1321 
1322             // Build the set of resolvers.
1323             List<Resolver[]> jaggedResolvers = new List<Resolver[]>();
1324 
1325             // If a reference has an SDK name on it then we must ONLY resolve it from the SDK which matches the SDKName on the refernce metadata
1326             // this is to support the case where a single reference assembly is selected from the SDK.
1327             // If a reference has the SDKName metadata on it then we will only search using a single resolver, that is the InstalledSDKResolver.
1328             if (reference.SDKName.Length > 0)
1329             {
1330                 jaggedResolvers.Add(new Resolver[] { new InstalledSDKResolver(_resolvedSDKReferences, "SDKResolver", _getAssemblyName, _fileExists, _getRuntimeVersion, _targetedRuntimeVersion) });
1331             }
1332             else
1333             {
1334                 // Do not probe near dependees if the reference is primary and resolved externally. If resolved externally, the search paths should have been specified in such a way to point to the assembly file.
1335                 if (assemblyName == null || !_externallyResolvedPrimaryReferences.Contains(assemblyName.Name))
1336                 {
1337                     jaggedResolvers.Add(AssemblyResolution.CompileDirectories(parentReferenceFolders, _fileExists, _getAssemblyName, _getRuntimeVersion, _targetedRuntimeVersion));
1338                 }
1339 
1340                 jaggedResolvers.Add(_compiledSearchPaths);
1341             }
1342 
1343             // Resolve
1344             try
1345             {
1346                 resolvedPath = AssemblyResolution.ResolveReference
1347                 (
1348                     jaggedResolvers,
1349                     assemblyName,
1350                     reference.SDKName,
1351                     rawFileNameCandidate,
1352                     reference.IsPrimary,
1353                     reference.WantSpecificVersion,
1354                     reference.GetExecutableExtensions(_allowedAssemblyExtensions),
1355                     reference.HintPath,
1356                     reference.AssemblyFolderKey,
1357                     assembliesConsideredAndRejected,
1358                     out resolvedSearchPath,
1359                     out userRequestedSpecificFile
1360                 );
1361             }
1362             catch (System.BadImageFormatException e)
1363             {
1364                 reference.AddError(new DependencyResolutionException(e.Message, e));
1365             }
1366 
1367             // Update the list of assemblies considered and rejected.
1368             reference.AddAssembliesConsideredAndRejected(assembliesConsideredAndRejected);
1369 
1370             // If the path was resolved, then specify the full path on the reference.
1371             if (resolvedPath != null)
1372             {
1373                 if (!Path.IsPathRooted(resolvedPath))
1374                 {
1375                     resolvedPath = Path.GetFullPath(resolvedPath);
1376                 }
1377 
1378                 reference.FullPath = resolvedPath;
1379                 reference.ResolvedSearchPath = resolvedSearchPath;
1380                 reference.UserRequestedSpecificFile = userRequestedSpecificFile;
1381             }
1382             else
1383             {
1384                 if (assemblyName != null)
1385                 {
1386                     reference.AddError
1387                     (
1388                         new ReferenceResolutionException
1389                         (
1390                             ResourceUtilities.FormatResourceString("General.CouldNotLocateAssembly", assemblyName.FullName),
1391                             null
1392                         )
1393                     );
1394                 }
1395             }
1396         }
1397 
1398         /// <summary>
1399         /// This method will remove references from the reference table which are contained in the blacklist.
1400         /// References which are primary references but are in the black list will be placed in the invalidResolvedFiles list.
1401         /// References which are dependency references but are in the black list will be placed in the invalidResolvedDependencyFiles list.
1402         /// </summary>
RemoveReferencesMarkedForExclusion(bool removeOnlyNoWarning, string subsetName)1403         internal void RemoveReferencesMarkedForExclusion(bool removeOnlyNoWarning, string subsetName)
1404         {
1405 #if (!STANDALONEBUILD)
1406             using (new CodeMarkerStartEnd(CodeMarkerEvent.perfMSBuildRARRemoveFromExclusionListBegin, CodeMarkerEvent.perfMSBuildRARRemoveFromExclusionListEnd))
1407 #endif
1408             {
1409                 // Create a table which will contain the references which are not in the black list
1410                 Dictionary<AssemblyNameExtension, Reference> goodReferences = new Dictionary<AssemblyNameExtension, Reference>(AssemblyNameComparer.GenericComparer);
1411 
1412                 // List of references which were removed from the reference table, we will loop through these and make sure that we get rid of the dependent references also.
1413                 List<Reference> removedReferences = new List<Reference>();
1414 
1415                 // For each reference, have a list of dependency references and their assembly names. (List<KeyValuePair<Reference, AssemblyNameExtension>>) == the dependent reference and the assembly name.
1416                 Dictionary<Reference, List<ReferenceAssemblyExtensionPair>> dependencyGraph = new Dictionary<Reference, List<ReferenceAssemblyExtensionPair>>();
1417 
1418                 LogExclusionReason logExclusionReason = null;
1419 
1420                 if (subsetName == null)
1421                 {
1422                     subsetName = String.Empty;
1423                 }
1424 
1425                 // Go through each of the references, we go through this table because in general it will be considerably smaller than the blacklist. (10's of references vs 100's of black list items)
1426                 foreach (AssemblyNameExtension assemblyName in _references.Keys)
1427                 {
1428                     Reference assemblyReference = _references[assemblyName];
1429 
1430                     AddToDependencyGraph(dependencyGraph, assemblyName, assemblyReference);
1431 
1432                     // Is the assembly name not in the black list. This means the assembly could be allowed.
1433                     bool isMarkedForExclusion = assemblyReference.ExclusionListLoggingProperties.IsInExclusionList;
1434                     logExclusionReason = assemblyReference.ExclusionListLoggingProperties.ExclusionReasonLogDelegate;
1435 
1436                     // Case one, the assembly is a primary reference
1437                     if (assemblyReference.IsPrimary)
1438                     {
1439                         // The assembly is good if it is not in the black list or it has specific version set to true.
1440                         if (!isMarkedForExclusion || assemblyReference.WantSpecificVersion)
1441                         {
1442                             // Do not add the reference to the good list if it has been added to the removed references list, possibly because of us processing another reference.
1443                             if (!removedReferences.Contains(assemblyReference))
1444                             {
1445                                 goodReferences[assemblyName] = assemblyReference;
1446                             }
1447                         }
1448                         else
1449                         {
1450                             RemovePrimaryReferenceMarkedForExclusion(logExclusionReason, removeOnlyNoWarning, subsetName, removedReferences, assemblyName, assemblyReference);
1451                         }
1452                     }
1453 
1454                     // A Primary reference can also be dependency of other references. This means there may be other primary reference which depend on
1455                     // the current primary reference and they need to be removed.
1456                     ICollection dependees = assemblyReference.GetSourceItems();
1457 
1458                     // Need to deal with dependencies, this can also include primary references who are dependencies themselves and are in the black list
1459                     if (!assemblyReference.IsPrimary || (assemblyReference.IsPrimary && isMarkedForExclusion && (dependees != null && dependees.Count > 1)))
1460                     {
1461                         // Does the assembly have specific version true, or does any of its primary parent references have specific version true.
1462                         // This is checked because, if an assembly is in the black list, the only way it can possibly be allowed is if
1463                         // ANY of the primary references which caused it have specific version set to true. To see if any primary references have the metadata we pass true to the method indicating
1464                         // we want to know if any primary references have specific version set to true.
1465                         bool hasSpecificVersionTrue = assemblyReference.CheckForSpecificVersionMetadataOnParentsReference(true);
1466 
1467                         //A dependency is "good" if it is not in the black list or any of its parents have specific version set to true
1468                         if (!isMarkedForExclusion || hasSpecificVersionTrue)
1469                         {
1470                             // Do not add the reference to the good list if it has been added to the removed references list, possibly because of us processing another reference.
1471                             if (!removedReferences.Contains(assemblyReference))
1472                             {
1473                                 goodReferences[assemblyName] = assemblyReference;
1474                             }
1475                         }
1476 
1477                         // If the dependency is in the black list we need to remove the primary references which depend on this refernce.
1478                         // note, a reference can both be in the good references list and in the black list. This can happen if a multiple primary references
1479                         // depend on a single dependency. The dependency can be good for one reference but not allowed for the other.
1480                         if (isMarkedForExclusion)
1481                         {
1482                             RemoveDependencyMarkedForExclusion(logExclusionReason, removeOnlyNoWarning, subsetName, goodReferences, removedReferences, assemblyName, assemblyReference);
1483                         }
1484                     }
1485                 }
1486 
1487                 // Go through each of the reference which were removed from the reference list and make sure that we get rid of all of the assemblies which were
1488                 // dependencies of them.
1489                 foreach (Reference reference in removedReferences)
1490                 {
1491                     RemoveDependencies(reference, goodReferences, dependencyGraph);
1492                 }
1493 
1494                 // Replace the references table with the list only containing good references.
1495                 _references = goodReferences;
1496             }
1497         }
1498 
1499         /// <summary>
1500         /// References usually only contains who they depend on, they do not know who depends on them. Given a reference
1501         /// A we cannot inspect A to find out that B,C,D depend on it. This method will traverse the references and build up this other direction of the graph,
1502         /// therefore we will be able to know given reference A, that B,C,D depend on it.
1503         /// </summary>
AddToDependencyGraph(Dictionary<Reference, List<ReferenceAssemblyExtensionPair>> dependencyGraph, AssemblyNameExtension assemblyName, Reference assemblyReference)1504         private static void AddToDependencyGraph(Dictionary<Reference, List<ReferenceAssemblyExtensionPair>> dependencyGraph, AssemblyNameExtension assemblyName, Reference assemblyReference)
1505         {
1506             // Find the references who the current reference is a dependency for
1507             foreach (Reference dependee in assemblyReference.GetDependees())
1508             {
1509                 // This list will contain a list of key value pairs (K: Dependent reference V: assembly Name)
1510                 List<ReferenceAssemblyExtensionPair> dependencies = null;
1511 
1512                 // For a dependee see if we already have a list started
1513                 if (!dependencyGraph.TryGetValue(dependee, out dependencies))
1514                 {
1515                     dependencies = new List<ReferenceAssemblyExtensionPair>();
1516                     dependencyGraph.Add(dependee, dependencies);
1517                 }
1518 
1519                 dependencies.Add(new ReferenceAssemblyExtensionPair(assemblyReference, assemblyName));
1520             }
1521         }
1522 
1523         /// <summary>
1524         /// We have determined the given assembly reference is in the black list, we now need to find the primary references which caused it and make sure those are removed from the list of references.
1525         /// </summary>
RemoveDependencyMarkedForExclusion(LogExclusionReason logExclusionReason, bool removeOnlyNoWarning, string subsetName, Dictionary<AssemblyNameExtension, Reference> goodReferences, List<Reference> removedReferences, AssemblyNameExtension assemblyName, Reference assemblyReference)1526         private void RemoveDependencyMarkedForExclusion(LogExclusionReason logExclusionReason, bool removeOnlyNoWarning, string subsetName, Dictionary<AssemblyNameExtension, Reference> goodReferences, List<Reference> removedReferences, AssemblyNameExtension assemblyName, Reference assemblyReference)
1527         {
1528             // For a dependency we would like to remove the primary references which caused this dependency to be found.
1529             // Source Items is the list of primary itemspecs which lead to the current reference being discovered.
1530             ICollection dependees = assemblyReference.GetSourceItems();
1531             foreach (ITaskItem dependee in dependees)
1532             {
1533                 string dependeeItemSpec = dependee.ItemSpec;
1534 
1535                 if (assemblyReference.IsPrimary)
1536                 {
1537                     // Dont process yourself
1538                     if (String.Compare(dependeeItemSpec, assemblyReference.PrimarySourceItem.ItemSpec, StringComparison.OrdinalIgnoreCase) == 0)
1539                     {
1540                         continue;
1541                     }
1542                 }
1543 
1544                 // Get the primary reference assemblyName
1545                 AssemblyNameExtension primaryAssemblyName = GetReferenceFromItemSpec(dependeeItemSpec);
1546 
1547                 if (primaryAssemblyName != null)
1548                 {
1549                     // Get the specific primary reference which caused this dependency
1550                     Reference primaryAssemblyReference = _references[primaryAssemblyName];
1551                     bool hasSpecificVersionMetadata = primaryAssemblyReference.WantSpecificVersion;
1552 
1553                     if (!hasSpecificVersionMetadata)
1554                     {
1555                         // If the reference has not been removed we need to remove it and possibly remove it from the good reference list.
1556                         if (!removedReferences.Contains(primaryAssemblyReference))
1557                         {
1558                             removedReferences.Add(primaryAssemblyReference);
1559                             goodReferences.Remove(primaryAssemblyName);
1560                         }
1561 
1562                         if (!removeOnlyNoWarning && logExclusionReason != null)
1563                         {
1564                             logExclusionReason(false, assemblyName, assemblyReference, dependee, subsetName);
1565                         }
1566                     }
1567                 }
1568             }
1569         }
1570 
1571         /// <summary>
1572         /// A primary references has been determined to be in the black list, it needs to be removed from the list of references by not being added to the list of good references
1573         /// and added to the list of removed references.
1574         /// </summary>
RemovePrimaryReferenceMarkedForExclusion(LogExclusionReason logExclusionReason, bool removeOnlyNoWarning, string subsetName, List<Reference> removedReferences, AssemblyNameExtension assemblyName, Reference assemblyReference)1575         private static void RemovePrimaryReferenceMarkedForExclusion(LogExclusionReason logExclusionReason, bool removeOnlyNoWarning, string subsetName, List<Reference> removedReferences, AssemblyNameExtension assemblyName, Reference assemblyReference)
1576         {
1577             removedReferences.Add(assemblyReference);
1578 
1579             if (!removeOnlyNoWarning && logExclusionReason != null)
1580             {
1581                 // Note a primary references will always have a PrimarySourceItem which is not null
1582                 logExclusionReason(true, assemblyName, assemblyReference, assemblyReference.PrimarySourceItem, subsetName);
1583             }
1584         }
1585 
1586         /// <summary>
1587         /// Get the primary reference based on the Itemspec
1588         /// </summary>
GetReferenceFromItemSpec(string itemSpec)1589         internal AssemblyNameExtension GetReferenceFromItemSpec(string itemSpec)
1590         {
1591             foreach (AssemblyNameExtension assemblyName in _references.Keys)
1592             {
1593                 Reference assemblyReference = _references[assemblyName];
1594                 if (assemblyReference.IsPrimary && assemblyReference.PrimarySourceItem.ItemSpec.Equals(itemSpec, StringComparison.OrdinalIgnoreCase))
1595                 {
1596                     return assemblyName;
1597                 }
1598             }
1599 
1600             return null;
1601         }
1602 
1603         /// <summary>
1604         /// Go through the dependency graph and make sure that for a reference to remove that we get rid of all dependency assemblies which are not referenced by any other
1605         /// assembly. The remove reference list should contain ALL primary references which should be removed because they, or one of their dependencies is in the black list.
1606         /// </summary>
1607         /// <param name="removedReference">Reference to remove dependencies for</param>
1608         /// <param name="referenceList">Reference list which contains reference to be used in unification and returned as resolved items</param>
1609         /// <param name="dependencyList"> A dictionary (Key: Reference Value: List of dependencies and their assembly name)</param>
RemoveDependencies(Reference removedReference, Dictionary<AssemblyNameExtension, Reference> referenceList, Dictionary<Reference, List<ReferenceAssemblyExtensionPair>> dependencyList)1610         private void RemoveDependencies(Reference removedReference, Dictionary<AssemblyNameExtension, Reference> referenceList, Dictionary<Reference, List<ReferenceAssemblyExtensionPair>> dependencyList)
1611         {
1612             List<ReferenceAssemblyExtensionPair> dependencies = null;
1613 
1614             // See if the reference has a list of dependencies
1615             if (!dependencyList.TryGetValue(removedReference, out dependencies))
1616             {
1617                 return;
1618             }
1619 
1620             // Go through each of the dependency assemblies and remove the removedReference from the
1621             // dependee list.
1622             foreach (ReferenceAssemblyExtensionPair dependency in dependencies)
1623             {
1624                 Reference reference = dependency.Key;
1625 
1626                 // Remove the referenceToRemove from the dependee list, this will "unlink" them, in that the dependency reference will no longer know that
1627                 // referenceToRemove had a dependency on it
1628                 reference.RemoveDependee(removedReference);
1629 
1630                 // A primary reference is special because it is declared in the project file so even if no one else deppends on it, the reference is still needed.
1631                 if (reference.IsPrimary)
1632                 {
1633                     continue;
1634                 }
1635 
1636                 // If the referenceToRemove was the last dependee of the current dependency reference, remove the dependency reference from the reference list.
1637                 if (reference.GetDependees().Count == 0)
1638                 {
1639                     referenceList.Remove(dependency.Value);
1640 
1641                     // Recurse using the current refererence so that we remove the next set of dependencies.
1642                     RemoveDependencies(reference, referenceList, dependencyList);
1643                 }
1644             }
1645         }
1646 
1647         /// <summary>
1648         /// Searches the table for references that haven't been resolved to their full file names and
1649         /// for dependencies that haven't yet been found.
1650         ///
1651         /// If any are found, they're resolved and then dependencies are found. Then the process is repeated
1652         /// until nothing is left unresolved.
1653         /// </summary>
1654         /// <param name="remappedAssemblies">The table of remapped assemblies.</param>
1655         /// <param name="referenceAssemblyFiles">The task items which contain file names to add.</param>
1656         /// <param name="referenceAssemblyNames">The task items which contain fusion names to add.</param>
1657         /// <param name="exceptions">Errors encountered while computing closure.</param>
ComputeClosure( DependentAssembly[] remappedAssembliesValue, ITaskItem[] referenceAssemblyFiles, ITaskItem[] referenceAssemblyNames, ArrayList exceptions )1658         internal void ComputeClosure
1659         (
1660             DependentAssembly[] remappedAssembliesValue,
1661             ITaskItem[] referenceAssemblyFiles,
1662             ITaskItem[] referenceAssemblyNames,
1663             ArrayList exceptions
1664         )
1665         {
1666 #if (!STANDALONEBUILD)
1667             using (new CodeMarkerStartEnd(CodeMarkerEvent.perfMSBuildRARComputeClosureBegin, CodeMarkerEvent.perfMSBuildRARComputeClosureEnd))
1668 #endif
1669             {
1670                 _references.Clear();
1671                 _externallyResolvedPrimaryReferences.Clear();
1672                 SkippedFindingExternallyResolvedDependencies = false;
1673 
1674                 _remappedAssemblies = remappedAssembliesValue;
1675                 SetPrimaryItems(referenceAssemblyFiles, referenceAssemblyNames, exceptions);
1676 
1677                 ComputeClosure();
1678             }
1679         }
1680 
1681         /// <summary>
1682         /// Implementation of ComputeClosure.
1683         /// </summary>
ComputeClosure()1684         private void ComputeClosure()
1685         {
1686             bool moreResolvable = true;
1687             int moreResolvableIterations = 0;
1688             const int maxIterations = 100000; // Wait for a ridiculously large number of iterations before bailing out.
1689 
1690             do
1691             {
1692                 bool moreDependencies = true;
1693 
1694                 int dependencyIterations = 0;
1695                 do
1696                 {
1697                     // Resolve all references.
1698                     ResolveAssemblyFilenames();
1699 
1700                     // Find prerequisites.
1701                     moreDependencies = FindAssociatedFiles();
1702 
1703                     ++dependencyIterations;
1704                     ErrorUtilities.VerifyThrow(dependencyIterations < maxIterations, "Maximum iterations exceeded while looking for dependencies.");
1705                 } while (moreDependencies);
1706 
1707 
1708                 // If everything is either resolved or unresolvable, then we can quit.
1709                 // Otherwise, loop again.
1710                 moreResolvable = false;
1711                 foreach (Reference reference in References.Values)
1712                 {
1713                     if (!reference.IsResolved)
1714                     {
1715                         if (!reference.IsUnresolvable)
1716                         {
1717                             moreResolvable = true;
1718                             break;
1719                         }
1720                     }
1721                 }
1722 
1723                 ++moreResolvableIterations;
1724                 ErrorUtilities.VerifyThrow(moreResolvableIterations < maxIterations, "Maximum iterations exceeded while looking for resolvable references.");
1725             } while (moreResolvable);
1726         }
1727 
1728         /// <summary>
1729         /// Find associates for references that we haven't found associates for before.
1730         /// Returns true if new dependent assemblies were found.
1731         /// </summary>
FindAssociatedFiles()1732         private bool FindAssociatedFiles()
1733         {
1734             bool newDependencies = false;
1735 
1736             ArrayList newEntries = new ArrayList();
1737 
1738             foreach (Reference reference in References.Values)
1739             {
1740                 // If the reference is resolved, but dependencies haven't been found,
1741                 // then find dependencies.
1742                 if (reference.IsResolved && !reference.DependenciesFound)
1743                 {
1744                     // Set this reference to 'resolved' so it won't be processed the next time.
1745                     reference.DependenciesFound = true;
1746 
1747                     try
1748                     {
1749                         // We don't look for associated files for FX assemblies.
1750                         bool hasFrameworkPath = false;
1751                         string referenceDirectoryName = FileUtilities.EnsureTrailingSlash(reference.DirectoryName);
1752 
1753                         foreach (string frameworkPath in _frameworkPaths)
1754                         {
1755                             // frameworkPath is guaranteed to have a trailing slash, because
1756                             // ResolveAssemblyReference.Execute takes care of adding it.
1757 
1758                             if (String.Compare(referenceDirectoryName, frameworkPath, StringComparison.OrdinalIgnoreCase) == 0)
1759                             {
1760                                 hasFrameworkPath = true;
1761                             }
1762                         }
1763 
1764                         // We do not want to find dependencies of framework assembles, embedded interoptypes or assemblies in sdks.
1765                         if (!hasFrameworkPath && !reference.EmbedInteropTypes && reference.SDKName.Length == 0)
1766                         {
1767                             if (!reference.ExternallyResolved)
1768                             {
1769                                 // Look for companion files like pdbs and xmls that ride along with
1770                                 // assemblies.
1771                                 if (_findRelatedFiles)
1772                                 {
1773                                     FindRelatedFiles(reference);
1774                                 }
1775 
1776                                 // Satellite assemblies are named <CultureDir>\<AppBaseName>.resources.dll
1777                                 // where <CultureDir> is like 'en', 'fr', etc.
1778                                 if (_findSatellites)
1779                                 {
1780                                     FindSatellites(reference);
1781                                 }
1782 
1783                                 // Look for serialization assemblies.
1784                                 if (_findSerializationAssemblies)
1785                                 {
1786                                     FindSerializationAssemblies(reference);
1787                                 }
1788                             }
1789 
1790                             if (!reference.ExternallyResolved || FindDependenciesOfExternallyResolvedReferences)
1791                             {
1792                                 // Look for dependent assemblies.
1793                                 if (_findDependencies)
1794                                 {
1795                                     FindDependenciesAndScatterFiles(reference, newEntries);
1796                                 }
1797                             }
1798                             else
1799                             {
1800                                 SkippedFindingExternallyResolvedDependencies = true;
1801                             }
1802 
1803                             // If something was found, then break out and start fresh.
1804                             if (newEntries.Count > 0)
1805                             {
1806                                 break;
1807                             }
1808                         }
1809                     }
1810                     catch (PathTooLongException e)
1811                     {
1812                         // If the directory path is too long then record the error and move on.
1813                         reference.AddError(new DependencyResolutionException(e.Message, e));
1814                     }
1815                 }
1816             }
1817 
1818             // Add each new dependency found.
1819             foreach (DictionaryEntry newEntry in newEntries)
1820             {
1821                 newDependencies = true;
1822                 AddReference((AssemblyNameExtension)newEntry.Key, (Reference)newEntry.Value);
1823             }
1824 
1825             return newDependencies;
1826         }
1827 
1828         /// <summary>
1829         /// Resolve all references that have not been resolved yet to real files on disk.
1830         /// </summary>
ResolveAssemblyFilenames()1831         private void ResolveAssemblyFilenames()
1832         {
1833             foreach (AssemblyNameExtension assemblyName in References.Keys)
1834             {
1835                 Reference reference = GetReference(assemblyName);
1836 
1837                 // Has this reference been resolved to a file name?
1838                 if (!reference.IsResolved && !reference.IsUnresolvable)
1839                 {
1840                     ResolveReference(assemblyName, null, reference);
1841                 }
1842             }
1843         }
1844 
1845         /// <summary>
1846         /// This methods looks for conflicts between assemblies and attempts to
1847         /// resolve them.
1848         /// </summary>
ResolveConflictsBetweenReferences()1849         private int ResolveConflictsBetweenReferences()
1850         {
1851             int count = 0;
1852 
1853             // Get a table of simple name mapped to (perhaps multiple) reference.
1854             Hashtable baseNames = BuildSimpleNameTable();
1855 
1856             // Now we have references organized into groups that would conflict.
1857             foreach (string baseName in baseNames.Keys)
1858             {
1859                 ArrayList assemblyReferences = (ArrayList)baseNames[baseName];
1860 
1861                 // Sort to make it predictable. Choose to sort by ascending version number
1862                 // since this is known to reveal bugs in at least one circumstance.
1863                 assemblyReferences.Sort(AssemblyNameReferenceAscendingVersionComparer.comparer);
1864 
1865                 // Two or more references required for there to be a conflict.
1866                 while (assemblyReferences.Count > 1)
1867                 {
1868                     // Resolve the conflict. Victim is the index of the item that lost.
1869                     int victim = ResolveAssemblyNameConflict
1870                     (
1871                         (AssemblyNameReference)assemblyReferences[0],
1872                         (AssemblyNameReference)assemblyReferences[1]
1873                     );
1874 
1875                     assemblyReferences.RemoveAt(victim);
1876                     count++;
1877                 }
1878             }
1879 
1880             return count;
1881         }
1882 
1883         /// <summary>
1884         /// Based on the closure, get a table of ideal remappings needed to
1885         /// produce zero conflicts.
1886         /// </summary>
ResolveConflicts( out DependentAssembly[] idealRemappings, out AssemblyNameReference[] conflictingReferences )1887         internal void ResolveConflicts
1888         (
1889             out DependentAssembly[] idealRemappings,
1890             out AssemblyNameReference[] conflictingReferences
1891         )
1892         {
1893             idealRemappings = null;
1894             conflictingReferences = null;
1895 
1896             // First, resolve all conflicts between references.
1897             if (0 == ResolveConflictsBetweenReferences())
1898             {
1899                 // If there were no basename conflicts then there can be no version-to-version conflicts.
1900                 // In this case, short-circuit now rather than building up all the tables below.
1901                 return;
1902             }
1903 
1904             // Build two tables, one with a count and one with the corresponding references.
1905             // Dependencies which differ only by version number need a suggested redirect.
1906             // The count tells us whether there are two or more.
1907             Hashtable counts = new Hashtable(StringComparer.OrdinalIgnoreCase);
1908             Hashtable references = new Hashtable(StringComparer.OrdinalIgnoreCase);
1909 
1910             foreach (AssemblyNameExtension assemblyName in References.Keys)
1911             {
1912                 Reference reference = GetReference(assemblyName);
1913 
1914                 // If the assembly has a parent which has specific version set to true then we need to see if it is framework assembly
1915                 if (reference.CheckForSpecificVersionMetadataOnParentsReference(true))
1916                 {
1917                     // Try and find an entry in the redist list by comparing everything except the version.
1918                     AssemblyEntry entry = null;
1919 
1920                     if (_installedAssemblies != null)
1921                     {
1922                         entry = _installedAssemblies.FindHighestVersionInRedistList(assemblyName);
1923                     }
1924 
1925                     if (entry != null)
1926                     {
1927                         // We have found an entry in the redist list that this assembly is a framework assembly of some version
1928                         // also one if its parent references has specific version set to true, therefore we need to make sure
1929                         // that we do not consider it for conflict resolution.
1930                         continue;
1931                     }
1932                 }
1933 
1934                 byte[] pkt = assemblyName.GetPublicKeyToken();
1935                 if (pkt != null && pkt.Length > 0)
1936                 {
1937                     AssemblyName baseKey = assemblyName.AssemblyName.CloneIfPossible();
1938                     Version version = baseKey.Version;
1939                     baseKey.Version = null;
1940                     string key = baseKey.ToString();
1941 
1942                     if (counts.ContainsKey(key))
1943                     {
1944                         counts[key] = ((int)counts[key]) + 1;
1945                         Version lastVersion = ((AssemblyNameReference)references[key]).assemblyName.Version;
1946 
1947                         if (lastVersion == null || lastVersion < version)
1948                         {
1949                             references[key] = AssemblyNameReference.Create(assemblyName, reference);
1950                         }
1951                     }
1952                     else
1953                     {
1954                         counts[key] = 1;
1955                         references[key] = AssemblyNameReference.Create(assemblyName, reference);
1956                     }
1957                 }
1958             }
1959 
1960             // Build the list of conflicted assemblies.
1961             List<AssemblyNameReference> assemblyNamesList = new List<AssemblyNameReference>();
1962             foreach (string versionLessAssemblyName in counts.Keys)
1963             {
1964                 if (((int)counts[versionLessAssemblyName]) > 1)
1965                 {
1966                     assemblyNamesList.Add((AssemblyNameReference)references[versionLessAssemblyName]);
1967                 }
1968             }
1969 
1970             // Pass over the list of conflicting references and make a binding redirect for each.
1971             List<DependentAssembly> idealRemappingsList = new List<DependentAssembly>();
1972 
1973             foreach (AssemblyNameReference assemblyNameReference in assemblyNamesList)
1974             {
1975                 DependentAssembly remapping = new DependentAssembly();
1976                 remapping.PartialAssemblyName = assemblyNameReference.assemblyName.AssemblyName;
1977                 BindingRedirect bindingRedirect = new BindingRedirect();
1978                 bindingRedirect.OldVersionLow = new Version("0.0.0.0");
1979                 bindingRedirect.OldVersionHigh = assemblyNameReference.assemblyName.AssemblyName.Version;
1980                 bindingRedirect.NewVersion = assemblyNameReference.assemblyName.AssemblyName.Version;
1981                 remapping.BindingRedirects = new BindingRedirect[] { bindingRedirect };
1982 
1983                 idealRemappingsList.Add(remapping);
1984             }
1985 
1986             idealRemappings = idealRemappingsList.ToArray();
1987             conflictingReferences = assemblyNamesList.ToArray();
1988         }
1989 
1990         /// <summary>
1991         /// If a reference is a higher version than what exists in the redist list of the target framework then
1992         /// this reference needs to be marked as excluded so that it is not not allowed to be referenced.
1993         ///
1994         /// If the user needs this reference then they need to set specific version to true.
1995         /// </summary>
MarkReferencesExcludedDueToOtherFramework(AssemblyNameExtension assemblyName, Reference reference)1996         internal bool MarkReferencesExcludedDueToOtherFramework(AssemblyNameExtension assemblyName, Reference reference)
1997         {
1998             bool haveMarkedReference = false;
1999 
2000             // If the reference was not resolved from the GAC or AssemblyFolders then
2001             // we do not need to check it if came from another framework
2002             string resolvedSearchPath = reference.ResolvedSearchPath;
2003             bool resolvedFromGAC = resolvedSearchPath.Equals(AssemblyResolutionConstants.gacSentinel, StringComparison.OrdinalIgnoreCase);
2004             bool resolvedFromAssemblyFolders = resolvedSearchPath.Equals(AssemblyResolutionConstants.assemblyFoldersSentinel, StringComparison.OrdinalIgnoreCase);
2005 
2006             if (!resolvedFromGAC && !resolvedFromAssemblyFolders && reference.IsResolved)
2007             {
2008                 return false;
2009             }
2010 
2011             bool inLaterRedistListAndFromGlobalLocation = false;
2012 
2013             // Check against target framework version if projectTargetFramework is null or less than 4.5, also when flag to force check is set to true
2014             if (_checkAssemblyVersionAgainstTargetFrameworkVersion)
2015             {
2016                 // Did the assembly name get resolved from a GlobalLocation, GAC or AssemblyFolders and is it in the frameworkList.xml for the
2017                 // highest version of the currently targeted framework identifier.
2018                 inLaterRedistListAndFromGlobalLocation = InLatestRedistList(assemblyName, reference);
2019 
2020                 if (inLaterRedistListAndFromGlobalLocation)
2021                 {
2022                     LogExclusionReason reason = new LogExclusionReason(LogAnotherFrameworkUnResolve);
2023                     reference.ExclusionListLoggingProperties.ExclusionReasonLogDelegate = reason;
2024                     reference.ExclusionListLoggingProperties.IsInExclusionList = true;
2025                     haveMarkedReference = true;
2026                 }
2027             }
2028 
2029             return haveMarkedReference;
2030         }
2031 
2032         /// <summary>
2033         /// Is the assembly in the latest framework redist list as either passed into RAR on the lastestFrameworkDirectories property or determined by inspecting the file system.
2034         /// </summary>
InLatestRedistList(AssemblyNameExtension assemblyName, Reference reference)2035         private bool InLatestRedistList(AssemblyNameExtension assemblyName, Reference reference)
2036         {
2037             bool inLaterRedistList = false;
2038 
2039             Tuple<RedistList, string> redistListOtherFramework = GetHighestVersionFullFrameworkForTFM(_targetFrameworkMoniker);
2040 
2041             if (redistListOtherFramework != null && redistListOtherFramework.Item1 != null && redistListOtherFramework.Item1.FrameworkAssemblyEntryInRedist(assemblyName))
2042             {
2043                 inLaterRedistList = true;
2044             }
2045 
2046             return inLaterRedistList;
2047         }
2048 
2049         /// <summary>
2050         /// Get the redist list which corresponds to the highest target framework for a given target framework moniker.
2051         ///
2052         /// This is done in two ways:
2053         ///  First, if the latestTargetFrameworkDirectories parameter is passed into RAR those directories will be used to get the redist list
2054         ///  regardless of the target framework moniker.
2055         ///
2056         /// Second, if latest Target Framework Directories is not passed in then we ask the ToollocationHelper for the highest target framework which has
2057         /// a TargetFrameworkIdentifier which matches the passed in TargetFrameworkMoniker.
2058         /// </summary>
GetHighestVersionFullFrameworkForTFM(FrameworkNameVersioning targetFrameworkMoniker)2059         private Tuple<RedistList, string> GetHighestVersionFullFrameworkForTFM(FrameworkNameVersioning targetFrameworkMoniker)
2060         {
2061             RedistList redistList = null;
2062             Tuple<RedistList, string> redistListAndOtherFrameworkName = null;
2063             if (targetFrameworkMoniker != null)
2064             {
2065                 lock (s_monikerToHighestRedistList)
2066                 {
2067                     if (!s_monikerToHighestRedistList.TryGetValue(targetFrameworkMoniker.Identifier, out redistListAndOtherFrameworkName))
2068                     {
2069                         IList<string> referenceAssemblyDirectories = null;
2070 
2071                         string otherFrameworkName = null;
2072 
2073                         // The latestTargetFrameworkDirectories can be passed into RAR, if they are then use those directories rather than
2074                         // getting a list by looking at the file system.
2075                         if (_latestTargetFrameworkDirectories != null && _latestTargetFrameworkDirectories.Length > 0)
2076                         {
2077                             referenceAssemblyDirectories = new List<string>(_latestTargetFrameworkDirectories);
2078                             otherFrameworkName = String.Join(";", _latestTargetFrameworkDirectories);
2079                         }
2080                         else
2081                         {
2082                             FrameworkNameVersioning highestFrameworkName = null;
2083                             referenceAssemblyDirectories = GetHighestVersionReferenceAssemblyDirectories(targetFrameworkMoniker, out highestFrameworkName);
2084                             if (highestFrameworkName != null)
2085                             {
2086                                 otherFrameworkName = highestFrameworkName.FullName;
2087                             }
2088                         }
2089 
2090                         if (referenceAssemblyDirectories != null && referenceAssemblyDirectories.Count > 0)
2091                         {
2092                             HashSet<string> seenFrameworkDirectories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
2093                             List<AssemblyTableInfo> assemblyTableInfos = new List<AssemblyTableInfo>();
2094                             foreach (string path in referenceAssemblyDirectories)
2095                             {
2096                                 string[] listPaths = RedistList.GetRedistListPathsFromDisk(path);
2097                                 foreach (string listPath in listPaths)
2098                                 {
2099                                     if (!seenFrameworkDirectories.Contains(listPath))
2100                                     {
2101                                         assemblyTableInfos.Add(new AssemblyTableInfo(listPath, path));
2102                                         seenFrameworkDirectories.Add(listPath);
2103                                     }
2104                                 }
2105                             }
2106 
2107                             // If the same set of directories was passed in before then the redist list will already be cached.
2108                             redistList = RedistList.GetRedistList(assemblyTableInfos.ToArray());
2109                         }
2110 
2111                         redistListAndOtherFrameworkName = new Tuple<RedistList, string>(redistList, otherFrameworkName);
2112                         s_monikerToHighestRedistList.Add(targetFrameworkMoniker.Identifier, redistListAndOtherFrameworkName);
2113                     }
2114                 }
2115             }
2116 
2117             return redistListAndOtherFrameworkName;
2118         }
2119 
2120         /// <summary>
2121         /// Based on a target framework moniker, get the set of reference assembly directories which
2122         /// correspond to the highest version of the target framework identifier property on the target framework moniker.
2123         /// </summary>
GetHighestVersionReferenceAssemblyDirectories(FrameworkNameVersioning targetFrameworkMoniker, out FrameworkNameVersioning highestVersionMoniker)2124         private static IList<string> GetHighestVersionReferenceAssemblyDirectories(FrameworkNameVersioning targetFrameworkMoniker, out FrameworkNameVersioning highestVersionMoniker)
2125         {
2126             IList<string> referenceAssemblyDirectories = null;
2127             string targetFrameworkRootDirectory = ToolLocationHelper.GetProgramFilesReferenceAssemblyRoot();
2128 
2129             highestVersionMoniker = ToolLocationHelper.HighestVersionOfTargetFrameworkIdentifier(targetFrameworkRootDirectory, targetFrameworkMoniker.Identifier);
2130             if (highestVersionMoniker == null)
2131             {
2132                 referenceAssemblyDirectories = new List<string>();
2133             }
2134             else
2135             {
2136                 referenceAssemblyDirectories = ToolLocationHelper.GetPathToReferenceAssemblies(targetFrameworkRootDirectory, highestVersionMoniker);
2137             }
2138             return referenceAssemblyDirectories;
2139         }
2140 
2141 
2142         /// <summary>
2143         /// Is the assemblyName in the current redist list and does it have a version number which is higher than what is in the current redist list.
2144         /// This may happen if someone passes in a p2p reference whcih is a framework assembly which is a higher version than what is in the redist list.
2145         /// </summary>
MarkReferenceWithHighestVersionInCurrentRedistList(AssemblyNameExtension assemblyName, Reference reference)2146         internal void MarkReferenceWithHighestVersionInCurrentRedistList(AssemblyNameExtension assemblyName, Reference reference)
2147         {
2148             if (_installedAssemblies != null)
2149             {
2150                 // Find the highest version of the assembly in the current redist list
2151                 AssemblyEntry highestInRedistList = _installedAssemblies.FindHighestVersionInRedistList(assemblyName);
2152 
2153                 if (highestInRedistList != null)
2154                 {
2155                     reference.ExclusionListLoggingProperties.HighestVersionInRedist = highestInRedistList.AssemblyNameExtension.Version;
2156                 }
2157             }
2158         }
2159 
2160         /// <summary>
2161         /// Is the assemblyName in the current redist list and does it have a version number which is higher than what is in the current redist list.
2162         /// This may happen if someone passes in a p2p reference whcih is a framework assembly which is a higher version than what is in the redist list.
2163         /// </summary>
MarkReferenceForExclusionDueToHigherThanCurrentFramework(AssemblyNameExtension assemblyName, Reference reference)2164         internal bool MarkReferenceForExclusionDueToHigherThanCurrentFramework(AssemblyNameExtension assemblyName, Reference reference)
2165         {
2166             bool higherThanCurrentRedistList = false;
2167 
2168             // In this method have we marked a reference as needing to be excluded
2169             bool haveMarkedReference = false;
2170 
2171             // Mark reference as excluded
2172 
2173             // Check against target framework version if projectTargetFramework is null or less than 4.5, also when flag to force check is set to true
2174             if (_checkAssemblyVersionAgainstTargetFrameworkVersion)
2175             {
2176                 // Check assemblies versions when target framework version is less than 4.5
2177 
2178                 // Make sure the version is higher than the version in the redist.
2179                 higherThanCurrentRedistList = (reference.ReferenceVersion != null && reference.ExclusionListLoggingProperties.HighestVersionInRedist != null)
2180                                               && reference.ReferenceVersion.CompareTo(reference.ExclusionListLoggingProperties.HighestVersionInRedist) > 0;
2181 
2182                 if (higherThanCurrentRedistList)
2183                 {
2184                     LogExclusionReason reason = new LogExclusionReason(LogHigherVersionUnresolve);
2185                     reference.ExclusionListLoggingProperties.ExclusionReasonLogDelegate = reason;
2186                     reference.ExclusionListLoggingProperties.IsInExclusionList = true;
2187                     haveMarkedReference = true;
2188                 }
2189             }
2190 
2191             return haveMarkedReference;
2192         }
2193 
2194         /// <summary>
2195         /// Does the assembly have a targetFrameworkAttribute which has a higher framework version than what the project is currently targeting.
2196         /// This may happen for example if a p2p is done between two projects with built against different target frameworks.
2197         /// </summary>
MarkReferenceForExclusionDueToHigherThanCurrentFrameworkAttribute(AssemblyNameExtension assemblyName, Reference reference)2198         internal bool MarkReferenceForExclusionDueToHigherThanCurrentFrameworkAttribute(AssemblyNameExtension assemblyName, Reference reference)
2199         {
2200             bool higherThanCurrentFramework = false;
2201             // In this method have we marked a reference as needing to be excluded
2202             bool haveMarkedReference = false;
2203 
2204             if (!(reference.IsResolved && _fileExists(reference.FullPath)) || reference.IsPrerequisite || (_frameworkPaths != null && Reference.IsFrameworkFile(reference.FullPath, _frameworkPaths)))
2205             {
2206                 return false;
2207             }
2208 
2209             // Make sure the version is higher than the version in the redist.
2210             // If the identifier are not equal we do not check since we are not trying to catch cross framework incompatibilities.
2211             higherThanCurrentFramework = reference.FrameworkNameAttribute != null
2212                                          && _targetFrameworkMoniker != null
2213                                          && String.Equals(reference.FrameworkNameAttribute.Identifier, _targetFrameworkMoniker.Identifier, StringComparison.OrdinalIgnoreCase)
2214                                          && reference.FrameworkNameAttribute.Version > _targetFrameworkMoniker.Version;
2215 
2216             // Mark reference as excluded
2217             if (higherThanCurrentFramework)
2218             {
2219                 LogExclusionReason reason = new LogExclusionReason(LogHigherVersionUnresolveDueToAttribute);
2220                 reference.ExclusionListLoggingProperties.ExclusionReasonLogDelegate = reason;
2221                 reference.ExclusionListLoggingProperties.IsInExclusionList = true;
2222                 haveMarkedReference = true;
2223             }
2224 
2225             return haveMarkedReference;
2226         }
2227 
2228 
2229         /// <summary>
2230         /// Build a table of simple names mapped to assemblyname+reference.
2231         /// </summary>
BuildSimpleNameTable()2232         private Hashtable BuildSimpleNameTable()
2233         {
2234             // Build a list of base file names from references.
2235             // These would conflict with each other if copied to the output directory.
2236             Hashtable baseNames = new Hashtable(StringComparer.CurrentCultureIgnoreCase);
2237             AssemblyNameReference assemblyReference;
2238 
2239             foreach (AssemblyNameExtension assemblyName in References.Keys)
2240             {
2241                 assemblyReference.assemblyName = assemblyName;
2242                 assemblyReference.reference = GetReference(assemblyName);
2243 
2244                 // Notice that unresolved assemblies are still added to the table.
2245                 // This is because an unresolved assembly may have a different version
2246                 // which would influence unification. We want to report this to the user.
2247                 string baseName = assemblyName.Name;
2248 
2249                 if (!baseNames.ContainsKey(baseName))
2250                 {
2251                     baseNames[baseName] = new ArrayList();
2252                 }
2253 
2254                 ((ArrayList)baseNames[baseName]).Add(assemblyReference);
2255             }
2256 
2257             return baseNames;
2258         }
2259 
2260         /// <summary>
2261         /// Given two references along with their fusion names, resolve the filename conflict that they
2262         /// would have if both assemblies need to be copied to the same directory.
2263         /// </summary>
ResolveAssemblyNameConflict(AssemblyNameReference assemblyReference0, AssemblyNameReference assemblyReference1)2264         private static int ResolveAssemblyNameConflict(AssemblyNameReference assemblyReference0, AssemblyNameReference assemblyReference1)
2265         {
2266             int victim = 0;
2267 
2268             // Extra checks for PInvoke-destined data.
2269             ErrorUtilities.VerifyThrow(assemblyReference0.assemblyName.FullName != null, "Got a null assembly name fullname. (0)");
2270             ErrorUtilities.VerifyThrow(assemblyReference1.assemblyName.FullName != null, "Got a null assembly name fullname. (1)");
2271 
2272             string[] conflictFusionNames = new string[] { assemblyReference0.assemblyName.FullName, assemblyReference1.assemblyName.FullName };
2273             Reference[] conflictReferences = new Reference[] { assemblyReference0.reference, assemblyReference1.reference };
2274             AssemblyNameExtension[] conflictAssemblyNames = new AssemblyNameExtension[] { assemblyReference0.assemblyName, assemblyReference1.assemblyName };
2275             bool[] conflictLegacyUnified = new bool[] { assemblyReference0.reference.IsPrimary, assemblyReference1.reference.IsPrimary };
2276 
2277             //  If both assemblies being compared are primary references, the caller should pass in a zero-flag
2278             // (non-unified) for both. (This conforms to the C# assumption that two direct references are meant to be
2279             // SxS.)
2280             if (conflictReferences[0].IsPrimary && conflictReferences[1].IsPrimary)
2281             {
2282                 conflictLegacyUnified[0] = false;
2283                 conflictLegacyUnified[1] = false;
2284             }
2285 
2286             // This is ok here because even if the method says two versions are equivilant the algorithm below will still pick the highest version.
2287             bool equivalent = false;
2288             NativeMethods.AssemblyComparisonResult result = 0;
2289             NativeMethods.CompareAssemblyIdentity
2290             (
2291                 conflictFusionNames[0],
2292                 conflictLegacyUnified[0],
2293                 conflictFusionNames[1],
2294                 conflictLegacyUnified[1],
2295                 out equivalent,
2296                 out result
2297             );
2298 
2299             // Remove one and provide some information about why.
2300             victim = 0;
2301             ConflictLossReason reason = ConflictLossReason.InsolubleConflict;
2302 
2303             // Pick the one with the highest version number.
2304             if (conflictReferences[0].IsPrimary && !conflictReferences[1].IsPrimary)
2305             {
2306                 // Choose the primary version.
2307                 victim = 1;
2308                 reason = ConflictLossReason.WasNotPrimary;
2309             }
2310             else if (!conflictReferences[0].IsPrimary && conflictReferences[1].IsPrimary)
2311             {
2312                 // Choose the primary version.
2313                 victim = 0;
2314                 reason = ConflictLossReason.WasNotPrimary;
2315             }
2316             else if (!conflictReferences[0].IsPrimary && !conflictReferences[1].IsPrimary)
2317             {
2318                 if
2319                 (
2320                     // Version comparison only if there are two versions to compare.
2321                     // Null versions can occur when simply-named assemblies are unresolved.
2322                     conflictAssemblyNames[0].Version != null && conflictAssemblyNames[1].Version != null
2323                     && conflictAssemblyNames[0].Version > conflictAssemblyNames[1].Version
2324                 )
2325                 {
2326                     // Choose the higher version
2327                     victim = 1;
2328                     if (equivalent)
2329                     {
2330                         reason = ConflictLossReason.HadLowerVersion;
2331                     }
2332                 }
2333                 else if
2334                 (
2335                     // Version comparison only if there are two versions to compare.
2336                     // Null versions can occur when simply-named assemblies are unresolved.
2337                     conflictAssemblyNames[0].Version != null && conflictAssemblyNames[1].Version != null
2338                     && conflictAssemblyNames[0].Version < conflictAssemblyNames[1].Version
2339                 )
2340                 {
2341                     // Choose the higher version
2342                     victim = 0;
2343                     if (equivalent)
2344                     {
2345                         reason = ConflictLossReason.HadLowerVersion;
2346                     }
2347                 }
2348                 else
2349                 {
2350                     victim = 0;
2351 
2352                     if (equivalent)
2353                     {
2354                         // Fusion thinks they're interchangeable.
2355                         reason = ConflictLossReason.FusionEquivalentWithSameVersion;
2356                     }
2357                 }
2358             }
2359 
2360 
2361             // Remove the one chosen.
2362             int victor = 1 - victim;
2363             conflictReferences[victim].ConflictVictorName = conflictAssemblyNames[victor];
2364             conflictReferences[victim].ConflictLossExplanation = reason;
2365             conflictReferences[victor].AddConflictVictim(conflictAssemblyNames[victim]);
2366 
2367             return victim;
2368         }
2369 
2370         /// <summary>
2371         /// Returns true if an assembly has been removed from the .NET framework
2372         /// </summary>
IsAssemblyRemovedFromDotNetFramework(AssemblyNameExtension assemblyName, string fullPath, string[] frameworkPaths, InstalledAssemblies installedAssemblies)2373         private bool IsAssemblyRemovedFromDotNetFramework(AssemblyNameExtension assemblyName, string fullPath, string[] frameworkPaths, InstalledAssemblies installedAssemblies)
2374         {
2375             if (installedAssemblies != null)
2376             {
2377                 AssemblyEntry redistListEntry = installedAssemblies.FindHighestVersionInRedistList(assemblyName);
2378                 if (redistListEntry != null)
2379                 {
2380                     Version redistListVersion = redistListEntry.AssemblyNameExtension.Version;
2381 
2382                     if (redistListVersion != null && assemblyName.Version >= redistListVersion && !Reference.IsFrameworkFile(fullPath, frameworkPaths))
2383                     {
2384                         return true;
2385                     }
2386                 }
2387             }
2388             return false;
2389         }
2390 
2391         /// <summary>
2392         /// Get unification information for the given assembly name.
2393         /// </summary>
2394         /// <param name="assemblyName">The assembly name.</param>
2395         /// <param name="unifiedVersion">The new version of the assembly to use.</param>
2396         /// <param name="unificationReason">The reason this reference was unified.</param>
2397         /// <param name="isPrerequisite">True if this is a prereq assembly.</param>
2398         /// <returns>True if there was a unification.</returns>
UnifyAssemblyNameVersions( AssemblyNameExtension assemblyName, out Version unifiedVersion, out UnificationReason unificationReason, out bool isPrerequisite, out bool? isRedistRoot, out string redistName )2399         private bool UnifyAssemblyNameVersions
2400         (
2401             AssemblyNameExtension assemblyName,
2402             out Version unifiedVersion,
2403             out UnificationReason unificationReason,
2404             out bool isPrerequisite,
2405             out bool? isRedistRoot,
2406             out string redistName
2407         )
2408         {
2409             unifiedVersion = assemblyName.Version;
2410             isPrerequisite = false;
2411             isRedistRoot = null;
2412             redistName = null;
2413             unificationReason = UnificationReason.DidntUnify;
2414 
2415             // If there's no version, for example in a simple name, then no remapping is possible.
2416             if (assemblyName.Version == null)
2417             {
2418                 return false;
2419             }
2420 
2421             // Try for a remapped assemblies unification.
2422             if (_remappedAssemblies != null)
2423             {
2424                 foreach (DependentAssembly remappedAssembly in _remappedAssemblies)
2425                 {
2426                     AssemblyName comparisonAssembly = remappedAssembly.AssemblyNameReadOnly;
2427 
2428                     if (CompareAssembliesIgnoringVersion(assemblyName.AssemblyName, comparisonAssembly))
2429                     {
2430                         foreach (BindingRedirect bindingRedirect in remappedAssembly.BindingRedirects)
2431                         {
2432                             if (assemblyName.Version >= bindingRedirect.OldVersionLow && assemblyName.Version <= bindingRedirect.OldVersionHigh)
2433                             {
2434                                 // If the new version is different than the old version, then there is a unification.
2435                                 if (assemblyName.Version != bindingRedirect.NewVersion)
2436                                 {
2437                                     unifiedVersion = bindingRedirect.NewVersion;
2438                                     unificationReason = UnificationReason.BecauseOfBindingRedirect;
2439                                     return true;
2440                                 }
2441                             }
2442                         }
2443                     }
2444                 }
2445             }
2446 
2447             // Try for an installed assemblies unification.
2448             if (_installedAssemblies != null)
2449             {
2450                 _installedAssemblies.GetInfo
2451                 (
2452                     assemblyName,
2453                     out unifiedVersion,
2454                     out isPrerequisite,
2455                     out isRedistRoot,
2456                     out redistName
2457                 );
2458 
2459                 // Was there a unification?
2460                 if (unifiedVersion != assemblyName.Version)
2461                 {
2462                     unificationReason = UnificationReason.FrameworkRetarget;
2463                     return assemblyName.Version != unifiedVersion;
2464                 }
2465             }
2466 
2467 
2468             return false;
2469         }
2470 
2471         /// <summary>
2472         /// Used to avoid extra allocations from cloning AssemblyNameExtension and AssemblyName
2473         /// </summary>
CompareAssembliesIgnoringVersion(AssemblyName a, AssemblyName b)2474         private bool CompareAssembliesIgnoringVersion(AssemblyName a, AssemblyName b)
2475         {
2476             ErrorUtilities.VerifyThrowInternalNull(a, nameof(a));
2477             ErrorUtilities.VerifyThrowInternalNull(b, nameof(b));
2478 
2479             if (a == b)
2480             {
2481                 return true;
2482             }
2483 
2484             if (0 != String.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase))
2485             {
2486                 return false;
2487             }
2488 
2489             if (!AssemblyNameExtension.CompareCultures(a, b))
2490             {
2491                 return false;
2492             }
2493 
2494             if (!AssemblyNameExtension.ComparePublicKeyTokens(a.GetPublicKeyToken(), b.GetPublicKeyToken()))
2495             {
2496                 return false;
2497             }
2498 
2499             return true;
2500         }
2501 
2502         /// <summary>
2503         /// Return the resulting reference items, dependencies and other files.
2504         /// </summary>
2505         /// <param name="primaryFiles">Primary references fully resolved.</param>
2506         /// <param name="dependencyFiles">Dependent references fully resolved.</param>
2507         /// <param name="relatedFiles">Related files like .xmls and .pdbs.</param>
2508         /// <param name="satelliteFiles">Satellite files.</param>
2509         /// <param name="copyLocalFiles">All copy-local files out of primaryFiles+dependencyFiles+relatedFiles+satelliteFiles.</param>
GetReferenceItems( out ITaskItem[] primaryFiles, out ITaskItem[] dependencyFiles, out ITaskItem[] relatedFiles, out ITaskItem[] satelliteFiles, out ITaskItem[] serializationAssemblyFiles, out ITaskItem[] scatterFiles, out ITaskItem[] copyLocalFiles )2510         internal void GetReferenceItems
2511         (
2512             out ITaskItem[] primaryFiles,
2513             out ITaskItem[] dependencyFiles,
2514             out ITaskItem[] relatedFiles,
2515             out ITaskItem[] satelliteFiles,
2516             out ITaskItem[] serializationAssemblyFiles,
2517             out ITaskItem[] scatterFiles,
2518             out ITaskItem[] copyLocalFiles
2519         )
2520         {
2521             primaryFiles = Array.Empty<ITaskItem>();
2522             dependencyFiles = Array.Empty<ITaskItem>();
2523             relatedFiles = Array.Empty<ITaskItem>();
2524             satelliteFiles = Array.Empty<ITaskItem>();
2525             serializationAssemblyFiles = Array.Empty<ITaskItem>();
2526             scatterFiles = Array.Empty<ITaskItem>();
2527             copyLocalFiles = Array.Empty<ITaskItem>();
2528 
2529             ArrayList primaryItems = new ArrayList();
2530             ArrayList dependencyItems = new ArrayList();
2531             ArrayList relatedItems = new ArrayList();
2532             ArrayList satelliteItems = new ArrayList();
2533             ArrayList serializationAssemblyItems = new ArrayList();
2534             ArrayList scatterItems = new ArrayList();
2535             ArrayList copyLocalItems = new ArrayList();
2536 
2537             foreach (AssemblyNameExtension assemblyName in References.Keys)
2538             {
2539                 string fusionName = assemblyName.FullName;
2540                 Reference reference = GetReference(assemblyName);
2541 
2542                 // Conflict victims and badimages are filtered out.
2543                 if (!reference.IsBadImage)
2544                 {
2545                     reference.SetFinalCopyLocalState
2546                     (
2547                         assemblyName,
2548                         _frameworkPaths,
2549                         _targetProcessorArchitecture,
2550                         _getRuntimeVersion,
2551                         _targetedRuntimeVersion,
2552                         _fileExists,
2553                         _getAssemblyPathInGac,
2554                         _copyLocalDependenciesWhenParentReferenceInGac,
2555                         _doNotCopyLocalIfInGac,
2556                         this
2557                     );
2558 
2559                     // If mscorlib was found as a dependency and not a primary reference we will assume that mscorlib on the target machine will be ok to use.
2560                     // If mscorlib was a primary reference then we may have resolved one which is a differnt version that is on the target
2561                     // machine and we should gather it along with the other references.
2562                     if (!reference.IsPrimary && IsPseudoAssembly(assemblyName.Name))
2563                     {
2564                         continue;
2565                     }
2566 
2567                     if (reference.IsResolved)
2568                     {
2569                         ITaskItem referenceItem = SetItemMetadata(relatedItems, satelliteItems, serializationAssemblyItems, scatterItems, fusionName, reference, assemblyName, _fileExists);
2570 
2571                         if (reference.IsPrimary)
2572                         {
2573                             if (!reference.IsBadImage)
2574                             {
2575                                 // Add a primary item.
2576                                 primaryItems.Add(referenceItem);
2577                             }
2578                         }
2579                         else
2580                         {
2581                             // Add the reference item.
2582                             dependencyItems.Add(referenceItem);
2583                         }
2584                     }
2585                 }
2586             }
2587 
2588             primaryFiles = new ITaskItem[primaryItems.Count];
2589             primaryItems.CopyTo(primaryFiles, 0);
2590 
2591             dependencyFiles = (ITaskItem[])dependencyItems.ToArray(typeof(ITaskItem));
2592             relatedFiles = (ITaskItem[])relatedItems.ToArray(typeof(ITaskItem));
2593             satelliteFiles = (ITaskItem[])satelliteItems.ToArray(typeof(ITaskItem));
2594             serializationAssemblyFiles = (ITaskItem[])serializationAssemblyItems.ToArray(typeof(ITaskItem));
2595             scatterFiles = (ITaskItem[])scatterItems.ToArray(typeof(ITaskItem));
2596 
2597             // Sort for stable outputs. (These came from a hashtable, which as undefined enumeration order.)
2598             Array.Sort(primaryFiles, TaskItemSpecFilenameComparer.Comparer);
2599 
2600             // Find the copy-local items.
2601             FindCopyLocalItems(primaryFiles, copyLocalItems);
2602             FindCopyLocalItems(dependencyFiles, copyLocalItems);
2603             FindCopyLocalItems(relatedFiles, copyLocalItems);
2604             FindCopyLocalItems(satelliteFiles, copyLocalItems);
2605             FindCopyLocalItems(serializationAssemblyFiles, copyLocalItems);
2606             FindCopyLocalItems(scatterFiles, copyLocalItems);
2607             copyLocalFiles = (ITaskItem[])copyLocalItems.ToArray(typeof(ITaskItem));
2608         }
2609 
2610         /// <summary>
2611         /// Set metadata on the items which will be output from RAR.
2612         /// </summary>
SetItemMetadata(ArrayList relatedItems, ArrayList satelliteItems, ArrayList serializationAssemblyItems, ArrayList scatterItems, string fusionName, Reference reference, AssemblyNameExtension assemblyName, FileExists fileExists)2613         private ITaskItem SetItemMetadata(ArrayList relatedItems, ArrayList satelliteItems, ArrayList serializationAssemblyItems, ArrayList scatterItems, string fusionName, Reference reference, AssemblyNameExtension assemblyName, FileExists fileExists)
2614         {
2615             // Set up the main item.
2616             ITaskItem referenceItem = new TaskItem();
2617             referenceItem.ItemSpec = reference.FullPath;
2618             referenceItem.SetMetadata(ItemMetadataNames.resolvedFrom, reference.ResolvedSearchPath);
2619 
2620             // Set the CopyLocal metadata.
2621             if (reference.IsCopyLocal)
2622             {
2623                 referenceItem.SetMetadata(ItemMetadataNames.copyLocal, "true");
2624             }
2625             else
2626             {
2627                 referenceItem.SetMetadata(ItemMetadataNames.copyLocal, "false");
2628             }
2629 
2630             // Set the FusionName metadata.
2631             referenceItem.SetMetadata(ItemMetadataNames.fusionName, fusionName);
2632 
2633             // Set the Redist name metadata.
2634             if (!String.IsNullOrEmpty(reference.RedistName))
2635             {
2636                 referenceItem.SetMetadata(ItemMetadataNames.redist, reference.RedistName);
2637             }
2638 
2639             if (Reference.IsFrameworkFile(reference.FullPath, _frameworkPaths) || (_installedAssemblies != null && _installedAssemblies.FrameworkAssemblyEntryInRedist(assemblyName)))
2640             {
2641                 if (!IsAssemblyRemovedFromDotNetFramework(assemblyName, reference.FullPath, _frameworkPaths, _installedAssemblies))
2642                 {
2643                     referenceItem.SetMetadata(ItemMetadataNames.frameworkFile, "true");
2644                 }
2645             }
2646 
2647             if (!String.IsNullOrEmpty(reference.ImageRuntime))
2648             {
2649                 referenceItem.SetMetadata(ItemMetadataNames.imageRuntime, reference.ImageRuntime);
2650             }
2651 
2652             if (reference.IsWinMDFile)
2653             {
2654                 referenceItem.SetMetadata(ItemMetadataNames.winMDFile, "true");
2655 
2656                 // The ImplementationAssembly is only set if the implementation file exits on disk
2657                 if (reference.ImplementationAssembly != null)
2658                 {
2659                     if (VerifyArchitectureOfImplementationDll(reference.ImplementationAssembly, reference.FullPath))
2660                     {
2661                         referenceItem.SetMetadata(ItemMetadataNames.winmdImplmentationFile, Path.GetFileName(reference.ImplementationAssembly));
2662 
2663                         // Add the implementation item as a related file
2664                         ITaskItem item = new TaskItem(reference.ImplementationAssembly);
2665                         // Clone metadata.
2666                         referenceItem.CopyMetadataTo(item);
2667                         // Related files don't have a fusion name.
2668                         item.SetMetadata(ItemMetadataNames.fusionName, "");
2669                         RemoveNonForwardableMetadata(item);
2670 
2671                         // Add the related item.
2672                         relatedItems.Add(item);
2673                     }
2674                 }
2675 
2676                 if (reference.IsManagedWinMDFile)
2677                 {
2678                     referenceItem.SetMetadata(ItemMetadataNames.winMDFileType, "Managed");
2679                 }
2680                 else
2681                 {
2682                     referenceItem.SetMetadata(ItemMetadataNames.winMDFileType, "Native");
2683                 }
2684             }
2685 
2686             // Set the IsRedistRoot metadata
2687             if (reference.IsRedistRoot == true)
2688             {
2689                 referenceItem.SetMetadata(ItemMetadataNames.isRedistRoot, "true");
2690             }
2691             else if (reference.IsRedistRoot == false)
2692             {
2693                 referenceItem.SetMetadata(ItemMetadataNames.isRedistRoot, "false");
2694             }
2695             else
2696             {
2697                 // This happens when the redist root is "null". This means there
2698                 // was no IsRedistRoot flag in the Redist XML (or there was no
2699                 // redist XML at all for this item).
2700             }
2701 
2702             // If there was a primary source item, then forward metadata from it.
2703             // It's important that the metadata from the primary source item
2704             // win over the same metadata from other source items, so that's
2705             // why we put this first.  (CopyMetadataTo will never override an
2706             // already existing metadata.)  For example, if this reference actually
2707             // came directly from an item declared in the project file, we'd
2708             // want to use the metadata from it, not some other random item in
2709             // the project file that happened to have this reference as a dependency.
2710             if (reference.PrimarySourceItem != null)
2711             {
2712                 reference.PrimarySourceItem.CopyMetadataTo(referenceItem);
2713             }
2714             else
2715             {
2716                 bool hasImplementationFile = referenceItem.GetMetadata(ItemMetadataNames.winmdImplmentationFile).Length > 0;
2717                 bool hasImageRuntime = referenceItem.GetMetadata(ItemMetadataNames.imageRuntime).Length > 0;
2718                 bool hasWinMDFile = referenceItem.GetMetadata(ItemMetadataNames.winMDFile).Length > 0;
2719 
2720                 // If there were non-primary source items, then forward metadata from them.
2721                 ICollection sourceItems = reference.GetSourceItems();
2722                 foreach (ITaskItem sourceItem in sourceItems)
2723                 {
2724                     sourceItem.CopyMetadataTo(referenceItem);
2725                 }
2726 
2727                 // If the item originally did not have the implementation file metadata then we do not want to get it from the set of primary source items
2728                 // since the implementation file is something specific to the source item and not supposed to be propigated.
2729                 if (!hasImplementationFile)
2730                 {
2731                     referenceItem.RemoveMetadata(ItemMetadataNames.winmdImplmentationFile);
2732                 }
2733 
2734                 // If the item originally did not have the ImageRuntime metadata then we do not want to get it from the set of primary source items
2735                 // since the ImageRuntime is something specific to the source item and not supposed to be propigated.
2736                 if (!hasImageRuntime)
2737                 {
2738                     referenceItem.RemoveMetadata(ItemMetadataNames.imageRuntime);
2739                 }
2740 
2741                 // If the item originally did not have the WinMDFile metadata then we do not want to get it from the set of primary source items
2742                 // since the WinMDFile is something specific to the source item and not supposed to be propigated
2743                 if (!hasWinMDFile)
2744                 {
2745                     referenceItem.RemoveMetadata(ItemMetadataNames.winMDFile);
2746                 }
2747             }
2748 
2749             if (reference.ReferenceVersion != null)
2750             {
2751                 referenceItem.SetMetadata(ItemMetadataNames.version, reference.ReferenceVersion.ToString());
2752             }
2753             else
2754             {
2755                 referenceItem.SetMetadata(ItemMetadataNames.version, String.Empty);
2756             }
2757 
2758             // Now clone all properties onto the related files.
2759             foreach (string relatedFileExtension in reference.GetRelatedFileExtensions())
2760             {
2761                 ITaskItem item = new TaskItem(reference.FullPathWithoutExtension + relatedFileExtension);
2762                 // Clone metadata.
2763                 referenceItem.CopyMetadataTo(item);
2764                 // Related files don't have a fusion name.
2765                 item.SetMetadata(ItemMetadataNames.fusionName, "");
2766                 RemoveNonForwardableMetadata(item);
2767 
2768                 // Add the related item.
2769                 relatedItems.Add(item);
2770             }
2771 
2772             // Set up the satellites.
2773             foreach (string satelliteFile in reference.GetSatelliteFiles())
2774             {
2775                 ITaskItem item = new TaskItem(Path.Combine(reference.DirectoryName, satelliteFile));
2776                 // Clone metadata.
2777                 referenceItem.CopyMetadataTo(item);
2778                 // Set the destination directory.
2779                 item.SetMetadata(ItemMetadataNames.destinationSubDirectory, FileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(satelliteFile)));
2780                 // Satellite files don't have a fusion name.
2781                 item.SetMetadata(ItemMetadataNames.fusionName, "");
2782                 RemoveNonForwardableMetadata(item);
2783 
2784                 // Add the satellite item.
2785                 satelliteItems.Add(item);
2786             }
2787 
2788             // Set up the serialization assemblies
2789             foreach (string serializationAssemblyFile in reference.GetSerializationAssemblyFiles())
2790             {
2791                 ITaskItem item = new TaskItem(Path.Combine(reference.DirectoryName, serializationAssemblyFile));
2792                 // Clone metadata.
2793                 referenceItem.CopyMetadataTo(item);
2794                 // serialization assemblies files don't have a fusion name.
2795                 item.SetMetadata(ItemMetadataNames.fusionName, "");
2796                 RemoveNonForwardableMetadata(item);
2797 
2798                 // Add the serialization assembly item.
2799                 serializationAssemblyItems.Add(item);
2800             }
2801 
2802             // Set up the scatter files.
2803             foreach (string scatterFile in reference.GetScatterFiles())
2804             {
2805                 ITaskItem item = new TaskItem(Path.Combine(reference.DirectoryName, scatterFile));
2806                 // Clone metadata.
2807                 referenceItem.CopyMetadataTo(item);
2808                 // We don't have a fusion name for scatter files.
2809                 item.SetMetadata(ItemMetadataNames.fusionName, "");
2810                 RemoveNonForwardableMetadata(item);
2811 
2812                 // Add the satellite item.
2813                 scatterItems.Add(item);
2814             }
2815 
2816             // As long as the item has not come from somewhere else say it came from rar (p2p's can come from somewhere else).
2817             if (referenceItem.GetMetadata(ItemMetadataNames.msbuildReferenceSourceTarget).Length == 0)
2818             {
2819                 referenceItem.SetMetadata(ItemMetadataNames.msbuildReferenceSourceTarget, "ResolveAssemblyReference");
2820             }
2821 
2822             if (referenceItem.GetMetadata(ItemMetadataNames.msbuildReferenceSourceTarget).Equals("ProjectReference"))
2823             {
2824                 if (reference.PrimarySourceItem != null)
2825                 {
2826                     referenceItem.SetMetadata(ItemMetadataNames.projectReferenceOriginalItemSpec, reference.PrimarySourceItem.GetMetadata("OriginalItemSpec"));
2827                 }
2828             }
2829 
2830             return referenceItem;
2831         }
2832 
2833         /// <summary>
2834         /// Verify that the implementation dll has a matching architecture to what the project is targeting.
2835         /// </summary>
VerifyArchitectureOfImplementationDll(string dllPath, string winmdFile)2836         private bool VerifyArchitectureOfImplementationDll(string dllPath, string winmdFile)
2837         {
2838             try
2839             {
2840                 UInt16 machineType = _readMachineTypeFromPEHeader(dllPath);
2841                 SystemProcessorArchitecture dllArchitecture = SystemProcessorArchitecture.None;
2842 
2843                 if (machineType == NativeMethods.IMAGE_FILE_MACHINE_INVALID)
2844                 {
2845                     throw new BadImageFormatException(ResourceUtilities.GetResourceString("ResolveAssemblyReference.ImplementationDllHasInvalidPEHeader"));
2846                 }
2847 
2848                 switch (machineType)
2849                 {
2850                     case NativeMethods.IMAGE_FILE_MACHINE_AMD64:
2851                         dllArchitecture = SystemProcessorArchitecture.Amd64;
2852                         break;
2853                     case NativeMethods.IMAGE_FILE_MACHINE_ARM:
2854                     case NativeMethods.IMAGE_FILE_MACHINE_ARMV7:
2855                         dllArchitecture = SystemProcessorArchitecture.Arm;
2856                         break;
2857                     case NativeMethods.IMAGE_FILE_MACHINE_I386:
2858                         dllArchitecture = SystemProcessorArchitecture.X86;
2859                         break;
2860                     case NativeMethods.IMAGE_FILE_MACHINE_IA64:
2861                         dllArchitecture = SystemProcessorArchitecture.IA64;
2862                         break;
2863                     case NativeMethods.IMAGE_FILE_MACHINE_UNKNOWN:
2864                         dllArchitecture = SystemProcessorArchitecture.None;
2865                         break;
2866                     default:
2867                         if (_warnOrErrorOnTargetArchitectureMismatch == WarnOrErrorOnTargetArchitectureMismatchBehavior.Error)
2868                         {
2869                             _log.LogErrorWithCodeFromResources("ResolveAssemblyReference.UnknownProcessorArchitecture", dllPath, winmdFile, machineType.ToString("X", CultureInfo.InvariantCulture));
2870                             return false;
2871                         }
2872                         else if (_warnOrErrorOnTargetArchitectureMismatch == WarnOrErrorOnTargetArchitectureMismatchBehavior.Warning)
2873                         {
2874                             _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.UnknownProcessorArchitecture", dllPath, winmdFile, machineType.ToString("X", CultureInfo.InvariantCulture));
2875                             return true;
2876                         }
2877                         break;
2878                 }
2879 
2880                 // If the assembly is MSIL or none it can work anywhere so there does not need to be any warning ect.
2881                 if (dllArchitecture == SystemProcessorArchitecture.MSIL || dllArchitecture == SystemProcessorArchitecture.None)
2882                 {
2883                     return true;
2884                 }
2885 
2886                 if (_targetProcessorArchitecture != dllArchitecture)
2887                 {
2888                     if (_warnOrErrorOnTargetArchitectureMismatch == WarnOrErrorOnTargetArchitectureMismatchBehavior.Error)
2889                     {
2890                         _log.LogErrorWithCodeFromResources("ResolveAssemblyReference.MismatchBetweenTargetedAndReferencedArchOfImplementation", ResolveAssemblyReference.ProcessorArchitectureToString(_targetProcessorArchitecture), ResolveAssemblyReference.ProcessorArchitectureToString(dllArchitecture), dllPath, winmdFile);
2891                         return false;
2892                     }
2893                     else if (_warnOrErrorOnTargetArchitectureMismatch == WarnOrErrorOnTargetArchitectureMismatchBehavior.Warning)
2894                     {
2895                         _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.MismatchBetweenTargetedAndReferencedArchOfImplementation", ResolveAssemblyReference.ProcessorArchitectureToString(_targetProcessorArchitecture), ResolveAssemblyReference.ProcessorArchitectureToString(dllArchitecture), dllPath, winmdFile);
2896                     }
2897                 }
2898 
2899                 return true;
2900             }
2901             catch (Exception e)
2902             {
2903                 if (ExceptionHandling.IsCriticalException(e))
2904                 {
2905                     throw;
2906                 }
2907 
2908                 _log.LogErrorWithCodeFromResources("ResolveAssemblyReference.ProblemReadingImplementationDll", dllPath, e.Message);
2909                 return false;
2910             }
2911         }
2912 
2913         /// <summary>
2914         /// Read the PE header to get the machine type
2915         /// </summary>
ReadMachineTypeFromPEHeader(string dllPath)2916         internal static UInt16 ReadMachineTypeFromPEHeader(string dllPath)
2917         {
2918             /*
2919              At location 0x3c, the stub has the file offset to the PE signature. This information enables Windows to properly execute the image file, even though it has an MS DOS stub. This file offset is placed at location 0x3c during linking.
2920             * After the MS DOS stub, at the file offset specified at offset 0x3c, is a 4-byte signature that identifies the file as a PE format image file. This signature is "PE\0\0" (the letters "P" and "E" followed by two null bytes).
2921             * At the beginning of an object file, or immediately after the signature of an image file, is a standard COFF file header in the following format. Note that the Windows loader limits the number of sections to 96.
2922             Offset
2923                     Size	Field	Description
2924                     0	2	Machine	The number that identifies the type of target machine. For more information, see section 3.3.1, "Machine Types."
2925 
2926                     IMAGE_FILE_MACHINE_UNKNOWN	0x0	The contents of this field are assumed to be applicable to any machine type
2927                     IMAGE_FILE_MACHINE_AMD64	0x8664	x64
2928                     IMAGE_FILE_MACHINE_ARM	0x1c0	ARM little endian
2929                     IMAGE_FILE_MACHINE_I386	0x14c	Intel 386 or later processors and compatible processors
2930                     IMAGE_FILE_MACHINE_IA64	0x200	Intel Itanium processor family
2931             * */
2932 
2933             UInt16 machineType = NativeMethods.IMAGE_FILE_MACHINE_INVALID;
2934             using (FileStream implementationStream = new FileStream(dllPath, FileMode.Open, FileAccess.Read))
2935             {
2936                 // Seek to location that contains PE offset.
2937                 implementationStream.Seek(PEOFFSET, SeekOrigin.Begin);
2938 
2939                 using (BinaryReader reader = new BinaryReader(implementationStream))
2940                 {
2941                     // Read the offset to the PE header
2942                     Int32 offSet = reader.ReadInt32();
2943                     implementationStream.Seek(offSet, SeekOrigin.Begin);
2944 
2945                     // Read the PE header should be PE\0\0
2946                     UInt32 peHeader = reader.ReadUInt32();
2947                     if (peHeader == PEHEADER)
2948                     {
2949                         machineType = reader.ReadUInt16();
2950                     }
2951                 }
2952             }
2953 
2954             return machineType;
2955         }
2956 
2957         /// <summary>
2958         /// Some metadata should not be forwarded between the parent and child items.
2959         /// </summary>
RemoveNonForwardableMetadata(ITaskItem item)2960         private static void RemoveNonForwardableMetadata(ITaskItem item)
2961         {
2962             item.RemoveMetadata(ItemMetadataNames.winmdImplmentationFile);
2963             item.RemoveMetadata(ItemMetadataNames.imageRuntime);
2964             item.RemoveMetadata(ItemMetadataNames.winMDFile);
2965         }
2966 
2967 
2968         /// <summary>
2969         /// Given a list of items, find all that have CopyLocal==true and add it to the list.
2970         /// </summary>
2971         /// <param name="items"></param>
2972         /// <param name="copyLocalItems"></param>
FindCopyLocalItems(ITaskItem[] items, ArrayList copyLocalItems)2973         private static void FindCopyLocalItems(ITaskItem[] items, ArrayList copyLocalItems)
2974         {
2975             foreach (ITaskItem i in items)
2976             {
2977                 bool found;
2978                 bool copyLocal = MetadataConversionUtilities.TryConvertItemMetadataToBool
2979                     (
2980                         i,
2981                         ItemMetadataNames.copyLocal,
2982                         out found
2983                     );
2984 
2985 
2986                 if (found && copyLocal)
2987                 {
2988                     copyLocalItems.Add(i);
2989                 }
2990             }
2991         }
2992 
2993         #region ExclusionList LoggingMessage helpers
2994 
2995         /// <summary>
2996         /// The reference was determined to have a version which is higher than what is in the currently targeted redist list.
2997         /// </summary>
LogHigherVersionUnresolve(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)2998         internal void LogHigherVersionUnresolve(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)
2999         {
3000             if (displayPrimaryReferenceMessage)
3001             {
3002                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.PrimaryReferenceOutsideOfFramework", reference.PrimarySourceItem.ItemSpec /* primary item spec*/, reference.ReferenceVersion /*Version of dependent assemby*/, reference.ExclusionListLoggingProperties.HighestVersionInRedist /*Version found in redist*/);
3003             }
3004             else
3005             {
3006                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.DependencyReferenceOutsideOfFramework", referenceItem.ItemSpec /* primary item spec*/, assemblyName.FullName /*Dependent assemblyName*/, reference.ReferenceVersion /*Version of dependent assemby*/, reference.ExclusionListLoggingProperties.HighestVersionInRedist /*Version found in redist*/);
3007             }
3008         }
3009 
3010         /// <summary>
3011         /// The reference was determined to have a version which is higher than what is in the currently targeted using the framework attribute.
3012         /// </summary>
LogHigherVersionUnresolveDueToAttribute(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)3013         internal void LogHigherVersionUnresolveDueToAttribute(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)
3014         {
3015             if (displayPrimaryReferenceMessage)
3016             {
3017                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.PrimaryReferenceOutsideOfFrameworkUsingAttribute", reference.PrimarySourceItem.ItemSpec /* primary item spec*/, reference.FrameworkNameAttribute /*Version of dependent assemby*/, targetedFramework);
3018             }
3019             else
3020             {
3021                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.DependencyReferenceOutsideOfFrameworkUsingAttribute", referenceItem.ItemSpec /* primary item spec*/, assemblyName.FullName /*Dependent assemblyName*/, reference.FrameworkNameAttribute, targetedFramework);
3022             }
3023         }
3024 
3025         /// <summary>
3026         /// The reference was determined to not be in the current redist list but in fact are from another framework.
3027         /// </summary>
LogAnotherFrameworkUnResolve(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)3028         internal void LogAnotherFrameworkUnResolve(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)
3029         {
3030             if (displayPrimaryReferenceMessage)
3031             {
3032                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.PrimaryReferenceInAnotherFramework", reference.PrimarySourceItem.ItemSpec /* primary item spec*/, targetedFramework);
3033             }
3034             else
3035             {
3036                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.DependencyReferenceInAnotherFramework", referenceItem.ItemSpec /* primary item spec*/, assemblyName.FullName /*Dependent assemblyName*/, targetedFramework);
3037             }
3038         }
3039 
3040         /// <summary>
3041         /// The reference was found to be resolved from a full framework while we are actually targeting a profile.
3042         /// </summary>
LogProfileExclusionUnresolve(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)3043         internal void LogProfileExclusionUnresolve(bool displayPrimaryReferenceMessage, AssemblyNameExtension assemblyName, Reference reference, ITaskItem referenceItem, string targetedFramework)
3044         {
3045             if (displayPrimaryReferenceMessage)
3046             {
3047                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.FailedToResolveReferenceBecausePrimaryAssemblyInExclusionList", reference.PrimarySourceItem.ItemSpec, targetedFramework);
3048             }
3049             else
3050             {
3051                 _log.LogWarningWithCodeFromResources("ResolveAssemblyReference.FailBecauseDependentAssemblyInExclusionList", referenceItem.ItemSpec, assemblyName.FullName, targetedFramework);
3052             }
3053         }
3054         #endregion
3055 
3056         #region Helper structures
3057 
3058         /// <summary>
3059         ///  Provide a class which has a key value pair for references and their assemblyNameExtensions.
3060         ///  This is used to prevent JIT'ing when using a generic list.
3061         /// </summary>
3062         internal struct ReferenceAssemblyExtensionPair
3063         {
3064             private Reference _assemblyKey;
3065             private AssemblyNameExtension _assemblyValue;
3066 
ReferenceAssemblyExtensionPairMicrosoft.Build.Tasks.ReferenceTable.ReferenceAssemblyExtensionPair3067             internal ReferenceAssemblyExtensionPair(Reference key, AssemblyNameExtension value)
3068             {
3069                 _assemblyKey = key;
3070                 _assemblyValue = value;
3071             }
3072 
3073             internal Reference Key
3074             {
3075                 get { return _assemblyKey; }
3076             }
3077 
3078             internal AssemblyNameExtension Value
3079             {
3080                 get { return _assemblyValue; }
3081             }
3082         }
3083 
3084         #endregion
3085 
3086         /// <summary>
3087         /// Rather than have exclusion lists float around, we may as well just mark the reference themselves. This allows us to attach to a reference
3088         /// whether or not it is excluded and why.  This method will do a number of checks in a specific order and mark the reference as being excluded or not.
3089         /// </summary>
MarkReferencesForExclusion(Hashtable exclusionList)3090         internal bool MarkReferencesForExclusion(Hashtable exclusionList)
3091         {
3092             bool anyMarkedReference = false;
3093             _listOfExcludedAssemblies = new List<string>();
3094 
3095             foreach (AssemblyNameExtension assemblyName in References.Keys)
3096             {
3097                 string assemblyFullName = assemblyName.FullName;
3098                 Reference reference = GetReference(assemblyName);
3099                 reference.ReferenceVersion = assemblyName.Version;
3100 
3101                 MarkReferenceWithHighestVersionInCurrentRedistList(assemblyName, reference);
3102 
3103                 // If CheckForSpecificVersionMetadataOnParentsReference is passed true then we will return true if any parent primary reference has the specific
3104                 // version metadata set to true,
3105                 // If false is passed in we will return true ONLY if all parent primary references have the metadata set to true.
3106                 if (!reference.CheckForSpecificVersionMetadataOnParentsReference(false))
3107                 {
3108                     // Check to see if the reference is not in a profile or subset
3109                     if (exclusionList != null)
3110                     {
3111                         if (exclusionList.ContainsKey(assemblyFullName))
3112                         {
3113                             anyMarkedReference = true;
3114                             reference.ExclusionListLoggingProperties.ExclusionReasonLogDelegate = new LogExclusionReason(LogProfileExclusionUnresolve);
3115                             reference.ExclusionListLoggingProperties.IsInExclusionList = true;
3116                             _listOfExcludedAssemblies.Add(assemblyFullName);
3117                         }
3118                     }
3119 
3120                     // Check to see if the reference is in the current target framework but has a higher version than what exists in the target framework
3121                     if (!reference.ExclusionListLoggingProperties.IsInExclusionList)
3122                     {
3123                         if (MarkReferenceForExclusionDueToHigherThanCurrentFramework(assemblyName, reference))
3124                         {
3125                             anyMarkedReference = true;
3126                             _listOfExcludedAssemblies.Add(assemblyFullName);
3127                         }
3128                     }
3129 
3130                     // Check to see if the reference came from the GAC or AssemblyFolders and is in the highest redist list on the machine for the targeted framework identifier.
3131                     if (!reference.ExclusionListLoggingProperties.IsInExclusionList)
3132                     {
3133                         if (MarkReferencesExcludedDueToOtherFramework(assemblyName, reference))
3134                         {
3135                             anyMarkedReference = true;
3136                             _listOfExcludedAssemblies.Add(assemblyFullName);
3137                         }
3138                     }
3139 
3140                     // Check to see if the reference is built against a compatible framework
3141                     if (!reference.ExclusionListLoggingProperties.IsInExclusionList)
3142                     {
3143                         if (!_ignoreFrameworkAttributeVersionMismatch && MarkReferenceForExclusionDueToHigherThanCurrentFrameworkAttribute(assemblyName, reference))
3144                         {
3145                             anyMarkedReference = true;
3146                             _listOfExcludedAssemblies.Add(assemblyFullName);
3147                         }
3148                     }
3149                 }
3150             }
3151 
3152             return anyMarkedReference;
3153         }
3154     }
3155 }
3156