using System; using System.IO; using System.Linq; using System.Collections; using System.Collections.Generic; using Microsoft.Build.Utilities; using Microsoft.Build.Framework; using System.Reflection.PortableExecutable; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; namespace ILLink.Tasks { public class FindNativeDeps : Task { /// /// The managed assemblies to scan for references to native /// dependencies. /// [Required] public ITaskItem[] ManagedAssemblyPaths { get; set; } /// /// The set of native dependencies to keep even if they /// aren't found to be referenced by a managed assembly.. /// public ITaskItem[] NativeDepsToKeep { get; set; } /// /// The paths to the available native dependencies. We /// expect that all references found point to existing /// native files. /// [Required] public ITaskItem[] NativeDepsPaths { get; set; } /// /// The set of native dependencies to keep, including those /// found in the analysis, and those explicitly marked keep /// by NativeDepsToKeep. This includes metadata from the /// input NativeDepsToKeep. /// [Output] public ITaskItem[] KeptNativeDepsPaths { get; set; } public override bool Execute() { var allNative = new Dictionary (); foreach (var n in NativeDepsPaths) { var fileName = Path.GetFileName(n.ItemSpec); if (!allNative.ContainsKey(fileName)) { allNative.Add(fileName, n); } } var keptNative = new List (); var managedAssemblies = ManagedAssemblyPaths.Select (i => i.ItemSpec).ToArray(); foreach (string managedAssembly in managedAssemblies) { using (var peReader = new PEReader(new FileStream(managedAssembly, FileMode.Open, FileAccess.Read, FileShare.Read))) { if (peReader.HasMetadata) { var reader = peReader.GetMetadataReader(); 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" }; ITaskItem referencedNativeFile = null; foreach (string moduleRefCandidate in moduleRefCandidates) { if (allNative.TryGetValue (moduleRefCandidate, out referencedNativeFile)) { break; } } if (referencedNativeFile != null) { keptNative.Add(referencedNativeFile); } else { // DLLImport that wasn't satisfied Log.LogMessage(MessageImportance.High, "unsatisfied DLLImport: " + managedAssembly + " -> " + moduleName); } } } } } foreach (var n in NativeDepsToKeep) { ITaskItem nativeFile = null; if (allNative.TryGetValue (n.ItemSpec, out nativeFile)) { keptNative.Add(nativeFile); } } KeptNativeDepsPaths = keptNative.ToArray(); return true; } } }