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