using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; 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 allNativeNames = new HashSet (); foreach (var nativeDep in NativeDepsPaths) allNativeNames.Add (Path.GetFileName (nativeDep.ItemSpec)); var keptNativeNames = new HashSet (); foreach (var nativeDep in NativeDepsToKeep) keptNativeNames.Add (Path.GetFileName (nativeDep.ItemSpec)); 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" }; bool foundModuleRef = false; foreach (string moduleRefCandidate in moduleRefCandidates) { if (allNativeNames.Contains (moduleRefCandidate)) { keptNativeNames.Add (moduleRefCandidate); foundModuleRef = true; } } if (!foundModuleRef) Log.LogMessage("unsatisfied DLLImport: " + managedAssembly + " -> " + moduleName); } } } } var keptNativeDeps = new List (); foreach (var nativeDep in NativeDepsPaths) { var fileName = Path.GetFileName (nativeDep.ItemSpec); if (keptNativeNames.Contains (fileName)) keptNativeDeps.Add (nativeDep); } KeptNativeDepsPaths = keptNativeDeps.ToArray (); return true; } } }