Esempio n. 1
0
        /// <summary>
        /// Generates a Package.proj file for MSBuild to invoke
        /// - Generates Resource.designer.dll for rewiring resource values from the final Java project
        /// - Links .NET assemblies and places output into /android/assets/assemblies
        /// - Extracts assets and resources from Android library projects into /obj/
        /// - Copies assets and resources into AAR
        /// - Invokes aapt to generate R.txt
        /// - One day I would like to get rid of the temp files, but I could not get the MSBuild APIs to work in-process
        /// </summary>
        public static string GeneratePackageProject(List <IKVM.Reflection.Assembly> assemblies, string outputDirectory, string assembliesDirectory)
        {
            var mainAssembly = assemblies[0].Location;

            outputDirectory     = Path.GetFullPath(outputDirectory);
            assembliesDirectory = Path.GetFullPath(assembliesDirectory);

            var androidDir   = Path.Combine(outputDirectory, "android");
            var assetsDir    = Path.Combine(androidDir, "assets");
            var resourceDir  = Path.Combine(androidDir, "res");
            var manifestPath = Path.Combine(androidDir, "AndroidManifest.xml");
            var packageName  = Generators.JavaGenerator.GetNativeLibPackageName(mainAssembly);
            var project      = CreateProject();
            var target       = project.AddTarget("Build");

            //Generate Resource.designer.dll
            var resourceDesigner = new ResourceDesignerGenerator
            {
                Assemblies      = assemblies,
                MainAssembly    = mainAssembly,
                OutputDirectory = outputDirectory,
                PackageName     = packageName,
            };

            resourceDesigner.Generate();

            if (!resourceDesigner.WriteAssembly())
            {
                //Let's generate CS if this failed
                string resourcePath = resourceDesigner.WriteSource();
                throw new Exception($"Resource.designer.dll compilation failed! See {resourcePath} for details.");
            }

            //ResolveAssemblies Task
            ResolveAssemblies(target, mainAssembly);

            //LinkAssemblies Task
            var linkAssemblies = target.AddTask("LinkAssemblies");

            linkAssemblies.SetParameter("UseSharedRuntime", "False");
            linkAssemblies.SetParameter("LinkMode", LinkMode);
            linkAssemblies.SetParameter("LinkDescriptions", "@(LinkDescription)");
            linkAssemblies.SetParameter("DumpDependencies", "True");
            linkAssemblies.SetParameter("ResolvedAssemblies", "@(ResolvedAssemblies);" + Path.Combine(outputDirectory, "Resource.designer.dll"));
            linkAssemblies.SetParameter("MainAssembly", mainAssembly);
            linkAssemblies.SetParameter("OutputDirectory", assembliesDirectory);

            //Aapt Task to generate R.txt
            var aapt = target.AddTask("Aapt");

            aapt.SetParameter("ImportsDirectory", outputDirectory);
            aapt.SetParameter("OutputImportDirectory", outputDirectory);
            aapt.SetParameter("ManifestFiles", manifestPath);
            aapt.SetParameter("ApplicationName", packageName);
            aapt.SetParameter("JavaPlatformJarPath", Path.Combine(XamarinAndroid.PlatformDirectory, "android.jar"));
            aapt.SetParameter("JavaDesignerOutputDirectory", outputDirectory);
            aapt.SetParameter("AssetDirectory", assetsDir);
            aapt.SetParameter("ResourceDirectory", resourceDir);
            aapt.SetParameter("ToolPath", AndroidSdk.GetBuildToolsPaths().First());
            aapt.SetParameter("ToolExe", "aapt");
            aapt.SetParameter("ApiLevel", XamarinAndroid.TargetSdkVersion);
            aapt.SetParameter("ExtraArgs", "--output-text-symbols " + androidDir);

            //There is an extra /manifest/AndroidManifest.xml file created
            var removeDir = target.AddTask("RemoveDir");

            removeDir.SetParameter("Directories", Path.Combine(androidDir, "manifest"));

            //NOTE: might avoid the temp file later
            var projectFile = Path.Combine(outputDirectory, "Package.proj");

            project.Save(projectFile);
            return(projectFile);
        }
        bool CreateAar()
        {
            var executableSuffix = Platform.IsWindows ? ".exe" : string.Empty;
            var jar        = Path.Combine(XamarinAndroid.JavaSdkPath, "bin", "jar" + executableSuffix);
            var classesDir = Path.Combine(Options.OutputDir, "classes");
            var androidDir = Path.Combine(Options.OutputDir, "android");
            var name       = Path.GetFileNameWithoutExtension(Project.Assemblies[0]).Replace('-', '_');

            var args = new List <string> {
                "cvf",
                Path.Combine(Options.OutputDir, name + ".aar"),
                $"-C {androidDir} ."
            };

            //Copy libmonosgen-2.0.so and libmonodroid.so
            const string libMonoSgen    = "libmonosgen-2.0.so";
            const string libMonoAndroid = "libmono-android.release.so";

            foreach (var abi in Directory.GetDirectories(XamarinAndroid.LibraryPath))
            {
                var abiDir = Path.Combine(androidDir, "jni", Path.GetFileName(abi));

                string libMonoSgenSourcePath = Path.Combine(abi, libMonoSgen);
                string libMonoSgenDestPath   = Path.Combine(abiDir, libMonoSgen);

                string libMonoAndroidSourcePath = Path.Combine(abi, libMonoAndroid);
                string libMonoAndroidDestPath   = Path.Combine(abiDir, "libmonodroid.so"); //NOTE: Xamarin.Android runtime uses different name from APK

                if (!File.Exists(libMonoSgenSourcePath) || !File.Exists(libMonoAndroidSourcePath))
                {
                    continue;
                }

                if (!Directory.Exists(abiDir))
                {
                    Directory.CreateDirectory(abiDir);
                }
                File.Copy(libMonoSgenSourcePath, libMonoSgenDestPath, true);
                File.Copy(libMonoAndroidSourcePath, libMonoAndroidDestPath, true);
            }

            //Copy JNA native libs
            foreach (var file in Directory.GetFiles(Path.Combine(FindDirectory("external"), "jna"), "android-*"))
            {
                using (var stream = File.OpenRead(file))
                    using (var zip = new ZipArchive(stream))
                    {
                        foreach (var entry in zip.Entries)
                        {
                            //Skip non-*.so files
                            if (!entry.FullName.EndsWith(".so", StringComparison.Ordinal))
                            {
                                continue;
                            }

                            var    arch = Path.GetFileNameWithoutExtension(file);
                            string abi;
                            switch (arch)
                            {
                            case "android-aarch64":
                                abi = "arm64-v8a";
                                break;

                            case "android-arm":
                                abi = "armeabi";
                                break;

                            case "android-armv7":
                                abi = "armeabi-v7a";
                                break;

                            case "android-x86-64":
                                abi = "x86_64";
                                break;

                            default:
                                abi = arch.Replace("android-", string.Empty);
                                break;
                            }

                            var abiDir = Path.Combine(androidDir, "jni", Path.GetFileName(abi));
                            if (!Directory.Exists(abiDir))
                            {
                                Directory.CreateDirectory(abiDir);
                            }

                            using (var zipEntryStream = entry.Open())
                                using (var fileStream = File.Create(Path.Combine(abiDir, entry.Name)))
                                {
                                    zipEntryStream.CopyTo(fileStream);
                                }
                        }
                    }
            }

            //Copy jar to android/classes.jar
            File.Copy(Path.Combine(Options.OutputDir, name + ".jar"), Path.Combine(androidDir, "classes.jar"), true);

            //Copy .NET assemblies
            var assembliesDir = Path.Combine(androidDir, "assets", "assemblies");

            if (!Directory.Exists(assembliesDir))
            {
                Directory.CreateDirectory(assembliesDir);
            }

            foreach (var assembly in Project.Assemblies)
            {
                File.Copy(assembly, Path.Combine(assembliesDir, Path.GetFileName(assembly)), true);
            }

            //Copy any referenced assemblies such as mscorlib.dll
            List <string> referencedAssemblies = new List <string>();

            foreach (var assembly in Assemblies)
            {
                foreach (var reference in assembly.GetReferencedAssemblies())
                {
                    if (!referencedAssemblies.Contains(reference.Name))
                    {
                        referencedAssemblies.Add(reference.Name);
                    }
                }
            }

            Diagnostics.Message("Linking assemblies...");

            //Performs Xamarin.Android build tasks such as Linking, Resource/Asset extraction, invoking aapt.
            var project = XamarinAndroidBuild.GeneratePackageProject(Assemblies, Options);

            if (!MSBuild(project))
            {
                return(false);
            }

            //Generate Resource.designer.dll
            var resourceDesigner = new ResourceDesignerGenerator
            {
                Assemblies       = Assemblies,
                MainAssembly     = Assemblies[0].Location,
                OutputDirectory  = assembliesDir,
                JavaResourceFile = Path.Combine(androidDir, "R.txt"),
            };

            resourceDesigner.Generate();

            if (!resourceDesigner.WriteAssembly())
            {
                //Let's generate CS if this failed
                string resourcePath = Path.Combine(Options.OutputDir, "Resource.designer.cs");
                resourceDesigner.WriteSource(resourcePath);
                throw new Exception($"Resource.designer.dll compilation failed! See {resourcePath} for details.");
            }

            //Runs some final processing on .NET assemblies
            XamarinAndroidBuild.ProcessAssemblies(Options.OutputDir);

            var invocation = string.Join(" ", args);
            var output     = Invoke(jar, invocation);

            return(output.ExitCode == 0);
        }