public override bool Execute()
        {
            Log.LogDebugMessage ("StripEmbeddedLibraries Task");
            Log.LogDebugTaskItems ("  Assemblies: ", Assemblies);

            var res = new DirectoryAssemblyResolver (Log.LogWarning, true);
            foreach (var assembly in Assemblies)
                res.Load (Path.GetFullPath (assembly.ItemSpec));

            foreach (var assemblyName in Assemblies) {
                var suffix = assemblyName.ItemSpec.EndsWith (".dll") ? String.Empty : ".dll";
                string hintPath = assemblyName.GetMetadata ("HintPath").Replace (Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                string fileName = assemblyName.ItemSpec + suffix;
                if (!String.IsNullOrEmpty (hintPath) && !File.Exists (hintPath)) // ignore invalid HintPath
                    hintPath = null;
                string assemblyPath = String.IsNullOrEmpty (hintPath) ? fileName : hintPath;
                if (MonoAndroidHelper.IsFrameworkAssembly (fileName) && !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains (Path.GetFileName (fileName)))
                    continue;

                var assembly = res.GetAssembly (assemblyPath);
                bool assembly_modified = false;
                foreach (var mod in assembly.Modules) {
                    // embedded jars
                    var resjars = mod.Resources.Where (r => r.Name.EndsWith (".jar", StringComparison.InvariantCultureIgnoreCase)).Select (r => (EmbeddedResource) r);
                    foreach (var resjar in resjars.ToArray ()) {
                        Log.LogDebugMessage ("    Stripped {0}", resjar.Name);
                        mod.Resources.Remove (resjar);
                        assembly_modified = true;
                    }
                    // embedded AndroidNativeLibrary archive
                    var nativezip = mod.Resources.FirstOrDefault (r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                    if (nativezip != null) {
                        Log.LogDebugMessage ("    Stripped {0}", nativezip.Name);
                        mod.Resources.Remove (nativezip);
                        assembly_modified = true;
                    }
                    // embedded AndroidResourceLibrary archive
                    var reszip = mod.Resources.FirstOrDefault (r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                    if (reszip != null) {
                        Log.LogDebugMessage ("    Stripped {0}", reszip.Name);
                        mod.Resources.Remove (reszip);
                        assembly_modified = true;
                    }
                }
                if (assembly_modified) {
                    Log.LogDebugMessage ("    The stripped library is saved as {0}", assemblyPath);

                    // Output assembly needs to regenerate symbol file even if no IL/metadata was touched
                    // because Cecil still rewrites all assembly types in Cecil order (type A, nested types of A, type B, etc)
                    // and not in the original order causing symbols if original order doesn't match Cecil order
                    var wp = new WriterParameters () {
                        WriteSymbols = assembly.MainModule.HasSymbols
                    };

                    assembly.Write (assemblyPath, wp);
                }
            }
            return true;
        }
Example #2
0
        public static bool NeedsMarshalMethod(this MethodDefinition md, DirectoryAssemblyResolver resolver, TypeDefinitionCache cache, MethodInfo method, ref string name, ref string methodName, ref string signature)
        {
            var m = md;

            while (m != null)
            {
                if (CheckMethod(m, ref name, ref methodName, ref signature))
                {
                    return(true);
                }

                m = m.GetBaseDefinition(cache);

                if (m == md)
                {
                    break;
                }

                md = m;
            }

            foreach (var iface in method.DeclaringType.GetInterfaces())
            {
                if (iface.IsGenericType)
                {
                    continue;
                }

                var ifaceMap = method.DeclaringType.GetInterfaceMap(iface);
                var ad       = resolver.GetAssembly(iface.Assembly.Location);
                var id       = ad.MainModule.GetType(iface.GetCecilName());

                if (id == null)
                {
                    App.Warning($"Couln't find iterface {iface.FullName}");
                    continue;
                }

                for (int i = 0; i < ifaceMap.TargetMethods.Length; i++)
                {
                    if (ifaceMap.TargetMethods [i] == method)
                    {
                        var imd = id.GetMethodDefinition(ifaceMap.InterfaceMethods [i]);

                        if (CheckMethod(imd, ref name, ref methodName, ref signature))
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Example #3
0
        void AddNativeLibrariesFromAssemblies(ZipArchiveEx apk, string supportedAbis)
        {
            int count = 0;
            var abis  = supportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);

            using (var res = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: false)) {
                foreach (var assembly in EmbeddedNativeLibraryAssemblies)
                {
                    res.Load(assembly.ItemSpec);
                }
                foreach (var assemblyPath in EmbeddedNativeLibraryAssemblies)
                {
                    var assembly = res.GetAssembly(assemblyPath.ItemSpec);
                    foreach (var mod in assembly.Modules)
                    {
                        var ressozip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                        if (ressozip == null)
                        {
                            continue;
                        }
                        var data = ressozip.GetResourceData();
                        using (var ms = new MemoryStream(data)) {
                            using (var zip = ZipArchive.Open(ms)) {
                                foreach (var e in zip.Where(x => abis.Any(a => x.FullName.Contains($"/{a}/"))))
                                {
                                    if (e.IsDirectory)
                                    {
                                        continue;
                                    }
                                    var key = e.FullName.Replace("native_library_imports", "lib");
                                    if (apk.Archive.Any(k => k.FullName == key))
                                    {
                                        Log.LogCodedWarning("4301", "Apk already contains the item {0}; ignoring.", key);
                                        continue;
                                    }
                                    using (var s = new MemoryStream()) {
                                        e.Extract(s);
                                        s.Position = 0;
                                        apk.Archive.AddEntry(s.ToArray(), key);
                                    }
                                }
                            }
                        }
                    }
                    count++;
                    if (count == ZipArchiveEx.ZipFlushLimit)
                    {
                        apk.Flush();
                        count = 0;
                    }
                }
            }
        }
Example #4
0
        void AddNativeLibrariesFromAssemblies(ZipArchive apk, string supportedAbis)
        {
            var abis = supportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
            var res  = new DirectoryAssemblyResolver(Console.WriteLine, loadDebugSymbols: false);

            foreach (var assembly in EmbeddedNativeLibraryAssemblies)
            {
                res.Load(assembly.ItemSpec);
            }
            foreach (var assemblyPath in EmbeddedNativeLibraryAssemblies)
            {
                var assembly = res.GetAssembly(assemblyPath.ItemSpec);
                foreach (var mod in assembly.Modules)
                {
                    var ressozip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                    if (ressozip == null)
                    {
                        continue;
                    }
                    var data = ressozip.GetResourceData();
                    using (var ms = new MemoryStream(data)) {
                        using (var zip = new ZipArchive(ms, ZipArchiveMode.Read)) {
                            foreach (var e in zip.Entries.Where(x => abis.Any(a => x.FullName.Contains(a))))
                            {
                                if (e.IsDirectory())
                                {
                                    continue;
                                }
                                var key = e.FullName.Replace("native_library_imports", "lib");
                                if (apk.ContainsEntry(key))
                                {
                                    Log.LogCodedWarning("4301", "Apk already contains the item {0}; ignoring.", key);
                                    continue;
                                }
                                using (var s = new MemoryStream()) {
                                    e.Extract(s);
                                    apk.AddEntry(key, s.ToArray());
                                }
                            }
                        }
                    }
                }
            }
        }
        IEnumerable <AssemblyDefinition> GetRetainAssemblies(DirectoryAssemblyResolver res)
        {
            List <AssemblyDefinition> retainList = null;

            foreach (var assembly in ResolvedAssemblies)
            {
                var filename = Path.GetFileName(assembly.ItemSpec);
                if (!MonoAndroidHelper.IsForceRetainedAssembly(filename))
                {
                    continue;
                }
                if (retainList == null)
                {
                    retainList = new List <AssemblyDefinition> ();
                }
                retainList.Add(res.GetAssembly(assembly.ItemSpec));
            }
            return(retainList);
        }
Example #6
0
 int ExtractApiLevel(ITaskItem ass)
 {
     Log.LogDebugMessage(ass.ItemSpec);
     foreach (var ca in res.GetAssembly(ass.ItemSpec).CustomAttributes)
     {
         switch (ca.AttributeType.FullName)
         {
         case "System.Runtime.Versioning.TargetFrameworkAttribute":
             foreach (var p in ca.ConstructorArguments)
             {
                 var value = p.Value.ToString();
                 if (value.StartsWith("MonoAndroid"))
                 {
                     var values = value.Split('=');
                     return(AndroidVersion.TryOSVersionToApiLevel(values[1]));
                 }
             }
             break;
         }
     }
     return(0);
 }
Example #7
0
 void AddNativeLibrariesFromAssemblies(ZipFile apk, string supportedAbis)
 {
     var abis = supportedAbis.Split (new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
     var res = new DirectoryAssemblyResolver (Console.WriteLine, loadDebugSymbols: false);
     foreach (var assembly in EmbeddedNativeLibraryAssemblies)
         res.Load (assembly.ItemSpec);
     foreach (var assemblyPath in EmbeddedNativeLibraryAssemblies) {
         var assembly = res.GetAssembly (assemblyPath.ItemSpec);
         foreach (var mod in assembly.Modules) {
             var ressozip = mod.Resources.FirstOrDefault (r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
             if (ressozip == null)
                 continue;
             var data = ressozip.GetResourceData ();
             using (var ms = new MemoryStream (data)) {
                 using (var zip = ZipFile.Read (ms)) {
                     foreach (var e in zip.Entries.Where (x => abis.Any (a => x.FileName.Contains (a)))) {
                         if (e.IsDirectory)
                             continue;
                         var key = e.FileName.Replace ("native_library_imports", "lib");
                         if (apk.ContainsEntry (key)) {
                             Log.LogCodedWarning ("4301", "Apk already contains the item {0}; ignoring.", key);
                             continue;
                         }
                         using (var s = new MemoryStream ()) {
                             e.Extract (s);
                             apk.AddEntry (key, s.ToArray ());
                         }
                     }
                 }
             }
         }
     }
 }
Example #8
0
        public override bool RunTask()
        {
            if (SourceFiles.Length != DestinationFiles.Length)
            {
                throw new ArgumentException("source and destination count mismatch");
            }

            var readerParameters = new ReaderParameters {
                ReadSymbols = true,
            };
            var writerParameters = new WriterParameters {
                DeterministicMvid = Deterministic,
            };

            using (var resolver = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: true, loadReaderParameters: readerParameters)) {
                // Add SearchDirectories with ResolvedAssemblies
                foreach (var assembly in ResolvedAssemblies)
                {
                    var path = Path.GetFullPath(Path.GetDirectoryName(assembly.ItemSpec));
                    if (!resolver.SearchDirectories.Contains(path))
                    {
                        resolver.SearchDirectories.Add(path);
                    }
                }

                // Run the FixAbstractMethodsStep
                var step = new FixAbstractMethodsStep(resolver, Log);
                for (int i = 0; i < SourceFiles.Length; i++)
                {
                    var source      = SourceFiles [i];
                    var destination = DestinationFiles [i];

                    // Only run the step on "MonoAndroid" assemblies
                    if (MonoAndroidHelper.IsMonoAndroidAssembly(source) && !MonoAndroidHelper.IsSharedRuntimeAssembly(source.ItemSpec))
                    {
                        var assemblyDefinition = resolver.GetAssembly(source.ItemSpec);
                        if (step.FixAbstractMethods(assemblyDefinition))
                        {
                            Log.LogDebugMessage($"Saving modified assembly: {destination.ItemSpec}");
                            writerParameters.WriteSymbols = assemblyDefinition.MainModule.HasSymbols;
                            assemblyDefinition.Write(destination.ItemSpec, writerParameters);
                            continue;
                        }
                    }

                    if (MonoAndroidHelper.CopyAssemblyAndSymbols(source.ItemSpec, destination.ItemSpec))
                    {
                        Log.LogDebugMessage($"Copied: {destination.ItemSpec}");
                    }
                    else
                    {
                        Log.LogDebugMessage($"Skipped unchanged file: {destination.ItemSpec}");

                        // NOTE: We still need to update the timestamp on this file, or this target would run again
                        File.SetLastWriteTimeUtc(destination.ItemSpec, DateTime.UtcNow);
                    }
                }
            }

            return(!Log.HasLoggedErrors);
        }
Example #9
0
 protected override AssemblyDefinition GetMonoAndroidAssembly()
 {
     return(resolver.GetAssembly("Mono.Android.dll"));
 }
Example #10
0
        bool Execute(DirectoryAssemblyResolver res)
        {
            // Put every assembly we'll need in the resolver
            foreach (var assembly in ResolvedAssemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            var resolver = new AssemblyResolver(res.ToResolverCache());

            // Set up for linking
            var options = new LinkerOptions();

            options.MainAssembly     = res.GetAssembly(MainAssembly);
            options.OutputDirectory  = Path.GetFullPath(OutputDirectory);
            options.LinkSdkOnly      = string.Compare(LinkMode, "SdkOnly", true) == 0;
            options.LinkNone         = false;
            options.Resolver         = resolver;
            options.LinkDescriptions = LinkDescriptions.Select(item => Path.GetFullPath(item.ItemSpec)).ToArray();
            options.I18nAssemblies   = Linker.ParseI18nAssemblies(I18nAssemblies);
            if (!options.LinkSdkOnly)
            {
                options.RetainAssemblies = GetRetainAssemblies(res);
            }
            options.DumpDependencies          = DumpDependencies;
            options.HttpClientHandlerType     = HttpClientHandlerType;
            options.TlsProvider               = TlsProvider;
            options.PreserveJniMarshalMethods = PreserveJniMarshalMethods;
            options.DeterministicOutput       = Deterministic;

            var skiplist = new List <string> ();

            if (string.Compare(UseSharedRuntime, "true", true) == 0)
            {
                skiplist.AddRange(Profile.SharedRuntimeAssemblies.Where(a => a.EndsWith(".dll")).Select(a => Path.GetFileNameWithoutExtension(a)));
            }

            // Add LinkSkip options
            if (!string.IsNullOrWhiteSpace(LinkSkip))
            {
                skiplist.AddRange(LinkSkip.Split(',', ';'));
            }

            options.SkippedAssemblies = skiplist;

            if (EnableProguard)
            {
                options.ProguardConfiguration = ProguardConfiguration;
            }

            // Link!
            try {
                LinkContext link_context;
                Linker.Process(options, this, out link_context);

                foreach (var assembly in ResolvedAssemblies)
                {
                    var copysrc             = assembly.ItemSpec;
                    var filename            = Path.GetFileName(assembly.ItemSpec);
                    var assemblyDestination = Path.Combine(OutputDirectory, filename);

                    if (!MonoAndroidHelper.IsForceRetainedAssembly(filename))
                    {
                        continue;
                    }

                    MonoAndroidHelper.CopyAssemblyAndSymbols(copysrc, assemblyDestination);
                }
            } catch (ResolutionException ex) {
                Diagnostic.Error(2006, ex, "Could not resolve reference to '{0}' (defined in assembly '{1}') with scope '{2}'. When the scope is different from the defining assembly, it usually means that the type is forwarded.", ex.Member, ex.Member.Module.Assembly, ex.Scope);
            }

            return(true);
        }
        bool Execute(DirectoryAssemblyResolver res)
        {
            // Put every assembly we'll need in the resolver
            foreach (var assembly in ResolvedAssemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            var resolver = new AssemblyResolver(res.ToResolverCache());

            // Set up for linking
            var options = new LinkerOptions();

            options.MainAssembly     = res.GetAssembly(MainAssembly);
            options.OutputDirectory  = Path.GetFullPath(OutputDirectory);
            options.LinkSdkOnly      = string.Compare(LinkMode, "SdkOnly", true) == 0;
            options.LinkNone         = string.Compare(LinkMode, "None", true) == 0;
            options.Resolver         = resolver;
            options.LinkDescriptions = LinkDescriptions.Select(item => Path.GetFullPath(item.ItemSpec)).ToArray();
            options.I18nAssemblies   = Linker.ParseI18nAssemblies(I18nAssemblies);
            if (!options.LinkSdkOnly)
            {
                options.RetainAssemblies = GetRetainAssemblies(res);
            }
            options.DumpDependencies          = DumpDependencies;
            options.HttpClientHandlerType     = HttpClientHandlerType;
            options.TlsProvider               = TlsProvider;
            options.PreserveJniMarshalMethods = PreserveJniMarshalMethods;

            var skiplist = new List <string> ();

            if (string.Compare(UseSharedRuntime, "true", true) == 0)
            {
                skiplist.AddRange(Profile.SharedRuntimeAssemblies.Where(a => a.EndsWith(".dll")).Select(a => Path.GetFileNameWithoutExtension(a)));
            }
            if (!string.IsNullOrWhiteSpace(LinkOnlyNewerThan) && File.Exists(LinkOnlyNewerThan))
            {
                var newerThan   = File.GetLastWriteTime(LinkOnlyNewerThan);
                var skipOldOnes = ResolvedAssemblies.Where(a => File.GetLastWriteTime(a.ItemSpec) < newerThan);
                foreach (var old in skipOldOnes)
                {
                    Log.LogMessage(MBF.MessageImportance.Low, "  Skip linking unchanged file: " + old.ItemSpec);
                }
                skiplist = skipOldOnes.Select(a => Path.GetFileNameWithoutExtension(a.ItemSpec)).Concat(skiplist).ToList();
            }

            // Add LinkSkip options
            if (!string.IsNullOrWhiteSpace(LinkSkip))
            {
                foreach (var assembly in LinkSkip.Split(',', ';'))
                {
                    skiplist.Add(assembly);
                }
            }

            options.SkippedAssemblies = skiplist;

            if (EnableProguard)
            {
                options.ProguardConfiguration = ProguardConfiguration;
            }

            // Link!
            try {
                LinkContext link_context;
                Linker.Process(options, this, out link_context);

                var copydst = OptionalDestinationDirectory ?? OutputDirectory;

                foreach (var assembly in ResolvedAssemblies)
                {
                    var copysrc             = assembly.ItemSpec;
                    var filename            = Path.GetFileName(assembly.ItemSpec);
                    var assemblyDestination = Path.Combine(copydst, filename);

                    if (options.LinkNone)
                    {
                        if (skiplist.Any(s => Path.GetFileNameWithoutExtension(filename) == s))
                        {
                            // For skipped assemblies, skip if there is existing file in the destination.
                            // We cannot just copy the linker output from *current* run output, because
                            // it always renew the assemblies, in *different* binary values, whereas
                            // the dll in the OptionalDestinationDirectory must retain old and unchanged.
                            if (File.Exists(assemblyDestination))
                            {
                                MonoAndroidHelper.SetLastAccessAndWriteTimeUtc(assemblyDestination, DateTime.UtcNow, Log);
                                continue;
                            }
                        }
                        else
                        {
                            // Prefer fixup assemblies if exists, otherwise just copy the original.
                            copysrc = Path.Combine(OutputDirectory, filename);
                            copysrc = File.Exists(copysrc) ? copysrc : assembly.ItemSpec;
                        }
                    }
                    else if (!MonoAndroidHelper.IsForceRetainedAssembly(filename))
                    {
                        continue;
                    }

                    if (MonoAndroidHelper.CopyIfChanged(copysrc, assemblyDestination))
                    {
                        MonoAndroidHelper.SetLastAccessAndWriteTimeUtc(assemblyDestination, DateTime.UtcNow, Log);
                    }
                    try {
                        var mdbDestination = assemblyDestination + ".mdb";
                        if (MonoAndroidHelper.CopyIfChanged(assembly.ItemSpec + ".mdb", mdbDestination))
                        {
                            MonoAndroidHelper.SetLastAccessAndWriteTimeUtc(mdbDestination, DateTime.UtcNow, Log);
                        }
                    } catch (Exception) {                     // skip it, mdb sometimes fails to read and it's optional
                    }
                    var pdb = Path.ChangeExtension(copysrc, "pdb");
                    if (File.Exists(pdb) && Files.IsPortablePdb(pdb))
                    {
                        var pdbDestination = Path.ChangeExtension(Path.Combine(copydst, filename), "pdb");
                        if (MonoAndroidHelper.CopyIfChanged(pdb, pdbDestination))
                        {
                            MonoAndroidHelper.SetLastAccessAndWriteTimeUtc(pdbDestination, DateTime.UtcNow, Log);
                        }
                    }
                }
            } catch (ResolutionException ex) {
                Diagnostic.Error(2006, ex, "Could not resolve reference to '{0}' (defined in assembly '{1}') with scope '{2}'. When the scope is different from the defining assembly, it usually means that the type is forwarded.", ex.Member, ex.Member.Module.Assembly, ex.Scope);
            }

            return(true);
        }
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        void Extract(
            ICollection<string> jars,
            ICollection<string> resolvedResourceDirectories,
            ICollection<string> resolvedAssetDirectories,
            ICollection<string> resolvedEnvironments)
        {
            var outdir = new DirectoryInfo (OutputImportDirectory);
            if (!outdir.Exists)
                outdir.Create ();

            var res = new DirectoryAssemblyResolver (Log.LogWarning, loadDebugSymbols: false);
            foreach (var assembly in Assemblies)
                res.Load (assembly.ItemSpec);

            // FIXME: reorder references by import priority (not sure how to do that yet)
            foreach (var assemblyPath in Assemblies
                    .Select (a => GetTargetAssembly (a))
                    .Where (a => a != null)
                    .Distinct ()) {
                foreach (var imp in new string [] {imports_dir, "library_project_imports"}.Distinct ()) {
                    string assemblyIdentName = Path.GetFileNameWithoutExtension (assemblyPath);
                    if (UseShortFileNames) {
                        assemblyIdentName = Xamarin.Android.Tasks.MonoAndroidHelper.GetLibraryImportDirectoryNameForAssembly (assemblyIdentName);
                    }
                    string outDirForDll = Path.Combine (OutputImportDirectory, assemblyIdentName);
                    string importsDir = Path.Combine (outDirForDll, imp);
            #if SEPARATE_CRUNCH
                    // FIXME: review these binResDir thing and enable this. Eclipse does this.
                    // Enabling these blindly causes build failure on ActionBarSherlock.
                    //string binResDir = Path.Combine (importsDir, "bin", "res");
                    //string binAssemblyDir = Path.Combine (importsDir, "bin", "assets");
            #endif
                    string resDir = Path.Combine (importsDir, "res");
                    string assemblyDir = Path.Combine (importsDir, "assets");

                    // Skip already-extracted resources.
                    var stamp = new FileInfo (Path.Combine (outdir.FullName, assemblyIdentName + ".stamp"));
                    if (stamp.Exists && stamp.LastWriteTime > new FileInfo (assemblyPath).LastWriteTime) {
                        Log.LogDebugMessage ("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
            #if SEPARATE_CRUNCH
                        // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                        // Enabling these blindly causes build failure on ActionBarSherlock.
                        if (Directory.Exists (binResDir))
                            resolvedResourceDirectories.Add (binResDir);
                        if (Directory.Exists (binAssemblyDir))
                            resolvedAssetDirectories.Add (binAssemblyDir);
            #endif
                        if (Directory.Exists (resDir))
                            resolvedResourceDirectories.Add (resDir);
                        if (Directory.Exists (assemblyDir))
                            resolvedAssetDirectories.Add (assemblyDir);
                        continue;
                    }

                    if (Directory.Exists (outDirForDll))
                        Directory.Delete (outDirForDll, true);

                    Directory.CreateDirectory (importsDir);

                    var assembly = res.GetAssembly (assemblyPath);

                    foreach (var mod in assembly.Modules) {
                        // android environment files
                        foreach (var envtxt in mod.Resources
                                .Where (r => r.Name.StartsWith ("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase))
                                .Where (r => r is EmbeddedResource)
                                .Cast<EmbeddedResource> ()) {
                            if (!Directory.Exists (outDirForDll))
                                Directory.CreateDirectory (outDirForDll);
                            var finfo = new FileInfo (Path.Combine (outDirForDll, envtxt.Name));
                            using (var fs = finfo.Create ()) {
                                var data = envtxt.GetResourceData ();
                                fs.Write (data, 0, data.Length);
                            }
                            resolvedEnvironments.Add (finfo.FullName);
                        }

                        // embedded jars (EmbeddedJar, EmbeddedReferenceJar)
                        var resjars = mod.Resources
                            .Where (r => r.Name.EndsWith (".jar", StringComparison.InvariantCultureIgnoreCase))
                            .Select (r => (EmbeddedResource) r);
                        foreach (var resjar in resjars) {
                            var data = resjar.GetResourceData ();
                            using (var outfs = File.Create (Path.Combine (importsDir, resjar.Name)))
                                outfs.Write (data, 0, data.Length);
                        }

                        // embedded AndroidResourceLibrary archive
                        var reszip = mod.Resources.FirstOrDefault (r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                        if (reszip != null) {
                            if (!Directory.Exists (outDirForDll))
                                Directory.CreateDirectory (outDirForDll);
                            var finfo = new FileInfo (Path.Combine (outDirForDll, reszip.Name));
                            using (var fs = finfo.Create ()) {
                                var data = reszip.GetResourceData ();
                                fs.Write (data, 0, data.Length);
                            }

                            // temporarily extracted directory will look like:
                            //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                            using (var zip = new ZipFile (finfo.FullName)) {
                                Files.ExtractAll (zip, outDirForDll);
                            }

                            // We used to *copy* the resources to overwrite other resources,
                            // which resulted in missing resource issue.
                            // Here we replaced copy with use of '-S' option and made it to work.
            #if SEPARATE_CRUNCH
                            // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                            // Enabling these blindly causes build failure on ActionBarSherlock.
                            if (Directory.Exists (binResDir))
                                resolvedResourceDirectories.Add (binResDir);
                            if (Directory.Exists (binAssemblyDir))
                                resolvedAssetDirectories.Add (binAssemblyDir);
            #endif
                            if (Directory.Exists (resDir))
                                resolvedResourceDirectories.Add (resDir);
                            if (Directory.Exists (assemblyDir))
                                resolvedAssetDirectories.Add (assemblyDir);

                            finfo.Delete ();
                        }
                    }

                    stamp.Create ().Close ();
                }
            }

            foreach (var f in outdir.GetFiles ("*.jar")
                    .Select (fi => fi.FullName))
                jars.Add (f);
        }
        public override bool Execute()
        {
            // In Xamarin Studio, if the project name isn't a valid C# identifier
            // then $(RootNamespace) is not set, and the generated Activity is
            // placed into the "Application" namespace. VS just munges the project
            // name to be a valid C# identifier.
            // Use "Application" as the default namespace name to work with XS.
            Namespace = Namespace ?? "Application";

            Log.LogDebugMessage ("GenerateResourceDesigner Task");
            Log.LogDebugMessage ("  NetResgenOutputFile: {0}", NetResgenOutputFile);
            Log.LogDebugMessage ("  JavaResgenInputFile: {0}", JavaResgenInputFile);
            Log.LogDebugMessage ("  Namespace: {0}", Namespace);
            Log.LogDebugMessage ("  ResourceDirectory: {0}", ResourceDirectory);
            Log.LogDebugTaskItemsAndLogical ("  AdditionalResourceDirectories:", AdditionalResourceDirectories);
            Log.LogDebugMessage ("  IsApplication: {0}", IsApplication);
            Log.LogDebugTaskItemsAndLogical ("  Resources:", Resources);
            Log.LogDebugTaskItemsAndLogical ("  References:", References);

            if (!File.Exists (JavaResgenInputFile))
                return true;

            // ResourceDirectory may be a relative path, and
            // we need to compare it to absolute paths
            ResourceDirectory = Path.GetFullPath (ResourceDirectory);

            // Create our capitalization maps so we can support mixed case resources
            foreach (var item in Resources) {
                if (!item.ItemSpec.StartsWith (ResourceDirectory))
                    continue;

                var name = item.ItemSpec.Substring (ResourceDirectory.Length);
                var logical_name = item.GetMetadata ("LogicalName");

                AddRename (name.Replace ('/', Path.DirectorySeparatorChar), logical_name.Replace ('/', Path.DirectorySeparatorChar));
            }
            if (AdditionalResourceDirectories != null) {
                foreach (var additionalDir in AdditionalResourceDirectories) {
                    var file = Path.Combine (ProjectDir, Path.GetDirectoryName (additionalDir.ItemSpec), "__res_name_case_map.txt");
                    if (File.Exists (file)) {
                        foreach (var line in File.ReadAllLines (file).Where (l => !string.IsNullOrEmpty (l))) {
                            string [] tok = line.Split (';');
                            AddRename (tok [1].Replace ('/', Path.DirectorySeparatorChar), tok [0].Replace ('/', Path.DirectorySeparatorChar));
                        }
                    }
                }
            }

            // Parse out the resources from the R.java file
            JavaResourceParser.Log = Log;
            var resources = JavaResourceParser.Parse (JavaResgenInputFile, IsApplication, resource_fixup);

            var extension = Path.GetExtension (NetResgenOutputFile);
            var language = string.Compare (extension, ".fs", StringComparison.OrdinalIgnoreCase) == 0 ? "F#" : CodeDomProvider.GetLanguageFromExtension (extension);
            bool isVB = string.Equals (extension, ".vb", StringComparison.OrdinalIgnoreCase);
            bool isFSharp = string.Equals (language, "F#", StringComparison.OrdinalIgnoreCase);
            bool isCSharp = string.Equals (language, "C#", StringComparison.OrdinalIgnoreCase);

            // Let VB put this in the default namespace
            if (isVB)
                Namespace = string.Empty;

            // Create static resource overwrite methods for each Resource class in libraries.
            var assemblyNames = new List<string> ();
            if (IsApplication && References != null && References.Any ()) {
                // FIXME: should this be unified to some better code with ResolveLibraryProjectImports?
                var resolver = new DirectoryAssemblyResolver (Log.LogWarning, loadDebugSymbols: false);
                foreach (var assemblyName in References) {
                    var suffix = assemblyName.ItemSpec.EndsWith (".dll") ? String.Empty : ".dll";
                    string hintPath = assemblyName.GetMetadata ("HintPath").Replace (Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                    string fileName = assemblyName.ItemSpec + suffix;
                    resolver.Load (Path.GetFullPath (assemblyName.ItemSpec));
                    if (!String.IsNullOrEmpty (hintPath) && !File.Exists (hintPath)) // ignore invalid HintPath
                        hintPath = null;
                    string assemblyPath = String.IsNullOrEmpty (hintPath) ? fileName : hintPath;
                    if (MonoAndroidHelper.IsFrameworkAssembly (fileName) && !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains (Path.GetFileName (fileName)))
                        continue;
                    Log.LogDebugMessage ("Scan assembly {0} for resource generator", fileName);
                    assemblyNames.Add (assemblyPath);
                }
                var assemblies = assemblyNames.Select (assembly => resolver.GetAssembly (assembly));
                new ResourceDesignerImportGenerator (Namespace, resources)
                    .CreateImportMethods (assemblies);
            }

            AdjustConstructor (isFSharp, resources);
            foreach (var member in resources.Members)
                if (member is CodeTypeDeclaration)
                    AdjustConstructor (isFSharp, (CodeTypeDeclaration) member);

            // Write out our Resources.Designer.cs file

            WriteFile (NetResgenOutputFile, resources, language, isFSharp, isCSharp);

            return !Log.HasLoggedErrors;
        }
Example #14
0
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        void Extract(
            DirectoryAssemblyResolver res,
            ICollection <string> jars,
            ICollection <string> resolvedResourceDirectories,
            ICollection <string> resolvedAssetDirectories,
            ICollection <string> resolvedEnvironments)
        {
            var outdir = new DirectoryInfo(OutputImportDirectory);

            if (!outdir.Exists)
            {
                outdir.Create();
            }

            foreach (var assembly in Assemblies)
            {
                res.Load(assembly.ItemSpec);
            }

            // FIXME: reorder references by import priority (not sure how to do that yet)
            foreach (var assemblyPath in Assemblies
                     .Select(a => GetTargetAssembly(a))
                     .Where(a => a != null)
                     .Distinct())
            {
                foreach (var imp in new string [] { imports_dir, "library_project_imports" }.Distinct())
                {
                    string assemblyIdentName = Path.GetFileNameWithoutExtension(assemblyPath);
                    if (UseShortFileNames)
                    {
                        assemblyIdentName = Xamarin.Android.Tasks.MonoAndroidHelper.GetLibraryImportDirectoryNameForAssembly(assemblyIdentName);
                    }
                    string outDirForDll = Path.Combine(OutputImportDirectory, assemblyIdentName);
                    string importsDir   = Path.Combine(outDirForDll, imp);
#if SEPARATE_CRUNCH
                    // FIXME: review these binResDir thing and enable this. Eclipse does this.
                    // Enabling these blindly causes build failure on ActionBarSherlock.
                    //string binResDir = Path.Combine (importsDir, "bin", "res");
                    //string binAssemblyDir = Path.Combine (importsDir, "bin", "assets");
#endif
                    string resDir      = Path.Combine(importsDir, "res");
                    string assemblyDir = Path.Combine(importsDir, "assets");

                    // Skip already-extracted resources.
                    var stamp = new FileInfo(Path.Combine(outdir.FullName, assemblyIdentName + ".stamp"));
                    if (stamp.Exists && stamp.LastWriteTime > new FileInfo(assemblyPath).LastWriteTime)
                    {
                        Log.LogDebugMessage("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
#if SEPARATE_CRUNCH
                        // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                        // Enabling these blindly causes build failure on ActionBarSherlock.
                        if (Directory.Exists(binResDir))
                        {
                            resolvedResourceDirectories.Add(binResDir);
                        }
                        if (Directory.Exists(binAssemblyDir))
                        {
                            resolvedAssetDirectories.Add(binAssemblyDir);
                        }
#endif
                        if (Directory.Exists(resDir))
                        {
                            resolvedResourceDirectories.Add(resDir);
                        }
                        if (Directory.Exists(assemblyDir))
                        {
                            resolvedAssetDirectories.Add(assemblyDir);
                        }
                        continue;
                    }

                    if (Directory.Exists(outDirForDll))
                    {
                        Directory.Delete(outDirForDll, true);
                    }

                    Directory.CreateDirectory(importsDir);

                    var assembly = res.GetAssembly(assemblyPath);

                    foreach (var mod in assembly.Modules)
                    {
                        // android environment files
                        foreach (var envtxt in mod.Resources
                                 .Where(r => r.Name.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase))
                                 .Where(r => r is EmbeddedResource)
                                 .Cast <EmbeddedResource> ())
                        {
                            if (!Directory.Exists(outDirForDll))
                            {
                                Directory.CreateDirectory(outDirForDll);
                            }
                            var finfo = new FileInfo(Path.Combine(outDirForDll, envtxt.Name));
                            using (var fs = finfo.Create()) {
                                var data = envtxt.GetResourceData();
                                fs.Write(data, 0, data.Length);
                            }
                            resolvedEnvironments.Add(finfo.FullName);
                        }

                        // embedded jars (EmbeddedJar, EmbeddedReferenceJar)
                        var resjars = mod.Resources
                                      .Where(r => r.Name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase))
                                      .Select(r => (EmbeddedResource)r);
                        foreach (var resjar in resjars)
                        {
                            var data = resjar.GetResourceData();
                            using (var outfs = File.Create(Path.Combine(importsDir, resjar.Name)))
                                outfs.Write(data, 0, data.Length);
                        }

                        // embedded AndroidResourceLibrary archive
                        var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                        if (reszip != null)
                        {
                            if (!Directory.Exists(outDirForDll))
                            {
                                Directory.CreateDirectory(outDirForDll);
                            }
                            var finfo = new FileInfo(Path.Combine(outDirForDll, reszip.Name));
                            using (var fs = finfo.Create()) {
                                var data = reszip.GetResourceData();
                                fs.Write(data, 0, data.Length);
                            }

                            // temporarily extracted directory will look like:
                            //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                            using (var zip = MonoAndroidHelper.ReadZipFile(finfo.FullName))
                                Files.ExtractAll(zip, outDirForDll);

                            // We used to *copy* the resources to overwrite other resources,
                            // which resulted in missing resource issue.
                            // Here we replaced copy with use of '-S' option and made it to work.
#if SEPARATE_CRUNCH
                            // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                            // Enabling these blindly causes build failure on ActionBarSherlock.
                            if (Directory.Exists(binResDir))
                            {
                                resolvedResourceDirectories.Add(binResDir);
                            }
                            if (Directory.Exists(binAssemblyDir))
                            {
                                resolvedAssetDirectories.Add(binAssemblyDir);
                            }
#endif
                            if (Directory.Exists(resDir))
                            {
                                resolvedResourceDirectories.Add(resDir);
                            }
                            if (Directory.Exists(assemblyDir))
                            {
                                resolvedAssetDirectories.Add(assemblyDir);
                            }

                            finfo.Delete();
                        }
                    }

                    stamp.Create().Close();
                }
            }

            foreach (var f in outdir.GetFiles("*.jar")
                     .Select(fi => fi.FullName))
            {
                jars.Add(f);
            }
        }
        bool Execute(DirectoryAssemblyResolver res)
        {
            // Put every assembly we'll need in the resolver
            foreach (var assembly in ResolvedAssemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            var resolver = new AssemblyResolver(res.ToResolverCache());

            // Set up for linking
            var options = new LinkerOptions();

            options.MainAssembly     = res.GetAssembly(MainAssembly);
            options.OutputDirectory  = Path.GetFullPath(OutputDirectory);
            options.LinkSdkOnly      = string.Compare(LinkMode, "SdkOnly", true) == 0;
            options.LinkNone         = false;
            options.Resolver         = resolver;
            options.LinkDescriptions = LinkDescriptions.Select(item => Path.GetFullPath(item.ItemSpec)).ToArray();
            options.I18nAssemblies   = Linker.ParseI18nAssemblies(I18nAssemblies);
            if (!options.LinkSdkOnly)
            {
                options.RetainAssemblies = GetRetainAssemblies(res);
            }
            options.DumpDependencies          = DumpDependencies;
            options.HttpClientHandlerType     = HttpClientHandlerType;
            options.TlsProvider               = TlsProvider;
            options.AddKeepAlives             = AddKeepAlives;
            options.PreserveJniMarshalMethods = PreserveJniMarshalMethods;
            options.DeterministicOutput       = Deterministic;
            options.LinkResources             = LinkResources;

            var skiplist = new List <string> ();

            // Add LinkSkip options
            if (!string.IsNullOrWhiteSpace(LinkSkip))
            {
                skiplist.AddRange(LinkSkip.Split(',', ';'));
            }

            options.SkippedAssemblies = skiplist;

            if (EnableProguard)
            {
                options.ProguardConfiguration = ProguardConfiguration;
            }

            // Link!
            try {
                LinkContext link_context;
                Linker.Process(options, this, out link_context);

                foreach (var assembly in ResolvedAssemblies)
                {
                    var copysrc             = assembly.ItemSpec;
                    var filename            = Path.GetFileName(assembly.ItemSpec);
                    var assemblyDestination = Path.Combine(OutputDirectory, filename);

                    if (!MonoAndroidHelper.IsForceRetainedAssembly(filename))
                    {
                        continue;
                    }

                    MonoAndroidHelper.CopyAssemblyAndSymbols(copysrc, assemblyDestination);
                }
            } catch (ResolutionException ex) {
                Diagnostic.Error(2006, ex, Properties.Resources.XA2006, ex.Member, ex.Member.Module.Assembly, ex.Scope);
            }

            return(true);
        }
Example #16
0
 protected override AssemblyDefinition GetCorlibAssembly()
 {
     return(resolver.GetAssembly(hasSystemPrivateCoreLib ? "System.Private.CoreLib.dll" : "mscorlib.dll"));
 }
Example #17
0
        void CreateMarshalMethodAssembly(string path)
        {
            var assembly = Assembly.LoadFile(Path.GetFullPath(path));

            var baseName     = Path.GetFileNameWithoutExtension(path);
            var assemblyName = new AssemblyName(baseName + "-JniMarshalMethods");
            var fileName     = assemblyName.Name + ".dll";
            var destDir      = string.IsNullOrEmpty(outDirectory) ? Path.GetDirectoryName(path) : outDirectory;
            var builder      = CreateExportedMemberBuilder();
            var matchType    = typeNameRegexes.Count > 0;

            if (Verbose)
            {
                ColorWriteLine($"Preparing marshal method assembly '{assemblyName}'", ConsoleColor.Cyan);
            }

            var da = AppDomain.CurrentDomain.DefineDynamicAssembly(
                assemblyName,
                AssemblyBuilderAccess.Save,
                destDir);

            var dm = da.DefineDynamicModule("<default>", fileName);

            var ad = resolver.GetAssembly(path);

            PrepareTypeMap(ad.MainModule);

            Type[] types = null;
            try {
                types = assembly.GetTypes();
            } catch (ReflectionTypeLoadException e) {
                types = e.Types;
                foreach (var le in e.LoaderExceptions)
                {
                    Warning($"Type Load exception{Environment.NewLine}{le}");
                }
            }

            foreach (var systemType in types)
            {
                if (systemType == null)
                {
                    continue;
                }

                var type = systemType.GetTypeInfo();

                if (matchType)
                {
                    var matched = false;

                    foreach (var r in typeNameRegexes)
                    {
                        matched |= r.IsMatch(type.FullName);
                    }

                    if (!matched)
                    {
                        continue;
                    }
                }

                if (type.IsInterface || type.IsGenericType || type.IsGenericTypeDefinition)
                {
                    continue;
                }

                var td = FindType(type);

                if (td == null)
                {
                    if (Verbose)
                    {
                        Warning($"Unable to find cecil's TypeDefinition of type {type}");
                    }
                    continue;
                }
                if (!td.ImplementsInterface("Java.Interop.IJavaPeerable", cache))
                {
                    continue;
                }

                var existingMarshalMethodsType = td.GetNestedType(TypeMover.NestedName);
                if (existingMarshalMethodsType != null && !forceRegeneration)
                {
                    Warning($"Marshal methods type '{existingMarshalMethodsType.GetAssemblyQualifiedName (cache)}' already exists. Skipped generation of marshal methods in assembly '{assemblyName}'. Use -f to force regeneration when desired.");

                    return;
                }

                if (Verbose)
                {
                    ColorWriteLine($"Processing {type} type", ConsoleColor.Yellow);
                }

                var         registrationElements = new List <Expression> ();
                var         targetType           = Expression.Variable(typeof(Type), "targetType");
                TypeBuilder dt = null;

                var flags = BindingFlags.Public | BindingFlags.NonPublic |
                            BindingFlags.Instance | BindingFlags.Static;

                var methods = type.GetMethods(flags);
                Array.Sort(methods, new MethodsComparer(type, td));

                addedMethods.Clear();

                foreach (var method in methods)
                {
                    // TODO: Constructors
                    var    export     = method.GetCustomAttribute <JavaCallableAttribute> ();
                    string signature  = null;
                    string name       = null;
                    string methodName = method.Name;

                    if (export == null)
                    {
                        if (method.IsGenericMethod || method.ContainsGenericParameters || method.IsGenericMethodDefinition || method.ReturnType.IsGenericType)
                        {
                            continue;
                        }

                        if (method.DeclaringType != type)
                        {
                            continue;
                        }

                        var md = td.GetMethodDefinition(method);

                        if (md == null)
                        {
                            if (Verbose)
                            {
                                Warning($"Unable to find cecil's MethodDefinition of method {method}");
                            }
                            continue;
                        }

                        if (!md.NeedsMarshalMethod(resolver, cache, method, ref name, ref methodName, ref signature))
                        {
                            continue;
                        }
                    }

#if !_ALL_THE_ARGUMENTS
                    if (method.GetParameters().Length > 14)
                    {
                        Warning($"Methods taking more than 14 parameters is not supported.");
                        continue;
                    }
#endif  // !_ALL_THE_ARGUMENTS

                    if (dt == null)
                    {
                        dt = GetTypeBuilder(dm, type);
                    }

                    if (addedMethods.Contains(methodName))
                    {
                        continue;
                    }

                    if (Verbose)
                    {
                        Console.Write("Adding marshal method for ");
                        ColorWriteLine($"{method}", ConsoleColor.Green);
                    }

                    var mb = dt.DefineMethod(
                        methodName,
                        System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static);

                    var lambda = builder.CreateMarshalToManagedExpression(method);
                    lambda.CompileToMethod(mb);

                    if (export != null)
                    {
                        name      = export.Name;
                        signature = export.Signature;
                    }

                    if (signature == null)
                    {
                        signature = builder.GetJniMethodSignature(method);
                    }

                    registrationElements.Add(CreateRegistration(name, signature, lambda, targetType, methodName));

                    addedMethods.Add(methodName);
                }
                if (dt != null)
                {
                    AddRegisterNativeMembers(dt, targetType, registrationElements);
                }
            }

            foreach (var tb in definedTypes)
            {
                tb.Value.CreateType();
            }

            da.Save(fileName);

            if (Verbose)
            {
                ColorWriteLine($"Marshal method assembly '{assemblyName}' created", ConsoleColor.Cyan);
            }

            resolver.SearchDirectories.Add(destDir);
            var dstAssembly = resolver.GetAssembly(fileName);

            if (!string.IsNullOrEmpty(outDirectory))
            {
                path = Path.Combine(outDirectory, Path.GetFileName(path));
            }

            var mover = new TypeMover(dstAssembly, ad, path, definedTypes, resolver, cache);
            mover.Move();

            if (!keepTemporary)
            {
                FilesToDelete.Add(dstAssembly.MainModule.FileName);
            }
        }
Example #18
0
        public override bool Execute()
        {
            Log.LogDebugMessage("LinkAssemblies Task");
            Log.LogDebugMessage("  UseSharedRuntime: {0}", UseSharedRuntime);
            Log.LogDebugMessage("  MainAssembly: {0}", MainAssembly);
            Log.LogDebugMessage("  OutputDirectory: {0}", OutputDirectory);
            Log.LogDebugMessage("  OptionalDestinationDirectory: {0}", OptionalDestinationDirectory);
            Log.LogDebugMessage("  I18nAssemblies: {0}", I18nAssemblies);
            Log.LogDebugMessage("  LinkMode: {0}", LinkMode);
            Log.LogDebugMessage("  LinkSkip: {0}", LinkSkip);
            Log.LogDebugTaskItems("  LinkDescriptions:", LinkDescriptions);
            Log.LogDebugTaskItems("  ResolvedAssemblies:", ResolvedAssemblies);
            Log.LogDebugMessage("  EnableProguard: {0}", EnableProguard);
            Log.LogDebugMessage("  ProguardConfiguration: {0}", ProguardConfiguration);
            Log.LogDebugMessage("  DumpDependencies: {0}", DumpDependencies);
            Log.LogDebugMessage("  LinkOnlyNewerThan: {0}", LinkOnlyNewerThan);

            var res = new DirectoryAssemblyResolver(Log.LogWarning, loadDebugSymbols: false);

            // Put every assembly we'll need in the resolver
            foreach (var assembly in ResolvedAssemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            var resolver = new AssemblyResolver(res.ToResolverCache());

            // Set up for linking
            var options = new LinkerOptions();

            options.MainAssembly     = res.GetAssembly(MainAssembly);
            options.OutputDirectory  = Path.GetFullPath(OutputDirectory);
            options.LinkSdkOnly      = string.Compare(LinkMode, "SdkOnly", true) == 0;
            options.LinkNone         = string.Compare(LinkMode, "None", true) == 0;
            options.Resolver         = resolver;
            options.LinkDescriptions = LinkDescriptions.Select(item => Path.GetFullPath(item.ItemSpec)).ToArray();
            options.I18nAssemblies   = Linker.ParseI18nAssemblies(I18nAssemblies);
            if (!options.LinkSdkOnly)
            {
                options.RetainAssemblies = GetRetainAssemblies(res);
            }
            options.DumpDependencies = DumpDependencies;

            var skiplist = new List <string> ();

            if (string.Compare(UseSharedRuntime, "true", true) == 0)
            {
                skiplist.AddRange(Profile.SharedRuntimeAssemblies.Where(a => a.EndsWith(".dll")).Select(a => Path.GetFileNameWithoutExtension(a)));
            }
            if (!string.IsNullOrWhiteSpace(LinkOnlyNewerThan) && File.Exists(LinkOnlyNewerThan))
            {
                var newerThan   = File.GetLastWriteTime(LinkOnlyNewerThan);
                var skipOldOnes = ResolvedAssemblies.Where(a => File.GetLastWriteTime(a.ItemSpec) < newerThan);
                foreach (var old in skipOldOnes)
                {
                    Log.LogMessage(MessageImportance.Low, "  Skip linking unchanged file: " + old.ItemSpec);
                }
                skiplist = skipOldOnes.Select(a => Path.GetFileNameWithoutExtension(a.ItemSpec)).Concat(skiplist).ToList();
            }

            // Add LinkSkip options
            if (!string.IsNullOrWhiteSpace(LinkSkip))
            {
                foreach (var assembly in LinkSkip.Split(',', ';'))
                {
                    skiplist.Add(assembly);
                }
            }

            options.SkippedAssemblies = skiplist;

            if (EnableProguard)
            {
                options.ProguardConfiguration = ProguardConfiguration;
            }

            // Link!
            try {
                LinkContext link_context;
                Linker.Process(options, out link_context);

                var copydst = OptionalDestinationDirectory ?? OutputDirectory;

                foreach (var assembly in ResolvedAssemblies)
                {
                    var copysrc  = assembly.ItemSpec;
                    var filename = Path.GetFileName(assembly.ItemSpec);

                    if (options.LinkNone)
                    {
                        if (skiplist.Any(s => Path.GetFileNameWithoutExtension(filename) == s))
                        {
                            // For skipped assemblies, skip if there is existing file in the destination.
                            // We cannot just copy the linker output from *current* run output, because
                            // it always renew the assemblies, in *different* binary values, whereas
                            // the dll in the OptionalDestinationDirectory must retain old and unchanged.
                            if (File.Exists(Path.Combine(copydst, filename)))
                            {
                                continue;
                            }
                            copysrc = assembly.ItemSpec;
                        }
                        else
                        {
                            // Prefer fixup assemblies if exists, otherwise just copy the original.
                            copysrc = Path.Combine(OutputDirectory, filename);
                            copysrc = File.Exists(copysrc) ? copysrc : assembly.ItemSpec;
                        }
                    }
                    else if (!MonoAndroidHelper.IsForceRetainedAssembly(filename))
                    {
                        continue;
                    }

                    MonoAndroidHelper.CopyIfChanged(copysrc, Path.Combine(copydst, filename));
                    try {
                        MonoAndroidHelper.CopyIfChanged(assembly.ItemSpec + ".mdb", Path.Combine(copydst, filename + ".mdb"));
                    } catch (Exception) {                     // skip it, mdb sometimes fails to read and it's optional
                    }
                }
            } catch (ResolutionException ex) {
                Diagnostic.Error(2006, ex, "Reference to metadata item '{0}' (defined in '{1}') from '{1}' could not be resolved.", ex.Member, ex.Member.Module.Assembly, ex.Scope);
            }

            return(true);
        }
        void DoExecute()
        {
            LogDebugMessage("GetAdditionalResourcesFromAssemblies Task");
            LogDebugMessage("  AndroidSdkDirectory: {0}", AndroidSdkDirectory);
            LogDebugMessage("  AndroidNdkDirectory: {0}", AndroidNdkDirectory);
            LogDebugMessage("  CacheFile: {0}", CacheFile);
            LogDebugTaskItems("  Assemblies: ", Assemblies);

            if (Environment.GetEnvironmentVariable("XA_DL_IGNORE_CERT_ERRROS") == "yesyesyes")
            {
                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
                LogDebugMessage("    Disabling download certificate validation callback.");
            }
            var androidResources = new HashSet <string> ();
            var javaLibraries    = new HashSet <string> ();
            var nativeLibraries  = new HashSet <string> ();
            var assemblies       = new HashSet <string> ();

            if (Assemblies == null)
            {
                return;
            }

            var cacheFileFullPath = CacheFile;

            if (!Path.IsPathRooted(cacheFileFullPath))
            {
                cacheFileFullPath = Path.Combine(WorkingDirectory, cacheFileFullPath);
            }

            System.Threading.Tasks.Task.Run(() => {
                // The cache location can be overriden by the (to be documented) XAMARIN_CACHEPATH
                CachePath = Environment.ExpandEnvironmentVariables(CachePathEnvironmentVar);
                CachePath = CachePath != CachePathEnvironmentVar
                                ? CachePath
                                : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CacheBaseDir);

                using (var resolver = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: false)) {
                    foreach (var assemblyItem in Assemblies)
                    {
                        string fullPath = Path.GetFullPath(assemblyItem.ItemSpec);
                        if (DesignTimeBuild && !File.Exists(fullPath))
                        {
                            LogWarning("Failed to load '{0}'. Check the file exists or the project has been built.", fullPath);
                            continue;
                        }
                        if (assemblies.Contains(fullPath))
                        {
                            LogDebugMessage("  Skip assembly: {0}, it was already processed", fullPath);
                            continue;
                        }
                        // don't try to even load mscorlib it will fail.
                        if (string.Compare(Path.GetFileNameWithoutExtension(fullPath), "mscorlib", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            continue;
                        }
                        assemblies.Add(fullPath);
                        resolver.Load(fullPath);
                        // Append source file name (without the Xamarin. prefix or extension) to the base folder
                        // This would help avoid potential collisions.
                        foreach (var ca in resolver.GetAssembly(assemblyItem.ItemSpec).CustomAttributes)
                        {
                            switch (ca.AttributeType.FullName)
                            {
                            case "Android.IncludeAndroidResourcesFromAttribute":
                                AddAttributeValue(androidResources, ca, "XA5206", "{0}. Android resource directory {1} doesn't exist.", true, fullPath);
                                break;

                            case "Java.Interop.JavaLibraryReferenceAttribute":
                                AddAttributeValue(javaLibraries, ca, "XA5207", "{0}. Java library file {1} doesn't exist.", false, fullPath);
                                break;

                            case "Android.NativeLibraryReferenceAttribute":
                                AddAttributeValue(nativeLibraries, ca, "XA5210", "{0}. Native library file {1} doesn't exist.", false, fullPath);
                                break;
                            }
                        }
                    }
                }
            }, Token).ContinueWith(Complete);

            var result = base.Execute();

            if (!result || Log.HasLoggedErrors)
            {
                if (File.Exists(cacheFileFullPath))
                {
                    File.Delete(cacheFileFullPath);
                }
                return;
            }

            var AdditionalAndroidResourcePaths    = androidResources.ToArray();
            var AdditionalJavaLibraryReferences   = javaLibraries.ToArray();
            var AdditionalNativeLibraryReferences = nativeLibraries
                                                    .Where(x => MonoAndroidHelper.GetNativeLibraryAbi(x) != null)
                                                    .ToArray();

            var document = new XDocument(
                new XDeclaration("1.0", "UTF-8", null),
                new XElement("Paths",
                             new XElement("AdditionalAndroidResourcePaths",
                                          AdditionalAndroidResourcePaths.Select(e => new XElement("AdditionalAndroidResourcePath", e))),
                             new XElement("AdditionalJavaLibraryReferences",
                                          AdditionalJavaLibraryReferences.Select(e => new XElement("AdditionalJavaLibraryReference", e))),
                             new XElement("AdditionalNativeLibraryReferences",
                                          AdditionalNativeLibraryReferences.Select(e => new XElement("AdditionalNativeLibraryReference", e)))
                             ));

            document.SaveIfChanged(cacheFileFullPath);

            LogDebugTaskItems("  AdditionalAndroidResourcePaths: ", AdditionalAndroidResourcePaths);
            LogDebugTaskItems("  AdditionalJavaLibraryReferences: ", AdditionalJavaLibraryReferences);
            LogDebugTaskItems("  AdditionalNativeLibraryReferences: ", AdditionalNativeLibraryReferences);
        }
        public override bool Execute()
        {
            LogDebugMessage ("GetAdditionalResourcesFromAssemblies Task");
            LogDebugMessage ("  AndroidSdkDirectory: {0}", AndroidSdkDirectory);
            LogDebugMessage ("  AndroidNdkDirectory: {0}", AndroidNdkDirectory);
            LogDebugTaskItems ("  Assemblies: ", Assemblies);

            if (Environment.GetEnvironmentVariable ("XA_DL_IGNORE_CERT_ERRROS") == "yesyesyes") {
                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
                LogDebugMessage ("    Disabling download certificate validation callback.");
            }
            var androidResources   = new HashSet<string> ();
            var javaLibraries      = new HashSet<string> ();
            var nativeLibraries    = new HashSet<string> ();
            var assemblies         = new HashSet<string> ();

            if (Assemblies == null)
                return true;

            System.Threading.Tasks.Task.Run (() => {
                // The cache location can be overriden by the (to be documented) XAMARIN_CACHEPATH
                CachePath = Environment.ExpandEnvironmentVariables (CachePathEnvironmentVar);
                CachePath = CachePath != CachePathEnvironmentVar
                ? CachePath
                : Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), CacheBaseDir);

                var resolver = new DirectoryAssemblyResolver (Log.LogWarning, loadDebugSymbols: false);
                foreach (var assemblyItem in Assemblies) {
                    string fullPath = Path.GetFullPath (assemblyItem.ItemSpec);
                    if (assemblies.Contains (fullPath)) {
                        LogDebugMessage ("  Skip assembly: {0}, it was already processed", fullPath);
                        continue;
                    }
                    assemblies.Add (fullPath);
                    resolver.Load (fullPath);
                    // Append source file name (without the Xamarin. prefix or extension) to the base folder
                    // This would help avoid potential collisions.
                    foreach (var ca in resolver.GetAssembly (assemblyItem.ItemSpec).CustomAttributes) {
                        switch (ca.AttributeType.FullName) {
                        case "Android.IncludeAndroidResourcesFromAttribute":
                            AddAttributeValue (androidResources, ca, "XA5206", "{0}. Android resource directory {1} doesn't exist.", true, fullPath);
                            break;
                        case "Java.Interop.JavaLibraryReferenceAttribute":
                            AddAttributeValue (javaLibraries, ca, "XA5207", "{0}. Java library file {1} doesn't exist.", false, fullPath);
                            break;
                        case "Android.NativeLibraryReferenceAttribute":
                            AddAttributeValue (nativeLibraries, ca, "XA5210", "{0}. Native library file {1} doesn't exist.", false, fullPath);
                            break;
                        }
                    }
                }
            }).ContinueWith ((t) => {
                if (t.Exception != null)
                    Log.LogErrorFromException (t.Exception.GetBaseException ());
                Complete ();
            });

            var result = base.Execute ();

            var AdditionalAndroidResourcePaths = androidResources.ToArray ();
            var AdditionalJavaLibraryReferences = javaLibraries.ToArray ();
            var AdditionalNativeLibraryReferences = nativeLibraries
                .Where (x => MonoAndroidHelper.GetNativeLibraryAbi (x) != null)
                .ToArray ();

            var document = new XDocument (
                new XDeclaration ("1.0", "UTF-8", null),
                new XElement ("Paths",
                    new XElement ("AdditionalAndroidResourcePaths",
                            AdditionalAndroidResourcePaths.Select(e => new XElement ("AdditionalAndroidResourcePath", e))),
                    new XElement ("AdditionalJavaLibraryReferences",
                            AdditionalJavaLibraryReferences.Select(e => new XElement ("AdditionalJavaLibraryReference", e))),
                    new XElement ("AdditionalNativeLibraryReferences",
                            AdditionalNativeLibraryReferences.Select(e => new XElement ("AdditionalNativeLibraryReference", e)))
                    ));
            document.Save (CacheFile);

            LogDebugTaskItems ("  AdditionalAndroidResourcePaths: ", AdditionalAndroidResourcePaths);
            LogDebugTaskItems ("  AdditionalJavaLibraryReferences: ", AdditionalJavaLibraryReferences);
            LogDebugTaskItems ("  AdditionalNativeLibraryReferences: ", AdditionalNativeLibraryReferences);

            return result && !Log.HasLoggedErrors;
        }
        public override bool RunTask()
        {
            if (SourceFiles.Length != DestinationFiles.Length)
            {
                throw new ArgumentException("source and destination count mismatch");
            }

            var readerParameters = new ReaderParameters {
                ReadSymbols = true,
            };
            var writerParameters = new WriterParameters {
                DeterministicMvid = Deterministic,
            };

            using (var resolver = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: true, loadReaderParameters: readerParameters)) {
                // Add SearchDirectories with ResolvedAssemblies
                foreach (var assembly in ResolvedAssemblies)
                {
                    var path = Path.GetFullPath(Path.GetDirectoryName(assembly.ItemSpec));
                    if (!resolver.SearchDirectories.Contains(path))
                    {
                        resolver.SearchDirectories.Add(path);
                    }
                }

                // Set up the FixAbstractMethodsStep
                var step1 = new FixAbstractMethodsStep(resolver, new TypeDefinitionCache(), Log);
                // Set up the AddKeepAlivesStep
                var step2 = new MonoDroid.Tuner.AddKeepAlivesStep(new TypeDefinitionCache());
                for (int i = 0; i < SourceFiles.Length; i++)
                {
                    var source      = SourceFiles [i];
                    var destination = DestinationFiles [i];
                    AssemblyDefinition assemblyDefinition = null;

                    var assemblyName = Path.GetFileNameWithoutExtension(source.ItemSpec);
                    if (!MTProfile.IsSdkAssembly(assemblyName) && !MTProfile.IsProductAssembly(assemblyName))
                    {
                        assemblyDefinition = resolver.GetAssembly(source.ItemSpec);
                        step1.CheckAppDomainUsage(assemblyDefinition, (string msg) => Log.LogMessageFromText(msg, MessageImportance.High));
                    }

                    // Only run the step on "MonoAndroid" assemblies
                    if (MonoAndroidHelper.IsMonoAndroidAssembly(source) && !MonoAndroidHelper.IsSharedRuntimeAssembly(source.ItemSpec))
                    {
                        if (assemblyDefinition == null)
                        {
                            assemblyDefinition = resolver.GetAssembly(source.ItemSpec);
                        }

                        if (step1.FixAbstractMethods(assemblyDefinition) ||
                            (AddKeepAlives && step2.AddKeepAlives(assemblyDefinition)))
                        {
                            Log.LogDebugMessage($"Saving modified assembly: {destination.ItemSpec}");
                            writerParameters.WriteSymbols = assemblyDefinition.MainModule.HasSymbols;
                            assemblyDefinition.Write(destination.ItemSpec, writerParameters);
                            continue;
                        }
                    }

                    if (MonoAndroidHelper.CopyAssemblyAndSymbols(source.ItemSpec, destination.ItemSpec))
                    {
                        Log.LogDebugMessage($"Copied: {destination.ItemSpec}");
                    }
                    else
                    {
                        Log.LogDebugMessage($"Skipped unchanged file: {destination.ItemSpec}");

                        // NOTE: We still need to update the timestamp on this file, or this target would run again
                        File.SetLastWriteTimeUtc(destination.ItemSpec, DateTime.UtcNow);
                    }
                }
            }

            return(!Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            LogDebugMessage("GetAdditionalResourcesFromAssemblies Task");
            LogDebugMessage("  AndroidSdkDirectory: {0}", AndroidSdkDirectory);
            LogDebugMessage("  AndroidNdkDirectory: {0}", AndroidNdkDirectory);
            LogDebugTaskItems("  Assemblies: ", Assemblies);

            if (Environment.GetEnvironmentVariable("XA_DL_IGNORE_CERT_ERRROS") == "yesyesyes")
            {
                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
                LogDebugMessage("    Disabling download certificate validation callback.");
            }
            var androidResources = new HashSet <string> ();
            var javaLibraries    = new HashSet <string> ();
            var nativeLibraries  = new HashSet <string> ();
            var assemblies       = new HashSet <string> ();

            if (Assemblies == null)
            {
                return(true);
            }

            System.Threading.Tasks.Task.Run(() => {
                // The cache location can be overriden by the (to be documented) XAMARIN_CACHEPATH
                CachePath = Environment.ExpandEnvironmentVariables(CachePathEnvironmentVar);
                CachePath = CachePath != CachePathEnvironmentVar
                                ? CachePath
                                : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CacheBaseDir);

                var resolver = new DirectoryAssemblyResolver(Log.LogWarning, loadDebugSymbols: false);
                foreach (var assemblyItem in Assemblies)
                {
                    string fullPath = Path.GetFullPath(assemblyItem.ItemSpec);
                    if (assemblies.Contains(fullPath))
                    {
                        LogDebugMessage("  Skip assembly: {0}, it was already processed", fullPath);
                        continue;
                    }
                    assemblies.Add(fullPath);
                    resolver.Load(fullPath);
                    // Append source file name (without the Xamarin. prefix or extension) to the base folder
                    // This would help avoid potential collisions.
                    foreach (var ca in resolver.GetAssembly(assemblyItem.ItemSpec).CustomAttributes)
                    {
                        switch (ca.AttributeType.FullName)
                        {
                        case "Android.IncludeAndroidResourcesFromAttribute":
                            AddAttributeValue(androidResources, ca, "XA5206", "{0}. Android resource directory {1} doesn't exist.", true, fullPath);
                            break;

                        case "Java.Interop.JavaLibraryReferenceAttribute":
                            AddAttributeValue(javaLibraries, ca, "XA5207", "{0}. Java library file {1} doesn't exist.", false, fullPath);
                            break;

                        case "Android.NativeLibraryReferenceAttribute":
                            AddAttributeValue(nativeLibraries, ca, "XA5210", "{0}. Native library file {1} doesn't exist.", false, fullPath);
                            break;
                        }
                    }
                }
            }).ContinueWith((t) => {
                if (t.Exception != null)
                {
                    Log.LogErrorFromException(t.Exception.GetBaseException());
                }
                Complete();
            });

            var result = base.Execute();

            var AdditionalAndroidResourcePaths    = androidResources.ToArray();
            var AdditionalJavaLibraryReferences   = javaLibraries.ToArray();
            var AdditionalNativeLibraryReferences = nativeLibraries
                                                    .Where(x => MonoAndroidHelper.GetNativeLibraryAbi(x) != null)
                                                    .ToArray();

            var document = new XDocument(
                new XDeclaration("1.0", "UTF-8", null),
                new XElement("Paths",
                             new XElement("AdditionalAndroidResourcePaths",
                                          AdditionalAndroidResourcePaths.Select(e => new XElement("AdditionalAndroidResourcePath", e))),
                             new XElement("AdditionalJavaLibraryReferences",
                                          AdditionalJavaLibraryReferences.Select(e => new XElement("AdditionalJavaLibraryReference", e))),
                             new XElement("AdditionalNativeLibraryReferences",
                                          AdditionalNativeLibraryReferences.Select(e => new XElement("AdditionalNativeLibraryReference", e)))
                             ));

            document.Save(CacheFile);

            LogDebugTaskItems("  AdditionalAndroidResourcePaths: ", AdditionalAndroidResourcePaths);
            LogDebugTaskItems("  AdditionalJavaLibraryReferences: ", AdditionalJavaLibraryReferences);
            LogDebugTaskItems("  AdditionalNativeLibraryReferences: ", AdditionalNativeLibraryReferences);

            return(result && !Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            Log.LogDebugMessage ("LinkAssemblies Task");
            Log.LogDebugMessage ("  UseSharedRuntime: {0}", UseSharedRuntime);
            Log.LogDebugMessage ("  MainAssembly: {0}", MainAssembly);
            Log.LogDebugMessage ("  OutputDirectory: {0}", OutputDirectory);
            Log.LogDebugMessage ("  OptionalDestinationDirectory: {0}", OptionalDestinationDirectory);
            Log.LogDebugMessage ("  I18nAssemblies: {0}", I18nAssemblies);
            Log.LogDebugMessage ("  LinkMode: {0}", LinkMode);
            Log.LogDebugMessage ("  LinkSkip: {0}", LinkSkip);
            Log.LogDebugTaskItems ("  LinkDescriptions:", LinkDescriptions);
            Log.LogDebugTaskItems ("  ResolvedAssemblies:", ResolvedAssemblies);
            Log.LogDebugMessage ("  EnableProguard: {0}", EnableProguard);
            Log.LogDebugMessage ("  ProguardConfiguration: {0}", ProguardConfiguration);
            Log.LogDebugMessage ("  DumpDependencies: {0}", DumpDependencies);
            Log.LogDebugMessage ("  LinkOnlyNewerThan: {0}", LinkOnlyNewerThan);

            var res = new DirectoryAssemblyResolver (Log.LogWarning, loadDebugSymbols: false);

            // Put every assembly we'll need in the resolver
            foreach (var assembly in ResolvedAssemblies) {
                res.Load (Path.GetFullPath (assembly.ItemSpec));
            }

            var resolver = new AssemblyResolver (res.ToResolverCache ());

            // Set up for linking
            var options = new LinkerOptions ();
            options.MainAssembly = res.GetAssembly (MainAssembly);
            options.OutputDirectory = Path.GetFullPath (OutputDirectory);
            options.LinkSdkOnly = string.Compare (LinkMode, "SdkOnly", true) == 0;
            options.LinkNone = string.Compare (LinkMode, "None", true) == 0;
            options.Resolver = resolver;
            options.LinkDescriptions = LinkDescriptions.Select (item => Path.GetFullPath (item.ItemSpec)).ToArray ();
            options.I18nAssemblies = Linker.ParseI18nAssemblies (I18nAssemblies);
            if (!options.LinkSdkOnly)
                options.RetainAssemblies = GetRetainAssemblies (res);
            options.DumpDependencies = DumpDependencies;

            var skiplist = new List<string> ();

            if (string.Compare (UseSharedRuntime, "true", true) == 0)
                skiplist.AddRange (Profile.SharedRuntimeAssemblies.Where (a => a.EndsWith (".dll")).Select (a => Path.GetFileNameWithoutExtension (a)));
            if (!string.IsNullOrWhiteSpace (LinkOnlyNewerThan) && File.Exists (LinkOnlyNewerThan)) {
                var newerThan = File.GetLastWriteTime (LinkOnlyNewerThan);
                var skipOldOnes = ResolvedAssemblies.Where (a => File.GetLastWriteTime (a.ItemSpec) < newerThan);
                foreach (var old in skipOldOnes)
                    Log.LogMessage (MessageImportance.Low, "  Skip linking unchanged file: " + old.ItemSpec);
                skiplist = skipOldOnes.Select (a => Path.GetFileNameWithoutExtension (a.ItemSpec)).Concat (skiplist).ToList ();
            }

            // Add LinkSkip options
            if (!string.IsNullOrWhiteSpace (LinkSkip))
                foreach (var assembly in LinkSkip.Split (',', ';'))
                    skiplist.Add (assembly);

            options.SkippedAssemblies = skiplist;

            if (EnableProguard)
                options.ProguardConfiguration = ProguardConfiguration;

            // Link!
            try {
                LinkContext link_context;
                Linker.Process (options, out link_context);

                var copydst = OptionalDestinationDirectory ?? OutputDirectory;

                foreach (var assembly in ResolvedAssemblies) {
                    var copysrc = assembly.ItemSpec;
                    var filename = Path.GetFileName (assembly.ItemSpec);

                    if (options.LinkNone) {
                        if (skiplist.Any (s => Path.GetFileNameWithoutExtension (filename) == s)) {
                            // For skipped assemblies, skip if there is existing file in the destination.
                            // We cannot just copy the linker output from *current* run output, because
                            // it always renew the assemblies, in *different* binary values, whereas
                            // the dll in the OptionalDestinationDirectory must retain old and unchanged.
                            if (File.Exists (Path.Combine (copydst, filename)))
                                continue;
                            copysrc = assembly.ItemSpec;
                        } else {
                            // Prefer fixup assemblies if exists, otherwise just copy the original.
                            copysrc = Path.Combine (OutputDirectory, filename);
                            copysrc = File.Exists (copysrc) ? copysrc : assembly.ItemSpec;
                        }
                    }
                    else if (!MonoAndroidHelper.IsForceRetainedAssembly (filename))
                        continue;

                    MonoAndroidHelper.CopyIfChanged (copysrc, Path.Combine (copydst, filename));
                    try {
                        MonoAndroidHelper.CopyIfChanged (assembly.ItemSpec + ".mdb", Path.Combine (copydst, filename + ".mdb"));
                    } catch (Exception) { // skip it, mdb sometimes fails to read and it's optional
                    }
                }
            } catch (ResolutionException ex) {
                Diagnostic.Error (2006, ex, "Reference to metadata item '{0}' (defined in '{1}') from '{1}' could not be resolved.", ex.Member, ex.Member.Module.Assembly, ex.Scope);
            }

            return true;
        }
        public override bool Execute()
        {
            Log.LogDebugMessage("StripEmbeddedLibraries Task");
            Log.LogDebugTaskItems("  Assemblies: ", Assemblies);

            var res = new DirectoryAssemblyResolver(Log.LogWarning, true);

            foreach (var assembly in Assemblies)
            {
                res.Load(Path.GetFullPath(assembly.ItemSpec));
            }

            foreach (var assemblyName in Assemblies)
            {
                var    suffix   = assemblyName.ItemSpec.EndsWith(".dll") ? String.Empty : ".dll";
                string hintPath = assemblyName.GetMetadata("HintPath").Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                string fileName = assemblyName.ItemSpec + suffix;
                if (!String.IsNullOrEmpty(hintPath) && !File.Exists(hintPath))                   // ignore invalid HintPath
                {
                    hintPath = null;
                }
                string assemblyPath = String.IsNullOrEmpty(hintPath) ? fileName : hintPath;
                if (MonoAndroidHelper.IsFrameworkAssembly(fileName) && !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains(Path.GetFileName(fileName)))
                {
                    continue;
                }

                var  assembly          = res.GetAssembly(assemblyPath);
                bool assembly_modified = false;
                foreach (var mod in assembly.Modules)
                {
                    // embedded jars
                    var resjars = mod.Resources.Where(r => r.Name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase)).Select(r => (EmbeddedResource)r);
                    foreach (var resjar in resjars.ToArray())
                    {
                        Log.LogDebugMessage("    Stripped {0}", resjar.Name);
                        mod.Resources.Remove(resjar);
                        assembly_modified = true;
                    }
                    // embedded AndroidNativeLibrary archive
                    var nativezip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                    if (nativezip != null)
                    {
                        Log.LogDebugMessage("    Stripped {0}", nativezip.Name);
                        mod.Resources.Remove(nativezip);
                        assembly_modified = true;
                    }
                    // embedded AndroidResourceLibrary archive
                    var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                    if (reszip != null)
                    {
                        Log.LogDebugMessage("    Stripped {0}", reszip.Name);
                        mod.Resources.Remove(reszip);
                        assembly_modified = true;
                    }
                }
                if (assembly_modified)
                {
                    Log.LogDebugMessage("    The stripped library is saved as {0}", assemblyPath);

                    // Output assembly needs to regenerate symbol file even if no IL/metadata was touched
                    // because Cecil still rewrites all assembly types in Cecil order (type A, nested types of A, type B, etc)
                    // and not in the original order causing symbols if original order doesn't match Cecil order
                    var wp = new WriterParameters()
                    {
                        WriteSymbols = assembly.MainModule.HasSymbols
                    };

                    assembly.Write(assemblyPath, wp);
                }
            }
            return(true);
        }
 IEnumerable<AssemblyDefinition> GetRetainAssemblies(DirectoryAssemblyResolver res)
 {
     List<AssemblyDefinition> retainList = null;
     foreach (var assembly in ResolvedAssemblies) {
         var filename = Path.GetFileName (assembly.ItemSpec);
         if (!MonoAndroidHelper.IsForceRetainedAssembly (filename))
             continue;
         if (retainList == null)
             retainList = new List<AssemblyDefinition> ();
         retainList.Add (res.GetAssembly (assembly.ItemSpec));
     }
     return retainList;
 }
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        // Extracts library project contents under e.g. obj/Debug/[lp/*.jar | res/*/*]
        void Extract(
            DirectoryAssemblyResolver res,
            IDictionary <string, ITaskItem> jars,
            ICollection <ITaskItem> resolvedResourceDirectories,
            ICollection <ITaskItem> resolvedAssetDirectories,
            ICollection <ITaskItem> resolvedEnvironments)
        {
            // lets "upgrade" the old directory.
            string oldPath = Path.GetFullPath(Path.Combine(OutputImportDirectory, "..", "__library_projects__"));

            if (!OutputImportDirectory.Contains("__library_projects__") && Directory.Exists(oldPath))
            {
                MonoAndroidHelper.SetDirectoryWriteable(Path.Combine(oldPath, ".."));
                Directory.Delete(oldPath, recursive: true);
            }
            var outdir = Path.GetFullPath(OutputImportDirectory);

            Directory.CreateDirectory(outdir);

            foreach (var assembly in Assemblies)
            {
                res.Load(assembly.ItemSpec);
            }

            // FIXME: reorder references by import priority (not sure how to do that yet)
            foreach (var assemblyPath in Assemblies
                     .Select(a => a.ItemSpec)
                     .Distinct())
            {
                var fileName = Path.GetFileName(assemblyPath);
                if (MonoAndroidHelper.IsFrameworkAssembly(fileName) &&
                    !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains(fileName) &&
                    !MonoAndroidHelper.FrameworkEmbeddedNativeLibraryAssemblies.Contains(fileName))
                {
                    Log.LogDebugMessage($"Skipping framework assembly '{fileName}'.");
                    continue;
                }
                if (DesignTimeBuild && !File.Exists(assemblyPath))
                {
                    Log.LogDebugMessage($"Skipping non-existent dependency '{assemblyPath}' during a design-time build.");
                    continue;
                }
                if (assembliestoSkipExtraction.Contains(assemblyPath))
                {
                    Log.LogDebugMessage("Skipping resource extraction for '{0}' .", assemblyPath);
                    continue;
                }
                string assemblyFileName  = Path.GetFileNameWithoutExtension(assemblyPath);
                string assemblyIdentName = assemblyFileName;
                if (UseShortFileNames)
                {
                    assemblyIdentName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(assemblyFileName);
                }
                string outDirForDll     = Path.Combine(OutputImportDirectory, assemblyIdentName);
                string importsDir       = Path.Combine(outDirForDll, ImportsDirectory);
                string nativeimportsDir = Path.Combine(outDirForDll, NativeImportsDirectory);
                string resDir           = Path.Combine(importsDir, "res");
                string assetsDir        = Path.Combine(importsDir, "assets");

                // Skip already-extracted resources.
                bool   updated      = false;
                string assemblyHash = MonoAndroidHelper.HashFile(assemblyPath);
                string stamp        = Path.Combine(outdir, assemblyIdentName + ".stamp");
                string stampHash    = File.Exists(stamp) ? File.ReadAllText(stamp) : null;
                if (assemblyHash == stampHash)
                {
                    Log.LogDebugMessage("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
                    if (Directory.Exists(importsDir))
                    {
                        foreach (var file in Directory.EnumerateFiles(importsDir, "*.jar", SearchOption.AllDirectories))
                        {
                            AddJar(jars, Path.GetFullPath(file));
                        }
                    }
                    if (Directory.Exists(resDir))
                    {
                        var taskItem = new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath },
                        });
                        if (assembliesToSkipCaseFixup.Contains(assemblyPath))
                        {
                            taskItem.SetMetadata(AndroidSkipResourceProcessing, "True");
                        }
                        resolvedResourceDirectories.Add(taskItem);
                    }
                    if (Directory.Exists(assetsDir))
                    {
                        resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }
                    foreach (var env in Directory.EnumerateFiles(outDirForDll, "__AndroidEnvironment__*", SearchOption.TopDirectoryOnly))
                    {
                        resolvedEnvironments.Add(new TaskItem(env, new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }
                    continue;
                }

                Log.LogDebugMessage($"Refreshing {assemblyFileName}.dll");

                var assembly = res.GetAssembly(assemblyPath);
                foreach (var mod in assembly.Modules)
                {
                    // android environment files
                    foreach (var envtxt in mod.Resources
                             .Where(r => r.Name.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase))
                             .Where(r => r is EmbeddedResource)
                             .Cast <EmbeddedResource> ())
                    {
                        var outFile = Path.Combine(outDirForDll, envtxt.Name);
                        using (var stream = envtxt.GetResourceStream()) {
                            updated |= MonoAndroidHelper.CopyIfStreamChanged(stream, outFile);
                        }
                        resolvedEnvironments.Add(new TaskItem(Path.GetFullPath(outFile), new Dictionary <string, string> {
                            { OriginalFile, assemblyPath }
                        }));
                    }

                    // embedded jars (EmbeddedJar, EmbeddedReferenceJar)
                    var resjars = mod.Resources
                                  .Where(r => r.Name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase))
                                  .Select(r => (EmbeddedResource)r);
                    foreach (var resjar in resjars)
                    {
                        using (var stream = resjar.GetResourceStream()) {
                            AddJar(jars, importsDir, resjar.Name, assemblyPath);
                            updated |= MonoAndroidHelper.CopyIfStreamChanged(stream, Path.Combine(importsDir, resjar.Name));
                        }
                    }

                    var libzip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource;
                    if (libzip != null)
                    {
                        List <string> files = new List <string> ();
                        using (var stream = libzip.GetResourceStream())
                            using (var zip = Xamarin.Tools.Zip.ZipArchive.Open(stream)) {
                                try {
                                    updated |= Files.ExtractAll(zip, nativeimportsDir, modifyCallback: (entryFullName) => {
                                        files.Add(Path.GetFullPath(Path.Combine(nativeimportsDir, entryFullName)));
                                        return(entryFullName
                                               .Replace("native_library_imports\\", "")
                                               .Replace("native_library_imports/", ""));
                                    }, deleteCallback: (fileToDelete) => {
                                        return(!files.Contains(fileToDelete));
                                    });
                                } catch (PathTooLongException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                } catch (NotSupportedException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                }
                            }
                    }

                    // embedded AndroidResourceLibrary archive
                    var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                    if (reszip != null)
                    {
                        // temporarily extracted directory will look like:
                        //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                        using (var stream = reszip.GetResourceStream())
                            using (var zip = Xamarin.Tools.Zip.ZipArchive.Open(stream)) {
                                try {
                                    updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                                        var path = entryFullName
                                                   .Replace("library_project_imports\\", "")
                                                   .Replace("library_project_imports/", "");
                                        if (path.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                                        {
                                            AddJar(jars, importsDir, path, assemblyPath);
                                        }
                                        return(path);
                                    }, deleteCallback: (fileToDelete) => {
                                        return(!jars.ContainsKey(fileToDelete));
                                    });
                                } catch (PathTooLongException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                } catch (NotSupportedException ex) {
                                    Log.LogCodedError("XA4303", $"Error extracting resources from \"{assemblyPath}\": {ex}");
                                    return;
                                }
                            }

                        // We used to *copy* the resources to overwrite other resources,
                        // which resulted in missing resource issue.
                        // Here we replaced copy with use of '-S' option and made it to work.
                        if (Directory.Exists(resDir))
                        {
                            var taskItem = new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                                { OriginalFile, assemblyPath }
                            });
                            if (assembliesToSkipCaseFixup.Contains(assemblyPath))
                            {
                                taskItem.SetMetadata(AndroidSkipResourceProcessing, "True");
                            }
                            resolvedResourceDirectories.Add(taskItem);
                        }
                        if (Directory.Exists(assetsDir))
                        {
                            resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                                { OriginalFile, assemblyPath }
                            }));
                        }
                    }
                }

                if (Directory.Exists(importsDir))
                {
                    // Delete unknown files in the top directory of importsDir
                    foreach (var file in Directory.EnumerateFiles(importsDir, "*"))
                    {
                        var fullPath = Path.GetFullPath(file);
                        if (file.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase) && !resolvedEnvironments.Any(x => x.ItemSpec == fullPath))
                        {
                            Log.LogDebugMessage($"Deleting unknown AndroidEnvironment file: {Path.GetFileName (file)}");
                            File.Delete(fullPath);
                            updated = true;
                        }
                        else if (file.EndsWith(".jar", StringComparison.OrdinalIgnoreCase) && !jars.ContainsKey(fullPath))
                        {
                            Log.LogDebugMessage($"Deleting unknown jar: {Path.GetFileName (file)}");
                            File.Delete(fullPath);
                            updated = true;
                        }
                    }
                    if (assemblyHash != stampHash)
                    {
                        Log.LogDebugMessage($"Saving hash to {stamp}, changes: {updated}");
                        //NOTE: if the hash is different we always want to write the file, but preserve the timestamp if no changes
                        WriteAllText(stamp, assemblyHash, preserveTimestamp: !updated);
                    }
                }
            }
            foreach (var aarFile in AarLibraries ?? new ITaskItem[0])
            {
                if (!File.Exists(aarFile.ItemSpec))
                {
                    continue;
                }
                string aarIdentityName = Path.GetFileNameWithoutExtension(aarFile.ItemSpec);
                if (UseShortFileNames)
                {
                    aarIdentityName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(aarIdentityName);
                }
                string outDirForDll = Path.Combine(OutputImportDirectory, aarIdentityName);
                string importsDir   = Path.Combine(outDirForDll, ImportsDirectory);
                string resDir       = Path.Combine(importsDir, "res");
                string assetsDir    = Path.Combine(importsDir, "assets");

                bool   updated     = false;
                string aarHash     = MonoAndroidHelper.HashFile(aarFile.ItemSpec);
                string stamp       = Path.Combine(outdir, aarIdentityName + ".stamp");
                string stampHash   = File.Exists(stamp) ? File.ReadAllText(stamp) : null;
                var    aarFullPath = Path.GetFullPath(aarFile.ItemSpec);
                if (aarHash == stampHash)
                {
                    Log.LogDebugMessage("Skipped {0}: extracted files are up to date", aarFile.ItemSpec);
                    if (Directory.Exists(importsDir))
                    {
                        foreach (var file in Directory.EnumerateFiles(importsDir, "*.jar", SearchOption.AllDirectories))
                        {
                            AddJar(jars, Path.GetFullPath(file));
                        }
                    }
                    if (Directory.Exists(resDir))
                    {
                        resolvedResourceDirectories.Add(new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                            { OriginalFile, Path.GetFullPath(aarFile.ItemSpec) },
                            { AndroidSkipResourceProcessing, "True" },
                        }));
                    }
                    if (Directory.Exists(assetsDir))
                    {
                        resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                            { OriginalFile, aarFullPath },
                        }));
                    }
                    continue;
                }

                Log.LogDebugMessage($"Refreshing {aarFile.ItemSpec}");

                // temporarily extracted directory will look like:
                // _lp_/[aarFile]

                using (var zip = MonoAndroidHelper.ReadZipFile(aarFile.ItemSpec)) {
                    try {
                        updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                            var entryFileName = Path.GetFileName(entryFullName);
                            var entryPath     = Path.GetDirectoryName(entryFullName);
                            if (entryFileName.StartsWith("internal_impl", StringComparison.InvariantCulture))
                            {
                                var hash = Files.HashString(entryFileName);
                                var jar  = Path.Combine(entryPath, $"internal_impl-{hash}.jar");
                                AddJar(jars, importsDir, jar, aarFullPath);
                                return(jar);
                            }
                            if (entryFullName.EndsWith(".jar", StringComparison.OrdinalIgnoreCase))
                            {
                                AddJar(jars, importsDir, entryFullName, aarFullPath);
                            }
                            return(entryFullName);
                        }, deleteCallback: (fileToDelete) => {
                            return(!jars.ContainsKey(fileToDelete));
                        });

                        if (Directory.Exists(importsDir) && aarHash != stampHash)
                        {
                            Log.LogDebugMessage($"Saving hash to {stamp}, changes: {updated}");
                            //NOTE: if the hash is different we always want to write the file, but preserve the timestamp if no changes
                            WriteAllText(stamp, aarHash, preserveTimestamp: !updated);
                        }
                    } catch (PathTooLongException ex) {
                        Log.LogErrorFromException(new PathTooLongException($"Error extracting resources from \"{aarFile.ItemSpec}\"", ex));
                    }
                }
                if (Directory.Exists(resDir))
                {
                    resolvedResourceDirectories.Add(new TaskItem(Path.GetFullPath(resDir), new Dictionary <string, string> {
                        { OriginalFile, aarFullPath },
                        { AndroidSkipResourceProcessing, "True" },
                    }));
                }
                if (Directory.Exists(assetsDir))
                {
                    resolvedAssetDirectories.Add(new TaskItem(Path.GetFullPath(assetsDir), new Dictionary <string, string> {
                        { OriginalFile, aarFullPath },
                    }));
                }
            }
        }
