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.IO;
6 using System.Collections;
7 using System.Reflection;
8 using System.Diagnostics;
9 using System.Collections.Generic;
10 using Microsoft.Build.Shared;
11 using Microsoft.Build.Framework;
12 using System.Runtime.Versioning;
13 
14 namespace Microsoft.Build.Tasks
15 {
16     /// <summary>
17     /// A reference to an assembly along with information about resolution.
18     /// </summary>
19     sealed internal class Reference
20     {
21         /// <summary>
22         /// Hashtable where ITaskItem.ItemSpec (a string) is the key and ITaskItem is the value.
23         /// A hash table is used to remove duplicates.
24         /// All source items that inspired this reference (possibly indirectly through a dependency chain).
25         /// </summary>
26         private Hashtable _sourceItems = new Hashtable(StringComparer.OrdinalIgnoreCase);
27 
28         /// <summary>
29         /// Hashtable of Key=Reference, Value=Irrelevent.
30         /// A list of unique dependies.
31         /// </summary>
32         private Hashtable _dependees = new Hashtable();
33 
34         /// <summary>
35         /// Hashset of Reference which depend on this reference
36         /// A list of unique dependencies.
37         /// </summary>
38         private HashSet<Reference> _dependencies = new HashSet<Reference>();
39 
40         /// <summary>
41         /// Scatter files associated with this reference.
42         /// </summary>
43         private string[] _scatterFiles = Array.Empty<string>();
44 
45         /// <summary>
46         /// ArrayList of Exception.
47         /// Any errors that occurred while resolving or finding dependencies on this item.
48         /// </summary>
49         private ArrayList _errors = new ArrayList();
50 
51         /// <summary>
52         /// ArrayList of string.
53         /// Contains any file extension that are related to this file. Pdbs and xmls are related.
54         /// This is an extension string starting with "."
55         /// </summary>
56         private ArrayList _relatedFileExtensions = new ArrayList();
57 
58         /// <summary>
59         /// ArrayList of string.
60         /// Contains satellite files for this reference.
61         /// This file path is relative to the location of the reference.
62         /// </summary>
63         private ArrayList _satelliteFiles = new ArrayList();
64 
65         /// <summary>
66         /// ArrayList of string.
67         /// Contains serializaion assembly files for this reference.
68         /// This file path is relative to the location of the reference.
69         /// </summary>
70         private ArrayList _serializationAssemblyFiles = new ArrayList();
71 
72         /// <summary>
73         /// The list of assemblies that were consider for matching but that
74         /// didn't pan out because they didn't match exactly.
75         /// </summary>
76         private ArrayList _assembliesConsideredAndRejected = new ArrayList();
77 
78         /// <summary>
79         /// The searchpath location that the reference was found at.
80         /// </summary>
81         private string _resolvedSearchPath = String.Empty;
82 
83         /// <summary>
84         /// This is the reference that won against this reference in the conflict contest.
85         /// </summary>
86         private AssemblyNameExtension _conflictVictorName = null;
87 
88         /// <summary>
89         /// The reason this reference lost
90         /// </summary>
91         private ConflictLossReason _conflictLossReason = ConflictLossReason.DidntLose;
92 
93         /// <summary>
94         /// AssemblyNames of references that lost collision conflicts with this reference.
95         /// </summary>
96         private ArrayList _conflictVictims = new ArrayList();
97 
98         /// <summary>
99         /// These are the versions (type UnificationVersion) that were unified from.
100         /// </summary>
101         private Dictionary<string, UnificationVersion> _preUnificationVersions = new Dictionary<string, UnificationVersion>(StringComparer.OrdinalIgnoreCase);
102 
103         /// <summary>
104         /// If 'true' then the path that this item points to is known to be a bad image.
105         /// This item shouldn't be passed to compilers and so forth.
106         /// </summary>
107         private bool _isBadImage = false;
108 
109         /// <summary>
110         /// The original source item, as passed into the task that is directly associated
111         /// with this reference.  This only applies to "primary" references.
112         /// </summary>
113         private ITaskItem _primarySourceItem;
114 
115         /// <summary>
116         /// The full path to the assembly. If this is "", then that means that this reference
117         /// has not been resolved.
118         /// </summary>
119         private string _fullPath = String.Empty;
120 
121         /// <summary>
122         /// The directory that this reference lives in.
123         /// </summary>
124         private string _directoryName = String.Empty;
125 
126         /// <summary>
127         /// The reference's filename without extension.
128         /// </summary>
129         private string _fileNameWithoutExtension = String.Empty;
130 
131         /// <summary>
132         /// The full path to the file name but without the extension.
133         /// </summary>
134         private string _fullPathWithoutExtension = String.Empty;
135 
136         /// <summary>
137         /// Whether this assembly came from the project. If 'false' then this reference was deduced
138         /// through the reference resolution process.
139         /// </summary>
140         private bool _isPrimary = false;
141 
142         /// <summary>
143         /// Whether or not this reference will be installed on the target machine.
144         /// </summary>
145         private bool _isPrerequisite = false;
146 
147         /// <summary>
148         /// Whether or not this reference is a redist root.
149         /// </summary>
150         private bool? _isRedistRoot = null;
151 
152         /// <summary>
153         /// The redist name for this reference (if any).
154         /// </summary>
155         private string _redistName = null;
156 
157         /// <summary>
158         /// Whether this reference should be copied to the local 'bin' dir or not and the reason this flag
159         /// was set that way.
160         /// </summary>
161         private CopyLocalState _copyLocalState = CopyLocalState.Undecided;
162 
163         /// <summary>
164         /// Whether or not we still need to find dependencies for this reference.
165         /// </summary>
166         private bool _dependenciesFound = false;
167 
168         /// <summary>
169         /// This is the HintPath from the source item. This is used to resolve the assembly.
170         /// </summary>
171         private string _hintPath = "";
172 
173         /// <summary>
174         /// The list of expected extensions.
175         /// </summary>
176         private ArrayList _expectedExtensions = null;
177 
178         /// <summary>
179         /// Whether or not the exact specific version is required.
180         /// Note that simple names like "MySimpleAssemblyName" will need to match exactly.
181         /// That is, no version that has other information will be accepted.
182         /// </summary>
183         private bool _wantSpecificVersion = true;
184 
185         /// <summary>
186         /// Whether or not the types from this reference need to be embedded into the target assembly
187         /// </summary>
188         private bool _embedInteropTypes = false;
189 
190         /// <summary>
191         /// This is the key that was passed in to the reference through the &lt;AssemblyFolderKey&gt; metadata.
192         /// </summary>
193         private string _assemblyFolderKey = String.Empty;
194 
195         /// <summary>
196         /// This will be true if the user requested a specific file. We know this when the file was resolved
197         /// by hintpath or if it was resolve as a raw file name for example.
198         /// </summary>
199         private bool _userRequestedSpecificFile = false;
200 
201         /// <summary>
202         /// Version of the references
203         /// </summary>
204         private Version _referenceVersion = null;
205 
206         /// <summary>
207         /// A set of properties which are useful to log the correct information for why this reference was not resolved.
208         /// </summary>
209         private ExclusionListProperties _exclusionListProperties = new ExclusionListProperties();
210 
211         /// <summary>
212         /// Is the reference a native winMD file. This means it has a image runtime of WindowsRuntime and not CLR.
213         /// </summary>
214         private bool _winMDFile;
215 
216         /// <summary>
217         ///  Is the file a managed winmd file. That means it has both windows runtime and CLR in the imageruntime string.
218         /// </summary>
219         private bool _isManagedWinMDFile;
220 
221         /// <summary>
222         /// The imageruntime version for this reference.
223         /// </summary>
224         private string _imageRuntimeVersion;
225 
226 
227         /// <summary>
228         /// If the reference has an SDK name metadata this will contain that string.
229         /// </summary>
230         private string _sdkName = String.Empty;
231 
232         /// <summary>
233         /// Set containing the names the reference was remapped from
234         /// </summary>
235         private HashSet<AssemblyRemapping> _remappedAssemblyNames = new HashSet<AssemblyRemapping>();
236 
237         /// <summary>
238         /// Delegate to determine if the file is a winmd file or not
239         /// </summary>
240         private IsWinMDFile _isWinMDFile;
241 
242         /// <summary>
243         /// Delegate to check to see if the file exists on disk
244         /// </summary>
245         private FileExists _fileExists;
246 
247         /// <summary>
248         /// Delegate to get the imageruntime version from a file.
249         /// </summary>
250         private GetAssemblyRuntimeVersion _getRuntimeVersion;
251 
252         /// <summary>
253         /// The frameworkName the reference was built against
254         /// </summary>
255         private FrameworkName _frameworkName;
256 
Reference(IsWinMDFile isWinMDFile, FileExists fileExists, GetAssemblyRuntimeVersion getRuntimeVersion)257         internal Reference(IsWinMDFile isWinMDFile, FileExists fileExists, GetAssemblyRuntimeVersion getRuntimeVersion)
258         {
259             _isWinMDFile = isWinMDFile;
260             _fileExists = fileExists;
261             _getRuntimeVersion = getRuntimeVersion;
262         }
263 
264         /// <summary>
265         /// Add items that caused (possibly indirectly through a dependency chain) this Reference.
266         /// </summary>
AddSourceItem(ITaskItem sourceItem)267         internal void AddSourceItem(ITaskItem sourceItem)
268         {
269             bool sourceItemAlreadyInList = _sourceItems.Contains(sourceItem.ItemSpec);
270             if (!sourceItemAlreadyInList)
271             {
272                 _sourceItems[sourceItem.ItemSpec] = sourceItem;
273                 PropagateSourceItems(sourceItem);
274             }
275         }
276 
277         /// <summary>
278         /// Add items that caused (possibly indirectly through a dependency chain) this Reference.
279         /// </summary>
AddSourceItems(IEnumerable sourceItemsToAdd)280         internal void AddSourceItems(IEnumerable sourceItemsToAdd)
281         {
282             foreach (ITaskItem sourceItem in sourceItemsToAdd)
283             {
284                 AddSourceItem(sourceItem);
285             }
286         }
287 
288 
289         /// <summary>
290         /// We have had our source item list updated, we need to propagate this change to any of our dependencies so they have the new information.
291         /// </summary>
PropagateSourceItems(ITaskItem sourceItem)292         internal void PropagateSourceItems(ITaskItem sourceItem)
293         {
294             if (_dependencies != null)
295             {
296                 foreach (Reference dependency in _dependencies)
297                 {
298                     dependency.AddSourceItem(sourceItem);
299                 }
300             }
301         }
302 
303         /// <summary>
304         /// Get the source items for this reference.
305         ///  This is collection of ITaskItems.
306         /// </summary>
GetSourceItems()307         internal ICollection GetSourceItems()
308         {
309             return (ICollection)_sourceItems.Values;
310         }
311 
312         /// <summary>
313         /// Add a reference which this reference depends on
314         /// </summary>
AddDependency(Reference dependency)315         internal void AddDependency(Reference dependency)
316         {
317             if (!_dependencies.Contains(dependency))
318             {
319                 _dependencies.Add(dependency);
320             }
321         }
322 
323         /// <summary>
324         /// Add a reference that caused (possibly indirectly through a dependency chain) this Reference.
325         /// </summary>
AddDependee(Reference dependee)326         internal void AddDependee(Reference dependee)
327         {
328             Debug.Assert(dependee.FullPath.Length > 0, "Cannot add dependee that doesn't have a full name. This should have already been resolved.");
329 
330             dependee.AddDependency(this);
331 
332             if (_dependees[dependee] == null)
333             {
334                 _dependees[dependee] = String.Empty;
335 
336                 // When a new dependee is added, this is a new place where a reference might be resolved.
337                 // Reset this item so it will be re-resolved if possible.
338                 if (IsUnresolvable)
339                 {
340                     _errors = new ArrayList();
341                     _assembliesConsideredAndRejected = new ArrayList();
342                 }
343             }
344         }
345 
346         /// <summary>
347         /// A dependee may be removed because it or its dependee's are in the black list
348         /// </summary>
349         /// <param name="dependeeToRemove"></param>
RemoveDependee(Reference dependeeToRemove)350         internal void RemoveDependee(Reference dependeeToRemove)
351         {
352             _dependees.Remove(dependeeToRemove);
353         }
354 
355         /// <summary>
356         /// A dependency may be removed because it may not be referenced any more due this reference being in the black list or being removed due to it depending on something in the black list
357         /// </summary>
RemoveDependency(Reference dependencyToRemove)358         internal void RemoveDependency(Reference dependencyToRemove)
359         {
360             _dependencies.Remove(dependencyToRemove);
361         }
362 
363 
364         /// <summary>
365         /// Get the dependee references for this reference.
366         ///  This is collection of References.
367         /// </summary>
GetDependees()368         internal ICollection GetDependees()
369         {
370             return (ICollection)_dependees.Keys;
371         }
372 
373         /// <summary>
374         /// Scatter files associated with this assembly.
375         /// </summary>
376         /// <value></value>
AttachScatterFiles(string[] scatterFilesToAttach)377         internal void AttachScatterFiles(string[] scatterFilesToAttach)
378         {
379             if (scatterFilesToAttach == null || scatterFilesToAttach.Length == 0)
380             {
381                 _scatterFiles = Array.Empty<string>();
382             }
383             else
384             {
385                 _scatterFiles = scatterFilesToAttach;
386             }
387         }
388 
389         /// <summary>
390         /// Scatter files associated with this assembly.
391         /// </summary>
392         /// <returns></returns>
GetScatterFiles()393         internal string[] GetScatterFiles()
394         {
395             return _scatterFiles;
396         }
397 
398         /// <summary>
399         /// Set one expected extension for this reference.
400         /// </summary>
SetExecutableExtension(string extension)401         internal void SetExecutableExtension(string extension)
402         {
403             if (_expectedExtensions == null)
404             {
405                 _expectedExtensions = new ArrayList();
406             }
407             else
408             {
409                 _expectedExtensions.Clear();
410             }
411             if (extension.Length > 0 && extension[0] != '.')
412             {
413                 extension = '.' + extension;
414             }
415             _expectedExtensions.Add(extension);
416         }
417 
418         /// <summary>
419         /// Get the list of expected extensions.
420         /// </summary>
GetExecutableExtensions(string[] allowedAssemblyExtensions)421         internal string[] GetExecutableExtensions(string[] allowedAssemblyExtensions)
422         {
423             if (_expectedExtensions == null)
424             {
425                 // Use the default.
426                 return allowedAssemblyExtensions;
427             }
428             return (string[])_expectedExtensions.ToArray(typeof(string));
429         }
430 
431         /// <summary>
432         /// Whether the name needs to match exactly or just the simple name part needs to match.
433         /// </summary>
434         /// <value></value>
435         internal bool WantSpecificVersion
436         {
437             get { return _wantSpecificVersion; }
438         }
439 
440         /// <summary>
441         /// Whether types need to be embedded into the target assembly
442         /// </summary>
443         /// <value></value>
444         internal bool EmbedInteropTypes
445         {
446             get { return _embedInteropTypes; }
447             set { _embedInteropTypes = value; }
448         }
449 
450         /// <summary>
451         /// This will be true if the user requested a specific file. We know this when the file was resolved
452         /// by hintpath or if it was resolve as a raw file name for example.
453         /// </summary>
454         internal bool UserRequestedSpecificFile
455         {
456             get { return _userRequestedSpecificFile; }
457             set { _userRequestedSpecificFile = value; }
458         }
459 
460         /// <summary>
461         /// The version number of this reference
462         /// </summary>
463         internal Version ReferenceVersion
464         {
465             get
466             {
467                 return _referenceVersion;
468             }
469 
470             set
471             {
472                 _referenceVersion = value;
473             }
474         }
475 
476         /// <summary>
477         /// True if the assembly was found to be in the GAC.
478         /// </summary>
479         internal bool? FoundInGac
480         {
481             get;
482             private set;
483         }
484 
485         /// <summary>
486         /// True if the assembly was resolved through the GAC. Otherwise, false.
487         /// </summary>
488         internal bool ResolvedFromGac
489         {
490             get
491             {
492                 return string.Equals(_resolvedSearchPath, AssemblyResolutionConstants.gacSentinel, StringComparison.OrdinalIgnoreCase);
493             }
494         }
495 
496         /// <summary>
497         /// Set of properties for this reference used to log why this reference could not be resolved.
498         /// </summary>
499         internal ExclusionListProperties ExclusionListLoggingProperties
500         {
501             get
502             {
503                 return _exclusionListProperties;
504             }
505         }
506 
507         /// <summary>
508         /// Determines if a given reference or its parent primary references have specific version metadata set to true.
509         /// If anyParentHasMetadata is set to true then we will return true if any parent primary reference has the specific version metadata set to true,
510         /// if the value is false we will return true ONLY if all parent primary references have the metadata set to true.
511         /// </summary>
CheckForSpecificVersionMetadataOnParentsReference(bool anyParentHasMetadata)512         internal bool CheckForSpecificVersionMetadataOnParentsReference(bool anyParentHasMetadata)
513         {
514             bool hasSpecificVersionMetadata = false;
515 
516             // We are our own parent, therefore the specific version metadata is what ever is passed into as wantspecificVersion for this reference.
517             // this saves us from having to read the metadata from our item again.
518             if (IsPrimary)
519             {
520                 hasSpecificVersionMetadata = _wantSpecificVersion;
521             }
522             else
523             {
524                 // Go through all of the primary items which lead to this dependency, if they all have specificVersion set to true then
525                 // hasSpecificVersionMetadata will be true. If any item has the metadata set to false or not set then the value will be false.
526                 foreach (ITaskItem item in GetSourceItems())
527                 {
528                     hasSpecificVersionMetadata = MetadataConversionUtilities.TryConvertItemMetadataToBool(item, ItemMetadataNames.specificVersion);
529 
530                     // Break if one of the primary references has specific version false or not set
531                     if (anyParentHasMetadata == hasSpecificVersionMetadata)
532                     {
533                         break;
534                     }
535                 }
536             }
537 
538             return hasSpecificVersionMetadata;
539         }
540 
541         /// <summary>
542         /// Add a dependency or resolution error to this reference's list of errors.
543         /// </summary>
544         /// <param name="e">The error.</param>
AddError(Exception e)545         internal void AddError(Exception e)
546         {
547             if (e is BadImageReferenceException)
548             {
549                 _isBadImage = true;
550             }
551             _errors.Add(e);
552         }
553 
554         /// <summary>
555         /// Return the list of dependency or resolution errors for this item.
556         /// </summary>
557         /// <returns>The collection of resolution errors.</returns>
GetErrors()558         internal ICollection GetErrors()
559         {
560             return (ICollection)_errors;
561         }
562 
563         /// <summary>
564         /// Add a new related file to this reference.
565         /// Related files always live in the same directory as the reference.
566         /// Examples include, MyAssembly.pdb and MyAssembly.xml
567         /// </summary>
568         /// <param name="filename">This is the filename extension.</param>
AddRelatedFileExtension(string filenameExtension)569         internal void AddRelatedFileExtension(string filenameExtension)
570         {
571 #if _DEBUG
572             Debug.Assert(filenameExtension[0]=='.', "Expected extension to start with '.'");
573 #endif
574             _relatedFileExtensions.Add(filenameExtension);
575         }
576 
577         /// <summary>
578         /// Return the list of related files for this item.
579         /// </summary>
580         /// <returns>The collection of related file extensions.</returns>
GetRelatedFileExtensions()581         internal ICollection GetRelatedFileExtensions()
582         {
583             return (ICollection)_relatedFileExtensions;
584         }
585 
586 
587         /// <summary>
588         /// Add a new satellite file
589         /// </summary>
590         /// <param name="filename">This is the filename relative the this reference.</param>
AddSatelliteFile(string filename)591         internal void AddSatelliteFile(string filename)
592         {
593 #if _DEBUG
594             Debug.Assert(!Path.IsPathRooted(filename), "Satellite path should be relative to the current reference.");
595 #endif
596             _satelliteFiles.Add(filename);
597         }
598 
599         /// <summary>
600         /// Add a new serialization assembly file.
601         /// </summary>
602         /// <param name="filename">This is the filename relative the this reference.</param>
AddSerializationAssemblyFile(string filename)603         internal void AddSerializationAssemblyFile(string filename)
604         {
605 #if _DEBUG
606             Debug.Assert(!Path.IsPathRooted(filename), "Serialization assembly path should be relative to the current reference.");
607 #endif
608             _serializationAssemblyFiles.Add(filename);
609         }
610 
611         /// <summary>
612         /// Return the list of satellite files for this item.
613         /// </summary>
614         /// <returns>The collection of satellit files.</returns>
GetSatelliteFiles()615         internal ICollection GetSatelliteFiles()
616         {
617             return (ICollection)_satelliteFiles;
618         }
619 
620         /// <summary>
621         /// Return the list of serialization assembly files for this item.
622         /// </summary>
623         /// <returns>The collection of serialization assembly files.</returns>
GetSerializationAssemblyFiles()624         internal ICollection GetSerializationAssemblyFiles()
625         {
626             return (ICollection)_serializationAssemblyFiles;
627         }
628 
629         /// <summary>
630         /// The full path to the assembly. If this is "", then that means that this reference
631         /// has not been resolved.
632         /// </summary>
633         /// <value>The full path to this assembly.</value>
634         internal string FullPath
635         {
636             get { return _fullPath; }
637             set
638             {
639                 if (_fullPath != value)
640                 {
641                     _fullPath = value;
642                     _fullPathWithoutExtension = null;
643                     _fileNameWithoutExtension = null;
644                     _directoryName = null;
645 
646                     if (_fullPath == null || _fullPath.Length == 0)
647                     {
648                         _scatterFiles = Array.Empty<string>();
649                         _satelliteFiles = new ArrayList();
650                         _serializationAssemblyFiles = new ArrayList();
651                         _assembliesConsideredAndRejected = new ArrayList();
652                         _resolvedSearchPath = String.Empty;
653                         _preUnificationVersions = new Dictionary<string, UnificationVersion>(StringComparer.OrdinalIgnoreCase);
654                         _isBadImage = false;
655                         _dependenciesFound = false;
656                         _userRequestedSpecificFile = false;
657                         _winMDFile = false;
658                     }
659                     else if (NativeMethodsShared.IsWindows)
660                     {
661                         _winMDFile = _isWinMDFile(_fullPath, _getRuntimeVersion, _fileExists, out _imageRuntimeVersion, out _isManagedWinMDFile);
662                     }
663                 }
664             }
665         }
666 
667         /// <summary>
668         /// The directory that this assembly lives in.
669         /// </summary>
670         /// <value></value>
671         internal string DirectoryName
672         {
673             get
674             {
675                 if ((_directoryName == null || _directoryName.Length == 0) && (_fullPath != null && _fullPath.Length != 0))
676                 {
677                     _directoryName = Path.GetDirectoryName(_fullPath);
678                     if (_directoryName.Length == 0)
679                     {
680                         _directoryName = ".";
681                     }
682                 }
683                 return _directoryName;
684             }
685         }
686 
687         /// <summary>
688         /// The file name without extension.
689         /// </summary>
690         /// <value></value>
691         internal string FileNameWithoutExtension
692         {
693             get
694             {
695                 if ((_fileNameWithoutExtension == null || _fileNameWithoutExtension.Length == 0) && (_fullPath != null && _fullPath.Length != 0))
696                 {
697                     _fileNameWithoutExtension = Path.GetFileNameWithoutExtension(_fullPath);
698                 }
699                 return _fileNameWithoutExtension;
700             }
701         }
702 
703         /// <summary>
704         /// The full path to the assembly but without an extension on the file namee
705         /// </summary>
706         /// <value></value>
707         internal string FullPathWithoutExtension
708         {
709             get
710             {
711                 if ((_fullPathWithoutExtension == null || _fullPathWithoutExtension.Length == 0) && (_fullPath != null && _fullPath.Length != 0))
712                 {
713                     _fullPathWithoutExtension = Path.Combine(DirectoryName, FileNameWithoutExtension);
714                 }
715                 return _fullPathWithoutExtension;
716             }
717         }
718 
719 
720         /// <summary>
721         /// This is the HintPath from the source item. This is used to resolve the assembly.
722         /// </summary>
723         /// <value>The hint path to this assembly.</value>
724         internal string HintPath
725         {
726             get { return _hintPath; }
727             set { _hintPath = value; }
728         }
729 
730         /// <summary>
731         /// This is the key that was passed in to the reference through the &lt;AssemblyFolderKey&gt; metadata.
732         /// </summary>
733         /// <value>The &lt;AssemblyFolderKey&gt; value.</value>
734         internal string AssemblyFolderKey
735         {
736             get { return _assemblyFolderKey; }
737             set { _assemblyFolderKey = value; }
738         }
739 
740         /// <summary>
741         /// Whether this assembly came from the project. If 'false' then this reference was deduced
742         /// through the reference resolution process.
743         /// </summary>
744         /// <value>'true' if this reference is a primary assembly.</value>
745         internal bool IsPrimary
746         {
747             get { return _isPrimary; }
748         }
749 
750         /// <summary>
751         /// Whether or not this reference will be installed on the target machine.
752         /// </summary>
753         internal bool IsPrerequisite
754         {
755             set { _isPrerequisite = value; }
756             get { return _isPrerequisite; }
757         }
758 
759         /// <summary>
760         /// Whether or not this reference is a redist root.
761         /// </summary>
762         internal bool? IsRedistRoot
763         {
764             set { _isRedistRoot = value; }
765             get { return _isRedistRoot; }
766         }
767 
768         /// <summary>
769         /// The redist name for this reference (if any)
770         /// </summary>
771         internal string RedistName
772         {
773             set { _redistName = value; }
774             get { return _redistName; }
775         }
776 
777 
778         /// <summary>
779         /// The original source item, as passed into the task that is directly associated
780         /// with this reference.  This only applies to "primary" references.
781         /// </summary>
782         internal ITaskItem PrimarySourceItem
783         {
784             get
785             {
786                 ErrorUtilities.VerifyThrow(
787                     !(_isPrimary && _primarySourceItem == null), "A primary reference must have a primary source item.");
788                 ErrorUtilities.VerifyThrow(
789                     (_isPrimary || _primarySourceItem == null), "Only a primary reference can have a primary source item.");
790 
791                 return _primarySourceItem;
792             }
793         }
794 
795         /// <summary>
796         /// If 'true' then the path that this item points to is known to be a bad image.
797         /// This item shouldn't be passed to compilers and so forth.
798         /// </summary>
799         /// <value>'true' if this reference points to a bad image.</value>
800         internal bool IsBadImage
801         {
802             get { return _isBadImage; }
803         }
804 
805         /// <summary>
806         ///  If true, then this item conflicted with another item and lost.
807         /// </summary>
808         internal bool IsConflictVictim
809         {
810             get
811             {
812                 return ConflictVictorName != null;
813             }
814         }
815 
816         /// <summary>
817         /// Add a conflict victim to this reference
818         /// </summary>
819         /// <param name="victim"></param>
AddConflictVictim(AssemblyNameExtension victim)820         internal void AddConflictVictim(AssemblyNameExtension victim)
821         {
822             _conflictVictims.Add(victim);
823         }
824 
825         /// <summary>
826         /// Return the list of conflict victims.
827         /// </summary>
GetConflictVictims()828         internal AssemblyNameExtension[] GetConflictVictims()
829         {
830             return (AssemblyNameExtension[])_conflictVictims.ToArray(typeof(AssemblyNameExtension));
831         }
832 
833         /// <summary>
834         ///  The name of the assembly that won over this reference.
835         /// </summary>
836         internal AssemblyNameExtension ConflictVictorName
837         {
838             get { return _conflictVictorName; }
839             set { _conflictVictorName = value; }
840         }
841 
842         /// <summary>
843         ///  The reason why this reference lost to another reference.
844         /// </summary>
845         internal ConflictLossReason ConflictLossExplanation
846         {
847             get { return _conflictLossReason; }
848             set { _conflictLossReason = value; }
849         }
850 
851         /// <summary>
852         /// Is the file a WinMDFile.
853         /// </summary>
854         internal bool IsWinMDFile
855         {
856             get { return _winMDFile; }
857             set { _winMDFile = value; }
858         }
859 
860         /// <summary>
861         /// Is the file a Managed.
862         /// </summary>
863         internal bool IsManagedWinMDFile
864         {
865             get { return _isManagedWinMDFile; }
866             set { _isManagedWinMDFile = value; }
867         }
868 
869         /// <summary>
870         /// For winmd files there may be an implementation file sitting beside the winmd called the assemblyName.dll
871         /// We need to attach a piece of metadata to if this is the case.
872         /// </summary>
873         public string ImplementationAssembly
874         {
875             get;
876             set;
877         }
878 
879         /// <summary>
880         /// ImageRuntime Information
881         /// </summary>
882         internal string ImageRuntime
883         {
884             get { return _imageRuntimeVersion; }
885             set { _imageRuntimeVersion = value; }
886         }
887 
888         /// <summary>
889         /// Return the list of versions that this reference is unified from.
890         /// </summary>
GetPreUnificationVersions()891         internal List<UnificationVersion> GetPreUnificationVersions()
892         {
893             return new List<UnificationVersion>(_preUnificationVersions.Values);
894         }
895 
896         /// <summary>
897         /// Return the list of versions that this reference is unified from.
898         /// </summary>
RemappedAssemblyNames()899         internal HashSet<AssemblyRemapping> RemappedAssemblyNames()
900         {
901             return _remappedAssemblyNames;
902         }
903 
904         /// <summary>
905         /// Add a new version number for a version of this reference
906         /// </summary>
AddPreUnificationVersion(String referencePath, Version version, UnificationReason reason)907         internal void AddPreUnificationVersion(String referencePath, Version version, UnificationReason reason)
908         {
909             string key = referencePath + version.ToString() + reason.ToString();
910 
911             // Only add a reference, version, and reason once.
912             UnificationVersion unificationVersion;
913             if (!_preUnificationVersions.TryGetValue(key, out unificationVersion))
914             {
915                 unificationVersion = new UnificationVersion();
916                 unificationVersion.referenceFullPath = referencePath;
917                 unificationVersion.version = version;
918                 unificationVersion.reason = reason;
919                 _preUnificationVersions[key] = unificationVersion;
920             }
921         }
922 
923         /// <summary>
924         /// Add the AssemblyNames name we were remapped from
925         /// </summary>
AddRemapping(AssemblyNameExtension remappedFrom, AssemblyNameExtension remappedTo)926         internal void AddRemapping(AssemblyNameExtension remappedFrom, AssemblyNameExtension remappedTo)
927         {
928             ErrorUtilities.VerifyThrow(remappedFrom.Immutable, " Remapped from is NOT immutable");
929             ErrorUtilities.VerifyThrow(remappedTo.Immutable, " Remapped to is NOT immutable");
930             _remappedAssemblyNames.Add(new AssemblyRemapping(remappedFrom, remappedTo));
931         }
932 
933         /// <summary>
934         ///  Whether or not this reference is unified from a different version or versions.
935         /// </summary>
936         internal bool IsUnified
937         {
938             get { return _preUnificationVersions.Count != 0; }
939         }
940 
941         /// <summary>
942         /// Whether this reference should be copied to the local 'bin' dir or not and the reason this flag
943         /// was set that way.
944         /// </summary>
945         /// <value>The current copy-local state.</value>
946         internal CopyLocalState CopyLocal
947         {
948             get { return _copyLocalState; }
949         }
950 
951         /// <summary>
952         /// Whether the reference should be CopyLocal. For the reason, see CopyLocalState.
953         /// </summary>
954         /// <value>'true' if this reference should be copied.</value>
955         internal bool IsCopyLocal
956         {
957             get
958             {
959                 return CopyLocalStateUtility.IsCopyLocal(_copyLocalState);
960             }
961         }
962 
963         /// <summary>
964         /// Whether this reference has already been resolved.
965         /// Resolved means that the actual filename of the assembly has been found.
966         /// </summary>
967         /// <value>'true' if this reference has been resolved.</value>
968         internal bool IsResolved
969         {
970             get { return _fullPath.Length > 0; }
971         }
972 
973         /// <summary>
974         /// Whether this reference can't be resolve.
975         /// References are usually unresolvable because they weren't found anywhere in the defined search paths.
976         /// </summary>
977         /// <value>'true' if this reference is unresolvable.</value>
978         internal bool IsUnresolvable
979         {
980             // If there are any resolution errors then this reference is unresolvable.
981             get
982             {
983                 return !IsResolved && _errors.Count > 0;
984             }
985         }
986 
987         /// <summary>
988         /// Whether or not we still need to find dependencies for this reference.
989         /// </summary>
990         internal bool DependenciesFound
991         {
992             get { return _dependenciesFound; }
993             set { _dependenciesFound = value; }
994         }
995 
996         /// <summary>
997         /// If the reference has an SDK name metadata this will contain that string.
998         /// </summary>
999         internal string SDKName
1000         {
1001             get
1002             {
1003                 return _sdkName;
1004             }
1005         }
1006 
1007         /// <summary>
1008         /// Add some records to the table of assemblies that were considered and then rejected.
1009         /// </summary>
AddAssembliesConsideredAndRejected(ArrayList assembliesConsideredAndRejectedToAdd)1010         internal void AddAssembliesConsideredAndRejected(ArrayList assembliesConsideredAndRejectedToAdd)
1011         {
1012             _assembliesConsideredAndRejected.AddRange(assembliesConsideredAndRejectedToAdd);
1013         }
1014 
1015         /// <summary>
1016         /// Returns a collection of strings. Each string is the full path to an assembly that was
1017         /// considered for resolution but then rejected because it wasn't a complete match.
1018         /// </summary>
1019         internal ArrayList AssembliesConsideredAndRejected
1020         {
1021             get { return _assembliesConsideredAndRejected; }
1022         }
1023 
1024         /// <summary>
1025         /// The searchpath location that the reference was found at.
1026         /// </summary>
1027         internal string ResolvedSearchPath
1028         {
1029             get { return _resolvedSearchPath; }
1030             set { _resolvedSearchPath = value; }
1031         }
1032 
1033         /// <summary>
1034         /// FrameworkName attribute on this reference
1035         /// </summary>
1036         internal FrameworkName FrameworkNameAttribute
1037         {
1038             get { return _frameworkName; }
1039             set
1040             {
1041                 _frameworkName = value;
1042             }
1043         }
1044 
1045         /// <summary>
1046         /// Indicates that the reference is primary and has ExternallyResolved=true metadata to denote that
1047         /// it was resolved by an external system (commonly from nuget). Such a system has already provided a
1048         /// resolved closure as primary references and therefore we can skip the expensive closure walk.
1049         internal bool ExternallyResolved
1050         {
1051             get;
1052             private set;
1053         }
1054 
1055         /// <summary>
1056         /// Make this reference an assembly that is a dependency of 'sourceReference'
1057         ///
1058         /// For example, if 'sourceReference' is MyAssembly.dll then a dependent assembly file
1059         /// might be en\MyAssembly.resources.dll
1060         ///
1061         /// Assembly references do not have their own dependencies, therefore they are
1062         /// </summary>
1063         /// <param name="sourceReference">The source reference that this reference will be dependent on</param>
MakeDependentAssemblyReference(Reference sourceReference)1064         internal void MakeDependentAssemblyReference(Reference sourceReference)
1065         {
1066             _copyLocalState = CopyLocalState.Undecided;
1067 
1068             // This is a true dependency, so its not primary.
1069             _isPrimary = false;
1070 
1071             // This is an assembly file, so we'll need to find dependencies later.
1072             DependenciesFound = false;
1073 
1074             // Dependencies must always be specific version.
1075             _wantSpecificVersion = true;
1076 
1077             // Add source items from the original item.
1078             AddSourceItems(sourceReference.GetSourceItems());
1079 
1080             // Add dependees
1081             AddDependee(sourceReference);
1082         }
1083 
1084         /// <summary>
1085         /// Make this reference a primary assembly reference.
1086         /// This is a refrence that is an assembly and is primary.
1087         /// </summary>
1088         /// <param name="sourceItem">The source item.</param>
1089         /// <param name="wantSpecificVersion">Whether the version needs to match exactly or loosely.</param>
1090         /// <param name="executableExtension">The filename extension that the resulting assembly must have.</param>
MakePrimaryAssemblyReference( ITaskItem sourceItem, bool wantSpecificVersionValue, string executableExtension )1091         internal void MakePrimaryAssemblyReference
1092         (
1093             ITaskItem sourceItem,
1094             bool wantSpecificVersionValue,
1095             string executableExtension
1096         )
1097         {
1098             _copyLocalState = CopyLocalState.Undecided;
1099 
1100             // This is a primary reference.
1101             _isPrimary = true;
1102 
1103             // This is the source item (from the list passed into the task) that
1104             // originally created this reference.
1105             _primarySourceItem = sourceItem;
1106             _sdkName = sourceItem.GetMetadata("SDKName");
1107 
1108             if (executableExtension != null && executableExtension.Length > 0)
1109             {
1110                 // Set the expected extension.
1111                 SetExecutableExtension(executableExtension);
1112             }
1113 
1114             // The specific version indicator.
1115             _wantSpecificVersion = wantSpecificVersionValue;
1116 
1117             // This is an assembly file, so we'll need to find dependencies later.
1118             DependenciesFound = false;
1119 
1120             ExternallyResolved = MetadataConversionUtilities.TryConvertItemMetadataToBool(sourceItem, "ExternallyResolved");
1121 
1122             // Add source items from the original item.
1123             AddSourceItem(sourceItem);
1124         }
1125 
1126         /// <summary>
1127         /// Determine whether the given assembly is an FX assembly.
1128         /// </summary>
1129         /// <param name="fullPath">The full path to the assembly.</param>
1130         /// <param name="frameworkPaths">The path to the frameworks.</param>
1131         /// <returns>True if this is a frameworks assembly.</returns>
IsFrameworkFile(string fullPath, string[] frameworkPaths)1132         internal static bool IsFrameworkFile(string fullPath, string[] frameworkPaths)
1133         {
1134             if (frameworkPaths != null)
1135             {
1136                 foreach (string frameworkPath in frameworkPaths)
1137                 {
1138                     if
1139                     (
1140                         String.Compare
1141                         (
1142                             frameworkPath, 0,
1143                             fullPath, 0,
1144                             frameworkPath.Length,
1145                             StringComparison.OrdinalIgnoreCase
1146                         ) == 0
1147                     )
1148                     {
1149                         return true;
1150                     }
1151                 }
1152             }
1153             return false;
1154         }
1155 
1156         /// <summary>
1157         /// Figure out the what the CopyLocal state of given assembly should be.
1158         /// </summary>
1159         /// <param name="assemblyName">The name of the assembly.</param>
1160         /// <param name="frameworkPaths">The framework paths.</param>
1161         /// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64.</param>
1162         /// <param name="getRuntimeVersion">Delegate to get runtime version.</param>
1163         /// <param name="targetedRuntimeVersion">The targeted runtime version.</param>
1164         /// <param name="fileExists">Delegate to check if a file exists.</param>
1165         /// <param name="getAssemblyPathInGac">Delegate to get the path to an assembly in the system GAC.</param>
1166         /// <param name="copyLocalDependenciesWhenParentReferenceInGac">if set to true, copy local dependencies when only parent reference in gac.</param>
1167         /// <param name="doNotCopyLocalIfInGac">If set to true, do not copy local a reference that exists in the GAC (legacy behavior).</param>
1168         /// <param name="referenceTable">The reference table.</param>
SetFinalCopyLocalState( AssemblyNameExtension assemblyName, string[] frameworkPaths, ProcessorArchitecture targetProcessorArchitecture, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVersion, FileExists fileExists, GetAssemblyPathInGac getAssemblyPathInGac, bool copyLocalDependenciesWhenParentReferenceInGac, bool doNotCopyLocalIfInGac, ReferenceTable referenceTable )1169         internal void SetFinalCopyLocalState
1170         (
1171             AssemblyNameExtension assemblyName,
1172             string[] frameworkPaths,
1173             ProcessorArchitecture targetProcessorArchitecture,
1174             GetAssemblyRuntimeVersion getRuntimeVersion,
1175             Version targetedRuntimeVersion,
1176             FileExists fileExists,
1177             GetAssemblyPathInGac getAssemblyPathInGac,
1178             bool copyLocalDependenciesWhenParentReferenceInGac,
1179             bool doNotCopyLocalIfInGac,
1180             ReferenceTable referenceTable
1181         )
1182         {
1183             // If this item was unresolvable, then copy-local is false.
1184             if (IsUnresolvable)
1185             {
1186                 _copyLocalState = CopyLocalState.NoBecauseUnresolved;
1187                 return;
1188             }
1189 
1190             if (EmbedInteropTypes)
1191             {
1192                 _copyLocalState = CopyLocalState.NoBecauseEmbedded;
1193                 return;
1194             }
1195 
1196             // If this item was a conflict victim, then it should not be copy-local.
1197             if (IsConflictVictim)
1198             {
1199                 _copyLocalState = CopyLocalState.NoBecauseConflictVictim;
1200                 return;
1201             }
1202 
1203             // If this is a primary reference then see if there's a Private metadata on the source item
1204             if (IsPrimary)
1205             {
1206                 bool found;
1207                 bool result = MetadataConversionUtilities.TryConvertItemMetadataToBool
1208                     (
1209                         PrimarySourceItem,
1210                         ItemMetadataNames.privateMetadata,
1211                         out found
1212                     );
1213 
1214                 if (found)
1215                 {
1216                     _copyLocalState = result
1217                         ? CopyLocalState.YesBecauseReferenceItemHadMetadata
1218                         : CopyLocalState.NoBecauseReferenceItemHadMetadata;
1219                     return;
1220                 }
1221             }
1222             else
1223             {
1224                 // This is a dependency. If any primary reference that lead to this dependency
1225                 // has Private=false, then this dependency should false too.
1226                 bool privateTrueFound = false;
1227                 bool privateFalseFound = false;
1228                 foreach (DictionaryEntry entry in _sourceItems)
1229                 {
1230                     bool found;
1231                     bool result = MetadataConversionUtilities.TryConvertItemMetadataToBool
1232                         (
1233                             (ITaskItem)entry.Value,
1234                             ItemMetadataNames.privateMetadata,
1235                             out found
1236                         );
1237 
1238                     if (found)
1239                     {
1240                         if (result)
1241                         {
1242                             privateTrueFound = true;
1243 
1244                             // Once we hit this once we know there will be no modification to CopyLocal state.
1245                             // so we can immediately...
1246                             break;
1247                         }
1248                         else
1249                         {
1250                             privateFalseFound = true;
1251                         }
1252                     }
1253                 }
1254 
1255                 if (privateFalseFound && !privateTrueFound)
1256                 {
1257                     _copyLocalState = CopyLocalState.NoBecauseReferenceItemHadMetadata;
1258                     return;
1259                 }
1260             }
1261 
1262             // If the item was determined to be an prereq assembly.
1263             if (IsPrerequisite && !UserRequestedSpecificFile)
1264             {
1265                 _copyLocalState = CopyLocalState.NoBecausePrerequisite;
1266                 return;
1267             }
1268 
1269             // Items in the frameworks directory shouldn't be copy-local
1270             if (IsFrameworkFile(_fullPath, frameworkPaths))
1271             {
1272                 _copyLocalState = CopyLocalState.NoBecauseFrameworkFile;
1273                 return;
1274             }
1275 
1276             // We are a dependency, check to see if all of our parent references have come from the GAC
1277             if (!IsPrimary && !copyLocalDependenciesWhenParentReferenceInGac)
1278             {
1279                 // Did we discover a parent reference which was not found in the GAC
1280                 bool foundSourceItemNotInGac = false;
1281 
1282                 // Go through all of the parent source items and check to see if they were found in the GAC
1283                 foreach (DictionaryEntry entry in _sourceItems)
1284                 {
1285                     AssemblyNameExtension primaryAssemblyName = referenceTable.GetReferenceFromItemSpec((string)entry.Key);
1286                     Reference primaryReference = referenceTable.GetReference(primaryAssemblyName);
1287 
1288                     if (doNotCopyLocalIfInGac)
1289                     {
1290                         // Legacy behavior, don't copy local if the assembly is in the GAC at all
1291                         if (!primaryReference.FoundInGac.HasValue)
1292                         {
1293                             primaryReference.FoundInGac = !string.IsNullOrEmpty(getAssemblyPathInGac(primaryAssemblyName, targetProcessorArchitecture, getRuntimeVersion, targetedRuntimeVersion, fileExists, true, false));
1294                         }
1295 
1296                         if (!primaryReference.FoundInGac.Value)
1297                         {
1298                             foundSourceItemNotInGac = true;
1299                             break;
1300                         }
1301                     }
1302                     else
1303                     {
1304                         if (!primaryReference.ResolvedFromGac)
1305                         {
1306                             foundSourceItemNotInGac = true;
1307                             break;
1308                         }
1309                     }
1310                 }
1311 
1312                 // All parent source items were found in the GAC.
1313                 if (!foundSourceItemNotInGac)
1314                 {
1315                     _copyLocalState = CopyLocalState.NoBecauseParentReferencesFoundInGAC;
1316                     return;
1317                 }
1318             }
1319 
1320             if (doNotCopyLocalIfInGac)
1321             {
1322                 // Legacy behavior, don't copy local if the assembly is in the GAC at all
1323                 if (!FoundInGac.HasValue)
1324                 {
1325                     FoundInGac = !string.IsNullOrEmpty(getAssemblyPathInGac(assemblyName, targetProcessorArchitecture, getRuntimeVersion, targetedRuntimeVersion, fileExists, true, false));
1326                 }
1327 
1328                 if (FoundInGac.Value)
1329                 {
1330                     _copyLocalState = CopyLocalState.NoBecauseReferenceFoundInGAC;
1331                     return;
1332                 }
1333             }
1334 
1335             if (ResolvedFromGac)
1336             {
1337                 _copyLocalState = CopyLocalState.NoBecauseReferenceResolvedFromGAC;
1338                 return;
1339             }
1340 
1341             //  It was resolved locally, so copy it.
1342             _copyLocalState = CopyLocalState.YesBecauseOfHeuristic;
1343         }
1344 
1345         /// <summary>
1346         /// Produce a string representation.
1347         /// </summary>
ToString()1348         public override string ToString()
1349         {
1350             if (IsResolved)
1351             {
1352                 return FullPath;
1353             }
1354             return "*Unresolved*";
1355         }
1356 
1357         /// <summary>
1358         /// There are a number of properties which are set when we generate exclusion lists and it is useful to have this information on the references so that
1359         /// the correct reasons can be logged for these references being in the black list.
1360         /// </summary>
1361         internal class ExclusionListProperties
1362         {
1363             #region Fields
1364             /// <summary>
1365             /// What is the highest version of an assembly found in the current redist list for the targeted framework
1366             /// </summary>
1367             private Version _highestVersionInRedist;
1368 
1369             /// <summary>
1370             /// Delegate which will log the reason the assembly was not resolved
1371             /// </summary>
1372             private ReferenceTable.LogExclusionReason _exclusionReasonLogDelegate;
1373 
1374             /// <summary>
1375             /// What is the target framework moniker of the highest redist list on the system.
1376             /// </summary>
1377             private string _highestRedistListMonkier;
1378 
1379             /// <summary>
1380             /// Is this reference in an exclusion list
1381             /// </summary>
1382             private bool _isInExclusionList;
1383             #endregion
1384 
1385             /// <summary>
1386             /// Is this reference in an exclusion list
1387             /// </summary>
1388             internal bool IsInExclusionList
1389             {
1390                 get { return _isInExclusionList; }
1391                 set { _isInExclusionList = value; }
1392             }
1393 
1394             /// <summary>
1395             /// What is the highest version of this assembly in the current redist list
1396             /// </summary>
1397             internal Version HighestVersionInRedist
1398             {
1399                 get { return _highestVersionInRedist; }
1400                 set { _highestVersionInRedist = value; }
1401             }
1402 
1403             /// <summary>
1404             /// What is the highest versioned redist list on the machine
1405             /// </summary>
1406             internal string HighestRedistListMonkier
1407             {
1408                 get { return _highestRedistListMonkier; }
1409                 set { _highestRedistListMonkier = value; }
1410             }
1411 
1412             /// <summary>
1413             /// Delegate which logs the reason for not resolving a reference
1414             /// </summary>
1415             internal Microsoft.Build.Tasks.ReferenceTable.LogExclusionReason ExclusionReasonLogDelegate
1416             {
1417                 get { return _exclusionReasonLogDelegate; }
1418                 set { _exclusionReasonLogDelegate = value; }
1419             }
1420         }
1421     }
1422 }
1423