private void PopulateDependenciesInternal(IDictionary <string, FileNode> allFiles, bool preferNativeImage, ILog log, Stack <FileNode> stack) { if (stack == null) { stack = new Stack <FileNode>(); } if (stack.Contains(this)) { log.LogMessage($"Cycle detected: {String.Join(" -> ", stack)} -> {this}"); } if (dependencies != null) { return; } stack.Push(this); dependencies = new HashSet <FileNode>(); if (File.Exists(SourceFile)) { PopulateDependenciesFromMetadata(allFiles, preferNativeImage, log, stack); } else { IsMissing = true; } // allow for components to specify their dependencies themselves, by placing a file next to their source file. var additionalDependenciesFile = SourceFile + AdditionalDependenciesFileSuffix; if (File.Exists(additionalDependenciesFile)) { foreach (var additionalDependency in File.ReadAllLines(additionalDependenciesFile)) { if (additionalDependency.Length == 0 || additionalDependency[0] == '#') { continue; } FileNode additionalDependencyFile; if (allFiles.TryGetValue(additionalDependency, out additionalDependencyFile)) { dependencies.Add(additionalDependencyFile); } else { log.LogMessage(LogImportance.Low, $"Could not locate explicit dependency {additionalDependency} of {SourceFile} specified in {additionalDependenciesFile}."); } } } // Files may be related to other files via OriginalItemSpec if (OriginalItem != null) { var relatedToPath = OriginalItem.GetMetadata("OriginalItemSpec"); if (!String.IsNullOrEmpty(relatedToPath)) { var relatedToFileName = Path.GetFileName(relatedToPath); FileNode relatedTo = null; if (allFiles.TryGetValue(relatedToFileName, out relatedTo)) { if (relatedTo.IsAggregate) { bool found = false; foreach (var dependency in relatedTo.Dependencies) { if (dependency.SourceFile.Equals(relatedToPath, StringComparison.OrdinalIgnoreCase)) { found = true; dependency.relatedFiles.Add(this); break; } } if (!found) { log.LogMessage(LogImportance.Low, $"Could not locate explicit parent {relatedToPath} of {SourceFile} specified in OriginalItemSpec. Considered {string.Join(";", relatedTo.Dependencies.Select(d => d.SourceFile))} but they did not match"); } } else if (relatedTo.SourceFile.Equals(relatedToPath, StringComparison.OrdinalIgnoreCase)) { relatedTo.relatedFiles.Add(this); } else { log.LogMessage(LogImportance.Low, $"Could not locate explicit parent {relatedToPath} of {SourceFile} specified in OriginalItemSpec. Considered {relatedTo.SourceFile} but it didn't match."); } } if (relatedTo == null) { log.LogMessage(LogImportance.Low, $"Could not locate explicit parent {relatedToPath} of {SourceFile} specified in OriginalItemSpec."); } } } stack.Pop(); }
private string GetMetadata(string name) { var value = OriginalItem.GetMetadata(name); return((value?.Length > 0) ? value : null); }
private void PopulateDependenciesInternal(IDictionary <string, FileNode> allFiles, bool preferNativeImage, ILog log, Stack <FileNode> stack) { if (stack == null) { stack = new Stack <FileNode>(); } if (stack.Contains(this)) { log.LogMessage($"Cycle detected: {String.Join(" -> ", stack)} -> {this}"); } if (dependencies != null) { return; } stack.Push(this); dependencies = new HashSet <FileNode>(); try { using (var peReader = new PEReader(new FileStream(SourceFile, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read))) { if (peReader.HasMetadata) { var reader = peReader.GetMetadataReader(); var includeDependencies = true; // map of facade handles to enable quickly getting to FileNode without repeatedly looking up by name var facadeHandles = new Dictionary <AssemblyReferenceHandle, FileNode>(); if (IsFullFacade(reader)) { // don't include dependencies in full facades. We'll instead follow their typeforwards and promote the dependencies to the parent. includeDependencies = false; // follow typeforwards in any full facade. followTypeForwards = true; } foreach (var handle in reader.AssemblyReferences) { var reference = reader.GetAssemblyReference(handle); var referenceName = reader.GetString(reference.Name); FileNode referencedFile = TryGetFileForReference(referenceName, allFiles, preferNativeImage); if (referencedFile != null) { if (includeDependencies) { dependencies.Add(referencedFile); } // populate dependencies of child referencedFile.PopulateDependenciesInternal(allFiles, preferNativeImage, log, stack); // if we're following type-forwards out of any dependency make sure to look at typerefs from this assembly. // and populate the type-forwards in the dependency if (referencedFile.followTypeForwards || followTypeForwards) { facadeHandles.Add(handle, referencedFile); } } else { // static dependency that wasn't satisfied, this can happen if folks use // lightup code to guard the static dependency. // this can also happen when referencing a package that isn't implemented // on this platform but don't fail the build here log.LogMessage(LogImportance.Low, $"Could not locate assembly dependency {referenceName} of {SourceFile}."); } } if (followTypeForwards) { // if following typeforwards out of this assembly, capture all type forwards foreach (var exportedTypeHandle in reader.ExportedTypes) { var exportedType = reader.GetExportedType(exportedTypeHandle); if (exportedType.IsForwarder) { var assemblyReferenceHandle = (AssemblyReferenceHandle)exportedType.Implementation; FileNode assemblyReferenceNode; if (facadeHandles.TryGetValue(assemblyReferenceHandle, out assemblyReferenceNode)) { var typeName = exportedType.Namespace.IsNil ? reader.GetString(exportedType.Name) : reader.GetString(exportedType.Namespace) + reader.GetString(exportedType.Name); typeForwards.Add(typeName, assemblyReferenceNode); } } } } else if (facadeHandles.Count > 0) { // if examining type forwards in some dependency, enumerate type-refs // for any that point at a facade assembly. foreach (var typeReferenceHandle in reader.TypeReferences) { var typeReference = reader.GetTypeReference(typeReferenceHandle); var resolutionScope = typeReference.ResolutionScope; if (resolutionScope.Kind == HandleKind.AssemblyReference) { var assemblyReferenceHandle = (AssemblyReferenceHandle)resolutionScope; FileNode assemblyReferenceNode; if (facadeHandles.TryGetValue(assemblyReferenceHandle, out assemblyReferenceNode)) { var typeName = typeReference.Namespace.IsNil ? reader.GetString(typeReference.Name) : reader.GetString(typeReference.Namespace) + reader.GetString(typeReference.Name); FileNode typeForwardedToNode = null; var forwardAssemblies = new Stack <FileNode>(); // while assembly forwarded to is also a facade, add a dependency on the target while (assemblyReferenceNode.followTypeForwards) { if (!assemblyReferenceNode.typeForwards.TryGetValue(typeName, out typeForwardedToNode)) { break; } dependencies.Add(typeForwardedToNode); forwardAssemblies.Push(assemblyReferenceNode); // look at the target in case it is also a facade assemblyReferenceNode = typeForwardedToNode; if (forwardAssemblies.Contains(assemblyReferenceNode)) { // type-forward cycle, bail log.LogMessage($"Cycle detected involving type-forwards: {String.Join(" -> ", forwardAssemblies)}"); break; } } } } } } // examine native module dependencies for (int i = 1, count = reader.GetTableRowCount(TableIndex.ModuleRef); i <= count; i++) { var moduleRef = reader.GetModuleReference(MetadataTokens.ModuleReferenceHandle(i)); var moduleName = reader.GetString(moduleRef.Name); var moduleRefCandidates = new[] { moduleName, moduleName + ".dll", moduleName + ".so", moduleName + ".dylib" }; FileNode referencedNativeFile = null; foreach (var moduleRefCandidate in moduleRefCandidates) { if (allFiles.TryGetValue(moduleRefCandidate, out referencedNativeFile)) { break; } } if (referencedNativeFile != null) { dependencies.Add(referencedNativeFile); } else { // DLLImport that wasn't satisfied } } } } } catch (BadImageFormatException) { // not a PE } // allow for components to specify their dependencies themselves, by placing a file next to their source file. var additionalDependenciesFile = SourceFile + AdditionalDependenciesFileSuffix; if (File.Exists(additionalDependenciesFile)) { foreach (var additionalDependency in File.ReadAllLines(additionalDependenciesFile)) { if (additionalDependency.Length == 0 || additionalDependency[0] == '#') { continue; } FileNode additionalDependencyFile; if (allFiles.TryGetValue(additionalDependency, out additionalDependencyFile)) { dependencies.Add(additionalDependencyFile); } else { log.LogMessage(LogImportance.Low, $"Could not locate explicit dependency {additionalDependency} of {SourceFile} specified in {additionalDependenciesFile}."); } } } // Files may be related to other files via OriginalItemSpec if (OriginalItem != null) { var relatedToPath = OriginalItem.GetMetadata("OriginalItemSpec"); if (!String.IsNullOrEmpty(relatedToPath)) { var relatedToFileName = Path.GetFileName(relatedToPath); FileNode relatedTo = null; if (allFiles.TryGetValue(relatedToFileName, out relatedTo)) { if (relatedTo.IsAggregate) { bool found = false; foreach (var dependency in relatedTo.Dependencies) { if (dependency.SourceFile.Equals(relatedToPath, StringComparison.OrdinalIgnoreCase)) { found = true; dependency.relatedFiles.Add(this); break; } } if (!found) { log.LogMessage(LogImportance.Low, $"Could not locate explicit parent {relatedToPath} of {SourceFile} specified in OriginalItemSpec. Considered {string.Join(";", relatedTo.Dependencies.Select(d => d.SourceFile))} but they did not match"); } } else if (relatedTo.SourceFile.Equals(relatedToPath, StringComparison.OrdinalIgnoreCase)) { relatedTo.relatedFiles.Add(this); } else { log.LogMessage(LogImportance.Low, $"Could not locate explicit parent {relatedToPath} of {SourceFile} specified in OriginalItemSpec. Considered {relatedTo.SourceFile} but it didn't match."); } } if (relatedTo == null) { log.LogMessage(LogImportance.Low, $"Could not locate explicit parent {relatedToPath} of {SourceFile} specified in OriginalItemSpec."); } } } stack.Pop(); }