Example #27
0
        // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*]
        // Extracts library project contents under e.g. obj/Debug/[lp/*.jar | res/*/*]
        void Extract(
            DirectoryAssemblyResolver res,
            ICollection <string> jars,
            ICollection <string> resolvedResourceDirectories,
            ICollection <string> resolvedAssetDirectories,
            ICollection <string> resolvedEnvironments)
        {
            // lets "upgrade" the old directory.
            string oldPath = Path.GetFullPath(Path.Combine(OutputImportDirectory, "..", "__library_projects__"));

            if (!OutputImportDirectory.Contains("__library_projects__") && Directory.Exists(oldPath))
            {
                MonoAndroidHelper.SetDirectoryWriteable(Path.Combine(oldPath, ".."));
                Directory.Delete(oldPath, recursive: true);
            }
            var outdir = new DirectoryInfo(OutputImportDirectory);

            if (!outdir.Exists)
            {
                outdir.Create();
            }

            foreach (var assembly in Assemblies)
            {
                res.Load(assembly.ItemSpec);
            }

            // FIXME: reorder references by import priority (not sure how to do that yet)
            foreach (var assemblyPath in Assemblies
                     .Select(a => GetTargetAssembly(a))
                     .Where(a => a != null)
                     .Distinct())
            {
                string assemblyIdentName = Path.GetFileNameWithoutExtension(assemblyPath);
                if (UseShortFileNames)
                {
                    assemblyIdentName = assemblyMap.GetLibraryImportDirectoryNameForAssembly(assemblyIdentName);
                }
                string outDirForDll = Path.Combine(OutputImportDirectory, assemblyIdentName);
                string importsDir   = Path.Combine(outDirForDll, ImportsDirectory);
#if SEPARATE_CRUNCH
                // FIXME: review these binResDir thing and enable this. Eclipse does this.
                // Enabling these blindly causes build failure on ActionBarSherlock.
                //string binResDir = Path.Combine (importsDir, "bin", "res");
                //string binAssemblyDir = Path.Combine (importsDir, "bin", "assets");
#endif
                string resDir      = Path.Combine(importsDir, "res");
                string assemblyDir = Path.Combine(importsDir, "assets");

                // Skip already-extracted resources.
                var stamp = new FileInfo(Path.Combine(outdir.FullName, assemblyIdentName + ".stamp"));
                if (stamp.Exists && stamp.LastWriteTimeUtc > new FileInfo(assemblyPath).LastWriteTimeUtc)
                {
                    Log.LogDebugMessage("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath);
#if SEPARATE_CRUNCH
                    // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                    // Enabling these blindly causes build failure on ActionBarSherlock.
                    if (Directory.Exists(binResDir))
                    {
                        resolvedResourceDirectories.Add(binResDir);
                    }
                    if (Directory.Exists(binAssemblyDir))
                    {
                        resolvedAssetDirectories.Add(binAssemblyDir);
                    }
#endif
                    if (Directory.Exists(resDir))
                    {
                        resolvedResourceDirectories.Add(resDir);
                    }
                    if (Directory.Exists(assemblyDir))
                    {
                        resolvedAssetDirectories.Add(assemblyDir);
                    }
                    foreach (var env in Directory.EnumerateFiles(outDirForDll, "__AndroidEnvironment__*", SearchOption.TopDirectoryOnly))
                    {
                        resolvedEnvironments.Add(env);
                    }
                    continue;
                }

                Log.LogDebugMessage("Refreshing {0}", assemblyPath);

                Directory.CreateDirectory(importsDir);

                var  assembly          = res.GetAssembly(assemblyPath);
                var  assemblyLastWrite = new FileInfo(assemblyPath).LastWriteTimeUtc;
                bool updated           = false;

                foreach (var mod in assembly.Modules)
                {
                    // android environment files
                    foreach (var envtxt in mod.Resources
                             .Where(r => r.Name.StartsWith("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase))
                             .Where(r => r is EmbeddedResource)
                             .Cast <EmbeddedResource> ())
                    {
                        if (!Directory.Exists(outDirForDll))
                        {
                            Directory.CreateDirectory(outDirForDll);
                        }
                        var finfo = new FileInfo(Path.Combine(outDirForDll, envtxt.Name));
                        if (!finfo.Exists || finfo.LastWriteTimeUtc > assemblyLastWrite)
                        {
                            using (var fs = finfo.Create()) {
                                var data = envtxt.GetResourceData();
                                fs.Write(data, 0, data.Length);
                            }
                            updated = true;
                        }
                        resolvedEnvironments.Add(finfo.FullName);
                    }

                    // embedded jars (EmbeddedJar, EmbeddedReferenceJar)
                    var resjars = mod.Resources
                                  .Where(r => r.Name.EndsWith(".jar", StringComparison.InvariantCultureIgnoreCase))
                                  .Select(r => (EmbeddedResource)r);
                    foreach (var resjar in resjars)
                    {
                        var outjarFile = Path.Combine(importsDir, resjar.Name);
                        var fi         = new FileInfo(outjarFile);
                        if (!fi.Exists || fi.LastWriteTimeUtc > assemblyLastWrite)
                        {
                            var data = resjar.GetResourceData();
                            using (var outfs = File.Create(outjarFile))
                                outfs.Write(data, 0, data.Length);
                            updated = true;
                        }
                        jars.Add(outjarFile);
                    }

                    // embedded AndroidResourceLibrary archive
                    var reszip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource;
                    if (reszip != null)
                    {
                        if (!Directory.Exists(outDirForDll))
                        {
                            Directory.CreateDirectory(outDirForDll);
                        }
                        var finfo = new FileInfo(Path.Combine(outDirForDll, reszip.Name));
                        using (var fs = finfo.Create()) {
                            var data = reszip.GetResourceData();
                            fs.Write(data, 0, data.Length);
                        }

                        // temporarily extracted directory will look like:
                        //    __library_projects__/[dllname]/[library_project_imports | jlibs]/bin
                        using (var zip = MonoAndroidHelper.ReadZipFile(finfo.FullName)) {
                            try {
                                updated |= Files.ExtractAll(zip, importsDir, modifyCallback: (entryFullName) => {
                                    return(entryFullName
                                           .Replace("library_project_imports\\", "")
                                           .Replace("library_project_imports/", ""));
                                }, deleteCallback: (fileToDelete) => {
                                    return(!jars.Contains(fileToDelete));
                                }, forceUpdate: false);
                            } catch (PathTooLongException ex) {
                                Log.LogErrorFromException(new PathTooLongException($"Error extracting resources from \"{assemblyPath}\"", ex));
                            }
                        }

                        // We used to *copy* the resources to overwrite other resources,
                        // which resulted in missing resource issue.
                        // Here we replaced copy with use of '-S' option and made it to work.
#if SEPARATE_CRUNCH
                        // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this.
                        // Enabling these blindly causes build failure on ActionBarSherlock.
                        if (Directory.Exists(binResDir))
                        {
                            resolvedResourceDirectories.Add(binResDir);
                        }
                        if (Directory.Exists(binAssemblyDir))
                        {
                            resolvedAssetDirectories.Add(binAssemblyDir);
                        }
#endif
                        if (Directory.Exists(resDir))
                        {
                            resolvedResourceDirectories.Add(resDir);
                        }
                        if (Directory.Exists(assemblyDir))
                        {
                            resolvedAssetDirectories.Add(assemblyDir);
                        }

                        finfo.Delete();
                    }
                }

                if (Directory.Exists(importsDir) && (updated || !stamp.Exists))
                {
                    Log.LogDebugMessage("Touch {0}", stamp.FullName);
                    stamp.Create().Close();
                }
            }
            foreach (var f in outdir.GetFiles("*.jar", SearchOption.AllDirectories)
                     .Select(fi => fi.FullName))
            {
                if (jars.Contains(f))
                {
                    continue;
                }
                jars.Add(f);
            }
        }
        public override bool Execute()
        {
            // In Xamarin Studio, if the project name isn't a valid C# identifier
            // then $(RootNamespace) is not set, and the generated Activity is
            // placed into the "Application" namespace. VS just munges the project
            // name to be a valid C# identifier.
            // Use "Application" as the default namespace name to work with XS.
            Namespace = Namespace ?? "Application";

            Log.LogDebugMessage("GenerateResourceDesigner Task");
            Log.LogDebugMessage("  NetResgenOutputFile: {0}", NetResgenOutputFile);
            Log.LogDebugMessage("  JavaResgenInputFile: {0}", JavaResgenInputFile);
            Log.LogDebugMessage("  Namespace: {0}", Namespace);
            Log.LogDebugMessage("  ResourceDirectory: {0}", ResourceDirectory);
            Log.LogDebugTaskItemsAndLogical("  AdditionalResourceDirectories:", AdditionalResourceDirectories);
            Log.LogDebugMessage("  IsApplication: {0}", IsApplication);
            Log.LogDebugMessage("  UseManagedResourceGenerator: {0}", UseManagedResourceGenerator);
            Log.LogDebugTaskItemsAndLogical("  Resources:", Resources);
            Log.LogDebugTaskItemsAndLogical("  References:", References);

            if (!File.Exists(JavaResgenInputFile) && !UseManagedResourceGenerator)
            {
                return(true);
            }

            // ResourceDirectory may be a relative path, and
            // we need to compare it to absolute paths
            ResourceDirectory = Path.GetFullPath(ResourceDirectory);

            // Create our capitalization maps so we can support mixed case resources
            foreach (var item in Resources)
            {
                if (!item.ItemSpec.StartsWith(ResourceDirectory))
                {
                    continue;
                }

                var name         = item.ItemSpec.Substring(ResourceDirectory.Length);
                var logical_name = item.GetMetadata("LogicalName").Replace('\\', '/');

                AddRename(name.Replace('/', Path.DirectorySeparatorChar), logical_name.Replace('/', Path.DirectorySeparatorChar));
            }
            if (AdditionalResourceDirectories != null)
            {
                foreach (var additionalDir in AdditionalResourceDirectories)
                {
                    var file = Path.Combine(ProjectDir, Path.GetDirectoryName(additionalDir.ItemSpec), "__res_name_case_map.txt");
                    if (File.Exists(file))
                    {
                        foreach (var line in File.ReadAllLines(file).Where(l => !string.IsNullOrEmpty(l)))
                        {
                            string [] tok = line.Split(';');
                            AddRename(tok [1].Replace('/', Path.DirectorySeparatorChar), tok [0].Replace('/', Path.DirectorySeparatorChar));
                        }
                    }
                }
            }

            // Parse out the resources from the R.java file
            CodeTypeDeclaration resources;

            if (UseManagedResourceGenerator)
            {
                var parser = new ManagedResourceParser()
                {
                    Log = Log
                };
                resources = parser.Parse(ResourceDirectory, AdditionalResourceDirectories?.Select(x => x.ItemSpec), IsApplication, resource_fixup);
            }
            else
            {
                var parser = new JavaResourceParser()
                {
                    Log = Log
                };
                resources = parser.Parse(JavaResgenInputFile, IsApplication, resource_fixup);
            }

            var  extension = Path.GetExtension(NetResgenOutputFile);
            var  language  = string.Compare(extension, ".fs", StringComparison.OrdinalIgnoreCase) == 0 ? "F#" : CodeDomProvider.GetLanguageFromExtension(extension);
            bool isVB      = string.Equals(extension, ".vb", StringComparison.OrdinalIgnoreCase);
            bool isFSharp  = string.Equals(language, "F#", StringComparison.OrdinalIgnoreCase);
            bool isCSharp  = string.Equals(language, "C#", StringComparison.OrdinalIgnoreCase);

            // Let VB put this in the default namespace
            if (isVB)
            {
                Namespace = string.Empty;
            }

            // Create static resource overwrite methods for each Resource class in libraries.
            var assemblyNames = new List <string> ();

            if (IsApplication && References != null && References.Any())
            {
                // FIXME: should this be unified to some better code with ResolveLibraryProjectImports?
                using (var resolver = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: false)) {
                    foreach (var assemblyName in References)
                    {
                        var    suffix   = assemblyName.ItemSpec.EndsWith(".dll") ? String.Empty : ".dll";
                        string hintPath = assemblyName.GetMetadata("HintPath").Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                        string fileName = assemblyName.ItemSpec + suffix;
                        string fullPath = Path.GetFullPath(assemblyName.ItemSpec);
                        // Skip non existing files in DesignTimeBuild
                        if (!File.Exists(fullPath) && DesignTimeBuild)
                        {
                            Log.LogDebugMessage("Skipping non existant dependancy '{0}' due to design time build.", fullPath);
                            continue;
                        }
                        resolver.Load(fullPath);
                        if (!String.IsNullOrEmpty(hintPath) && !File.Exists(hintPath))                           // ignore invalid HintPath
                        {
                            hintPath = null;
                        }
                        string assemblyPath = String.IsNullOrEmpty(hintPath) ? fileName : hintPath;
                        if (MonoAndroidHelper.IsFrameworkAssembly(fileName) && !MonoAndroidHelper.FrameworkEmbeddedJarLookupTargets.Contains(Path.GetFileName(fileName)))
                        {
                            continue;
                        }
                        Log.LogDebugMessage("Scan assembly {0} for resource generator", fileName);
                        assemblyNames.Add(assemblyPath);
                    }
                    var assemblies = assemblyNames.Select(assembly => resolver.GetAssembly(assembly));
                    new ResourceDesignerImportGenerator(Namespace, resources, Log)
                    .CreateImportMethods(assemblies);
                }
            }

            AdjustConstructor(isFSharp, resources);
            foreach (var member in resources.Members)
            {
                if (member is CodeTypeDeclaration)
                {
                    AdjustConstructor(isFSharp, (CodeTypeDeclaration)member);
                }
            }

            // Write out our Resources.Designer.cs file

            WriteFile(NetResgenOutputFile, resources, language, isFSharp, isCSharp);

            return(!Log.HasLoggedErrors);
        }
Example #29
0
        void CreateMarshalMethodAssembly(string path)
        {
            var assembly = Assembly.LoadFile(path);

            var baseName     = Path.GetFileNameWithoutExtension(path);
            var assemblyName = new AssemblyName(baseName + "-JniMarshalMethods");
            var destPath     = assemblyName.Name + ".dll";
            var builder      = CreateExportedMemberBuilder();
            var matchType    = typeNameRegexes.Count > 0;

            if (Verbose)
            {
                ColorWriteLine($"Preparing marshal method assembly '{assemblyName}'", ConsoleColor.Cyan);
            }

            var da = AppDomain.CurrentDomain.DefineDynamicAssembly(
                assemblyName,
                AssemblyBuilderAccess.Save,
                Path.GetDirectoryName(path));

            var dm = da.DefineDynamicModule("<default>", destPath);

            var ad = resolver.GetAssembly(path);

            foreach (var type in assembly.DefinedTypes)
            {
                if (matchType)
                {
                    var matched = false;

                    foreach (var r in typeNameRegexes)
                    {
                        matched |= r.IsMatch(type.FullName);
                    }

                    if (!matched)
                    {
                        continue;
                    }
                }

                if (type.IsGenericType || type.IsGenericTypeDefinition)
                {
                    continue;
                }

                var td = ad.MainModule.FindType(type);

                if (td == null)
                {
                    if (Verbose)
                    {
                        Warning($"Unable to find cecil's TypeDefinition of type {type}");
                    }
                    continue;
                }
                if (!td.ImplementsInterface("Java.Interop.IJavaPeerable"))
                {
                    continue;
                }

                var existingMarshalMethodsType = td.GetNestedType(TypeMover.NestedName);
                if (existingMarshalMethodsType != null && !forceRegeneration)
                {
                    Warning($"Marshal methods type '{existingMarshalMethodsType.GetAssemblyQualifiedName ()}' already exists. Skipped generation of marshal methods. Use -f to force regeneration when desired.");

                    continue;
                }

                var         registrationElements = new List <Expression> ();
                var         targetType           = Expression.Variable(typeof(Type), "targetType");
                TypeBuilder dt = null;

                var flags = BindingFlags.Public | BindingFlags.NonPublic |
                            BindingFlags.Instance | BindingFlags.Static;
                foreach (var method in type.GetMethods(flags))
                {
                    // TODO: Constructors
                    var    export     = method.GetCustomAttribute <JavaCallableAttribute> ();
                    string signature  = null;
                    string name       = null;
                    string methodName = method.Name;

                    if (export == null)
                    {
                        if (method.IsGenericMethod || method.ContainsGenericParameters || method.IsGenericMethodDefinition || method.ReturnType.IsGenericType)
                        {
                            continue;
                        }

                        if (method.DeclaringType != type)
                        {
                            continue;
                        }

                        var md = td.GetMethodDefinition(method);

                        if (md == null)
                        {
                            if (Verbose)
                            {
                                Warning($"Unable to find cecil's MethodDefinition of method {method}");
                            }
                            continue;
                        }

                        if (!md.NeedsMarshalMethod(resolver, method, ref name, ref methodName, ref signature))
                        {
                            continue;
                        }
                    }

                    if (dt == null)
                    {
                        dt = GetTypeBuilder(dm, type);
                    }

                    if (Verbose)
                    {
                        Console.Write("Adding marshal method for ");
                        ColorWriteLine($"{method}", ConsoleColor.Green);
                    }

                    var mb = dt.DefineMethod(
                        methodName,
                        System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static);

                    var lambda = builder.CreateMarshalToManagedExpression(method);
                    lambda.CompileToMethod(mb);

                    if (export != null)
                    {
                        name      = export.Name;
                        signature = export.Signature;
                    }

                    if (signature == null)
                    {
                        signature = builder.GetJniMethodSignature(method);
                    }

                    registrationElements.Add(CreateRegistration(name, signature, lambda, targetType, methodName));
                }
                if (dt != null)
                {
                    AddRegisterNativeMembers(dt, targetType, registrationElements);
                }
            }

            foreach (var tb in definedTypes)
            {
                tb.Value.CreateType();
            }

            da.Save(destPath);

            if (Verbose)
            {
                ColorWriteLine($"Marshal method assembly '{assemblyName}' created", ConsoleColor.Cyan);
            }

            var dstAssembly = resolver.GetAssembly(destPath);
            var mover       = new TypeMover(dstAssembly, ad, definedTypes, resolver);

            mover.Move();

            if (!keepTemporary)
            {
                FilesToDelete.Add(dstAssembly.MainModule.FileName);
            }

            definedTypes.Clear();
        }