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 <AssemblyFolderKey> 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 <AssemblyFolderKey> metadata. 732 /// </summary> 733 /// <value>The <AssemblyFolderKey> 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