private void AddNativeLibraries(ArchiveFileList files, string [] supportedAbis) { var frameworkLibs = FrameworkNativeLibraries.Select(v => new LibInfo { Path = v.ItemSpec, Link = v.GetMetadata("Link"), Abi = GetNativeLibraryAbi(v), ArchiveFileName = GetArchiveFileName(v) }); AddNativeLibraries(files, supportedAbis, frameworkLibs); var libs = NativeLibraries.Concat(BundleNativeLibraries ?? Enumerable.Empty <ITaskItem> ()) .Where(v => IncludeNativeLibrary(v)) .Select(v => new LibInfo { Path = v.ItemSpec, Link = v.GetMetadata("Link"), Abi = GetNativeLibraryAbi(v), ArchiveFileName = GetArchiveFileName(v) } ); AddNativeLibraries(files, supportedAbis, libs); if (String.IsNullOrWhiteSpace(CheckedBuild)) { return; } string mode = CheckedBuild; string sanitizerName; if (String.Compare("asan", mode, StringComparison.Ordinal) == 0) { sanitizerName = "asan"; } else if (String.Compare("ubsan", mode, StringComparison.Ordinal) == 0) { sanitizerName = "ubsan_standalone"; } else { LogSanitizerWarning($"Unknown checked build mode '{CheckedBuild}'"); return; } if (!IncludeWrapSh) { LogSanitizerError("Checked builds require the wrapper script to be packaged. Please set the `$(AndroidIncludeWrapSh)` MSBuild property to `true` in your project."); return; } if (!libs.Any(info => IsWrapperScript(info.Path, info.Link))) { LogSanitizerError($"Checked builds require the wrapper script to be packaged. Please add `wrap.sh` appropriate for the {CheckedBuild} checker to your project."); return; } NdkUtil.Init(AndroidNdkDirectory); string clangDir = NdkUtil.GetClangDeviceLibraryPath(AndroidNdkDirectory); if (String.IsNullOrEmpty(clangDir)) { LogSanitizerError($"Unable to find the clang compiler directory. Is NDK installed?"); return; } foreach (string abi in supportedAbis) { string clangAbi = MonoAndroidHelper.MapAndroidAbiToClang(abi); if (String.IsNullOrEmpty(clangAbi)) { LogSanitizerError($"Unable to map Android ABI {abi} to clang ABI"); return; } string sanitizerLib = $"libclang_rt.{sanitizerName}-{clangAbi}-android.so"; string sanitizerLibPath = Path.Combine(clangDir, sanitizerLib); if (!File.Exists(sanitizerLibPath)) { LogSanitizerError($"Unable to find sanitizer runtime for the {CheckedBuild} checker at {sanitizerLibPath}"); return; } AddNativeLibrary(files, sanitizerLibPath, abi, sanitizerLib); } }
bool DoExecute() { var results = new List <ITaskItem> (); string bundlepath = Path.Combine(TempOutputPath, "bundles"); if (!Directory.Exists(bundlepath)) { Directory.CreateDirectory(bundlepath); } else { Directory.Delete(bundlepath, true); } foreach (var abi in SupportedAbis) { AndroidTargetArch arch = AndroidTargetArch.Other; switch (abi) { case "arm64": case "arm64-v8a": case "aarch64": arch = AndroidTargetArch.Arm64; break; case "armeabi-v7a": arch = AndroidTargetArch.Arm; break; case "x86": arch = AndroidTargetArch.X86; break; case "x86_64": arch = AndroidTargetArch.X86_64; break; case "mips": arch = AndroidTargetArch.Mips; break; } if (!NdkUtil.ValidateNdkPlatform(Log, AndroidNdkDirectory, arch, enableLLVM: false)) { return(false); } int level = NdkUtil.GetMinimumApiLevelFor(arch, AndroidNdkDirectory); var outpath = Path.Combine(bundlepath, abi); if (!Directory.Exists(outpath)) { Directory.CreateDirectory(outpath); } var clb = new CommandLineBuilder(); clb.AppendSwitch("--dos2unix=false"); clb.AppendSwitch("--nomain"); clb.AppendSwitch("--i18n none"); clb.AppendSwitch("--bundled-header"); clb.AppendSwitch("--mono-api-struct-path"); clb.AppendFileNameIfNotNull(BundleApiPath); clb.AppendSwitch("--style"); clb.AppendSwitch("linux"); clb.AppendSwitch("-c"); clb.AppendSwitch("-o"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c")); clb.AppendSwitch("-oo"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o")); if (AutoDeps) { clb.AppendSwitch("--autodeps"); } if (KeepTemp) { clb.AppendSwitch("--keeptemp"); } clb.AppendSwitch("-z"); // Compress clb.AppendFileNamesIfNotNull(Assemblies, " "); var psi = new ProcessStartInfo() { FileName = MkbundlePath, Arguments = clb.ToString(), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, }; string windowsCompilerSwitches = NdkUtil.GetCompilerTargetParameters(AndroidNdkDirectory, arch, level); var compilerNoQuotes = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "gcc", level); var compiler = $"\"{compilerNoQuotes}\" {windowsCompilerSwitches}".Trim(); var gas = '"' + NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "as", level) + '"'; psi.EnvironmentVariables ["CC"] = compiler; psi.EnvironmentVariables ["AS"] = gas; Log.LogDebugMessage("CC=" + compiler); Log.LogDebugMessage("AS=" + gas); //psi.EnvironmentVariables ["PKG_CONFIG_PATH"] = Path.Combine (Path.GetDirectoryName (MonoDroidSdk.MandroidTool), "lib", abi); Log.LogDebugMessage("[mkbundle] " + psi.FileName + " " + clb); var proc = new Process(); proc.OutputDataReceived += OnMkbundleOutputData; proc.ErrorDataReceived += OnMkbundleErrorData; proc.StartInfo = psi; proc.Start(); proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); if (proc.ExitCode != 0) { Log.LogCodedError("XA5102", "Conversion from assembly to native code failed. Exit code {0}", proc.ExitCode); return(false); } // then compile temp.c into temp.o and ... clb = new CommandLineBuilder(); // See NdkUtils.GetNdkTool for reasons why if (!String.IsNullOrEmpty(windowsCompilerSwitches)) { clb.AppendTextUnquoted(windowsCompilerSwitches); } clb.AppendSwitch("-c"); // This is necessary only when unified headers are in use but it won't hurt to have it // defined even if we don't use them clb.AppendSwitch($"-D__ANDROID_API__={level}"); // This is necessary because of the injected code, which is reused between libmonodroid // and the bundle clb.AppendSwitch("-DANDROID"); clb.AppendSwitch("-o"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o")); if (!string.IsNullOrWhiteSpace(IncludePath)) { clb.AppendSwitch("-I"); clb.AppendFileNameIfNotNull(IncludePath); } string asmIncludePath = NdkUtil.GetNdkAsmIncludePath(AndroidNdkDirectory, arch, level); if (!String.IsNullOrEmpty(asmIncludePath)) { clb.AppendSwitch("-I"); clb.AppendFileNameIfNotNull(asmIncludePath); } clb.AppendSwitch("-I"); clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformIncludePath(AndroidNdkDirectory, arch, level)); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c")); Log.LogDebugMessage("[CC] " + compiler + " " + clb); if (MonoAndroidHelper.RunProcess(compilerNoQuotes, clb.ToString(), OnCcOutputData, OnCcErrorData) != 0) { Log.LogCodedError("XA5103", "NDK C compiler resulted in an error. Exit code {0}", proc.ExitCode); return(false); } // ... link temp.o and assemblies.o into app.so clb = new CommandLineBuilder(); clb.AppendSwitch("--shared"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o")); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o")); // API23+ requires that the shared library has its soname set or it won't load clb.AppendSwitch("-soname"); clb.AppendSwitch(BundleSharedLibraryName); clb.AppendSwitch("-o"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, BundleSharedLibraryName)); clb.AppendSwitch("-L"); clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level)); clb.AppendSwitch("-lc"); clb.AppendSwitch("-lm"); clb.AppendSwitch("-ldl"); clb.AppendSwitch("-llog"); clb.AppendSwitch("-lz"); // Compress string ld = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "ld", level); Log.LogMessage(MessageImportance.Normal, "[LD] " + ld + " " + clb); if (MonoAndroidHelper.RunProcess(ld, clb.ToString(), OnLdOutputData, OnLdErrorData) != 0) { Log.LogCodedError("XA5201", "NDK Linker resulted in an error. Exit code {0}", proc.ExitCode); return(false); } results.Add(new TaskItem(Path.Combine(outpath, "libmonodroid_bundle_app.so"))); } OutputNativeLibraries = results.ToArray(); return(true); }
public override bool Execute() { Log.LogDebugMessage("JarToXml Task"); Log.LogDebugMessage(" JavaOptions: {0}", JavaOptions); Log.LogDebugMessage(" JavaMaximumHeapSize: {0}", JavaMaximumHeapSize); Log.LogDebugMessage(" AndroidSdkDirectory: {0}", AndroidSdkDirectory); Log.LogDebugMessage(" AndroidApiLevel: {0}", AndroidApiLevel); Log.LogDebugMessage(" MonoAndroidToolsDirectory: {0}", MonoAndroidToolsDirectory); Log.LogDebugMessage(" JavaSdkDirectory: {0}", JavaSdkDirectory); Log.LogDebugMessage(" OutputFile: {0}", OutputFile); Log.LogDebugMessage(" DroidDocPaths: {0}", DroidDocPaths); Log.LogDebugMessage(" JavaDocPaths: {0}", JavaDocPaths); Log.LogDebugMessage(" Java7DocPaths: {0}", Java7DocPaths); Log.LogDebugMessage(" Java8DocPaths: {0}", Java8DocPaths); Log.LogDebugTaskItems(" JavaDocs: {0}", JavaDocs); Log.LogDebugMessage(" AndroidApiLevel: {0}", AndroidApiLevel); Log.LogDebugTaskItems(" LibraryProjectJars:", LibraryProjectJars); Log.LogDebugTaskItems(" SourceJars:", SourceJars); Log.LogDebugTaskItems(" ReferenceJars:", ReferenceJars); if (SourceJars == null || SourceJars.Count() == 0) { Log.LogError("At least one Java library is required for binding, this must be either 'EmbeddedJar', 'InputJar' (for jar), 'LibraryProjectZip' (for aar or zip) or 'LibraryProjectProperties' (project.properties) build action."); return(false); } // Ensure that the user has the platform they are targeting installed var jarpath = Path.Combine(AndroidSdkDirectory, "platforms", "android-" + MonoAndroidHelper.GetPlatformApiLevelName(AndroidApiLevel), "android.jar"); if (!File.Exists(jarpath)) { Log.LogError("Could not find android.jar for API Level {0}. This means the Android SDK platform for API Level {0} is not installed. Either install it in the Android SDK Manager, or change your Android Bindings project to target an API version that is installed. ({1} missing.)", AndroidApiLevel, jarpath); return(false); } // Ensure that all requested jars exist foreach (var jar in SourceJars) { if (!File.Exists(jar.ItemSpec)) { Log.LogError("Specified source jar not found: {0}", jar.ItemSpec); } } if (ReferenceJars != null) { foreach (var jar in ReferenceJars) { if (!File.Exists(jar.ItemSpec)) { Log.LogError("Specified reference jar not found: {0}", jar.ItemSpec); } } } if (Log.HasLoggedErrors) { return(false); } // Ensure our output directory exists Directory.CreateDirectory(Path.GetDirectoryName(OutputFile)); return(base.Execute()); }
IEnumerable <Config> GetLinkerConfigs() { var abis = new Dictionary <string, InputFiles> (StringComparer.Ordinal); ITaskItem[] dsos = ApplicationSharedLibraries; foreach (ITaskItem item in dsos) { string abi = item.GetMetadata("abi"); abis [abi] = GatherFilesForABI(item.ItemSpec, abi, ObjectFiles); } foreach (var kvp in abis) { string abi = kvp.Key; InputFiles inputs = kvp.Value; AndroidTargetArch arch; var linkerArgs = new List <string> { "--unresolved-symbols=ignore-in-shared-libs", "--export-dynamic", "-soname", "libxamarin-app.so", "-z", "relro", "-z", "noexecstack", "--enable-new-dtags", "--eh-frame-hdr", "-shared", "--build-id", "--warn-shared-textrel", "--fatal-warnings", "-o", QuoteFileName(inputs.OutputSharedLibrary), }; string elf_arch; switch (abi) { case "armeabi-v7a": arch = AndroidTargetArch.Arm; linkerArgs.Add("-X"); elf_arch = "armelf_linux_eabi"; break; case "arm64": case "arm64-v8a": case "aarch64": arch = AndroidTargetArch.Arm64; linkerArgs.Add("--fix-cortex-a53-843419"); elf_arch = "aarch64linux"; break; case "x86": arch = AndroidTargetArch.X86; elf_arch = "elf_i386"; break; case "x86_64": arch = AndroidTargetArch.X86_64; elf_arch = "elf_x86_64"; break; default: throw new NotSupportedException($"Unsupported Android target architecture ABI: {abi}"); } linkerArgs.Add("-m"); linkerArgs.Add(elf_arch); foreach (string file in inputs.ObjectFiles) { linkerArgs.Add(QuoteFileName(file)); } string ld = MonoAndroidHelper.GetExecutablePath(AndroidBinUtilsDirectory, $"{NdkUtil.GetNdkToolchainPrefix (arch, false)}ld"); yield return(new Config { LinkerPath = Path.Combine(AndroidBinUtilsDirectory, ld), LinkerOptions = String.Join(" ", linkerArgs), OutputSharedLibrary = inputs.OutputSharedLibrary, }); } }
private void AddAssemblies(ZipArchiveEx apk) { bool debug = _Debug; bool use_shared_runtime = String.Equals(UseSharedRuntime, "true", StringComparison.OrdinalIgnoreCase); int count = 0; foreach (ITaskItem assembly in ResolvedUserAssemblies) { if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value) { Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' "); continue; } if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec)) { Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, "{0} is a Reference Assembly!", assembly.ItemSpec); } // Add assembly AddFileToArchiveIfNewer(apk, assembly.ItemSpec, GetTargetDirectory(assembly.ItemSpec) + "/" + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod); // Try to add config if exists var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config"); AddAssemblyConfigEntry(apk, config); // Try to add symbols if Debug if (debug) { var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } if (use_shared_runtime) { return; } count = 0; // Add framework assemblies foreach (ITaskItem assembly in ResolvedFrameworkAssemblies) { if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value) { Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' "); continue; } if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec)) { Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, "{0} is a Reference Assembly!", assembly.ItemSpec); } AddFileToArchiveIfNewer(apk, assembly.ItemSpec, AssembliesPath + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod); var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config"); AddAssemblyConfigEntry(apk, config); // Try to add symbols if Debug if (debug) { var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, AssembliesPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } }
// 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 = 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 DoExecute() { var abis = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); var results = new List <ITaskItem> (); string bundlepath = Path.Combine(TempOutputPath, "bundles"); if (!Directory.Exists(bundlepath)) { Directory.CreateDirectory(bundlepath); } else { Directory.Delete(bundlepath, true); } foreach (var abi in abis) { AndroidTargetArch arch = AndroidTargetArch.Other; switch (abi) { case "arm64": case "arm64-v8a": case "aarch64": arch = AndroidTargetArch.Arm64; break; case "armeabi": case "armeabi-v7a": arch = AndroidTargetArch.Arm; break; case "x86": arch = AndroidTargetArch.X86; break; case "x86_64": arch = AndroidTargetArch.X86_64; break; case "mips": arch = AndroidTargetArch.Mips; break; } if (!NdkUtil.ValidateNdkPlatform(Log, AndroidNdkDirectory, arch, enableLLVM: false)) { return(false); } int level = NdkUtil.GetMinimumApiLevelFor(arch, AndroidNdkDirectory); var outpath = Path.Combine(bundlepath, abi); if (!Directory.Exists(outpath)) { Directory.CreateDirectory(outpath); } var clb = new CommandLineBuilder(); clb.AppendSwitch("--dos2unix=false"); clb.AppendSwitch("--nomain"); clb.AppendSwitch("--i18n none"); clb.AppendSwitch("--style"); clb.AppendSwitch("linux"); clb.AppendSwitch("-c"); clb.AppendSwitch("-o"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c")); clb.AppendSwitch("-oo"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o")); if (AutoDeps) { clb.AppendSwitch("--autodeps"); } if (KeepTemp) { clb.AppendSwitch("--keeptemp"); } clb.AppendSwitch("-z"); // Compress clb.AppendFileNamesIfNotNull(Assemblies, " "); var psi = new ProcessStartInfo() { FileName = MkbundlePath, Arguments = clb.ToString(), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, }; var gccNoQuotes = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "gcc"); var gcc = '"' + gccNoQuotes + '"'; var gas = '"' + NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "as") + '"'; psi.EnvironmentVariables ["CC"] = gcc; psi.EnvironmentVariables ["AS"] = gas; Log.LogDebugMessage("CC=" + gcc); Log.LogDebugMessage("AS=" + gas); //psi.EnvironmentVariables ["PKG_CONFIG_PATH"] = Path.Combine (Path.GetDirectoryName (MonoDroidSdk.MandroidTool), "lib", abi); Log.LogDebugMessage("[mkbundle] " + psi.FileName + " " + clb); var proc = new Process(); proc.OutputDataReceived += OnMkbundleOutputData; proc.ErrorDataReceived += OnMkbundleErrorData; proc.StartInfo = psi; proc.Start(); proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); if (proc.ExitCode != 0) { Log.LogCodedError("XA5102", "Conversion from assembly to native code failed. Exit code {0}", proc.ExitCode); return(false); } // make some changes in the mkbundle output so that it does not require libmonodroid.so var mkbundleOutput = File.ReadAllText(Path.Combine(outpath, "temp.c")); mkbundleOutput = mkbundleOutput.Replace("void mono_mkbundle_init ()", "void mono_mkbundle_init (void (register_bundled_assemblies_func)(const MonoBundledAssembly **), void (register_config_for_assembly_func)(const char *, const char *))") .Replace("mono_register_config_for_assembly (\"", "register_config_for_assembly_func (\"") .Replace("install_dll_config_files (void)", "install_dll_config_files (void (register_config_for_assembly_func)(const char *, const char *))") .Replace("install_dll_config_files ()", "install_dll_config_files (register_config_for_assembly_func)") .Replace("mono_register_bundled_assemblies(", "register_bundled_assemblies_func("); File.WriteAllText(Path.Combine(outpath, "temp.c"), mkbundleOutput); // then compile temp.c into temp.o and ... clb = new CommandLineBuilder(); clb.AppendSwitch("-c"); clb.AppendSwitch("-o"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o")); if (!string.IsNullOrWhiteSpace(IncludePath)) { clb.AppendSwitch("-I"); clb.AppendFileNameIfNotNull(IncludePath); } clb.AppendSwitch("-I"); clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformIncludePath(AndroidNdkDirectory, arch, level)); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.c")); Log.LogDebugMessage("[CC] " + gcc + " " + clb); if (MonoAndroidHelper.RunProcess(gccNoQuotes, clb.ToString(), OnCcOutputData, OnCcErrorData) != 0) { Log.LogCodedError("XA5103", "NDK C compiler resulted in an error. Exit code {0}", proc.ExitCode); return(false); } // ... link temp.o and assemblies.o into app.so clb = new CommandLineBuilder(); clb.AppendSwitch("--shared"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "temp.o")); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "assemblies.o")); clb.AppendSwitch("-o"); clb.AppendFileNameIfNotNull(Path.Combine(outpath, "libmonodroid_bundle_app.so")); clb.AppendSwitch("-L"); clb.AppendFileNameIfNotNull(NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level)); clb.AppendSwitch("-lc"); clb.AppendSwitch("-lm"); clb.AppendSwitch("-ldl"); clb.AppendSwitch("-llog"); clb.AppendSwitch("-lz"); // Compress string ld = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "ld"); Log.LogMessage(MessageImportance.Normal, "[LD] " + ld + " " + clb); if (MonoAndroidHelper.RunProcess(ld, clb.ToString(), OnLdOutputData, OnLdErrorData) != 0) { Log.LogCodedError("XA5201", "NDK Linker resulted in an error. Exit code {0}", proc.ExitCode); return(false); } results.Add(new TaskItem(Path.Combine(outpath, "libmonodroid_bundle_app.so"))); } OutputNativeLibraries = results.ToArray(); return(true); }
protected bool LogAapt2EventsFromOutput(string singleLine, MessageImportance messageImportance, bool apptResult) { if (string.IsNullOrEmpty(singleLine)) { return(true); } var match = AndroidRunToolTask.AndroidErrorRegex.Match(singleLine.Trim()); if (match.Success) { var file = match.Groups ["file"].Value; int line = 0; if (!string.IsNullOrEmpty(match.Groups ["line"]?.Value)) { line = int.Parse(match.Groups ["line"].Value.Trim()) + 1; } var level = match.Groups ["level"].Value.ToLowerInvariant(); var message = match.Groups ["message"].Value; // Handle the following which is NOT an error :( // W/ResourceType(23681): For resource 0x0101053d, entry index(1341) is beyond type entryCount(733) // W/ResourceType( 3681): For resource 0x0101053d, entry index(1341) is beyond type entryCount(733) if (file.StartsWith("W/", StringComparison.OrdinalIgnoreCase)) { LogCodedWarning(GetErrorCode(singleLine), singleLine); return(true); } if (message.StartsWith("unknown option", StringComparison.OrdinalIgnoreCase)) { // we need to filter out the remailing help lines somehow. LogCodedError("APT0001", $"{message}. This is the result of using `aapt` command line arguments with `aapt2`. The arguments are not compatible."); return(false); } if (message.Contains("in APK") && message.Contains("is compressed.")) { LogMessage(singleLine, messageImportance); return(true); } if (message.Contains("fakeLogOpen")) { LogMessage(singleLine, messageImportance); return(true); } if (message.Contains("note:")) { LogMessage(singleLine, messageImportance); return(true); } if (message.Contains("warn:")) { LogCodedWarning(GetErrorCode(singleLine), singleLine); return(true); } if (level.Contains("note")) { LogMessage(message, messageImportance); return(true); } if (level.Contains("warning") || level.StartsWith($"{ToolName} W", StringComparison.OrdinalIgnoreCase)) { LogCodedWarning(GetErrorCode(singleLine), singleLine); return(true); } // Try to map back to the original resource file, so when the user // double clicks the error, it won't take them to the obj/Debug copy if (ResourceDirectories != null) { foreach (var dir in ResourceDirectories) { var resourceDirectory = dir.ItemSpec; var resourceDirectoryFullPath = ResourceDirectoryFullPath(resourceDirectory); string newfile = MonoAndroidHelper.FixUpAndroidResourcePath(file, resourceDirectory, resourceDirectoryFullPath, resource_name_case_map); if (!string.IsNullOrEmpty(newfile)) { file = newfile; break; } } } // Strip any "Error:" text from aapt's output if (message.StartsWith("error: ", StringComparison.InvariantCultureIgnoreCase)) { message = message.Substring("error: ".Length); } if (level.Contains("error") || (line != 0 && !string.IsNullOrEmpty(file))) { LogCodedError(GetErrorCode(message), message, file, line); return(true); } } if (!apptResult) { var message = string.Format("{0} \"{1}\".", singleLine.Trim(), singleLine.Substring(singleLine.LastIndexOfAny(new char [] { '\\', '/' }) + 1)); LogCodedError(GetErrorCode(message), message, ToolName); } else { LogCodedWarning(GetErrorCode(singleLine), singleLine); } return(true); }
protected void LoadResourceCaseMap() { resource_name_case_map = MonoAndroidHelper.LoadResourceCaseMap(ResourceNameCaseMap); }
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 RunTask() { if (InputAssemblies == null) { return(true); } var output = new List <ITaskItem> (InputAssemblies.Length); foreach (var assemblyItem in InputAssemblies) { if (!File.Exists(assemblyItem.ItemSpec)) { Log.LogDebugMessage($"Skipping non-existent dependency '{assemblyItem.ItemSpec}'."); continue; } using (var pe = new PEReader(File.OpenRead(assemblyItem.ItemSpec))) { var reader = pe.GetMetadataReader(); // Check in-memory cache var module = reader.GetModuleDefinition(); var key = (nameof(FilterAssemblies), reader.GetGuid(module.Mvid)); var value = BuildEngine4.GetRegisteredTaskObject(key, Lifetime); if (value is bool isMonoAndroidAssembly) { if (isMonoAndroidAssembly) { Log.LogDebugMessage($"Cached: {assemblyItem.ItemSpec}"); output.Add(assemblyItem); } continue; } // Check assembly definition var assemblyDefinition = reader.GetAssemblyDefinition(); var targetFrameworkIdentifier = GetTargetFrameworkIdentifier(assemblyDefinition, reader); if (string.Compare(targetFrameworkIdentifier, TargetFrameworkIdentifier, StringComparison.OrdinalIgnoreCase) == 0) { output.Add(assemblyItem); BuildEngine4.RegisterTaskObject(key, true, Lifetime, AllowEarlyCollection); continue; } // Fallback to looking for a Mono.Android reference if (MonoAndroidHelper.HasMonoAndroidReference(reader)) { Log.LogDebugMessage($"Mono.Android reference found: {assemblyItem.ItemSpec}"); output.Add(assemblyItem); BuildEngine4.RegisterTaskObject(key, true, Lifetime, AllowEarlyCollection); continue; } // Fallback to looking for *.jar or __Android EmbeddedResource files if (HasEmbeddedResource(reader)) { Log.LogDebugMessage($"EmbeddedResource found: {assemblyItem.ItemSpec}"); output.Add(assemblyItem); BuildEngine4.RegisterTaskObject(key, true, Lifetime, AllowEarlyCollection); continue; } // Not a MonoAndroid assembly, store false BuildEngine4.RegisterTaskObject(key, false, Lifetime, AllowEarlyCollection); } } OutputAssemblies = output.ToArray(); return(!Log.HasLoggedErrors); }
public static string GetNdkTool(string androidNdkPath, AndroidTargetArch arch, string tool, int apiLevel) { if (!UsingClangNDK) { return(NdkUtilOld.GetNdkTool(androidNdkPath, arch, tool)); } string toolchainDir = Path.Combine(androidNdkPath, "toolchains", "llvm", "prebuilt", MonoAndroidHelper.AndroidSdk.AndroidNdkHostPlatform); string toolName; bool forCompiler = false; if (String.Compare(tool, "gcc", StringComparison.Ordinal) == 0 || String.Compare(tool, "clang", StringComparison.Ordinal) == 0) { forCompiler = true; toolName = "clang"; } else if (String.Compare(tool, "g++", StringComparison.Ordinal) == 0 || String.Compare(tool, "clang++", StringComparison.Ordinal) == 0) { forCompiler = true; toolName = "clang++"; } else { toolName = tool; } // // NDK r19 bug. // // The llvm toolchain directory contains a selection of shell scripts (both Unix and Windows) // which call `clang/clang++` with different `-target` parameters depending on both the target // architecture and API level. For instance, the clang/clang++ compilers targetting aarch64 on API level // 28 will have the following Unix shell scripts present in the toolchain `bin` directory: // // aarch64-linux-android28-clang // aarch64-linux-android28-clang++ // // However, the Windows version of the NDK has a bug where there is only one Windows // counterpart to the above Unix scripts: // // aarch64-linux-android28-clang.cmd // // This script, despite its name suggesting that it calls `clang.exe` in fact calls // `clang++.exe` which breaks compilation of some C programs (including the code generated by // Mono's mkbundle utility) because `clang++` treats the input as C++. There is no corresponding // `aarch64-linux-android28-clang++.cmd` and so invocation of `clang.exe` becomes harder and, // most certainly, non-standard as far as cross-platform NDK compatibility is concerned. // // The code below tries to rectify the situation by special-casing the compiler tool handling to // return path to the actual .exe instead of the CMD. Unfortunately, the caller of this code // will need to provide the correct parameters for the compilers. // string toolchainPrefix; if (forCompiler) { if (!OS.IsWindows) { toolchainPrefix = $"{GetNdkToolchainPrefix (arch, true)}{apiLevel}"; } else { toolchainPrefix = String.Empty; } } else { toolchainPrefix = GetNdkToolchainPrefix(arch, false); } string extension = OS.IsWindows ? ".exe" : String.Empty; if (forCompiler && OS.IsWindows) { toolName = $"{toolName}{extension}"; } else { toolName = $"{toolchainPrefix}-{toolName}{extension}"; } string binDir = Path.Combine(toolchainDir, "bin"); string toolExe = MonoAndroidHelper.GetExecutablePath(binDir, toolName); string toolPath = Path.Combine(binDir, toolExe); if (File.Exists(toolPath)) { return(toolPath); } Diagnostic.Error(5101, $"Toolchain utility '{toolName}' for target {arch} was not found. Tried in path: \"{toolchainDir}\""); return(null); }
public override bool Execute() { Log.LogDebugMessage("ResolveSdksTask:"); Log.LogDebugMessage(" AndroidApiLevel: {0}", AndroidApiLevel); Log.LogDebugMessage(" AndroidSdkBuildToolsVersion: {0}", AndroidSdkBuildToolsVersion); Log.LogDebugTaskItems(" ReferenceAssemblyPaths: ", ReferenceAssemblyPaths); Log.LogDebugMessage(" TargetFrameworkVersion: {0}", TargetFrameworkVersion); Log.LogDebugMessage(" UseLatestAndroidPlatformSdk: {0}", UseLatestAndroidPlatformSdk); Log.LogDebugMessage(" SequencePointsMode: {0}", SequencePointsMode); Log.LogDebugMessage(" MonoAndroidToolsPath: {0}", MonoAndroidToolsPath); Log.LogDebugMessage(" MonoAndroidBinPath: {0}", MonoAndroidBinPath); MonoAndroidHelper.InitializeAndroidLogger(Log); MonoAndroidHelper.RefreshAndroidSdk(AndroidSdkPath, AndroidNdkPath, JavaSdkPath); MonoAndroidHelper.RefreshMonoDroidSdk(MonoAndroidToolsPath, MonoAndroidBinPath, ReferenceAssemblyPaths); // OS X: $prefix/lib/mandroid // Windows: %ProgramFiles(x86)%\MSBuild\Xamarin\Android this.MonoAndroidToolsPath = MonoDroidSdk.RuntimePath; // OS X: $prefix/bin // Windows: %ProgramFiles(x86)%\MSBuild\Xamarin\Android this.MonoAndroidBinPath = MonoDroidSdk.BinPath; if (this.MonoAndroidBinPath == null) { Log.LogCodedError("XA0020", "Could not find mandroid!"); return(false); } string include; if (MonoAndroidToolsPath != null && Directory.Exists(include = Path.Combine(MonoAndroidToolsPath, "include"))) { MonoAndroidIncludePath = include; } this.AndroidNdkPath = AndroidSdk.AndroidNdkPath; this.AndroidSdkPath = AndroidSdk.AndroidSdkPath; this.JavaSdkPath = AndroidSdk.JavaSdkPath; if (string.IsNullOrEmpty(AndroidSdkPath)) { Log.LogCodedError("XA5205", "The Android SDK Directory could not be found. Please set via /p:AndroidSdkDirectory."); return(false); } string toolsZipAlignPath = Path.Combine(AndroidSdkPath, "tools", ZipAlign); bool findZipAlign = (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath)) && !File.Exists(toolsZipAlignPath); foreach (var dir in AndroidSdk.GetBuildToolsPaths(AndroidSdkBuildToolsVersion)) { Log.LogDebugMessage("Trying build-tools path: {0}", dir); if (dir == null || !Directory.Exists(dir)) { continue; } var toolsPaths = new string[] { Path.Combine(dir), Path.Combine(dir, "bin"), }; string aapt = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, Aapt))); if (string.IsNullOrEmpty(aapt)) { Log.LogDebugMessage("Could not find `{0}`; tried: {1}", Aapt, string.Join(";", toolsPaths.Select(x => Path.Combine(x, Aapt)))); continue; } AndroidSdkBuildToolsPath = Path.GetFullPath(dir); AndroidSdkBuildToolsBinPath = Path.GetFullPath(aapt); string zipalign = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, ZipAlign))); if (findZipAlign && string.IsNullOrEmpty(zipalign)) { Log.LogDebugMessage("Could not find `{0}`; tried: {1}", ZipAlign, string.Join(";", toolsPaths.Select(x => Path.Combine(x, ZipAlign)))); continue; } else { break; } } if (string.IsNullOrEmpty(AndroidSdkBuildToolsPath)) { Log.LogCodedError("XA5205", string.Format( "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.", Aapt, AndroidSdkPath, Path.DirectorySeparatorChar, Android)); return(false); } if (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath)) { ZipAlignPath = new[] { Path.Combine(AndroidSdkBuildToolsPath), Path.Combine(AndroidSdkBuildToolsBinPath), Path.Combine(AndroidSdkPath, "tools"), } .Where(p => File.Exists(Path.Combine(p, ZipAlign))) .FirstOrDefault(); } if (string.IsNullOrEmpty(ZipAlignPath)) { Log.LogCodedError("XA5205", string.Format( "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.", ZipAlign, AndroidSdkPath, Path.DirectorySeparatorChar, Android)); return(false); } if (!ValidateApiLevels()) { return(false); } string frameworksPath = Path.GetDirectoryName(MonoDroidSdk.FrameworkPath); if (!Directory.Exists(Path.Combine(frameworksPath, TargetFrameworkVersion))) { Log.LogError( subcategory: string.Empty, errorCode: "XA0001", helpKeyword: string.Empty, file: ProjectFilePath, lineNumber: 0, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: "Unsupported or invalid $(TargetFrameworkVersion) value of '{0}'. Please update your Project Options.", messageArgs: new[] { TargetFrameworkVersion, } ); return(false); } SequencePointsMode mode; if (!Aot.TryGetSequencePointsMode(SequencePointsMode ?? "None", out mode)) { Log.LogCodedError("XA0104", "Invalid Sequence Point mode: {0}", SequencePointsMode); } AndroidSequencePointsMode = mode.ToString(); AndroidApiLevelName = MonoAndroidHelper.GetPlatformApiLevelName(AndroidApiLevel); Log.LogDebugMessage("ResolveSdksTask Outputs:"); Log.LogDebugMessage(" AndroidApiLevel: {0}", AndroidApiLevel); Log.LogDebugMessage(" AndroidApiLevelName: {0}", AndroidApiLevelName); Log.LogDebugMessage(" AndroidNdkPath: {0}", AndroidNdkPath); Log.LogDebugMessage(" AndroidSdkBuildToolsPath: {0}", AndroidSdkBuildToolsPath); Log.LogDebugMessage(" AndroidSdkBuildToolsBinPath: {0}", AndroidSdkBuildToolsBinPath); Log.LogDebugMessage(" AndroidSdkPath: {0}", AndroidSdkPath); Log.LogDebugMessage(" JavaSdkPath: {0}", JavaSdkPath); Log.LogDebugMessage(" MonoAndroidBinPath: {0}", MonoAndroidBinPath); Log.LogDebugMessage(" MonoAndroidToolsPath: {0}", MonoAndroidToolsPath); Log.LogDebugMessage(" MonoAndroidIncludePath: {0}", MonoAndroidIncludePath); Log.LogDebugMessage(" TargetFrameworkVersion: {0}", TargetFrameworkVersion); Log.LogDebugMessage(" ZipAlignPath: {0}", ZipAlignPath); Log.LogDebugMessage(" SupportedApiLevel: {0}", SupportedApiLevel); Log.LogDebugMessage(" AndroidSequencePointMode: {0}", AndroidSequencePointsMode); if (!string.IsNullOrEmpty(CacheFile)) { Directory.CreateDirectory(Path.GetDirectoryName(CacheFile)); var document = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement("Sdk", new XElement("AndroidApiLevel", AndroidApiLevel), new XElement("AndroidApiLevelName", AndroidApiLevelName), new XElement("AndroidNdkPath", AndroidNdkPath), new XElement("AndroidSdkBuildToolsPath", AndroidSdkBuildToolsPath), new XElement("AndroidSdkBuildToolsBinPath", AndroidSdkBuildToolsBinPath), new XElement("AndroidSdkPath", AndroidSdkPath), new XElement("JavaSdkPath", JavaSdkPath), new XElement("MonoAndroidBinPath", MonoAndroidBinPath), new XElement("MonoAndroidToolsPath", MonoAndroidToolsPath), new XElement("ReferenceAssemblyPaths", (ReferenceAssemblyPaths ?? new string [0]) .Select(e => new XElement("ReferenceAssemblyPath", e))), new XElement("TargetFrameworkVersion", TargetFrameworkVersion), new XElement("ZipAlignPath", ZipAlignPath), new XElement("MonoAndroidIncludePath", MonoAndroidIncludePath), new XElement("SupportedApiLevel", SupportedApiLevel), new XElement("AndroidSequencePointsMode", AndroidSequencePointsMode.ToString()) )); document.Save(CacheFile); } //note: this task does not error out if it doesn't find all things. that's the job of the targets return(!Log.HasLoggedErrors); }
void AddEnvironment() { bool usesMonoAOT = false; bool usesAssemblyPreload = EnablePreloadAssembliesDefault; uint monoAOTMode = 0; string androidPackageName = null; var environmentVariables = new Dictionary <string, string> (StringComparer.Ordinal); var systemProperties = new Dictionary <string, string> (StringComparer.Ordinal); AotMode aotMode; if (AndroidAotMode != null && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode)) { usesMonoAOT = true; monoAOTMode = (uint)aotMode; } bool haveLogLevel = false; bool haveMonoDebug = false; bool havebuildId = false; bool haveHttpMessageHandler = false; bool haveTlsProvider = false; bool haveMonoGCParams = false; SequencePointsMode sequencePointsMode; if (!Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode)) { sequencePointsMode = SequencePointsMode.None; } foreach (ITaskItem env in Environments ?? new TaskItem[0]) { foreach (string line in File.ReadLines(env.ItemSpec)) { var lineToWrite = line; if (lineToWrite.StartsWith("MONO_LOG_LEVEL=", StringComparison.Ordinal)) { haveLogLevel = true; } if (lineToWrite.StartsWith("MONO_GC_PARAMS=", StringComparison.Ordinal)) { haveMonoGCParams = true; } if (lineToWrite.StartsWith("XAMARIN_BUILD_ID=", StringComparison.Ordinal)) { havebuildId = true; } if (lineToWrite.StartsWith("MONO_DEBUG=", StringComparison.Ordinal)) { haveMonoDebug = true; if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains("gen-compact-seq-points")) { lineToWrite = line + ",gen-compact-seq-points"; } } if (lineToWrite.StartsWith("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal)) { haveHttpMessageHandler = true; } if (lineToWrite.StartsWith("XA_TLS_PROVIDER=", StringComparison.Ordinal)) { haveTlsProvider = true; } if (lineToWrite.StartsWith("mono.enable_assembly_preload=", StringComparison.Ordinal)) { int idx = lineToWrite.IndexOf('='); uint val; if (idx < lineToWrite.Length - 1 && UInt32.TryParse(lineToWrite.Substring(idx + 1), out val)) { usesAssemblyPreload = idx == 1; } continue; } AddEnvironmentVariableLine(lineToWrite); } } if (_Debug && !haveLogLevel) { AddEnvironmentVariable(defaultLogLevel[0], defaultLogLevel[1]); } if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) { AddEnvironmentVariable(defaultMonoDebug[0], defaultMonoDebug[1]); } if (!havebuildId) { AddEnvironmentVariable("XAMARIN_BUILD_ID", BuildId); } if (!haveHttpMessageHandler) { if (HttpClientHandlerType == null) { AddEnvironmentVariable(defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]); } else { AddEnvironmentVariable("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim()); } } if (!haveTlsProvider) { if (TlsProvider == null) { AddEnvironmentVariable(defaultTlsProvider[0], defaultTlsProvider[1]); } else { AddEnvironmentVariable("XA_TLS_PROVIDER", TlsProvider.Trim()); } } if (!haveMonoGCParams) { if (EnableSGenConcurrent) { AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep-conc"); } else { AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep"); } } using (var ms = new MemoryStream()) { var utf8Encoding = new UTF8Encoding(false); foreach (string abi in SupportedAbis) { ms.SetLength(0); NativeAssemblerTargetProvider asmTargetProvider; string asmFileName = Path.Combine(EnvironmentOutputDirectory, $"environment.{abi.ToLowerInvariant ()}.s"); switch (abi.Trim()) { case "armeabi-v7a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(false); break; case "arm64-v8a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(true); break; case "x86": asmTargetProvider = new X86NativeAssemblerTargetProvider(false); break; case "x86_64": asmTargetProvider = new X86NativeAssemblerTargetProvider(true); break; default: throw new InvalidOperationException($"Unknown ABI {abi}"); } var asmgen = new ApplicationConfigNativeAssemblyGenerator(asmTargetProvider, environmentVariables, systemProperties) { IsBundledApp = IsBundledApplication, UsesMonoAOT = usesMonoAOT, UsesMonoLLVM = EnableLLVM, UsesAssemblyPreload = usesAssemblyPreload, MonoAOTMode = monoAOTMode.ToString().ToLowerInvariant(), AndroidPackageName = AndroidPackageName, }; using (var sw = new StreamWriter(ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) { asmgen.Write(sw, asmFileName); MonoAndroidHelper.CopyIfStreamChanged(ms, asmFileName); } } } void AddEnvironmentVariable(string name, string value) { if (Char.IsUpper(name [0]) || !Char.IsLetter(name [0])) { environmentVariables [ValidAssemblerString(name)] = ValidAssemblerString(value); } else { systemProperties [ValidAssemblerString(name)] = ValidAssemblerString(value); } } void AddEnvironmentVariableLine(string l) { string line = l?.Trim(); if (String.IsNullOrEmpty(line) || line [0] == '#') { return; } string[] nv = line.Split(new char[] { '=' }, 2); AddEnvironmentVariable(nv[0].Trim(), nv.Length < 2 ? String.Empty : nv[1].Trim()); } string ValidAssemblerString(string s) { return(s.Replace("\"", "\\\"")); } }
IEnumerable <Config> GetAotConfigs() { if (!Directory.Exists(AotOutputDirectory)) { Directory.CreateDirectory(AotOutputDirectory); } var sdkBinDirectory = MonoAndroidHelper.GetOSBinPath(); foreach (var abi in SupportedAbis) { string aotCompiler = ""; string outdir = ""; string mtriple = ""; AndroidTargetArch arch; switch (abi) { case "armeabi-v7a": aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm"); outdir = Path.Combine(AotOutputDirectory, "armeabi-v7a"); mtriple = "armv7-linux-gnueabi"; arch = AndroidTargetArch.Arm; break; case "arm64": case "arm64-v8a": case "aarch64": aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm64"); outdir = Path.Combine(AotOutputDirectory, "arm64-v8a"); mtriple = "aarch64-linux-android"; arch = AndroidTargetArch.Arm64; break; case "x86": aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86"); outdir = Path.Combine(AotOutputDirectory, "x86"); mtriple = "i686-linux-android"; arch = AndroidTargetArch.X86; break; case "x86_64": aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86_64"); outdir = Path.Combine(AotOutputDirectory, "x86_64"); mtriple = "x86_64-linux-android"; arch = AndroidTargetArch.X86_64; break; // case "mips": default: throw new Exception("Unsupported Android target architecture ABI: " + abi); } if (EnableLLVM && !NdkUtil.ValidateNdkPlatform(LogMessage, LogCodedError, AndroidNdkDirectory, arch, enableLLVM:EnableLLVM)) { yield return(Config.Invalid); yield break; } outdir = Path.GetFullPath(outdir); if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } // dont use a full path if the outdir is withing the WorkingDirectory. if (outdir.StartsWith(WorkingDirectory, StringComparison.InvariantCultureIgnoreCase)) { outdir = outdir.Replace(WorkingDirectory + Path.DirectorySeparatorChar, string.Empty); } int level = 0; string toolPrefix = EnableLLVM ? NdkUtil.GetNdkToolPrefix(AndroidNdkDirectory, arch, level = GetNdkApiLevel(AndroidNdkDirectory, AndroidApiLevel, arch)) : Path.Combine(AndroidBinUtilsDirectory, $"{NdkUtil.GetArchDirName (arch)}-"); var toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar)); var ldFlags = string.Empty; if (EnableLLVM) { if (string.IsNullOrEmpty(AndroidNdkDirectory)) { yield return(Config.Invalid); yield break; } string androidLibPath = string.Empty; try { androidLibPath = NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level); } catch (InvalidOperationException ex) { Diagnostic.Error(5101, ex.Message); } string toolchainLibDir; if (NdkUtil.UsingClangNDK) { toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath, arch); } else { toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath); } var libs = new List <string>(); if (NdkUtil.UsingClangNDK) { libs.Add($"-L{toolchainLibDir}"); libs.Add($"-L{androidLibPath}"); if (arch == AndroidTargetArch.Arm) { // Needed for -lunwind to work string compilerLibDir = Path.Combine(toolchainPath, "..", "sysroot", "usr", "lib", NdkUtil.GetArchDirName(arch)); libs.Add($"-L{compilerLibDir}"); } } libs.Add($"\\\"{Path.Combine (toolchainLibDir, "libgcc.a")}\\\""); libs.Add($"\\\"{Path.Combine (androidLibPath, "libc.so")}\\\""); libs.Add($"\\\"{Path.Combine (androidLibPath, "libm.so")}\\\""); ldFlags = string.Join(";", libs); } foreach (var assembly in ResolvedAssemblies) { string outputFile = Path.Combine(outdir, string.Format("libaot-{0}.so", Path.GetFileName(assembly.ItemSpec))); string seqpointsFile = Path.Combine(outdir, string.Format("{0}.msym", Path.GetFileName(assembly.ItemSpec))); string tempDir = Path.Combine(outdir, Path.GetFileName(assembly.ItemSpec)); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } List <string> aotOptions = new List <string> (); if (Profiles != null && Profiles.Length > 0) { aotOptions.Add("profile-only"); foreach (var p in Profiles) { var fp = Path.GetFullPath(p.ItemSpec); aotOptions.Add($"profile={fp}"); } } if (!string.IsNullOrEmpty(AotAdditionalArguments)) { aotOptions.Add(AotAdditionalArguments); } if (sequencePointsMode == SequencePointsMode.Offline) { aotOptions.Add($"msym-dir={outdir}"); } if (AotMode != AotMode.Normal) { aotOptions.Add(AotMode.ToString().ToLowerInvariant()); } aotOptions.Add($"outfile={outputFile}"); aotOptions.Add("asmwriter"); aotOptions.Add($"mtriple={mtriple}"); aotOptions.Add($"tool-prefix={toolPrefix}"); aotOptions.Add($"llvm-path={sdkBinDirectory}"); aotOptions.Add($"temp-path={tempDir}"); aotOptions.Add($"ld-flags={ldFlags}"); // we need to quote the entire --aot arguments here to make sure it is parsed // on windows as one argument. Otherwise it will be split up into multiple // values, which wont work. string aotOptionsStr = (EnableLLVM ? "--llvm " : "") + $"\"--aot={string.Join (",", aotOptions)}\""; if (!string.IsNullOrEmpty(ExtraAotOptions)) { aotOptionsStr += (aotOptions.Count > 0 ? " " : "") + ExtraAotOptions; } // Due to a Monodroid MSBuild bug we can end up with paths to assemblies that are not in the intermediate // assembly directory (typically obj/assemblies). This can lead to problems with the Mono loader not being // able to find their dependency laters, since framework assemblies are stored in different directories. // This can happen when linking is disabled (AndroidLinkMode=None). Workaround this problem by resolving // the paths to the right assemblies manually. var resolvedPath = Path.GetFullPath(assembly.ItemSpec); var intermediateAssemblyPath = Path.Combine(IntermediateAssemblyDir, Path.GetFileName(assembly.ItemSpec)); if (LinkMode.ToLowerInvariant() == "none") { if (!resolvedPath.Contains(IntermediateAssemblyDir) && File.Exists(intermediateAssemblyPath)) { resolvedPath = intermediateAssemblyPath; } } var assembliesPath = Path.GetFullPath(Path.GetDirectoryName(resolvedPath)); var assemblyPath = Path.GetFullPath(resolvedPath); yield return(new Config(assembliesPath, aotCompiler, aotOptionsStr, assemblyPath, outputFile, Path.Combine(tempDir, "response.txt"))); } } }
void Execute(DirectoryAssemblyResolver resolver) { foreach (var dir in ReferenceAssembliesDirectory.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { resolver.SearchDirectories.Add(dir); } var assemblies = new Dictionary <string, ITaskItem> (); var topAssemblyReferences = new List <AssemblyDefinition> (); var logger = new NuGetLogger((s) => { LogDebugMessage("{0}", s); }); LockFile lockFile = null; if (!string.IsNullOrEmpty(ProjectAssetFile) && File.Exists(ProjectAssetFile)) { lockFile = LockFileUtilities.GetLockFile(ProjectAssetFile, logger); } try { foreach (var assembly in Assemblies) { var assembly_path = Path.GetDirectoryName(assembly.ItemSpec); if (!resolver.SearchDirectories.Contains(assembly_path)) { resolver.SearchDirectories.Add(assembly_path); } // Add each user assembly and all referenced assemblies (recursive) var assemblyDef = resolver.Load(assembly.ItemSpec); if (assemblyDef == null) { throw new InvalidOperationException("Failed to load assembly " + assembly.ItemSpec); } if (MonoAndroidHelper.IsReferenceAssembly(assemblyDef)) { // Resolve "runtime" library var asmFullPath = Path.GetFullPath(assembly.ItemSpec); if (lockFile != null) { assemblyDef = ResolveRuntimeAssemblyForReferenceAssembly(lockFile, resolver, asmFullPath); } if (lockFile == null || assemblyDef == null) { LogCodedWarning("XA0107", asmFullPath, 0, "Ignoring {0} as it is a Reference Assembly", asmFullPath); continue; } } topAssemblyReferences.Add(assemblyDef); var taskItem = new TaskItem(assembly) { ItemSpec = Path.GetFullPath(assemblyDef.MainModule.FileName), }; if (string.IsNullOrEmpty(taskItem.GetMetadata("ReferenceAssembly"))) { taskItem.SetMetadata("ReferenceAssembly", taskItem.ItemSpec); } assemblies [assemblyDef.Name.Name] = taskItem; } } catch (Exception ex) { LogError("Exception while loading assemblies: {0}", ex); return; } try { foreach (var assembly in topAssemblyReferences) { AddAssemblyReferences(resolver, assemblies, assembly, null); } } catch (Exception ex) { LogError("Exception while loading assemblies: {0}", ex); return; } // Add I18N assemblies if needed AddI18nAssemblies(resolver, assemblies); var mainapiLevel = MonoAndroidHelper.SupportedVersions.GetApiLevelFromFrameworkVersion(TargetFrameworkVersion); foreach (var item in api_levels.Where(x => mainapiLevel < x.Value)) { var itemOSVersion = MonoAndroidHelper.SupportedVersions.GetFrameworkVersionFromApiLevel(item.Value); Log.LogCodedWarning("XA0105", ProjectFile, 0, "The $(TargetFrameworkVersion) for {0} ({1}) is greater than the $(TargetFrameworkVersion) for your project ({2}). " + "You need to increase the $(TargetFrameworkVersion) for your project.", Path.GetFileName(item.Key), itemOSVersion, TargetFrameworkVersion); } var resolvedAssemblies = new List <ITaskItem> (assemblies.Count); var resolvedSymbols = new List <ITaskItem> (assemblies.Count); var resolvedFrameworkAssemblies = new List <ITaskItem> (assemblies.Count); var resolvedUserAssemblies = new List <ITaskItem> (assemblies.Count); foreach (var assembly in assemblies.Values) { var mdb = assembly + ".mdb"; var pdb = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(mdb)) { resolvedSymbols.Add(new TaskItem(mdb)); } if (File.Exists(pdb) && Files.IsPortablePdb(pdb)) { resolvedSymbols.Add(new TaskItem(pdb)); } resolvedAssemblies.Add(assembly); if (MonoAndroidHelper.IsFrameworkAssembly(assembly.ItemSpec, checkSdkPath: true)) { resolvedFrameworkAssemblies.Add(assembly); } else { resolvedUserAssemblies.Add(assembly); } } ResolvedAssemblies = resolvedAssemblies.ToArray(); ResolvedSymbols = resolvedSymbols.ToArray(); ResolvedFrameworkAssemblies = resolvedFrameworkAssemblies.ToArray(); ResolvedUserAssemblies = resolvedUserAssemblies.ToArray(); ResolvedDoNotPackageAttributes = do_not_package_atts.ToArray(); }
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; 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, "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); }
void AddAssemblyReferences(DirectoryAssemblyResolver resolver, Dictionary <string, ITaskItem> assemblies, AssemblyDefinition assembly, List <string> resolutionPath) { var assemblyName = assembly.Name.Name; var fullPath = Path.GetFullPath(assembly.MainModule.FileName); // Don't repeat assemblies we've already done bool topLevel = resolutionPath == null; if (!topLevel && assemblies.ContainsKey(assemblyName)) { return; } if (resolutionPath == null) { resolutionPath = new List <string>(); } CheckAssemblyAttributes(assembly); LogMessage("{0}Adding assembly reference for {1}, recursively...", new string (' ', indent), assembly.Name); resolutionPath.Add(assembly.Name.Name); indent += 2; // Add this assembly if (!topLevel) { assemblies [assemblyName] = CreateAssemblyTaskItem(fullPath); } // Recurse into each referenced assembly foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences) { AssemblyDefinition reference_assembly; try { reference_assembly = resolver.Resolve(reference); } catch (FileNotFoundException ex) { var references = new StringBuilder(); for (int i = 0; i < resolutionPath.Count; i++) { if (i != 0) { references.Append(" > "); } references.Append('`'); references.Append(resolutionPath [i]); references.Append('`'); } string missingAssembly = Path.GetFileNameWithoutExtension(ex.FileName); string message = $"Can not resolve reference: `{missingAssembly}`, referenced by {references}."; if (MonoAndroidHelper.IsFrameworkAssembly(ex.FileName)) { LogCodedError("XA2002", $"{message} Perhaps it doesn't exist in the Mono for Android profile?"); } else { LogCodedError("XA2002", $"{message} Please add a NuGet package or assembly reference for `{missingAssembly}`, or remove the reference to `{resolutionPath [0]}`."); } return; } AddAssemblyReferences(resolver, assemblies, reference_assembly, resolutionPath); } indent -= 2; resolutionPath.RemoveAt(resolutionPath.Count - 1); }
// Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*] public override bool Execute() { Log.LogDebugMessage("ResolveLibraryProjectImports Task"); Log.LogDebugMessage(" ImportsDirectory: {0}", ImportsDirectory); Log.LogDebugMessage(" OutputDirectory: {0}", OutputDirectory); Log.LogDebugMessage(" OutputImportDirectory: {0}", OutputImportDirectory); Log.LogDebugMessage(" UseShortFileNames: {0}", UseShortFileNames); Log.LogDebugTaskItems(" Assemblies: ", Assemblies); var jars = new List <string> (); var resolvedResourceDirectories = new List <string> (); var resolvedAssetDirectories = new List <string> (); var resolvedEnvironmentFiles = new List <string> (); Extract(jars, resolvedResourceDirectories, resolvedAssetDirectories, resolvedEnvironmentFiles); Jars = jars.ToArray(); ResolvedResourceDirectories = resolvedResourceDirectories .Select(s => new TaskItem(Path.GetFullPath(s))) .ToArray(); ResolvedAssetDirectories = resolvedAssetDirectories.ToArray(); ResolvedEnvironmentFiles = resolvedEnvironmentFiles.ToArray(); ResolvedResourceDirectoryStamps = ResolvedResourceDirectories .Select(s => new TaskItem(Path.GetFullPath(Path.Combine(s.ItemSpec, "../..")) + ".stamp")) .ToArray(); foreach (var directory in ResolvedResourceDirectories) { MonoAndroidHelper.SetDirectoryWriteable(directory.ItemSpec); } foreach (var directory in ResolvedAssetDirectories) { MonoAndroidHelper.SetDirectoryWriteable(directory); } if (!string.IsNullOrEmpty(CacheFile)) { var document = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement("Paths", new XElement("Jars", string.Join(";", Jars)), new XElement("ResolvedResourceDirectories", ResolvedResourceDirectories.Select(e => new XElement("ResolvedResourceDirectory", e))), new XElement("ResolvedAssetDirectories", ResolvedAssetDirectories.Select(e => new XElement("ResolvedAssetDirectory", e))), new XElement("ResolvedEnvironmentFiles", ResolvedEnvironmentFiles.Select(e => new XElement("ResolvedEnvironmentFile", e))), new XElement("ResolvedResourceDirectoryStamps", ResolvedResourceDirectoryStamps.Select(e => new XElement("ResolvedResourceDirectoryStamp", e))) )); document.Save(CacheFile); } Log.LogDebugTaskItems(" Jars: ", Jars.Select(s => new TaskItem(s)).ToArray()); Log.LogDebugTaskItems(" ResolvedResourceDirectories: ", ResolvedResourceDirectories.Select(s => new TaskItem(s)).ToArray()); Log.LogDebugTaskItems(" ResolvedAssetDirectories: ", ResolvedAssetDirectories.Select(s => new TaskItem(s)).ToArray()); Log.LogDebugTaskItems(" ResolvedEnvironmentFiles: ", ResolvedEnvironmentFiles.Select(s => new TaskItem(s)).ToArray()); Log.LogDebugTaskItems(" ResolvedResourceDirectoryStamps: ", ResolvedResourceDirectoryStamps); return(!Log.HasLoggedErrors); }
void Run(DirectoryAssemblyResolver res) { PackageNamingPolicy pnp; JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse(PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseCrc64; foreach (var dir in FrameworkDirectories) { if (Directory.Exists(dir.ItemSpec)) { res.SearchDirectories.Add(dir.ItemSpec); } } // Put every assembly we'll need in the resolver bool hasExportReference = false; bool haveMonoAndroid = false; var allTypemapAssemblies = new HashSet <string> (StringComparer.OrdinalIgnoreCase); var userAssemblies = new Dictionary <string, string> (StringComparer.OrdinalIgnoreCase); foreach (var assembly in ResolvedAssemblies) { bool value; if (bool.TryParse(assembly.GetMetadata(AndroidSkipJavaStubGeneration), out value) && value) { Log.LogDebugMessage($"Skipping Java Stub Generation for {assembly.ItemSpec}"); continue; } bool addAssembly = false; string fileName = Path.GetFileName(assembly.ItemSpec); if (!hasExportReference && String.Compare("Mono.Android.Export.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) { hasExportReference = true; addAssembly = true; } else if (!haveMonoAndroid && String.Compare("Mono.Android.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) { haveMonoAndroid = true; addAssembly = true; } else if (MonoAndroidHelper.FrameworkAssembliesToTreatAsUserAssemblies.Contains(fileName)) { if (!bool.TryParse(assembly.GetMetadata(AndroidSkipJavaStubGeneration), out value) || !value) { string name = Path.GetFileNameWithoutExtension(fileName); if (!userAssemblies.ContainsKey(name)) { userAssemblies.Add(name, assembly.ItemSpec); } addAssembly = true; } } if (addAssembly) { allTypemapAssemblies.Add(assembly.ItemSpec); } res.Load(assembly.ItemSpec); } // However we only want to look for JLO types in user code for Java stub code generation foreach (var asm in ResolvedUserAssemblies) { if (bool.TryParse(asm.GetMetadata(AndroidSkipJavaStubGeneration), out bool value) && value) { Log.LogDebugMessage($"Skipping Java Stub Generation for {asm.ItemSpec}"); continue; } allTypemapAssemblies.Add(asm.ItemSpec); userAssemblies.Add(Path.GetFileNameWithoutExtension(asm.ItemSpec), asm.ItemSpec); } // Step 1 - Find all the JLO types var cache = new TypeDefinitionCache(); var scanner = new JavaTypeScanner(this.CreateTaskLogger(), cache) { ErrorOnCustomJavaObject = ErrorOnCustomJavaObject, }; List <TypeDefinition> allJavaTypes = scanner.GetJavaTypes(allTypemapAssemblies, res); // Step 2 - Generate type maps // Type mappings need to use all the assemblies, always. WriteTypeMappings(allJavaTypes); var javaTypes = new List <TypeDefinition> (); foreach (TypeDefinition td in allJavaTypes) { if (!userAssemblies.ContainsKey(td.Module.Assembly.Name.Name) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration(td, cache)) { continue; } javaTypes.Add(td); } // Step 3 - Generate Java stub code var success = CreateJavaSources(javaTypes, cache); if (!success) { return; } // We need to save a map of .NET type -> ACW type for resource file fixups var managed = new Dictionary <string, TypeDefinition> (javaTypes.Count, StringComparer.Ordinal); var java = new Dictionary <string, TypeDefinition> (javaTypes.Count, StringComparer.Ordinal); var managedConflicts = new Dictionary <string, List <string> > (0, StringComparer.Ordinal); var javaConflicts = new Dictionary <string, List <string> > (0, StringComparer.Ordinal); using (var acw_map = MemoryStreamPool.Shared.CreateStreamWriter(Encoding.Default)) { foreach (TypeDefinition type in javaTypes) { string managedKey = type.FullName.Replace('/', '.'); string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); acw_map.Write(type.GetPartialAssemblyQualifiedName(cache)); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); TypeDefinition conflict; bool hasConflict = false; if (managed.TryGetValue(managedKey, out conflict)) { if (!managedConflicts.TryGetValue(managedKey, out var list)) { managedConflicts.Add(managedKey, list = new List <string> { conflict.GetPartialAssemblyName(cache) }); } list.Add(type.GetPartialAssemblyName(cache)); hasConflict = true; } if (java.TryGetValue(javaKey, out conflict)) { if (!javaConflicts.TryGetValue(javaKey, out var list)) { javaConflicts.Add(javaKey, list = new List <string> { conflict.GetAssemblyQualifiedName(cache) }); } list.Add(type.GetAssemblyQualifiedName(cache)); success = false; hasConflict = true; } if (!hasConflict) { managed.Add(managedKey, type); java.Add(javaKey, type); acw_map.Write(managedKey); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); acw_map.Write(JavaNativeTypeManager.ToCompatJniName(type, cache).Replace('/', '.')); acw_map.Write(';'); acw_map.Write(javaKey); acw_map.WriteLine(); } } acw_map.Flush(); MonoAndroidHelper.CopyIfStreamChanged(acw_map.BaseStream, AcwMapFile); } foreach (var kvp in managedConflicts) { Log.LogCodedWarning("XA4214", Properties.Resources.XA4214, kvp.Key, string.Join(", ", kvp.Value)); Log.LogCodedWarning("XA4214", Properties.Resources.XA4214_Result, kvp.Key, kvp.Value [0]); } foreach (var kvp in javaConflicts) { Log.LogCodedError("XA4215", Properties.Resources.XA4215, kvp.Key); foreach (var typeName in kvp.Value) { Log.LogCodedError("XA4215", Properties.Resources.XA4215_Details, kvp.Key, typeName); } } // Step 3 - Merge [Activity] and friends into AndroidManifest.xml var manifest = new ManifestDocument(ManifestTemplate); manifest.PackageName = PackageName; manifest.ApplicationName = ApplicationName ?? PackageName; manifest.Placeholders = ManifestPlaceholders; manifest.Assemblies.AddRange(userAssemblies.Values); manifest.Resolver = res; manifest.SdkDir = AndroidSdkDir; manifest.SdkVersion = AndroidSdkPlatform; manifest.Debug = Debug; manifest.MultiDex = MultiDex; manifest.NeedsInternet = NeedsInternet; manifest.InstantRunEnabled = InstantRunEnabled; var additionalProviders = manifest.Merge(Log, cache, allJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments); // Only write the new manifest if it actually changed if (manifest.SaveIfChanged(Log, MergedAndroidManifestOutput)) { Log.LogDebugMessage($"Saving: {MergedAndroidManifestOutput}"); } // Create additional runtime provider java sources. string providerTemplateFile = UseSharedRuntime ? "MonoRuntimeProvider.Shared.java" : "MonoRuntimeProvider.Bundled.java"; string providerTemplate = GetResource(providerTemplateFile); foreach (var provider in additionalProviders) { var contents = providerTemplate.Replace("MonoRuntimeProvider", provider); var real_provider = Path.Combine(OutputDirectory, "src", "mono", provider + ".java"); MonoAndroidHelper.CopyIfStringChanged(contents, real_provider); } // Create additional application java sources. StringWriter regCallsWriter = new StringWriter(); regCallsWriter.WriteLine("\t\t// Application and Instrumentation ACWs must be registered first."); foreach (var type in javaTypes) { if (JavaNativeTypeManager.IsApplication(type, cache) || JavaNativeTypeManager.IsInstrumentation(type, cache)) { string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); regCallsWriter.WriteLine("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);", type.GetAssemblyQualifiedName(cache), javaKey); } } regCallsWriter.Close(); var real_app_dir = Path.Combine(OutputDirectory, "src", "mono", "android", "app"); string applicationTemplateFile = "ApplicationRegistration.java"; SaveResource(applicationTemplateFile, applicationTemplateFile, real_app_dir, template => template.Replace("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString())); }
public bool RunTask() { Log.LogDebugMessage("ResolveSdksTask:"); Log.LogDebugMessage(" AndroidApiLevel: {0}", AndroidApiLevel); Log.LogDebugMessage(" AndroidSdkBuildToolsVersion: {0}", AndroidSdkBuildToolsVersion); Log.LogDebugMessage($" {nameof (AndroidSdkPath)}: {AndroidSdkPath}"); Log.LogDebugMessage($" {nameof (AndroidNdkPath)}: {AndroidNdkPath}"); Log.LogDebugMessage($" {nameof (JavaSdkPath)}: {JavaSdkPath}"); Log.LogDebugTaskItems(" ReferenceAssemblyPaths: ", ReferenceAssemblyPaths); Log.LogDebugMessage(" TargetFrameworkVersion: {0}", TargetFrameworkVersion); Log.LogDebugMessage(" UseLatestAndroidPlatformSdk: {0}", UseLatestAndroidPlatformSdk); Log.LogDebugMessage(" SequencePointsMode: {0}", SequencePointsMode); Log.LogDebugMessage(" LintToolPath: {0}", LintToolPath); // OS X: $prefix/lib/xamarin.android/xbuild/Xamarin/Android // Windows: %ProgramFiles(x86)%\MSBuild\Xamarin\Android if (string.IsNullOrEmpty(MonoAndroidToolsPath)) { MonoAndroidToolsPath = Path.GetDirectoryName(typeof(ResolveSdks).Assembly.Location); } MonoAndroidBinPath = MonoAndroidHelper.GetOSBinPath() + Path.DirectorySeparatorChar; MonoAndroidHelper.RefreshSupportedVersions(ReferenceAssemblyPaths); MonoAndroidHelper.RefreshAndroidSdk(AndroidSdkPath, AndroidNdkPath, JavaSdkPath); this.AndroidNdkPath = MonoAndroidHelper.AndroidSdk.AndroidNdkPath; this.AndroidSdkPath = MonoAndroidHelper.AndroidSdk.AndroidSdkPath; this.JavaSdkPath = MonoAndroidHelper.AndroidSdk.JavaSdkPath; if (!ValidateJavaVersion(TargetFrameworkVersion, AndroidSdkBuildToolsVersion)) { return(false); } if (string.IsNullOrEmpty(AndroidSdkPath)) { Log.LogCodedError("XA5205", "The Android SDK Directory could not be found. Please set via /p:AndroidSdkDirectory."); return(false); } string toolsZipAlignPath = Path.Combine(AndroidSdkPath, "tools", ZipAlign); bool findZipAlign = (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath)) && !File.Exists(toolsZipAlignPath); var lintPaths = new string [] { LintToolPath ?? string.Empty, Path.Combine(AndroidSdkPath, "tools"), Path.Combine(AndroidSdkPath, "tools", "bin"), }; LintToolPath = null; foreach (var path in lintPaths) { if (File.Exists(Path.Combine(path, Lint))) { LintToolPath = path; break; } } foreach (var dir in MonoAndroidHelper.AndroidSdk.GetBuildToolsPaths(AndroidSdkBuildToolsVersion)) { Log.LogDebugMessage("Trying build-tools path: {0}", dir); if (dir == null || !Directory.Exists(dir)) { continue; } var toolsPaths = new string[] { Path.Combine(dir), Path.Combine(dir, "bin"), }; string aapt = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, Aapt))); if (string.IsNullOrEmpty(aapt)) { Log.LogDebugMessage("Could not find `{0}`; tried: {1}", Aapt, string.Join(";", toolsPaths.Select(x => Path.Combine(x, Aapt)))); continue; } AndroidSdkBuildToolsPath = Path.GetFullPath(dir); AndroidSdkBuildToolsBinPath = Path.GetFullPath(aapt); string zipalign = toolsPaths.FirstOrDefault(x => File.Exists(Path.Combine(x, ZipAlign))); if (findZipAlign && string.IsNullOrEmpty(zipalign)) { Log.LogDebugMessage("Could not find `{0}`; tried: {1}", ZipAlign, string.Join(";", toolsPaths.Select(x => Path.Combine(x, ZipAlign)))); continue; } else { break; } } if (string.IsNullOrEmpty(AndroidSdkBuildToolsPath)) { Log.LogCodedError("XA5205", string.Format( "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.", Aapt, AndroidSdkPath, Path.DirectorySeparatorChar, Android)); return(false); } ApkSignerJar = Path.Combine(AndroidSdkBuildToolsBinPath, "lib", ApkSigner); AndroidUseApkSigner = File.Exists(ApkSignerJar); if (string.IsNullOrEmpty(ZipAlignPath) || !Directory.Exists(ZipAlignPath)) { ZipAlignPath = new[] { Path.Combine(AndroidSdkBuildToolsPath), Path.Combine(AndroidSdkBuildToolsBinPath), Path.Combine(AndroidSdkPath, "tools"), } .Where(p => File.Exists(Path.Combine(p, ZipAlign))) .FirstOrDefault(); } if (string.IsNullOrEmpty(ZipAlignPath)) { Log.LogCodedError("XA5205", string.Format( "Cannot find `{0}`. Please install the Android SDK Build-tools package with the `{1}{2}tools{2}{3}` program.", ZipAlign, AndroidSdkPath, Path.DirectorySeparatorChar, Android)); return(false); } if (!ValidateApiLevels()) { return(false); } if (!MonoAndroidHelper.SupportedVersions.FrameworkDirectories.Any(p => Directory.Exists(Path.Combine(p, TargetFrameworkVersion)))) { Log.LogError( subcategory: string.Empty, errorCode: "XA0001", helpKeyword: string.Empty, file: ProjectFilePath, lineNumber: 0, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: "Unsupported or invalid $(TargetFrameworkVersion) value of '{0}'. Please update your Project Options.", messageArgs: new[] { TargetFrameworkVersion, } ); return(false); } SequencePointsMode mode; if (!Aot.TryGetSequencePointsMode(SequencePointsMode ?? "None", out mode)) { Log.LogCodedError("XA0104", "Invalid Sequence Point mode: {0}", SequencePointsMode); } AndroidSequencePointsMode = mode.ToString(); MonoAndroidHelper.TargetFrameworkDirectories = ReferenceAssemblyPaths; AndroidApiLevelName = MonoAndroidHelper.SupportedVersions.GetIdFromApiLevel(AndroidApiLevel); Log.LogDebugMessage("ResolveSdksTask Outputs:"); Log.LogDebugMessage(" AndroidApiLevel: {0}", AndroidApiLevel); Log.LogDebugMessage(" AndroidApiLevelName: {0}", AndroidApiLevelName); Log.LogDebugMessage(" AndroidNdkPath: {0}", AndroidNdkPath); Log.LogDebugMessage(" AndroidSdkBuildToolsPath: {0}", AndroidSdkBuildToolsPath); Log.LogDebugMessage(" AndroidSdkBuildToolsBinPath: {0}", AndroidSdkBuildToolsBinPath); Log.LogDebugMessage(" AndroidSdkPath: {0}", AndroidSdkPath); Log.LogDebugMessage(" JavaSdkPath: {0}", JavaSdkPath); Log.LogDebugMessage(" JdkVersion: {0}", JdkVersion); Log.LogDebugMessage(" MinimumRequiredJdkVersion: {0}", MinimumRequiredJdkVersion); Log.LogDebugMessage(" MonoAndroidBinPath: {0}", MonoAndroidBinPath); Log.LogDebugMessage(" MonoAndroidToolsPath: {0}", MonoAndroidToolsPath); Log.LogDebugMessage(" TargetFrameworkVersion: {0}", TargetFrameworkVersion); Log.LogDebugMessage(" ZipAlignPath: {0}", ZipAlignPath); Log.LogDebugMessage(" SupportedApiLevel: {0}", SupportedApiLevel); Log.LogDebugMessage(" AndroidSequencePointMode: {0}", AndroidSequencePointsMode); Log.LogDebugMessage(" LintToolPath: {0}", LintToolPath); if (!string.IsNullOrEmpty(CacheFile)) { Directory.CreateDirectory(Path.GetDirectoryName(CacheFile)); var document = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement("Sdk", new XElement("AndroidApiLevel", AndroidApiLevel), new XElement("AndroidApiLevelName", AndroidApiLevelName), new XElement("AndroidNdkPath", AndroidNdkPath), new XElement("AndroidSdkBuildToolsPath", AndroidSdkBuildToolsPath), new XElement("AndroidSdkBuildToolsBinPath", AndroidSdkBuildToolsBinPath), new XElement("AndroidSdkPath", AndroidSdkPath), new XElement("JavaSdkPath", JavaSdkPath), new XElement("MonoAndroidBinPath", MonoAndroidBinPath), new XElement("MonoAndroidToolsPath", MonoAndroidToolsPath), new XElement("ReferenceAssemblyPaths", (ReferenceAssemblyPaths ?? new string [0]) .Select(e => new XElement("ReferenceAssemblyPath", e))), new XElement("TargetFrameworkVersion", TargetFrameworkVersion), new XElement("ZipAlignPath", ZipAlignPath), new XElement("MonoAndroidIncludePath", MonoAndroidIncludePath), new XElement("SupportedApiLevel", SupportedApiLevel), new XElement("AndroidSequencePointsMode", AndroidSequencePointsMode.ToString()), new XElement("LintToolPath", LintToolPath) )); document.Save(CacheFile); } //note: this task does not error out if it doesn't find all things. that's the job of the targets return(!Log.HasLoggedErrors); }
bool CreateJavaSources(IEnumerable <TypeDefinition> javaTypes, TypeDefinitionCache cache) { string outputPath = Path.Combine(OutputDirectory, "src"); string monoInit = GetMonoInitSource(AndroidSdkPlatform, UseSharedRuntime); bool hasExportReference = ResolvedAssemblies.Any(assembly => Path.GetFileName(assembly.ItemSpec) == "Mono.Android.Export.dll"); bool generateOnCreateOverrides = int.Parse(AndroidSdkPlatform) <= 10; bool ok = true; foreach (var t in javaTypes) { using (var writer = MemoryStreamPool.Shared.CreateStreamWriter()) { try { var jti = new JavaCallableWrapperGenerator(t, Log.LogWarning, cache) { GenerateOnCreateOverrides = generateOnCreateOverrides, ApplicationJavaClass = ApplicationJavaClass, MonoRuntimeInitialization = monoInit, }; jti.Generate(writer); writer.Flush(); var path = jti.GetDestinationPath(outputPath); MonoAndroidHelper.CopyIfStreamChanged(writer.BaseStream, path); if (jti.HasExport && !hasExportReference) { Diagnostic.Error(4210, Properties.Resources.XA4210); } } catch (XamarinAndroidException xae) { ok = false; Log.LogError( subcategory: "", errorCode: "XA" + xae.Code, helpKeyword: string.Empty, file: xae.SourceFile, lineNumber: xae.SourceLine, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: xae.MessageWithoutCode, messageArgs: new object [0] ); } catch (DirectoryNotFoundException ex) { ok = false; if (OS.IsWindows) { Diagnostic.Error(5301, Properties.Resources.XA5301, t.FullName, ex); } else { Diagnostic.Error(4209, Properties.Resources.XA4209, t.FullName, ex); } } catch (Exception ex) { ok = false; Diagnostic.Error(4209, Properties.Resources.XA4209, t.FullName, ex); } } } return(ok); }
void ExecuteWithAbi(string [] supportedAbis, string apkInputPath, string apkOutputPath) { ArchiveFileList files = new ArchiveFileList(); bool refresh = true; if (apkInputPath != null && File.Exists(apkInputPath) && !File.Exists(apkOutputPath)) { Log.LogDebugMessage($"Copying {apkInputPath} to {apkInputPath}"); File.Copy(apkInputPath, apkOutputPath, overwrite: true); refresh = false; } using (var notice = Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt")) using (var apk = new ZipArchiveEx(apkOutputPath, File.Exists(apkOutputPath) ? FileMode.Open : FileMode.Create)) { if (refresh) { for (long i = 0; i < apk.Archive.EntryCount; i++) { ZipEntry e = apk.Archive.ReadEntry((ulong)i); Log.LogDebugMessage($"Registering item {e.FullName}"); existingEntries.Add(e.FullName); } } if (apkInputPath != null && File.Exists(apkInputPath) && refresh) { var lastWriteOutput = File.Exists(apkOutputPath) ? File.GetLastWriteTimeUtc(apkOutputPath) : DateTime.MinValue; var lastWriteInput = File.GetLastWriteTimeUtc(apkInputPath); using (var packaged = new ZipArchiveEx(apkInputPath, FileMode.Open)) { foreach (var entry in packaged.Archive) { Log.LogDebugMessage($"Deregistering item {entry.FullName}"); existingEntries.Remove(entry.FullName); if (lastWriteInput <= lastWriteOutput) { continue; } if (apk.Archive.ContainsEntry(entry.FullName)) { ZipEntry e = apk.Archive.ReadEntry(entry.FullName); // check the CRC values as the ModifiedDate is always 01/01/1980 in the aapt generated file. if (entry.CRC == e.CRC) { Log.LogDebugMessage($"Skipping {entry.FullName} from {apkInputPath} as its up to date."); continue; } } var ms = new MemoryStream(); entry.Extract(ms); Log.LogDebugMessage($"Refreshing {entry.FullName} from {apkInputPath}"); apk.Archive.AddStream(ms, entry.FullName, compressionMethod: entry.CompressionMethod); } } } apk.FixupWindowsPathSeparators((a, b) => Log.LogDebugMessage($"Fixing up malformed entry `{a}` -> `{b}`")); string noticeName = RootPath + "NOTICE"; existingEntries.Remove(noticeName); if (!apk.Archive.ContainsEntry(noticeName)) { apk.Archive.AddEntry(noticeName, notice); } // Add classes.dx foreach (var dex in DalvikClasses) { string apkName = dex.GetMetadata("ApkName"); string dexPath = string.IsNullOrWhiteSpace(apkName) ? Path.GetFileName(dex.ItemSpec) : apkName; AddFileToArchiveIfNewer(apk, dex.ItemSpec, DalvikPath + dexPath); } if (EmbedAssemblies && !BundleAssemblies) { AddAssemblies(apk); } AddRuntimeLibraries(apk, supportedAbis); apk.Flush(); AddNativeLibraries(files, supportedAbis); AddAdditionalNativeLibraries(files, supportedAbis); if (TypeMappings != null) { foreach (ITaskItem typemap in TypeMappings) { AddFileToArchiveIfNewer(apk, typemap.ItemSpec, RootPath + Path.GetFileName(typemap.ItemSpec), compressionMethod: UncompressedMethod); } } int count = 0; foreach (var file in files) { var item = Path.Combine(file.archivePath, Path.GetFileName(file.filePath)) .Replace(Path.DirectorySeparatorChar, '/'); existingEntries.Remove(item); if (apk.SkipExistingFile(file.filePath, item)) { Log.LogDebugMessage($"Skipping {file.filePath} as the archive file is up to date."); continue; } Log.LogDebugMessage("\tAdding {0}", file.filePath); apk.Archive.AddFile(file.filePath, item, compressionMethod: GetCompressionMethod(file.filePath)); count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } var jarFiles = (JavaSourceFiles != null) ? JavaSourceFiles.Where(f => f.ItemSpec.EndsWith(".jar")) : null; if (jarFiles != null && JavaLibraries != null) { jarFiles = jarFiles.Concat(JavaLibraries); } else if (JavaLibraries != null) { jarFiles = JavaLibraries; } var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars) .Where(jar => !MonoAndroidHelper.IsEmbeddedReferenceJar(jar)); var jarFilePaths = libraryProjectJars.Concat(jarFiles != null ? jarFiles.Select(j => j.ItemSpec) : Enumerable.Empty <string> ()); jarFilePaths = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths); count = 0; foreach (var jarFile in jarFilePaths) { using (var jar = ZipArchive.Open(File.OpenRead(jarFile))) { foreach (var jarItem in jar) { if (jarItem.IsDirectory) { continue; } var name = jarItem.FullName; if (!PackagingUtils.CheckEntryForPackaging(name)) { continue; } var path = RootPath + name; existingEntries.Remove(path); if (apk.SkipExistingEntry(jarItem, path)) { Log.LogDebugMessage($"Skipping {path} as the archive file is up to date."); continue; } byte [] data; using (var d = new MemoryStream()) { jarItem.Extract(d); data = d.ToArray(); } Log.LogDebugMessage($"Adding {path} as the archive file is out of date."); apk.Archive.AddEntry(data, path); } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } // Clean up Removed files. foreach (var entry in existingEntries) { Log.LogDebugMessage($"Removing {entry} as it is not longer required."); apk.Archive.DeleteEntry(entry); } apk.Flush(); FixupArchive(apk); } }
void ExecuteWithAbi(string supportedAbis, string apkInputPath, string apkOutputPath) { ArchiveFileList files = new ArchiveFileList(); if (apkInputPath != null) { File.Copy(apkInputPath, apkOutputPath + "new", overwrite: true); } using (var apk = new ZipArchiveEx(apkOutputPath + "new", apkInputPath != null ? FileMode.Open : FileMode.Create)) { apk.Archive.AddEntry("NOTICE", Assembly.GetExecutingAssembly().GetManifestResourceStream("NOTICE.txt")); // Add classes.dx apk.Archive.AddFiles(DalvikClasses, useFileDirectories: false); if (EmbedAssemblies && !BundleAssemblies) { AddAssemblies(apk); } AddEnvironment(apk); AddRuntimeLibraries(apk, supportedAbis); apk.Flush(); AddNativeLibraries(files, supportedAbis); apk.Flush(); AddAdditionalNativeLibraries(files, supportedAbis); apk.Flush(); AddNativeLibrariesFromAssemblies(apk, supportedAbis); apk.Flush(); foreach (ITaskItem typemap in TypeMappings) { apk.Archive.AddFile(typemap.ItemSpec, Path.GetFileName(typemap.ItemSpec), compressionMethod: CompressionMethod.Store); } int count = 0; foreach (var file in files) { var item = Path.Combine(file.Item2, Path.GetFileName(file.Item1)) .Replace(Path.DirectorySeparatorChar, '/'); if (apk.Archive.ContainsEntry(item)) { Log.LogWarning(null, "XA4301", null, file.Item1, 0, 0, 0, 0, "Apk already contains the item {0}; ignoring.", item); continue; } apk.Archive.AddFile(file.Item1, item); count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } if (_Debug) { AddGdbservers(apk, files, supportedAbis, debugServer); } var jarFiles = (JavaSourceFiles != null) ? JavaSourceFiles.Where(f => f.ItemSpec.EndsWith(".jar")) : null; if (jarFiles != null && JavaLibraries != null) { jarFiles = jarFiles.Concat(JavaLibraries); } else if (JavaLibraries != null) { jarFiles = JavaLibraries; } var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars) .Where(jar => !MonoAndroidHelper.IsEmbeddedReferenceJar(jar)); var jarFilePaths = libraryProjectJars.Concat(jarFiles != null ? jarFiles.Select(j => j.ItemSpec) : Enumerable.Empty <string> ()); jarFilePaths = MonoAndroidHelper.DistinctFilesByContent(jarFilePaths); count = 0; foreach (var jarFile in jarFilePaths) { using (var jar = ZipArchive.Open(File.OpenRead(jarFile))) { foreach (var jarItem in jar.Where(ze => !ze.IsDirectory && !ze.FullName.StartsWith("META-INF") && !ze.FullName.EndsWith(".class") && !ze.FullName.EndsWith(".java") && !ze.FullName.EndsWith("MANIFEST.MF"))) { byte [] data; using (var d = new System.IO.MemoryStream()) { jarItem.Extract(d); data = d.ToArray(); } if (apk.Archive.Any(e => e.FullName == jarItem.FullName)) { Log.LogMessage("Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk", jarItem.FullName, Path.GetFileName(jarFile)); } else { apk.Archive.AddEntry(data, jarItem.FullName); } } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } if (StubApplicationDataFile != null && File.Exists(StubApplicationDataFile)) { apk.Archive.AddFile(StubApplicationDataFile, Path.GetFileName(StubApplicationDataFile)); } } MonoAndroidHelper.CopyIfZipChanged(apkOutputPath + "new", apkOutputPath); File.Delete(apkOutputPath + "new"); }
bool Execute(DirectoryAssemblyResolver res) { 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(wp); } } return(true); }
string MakeSureLibraryIsInPlace(string destinationBase, string url, string version, string embeddedArchive, string sha1) { if (string.IsNullOrEmpty(url)) { return(null); } Log.LogDebugMessage("Making sure we have {0} downloaded and extracted {1} from it...", url, embeddedArchive); string destinationDir = version == null ? destinationBase : Path.Combine(destinationBase, version); bool createDestinationDirectory = !Directory.Exists(destinationDir); if (createDestinationDirectory) { Directory.CreateDirectory(destinationDir); } var hash = string.Concat(md5.ComputeHash(Encoding.UTF8.GetBytes(url)).Select(b => b.ToString("X02"))); var uri = new Uri(url); var extraPath = extraPaths.FirstOrDefault(x => File.Exists(Path.Combine(AndroidSdkDirectory, x, embeddedArchive ?? String.Empty))); string zipDir = !uri.IsFile ? Path.Combine(CachePath, "zips") : destinationDir; bool createZipDirectory = !Directory.Exists(zipDir); if (createZipDirectory) { Directory.CreateDirectory(zipDir); } string file = Path.Combine(zipDir, !uri.IsFile ? hash + ".zip" : Path.GetFileName(uri.AbsolutePath)); if (string.IsNullOrEmpty(extraPath) && (!File.Exists(file) || !IsValidDownload(file, sha1) || !MonoAndroidHelper.IsValidZip(file))) { int progress = -1; var downloadHandler = new Action <long, long, int>((r, t, p) => { if (p % 10 != 0 || progress == p) { return; } progress = p; LogMessage("\t({0}/{1}b), total {2:F1}%", r, t, p); }); LogMessage(" Downloading {0} into {1}", url, zipDir); try { Download(file, uri, downloadHandler); if (MonoAndroidHelper.IsValidZip(file)) { LogMessage(" Downloading Complete"); } else { LogCodedError("XA5208", "Download succeeded but the zip file was not valid. Please do a clean build and try again."); } } catch (Exception e) { LogCodedError("XA5208", "Download failed. Please build again."); LogCodedError("XA5208", "Reason: {0}", e.GetBaseException().Message); Log.LogMessage(MessageImportance.Low, e.ToString()); } } else { if (string.IsNullOrEmpty(extraPath)) { LogDebugMessage(" reusing existing archive: {0}", file); } else { LogDebugMessage(" found `{0}` in `{1}`", embeddedArchive, Path.Combine(AndroidSdkDirectory, extraPath)); } } string contentDir = string.IsNullOrEmpty(extraPath) ? Path.Combine(destinationDir, "content") : Path.Combine(AndroidSdkDirectory, extraPath); int attempt = 0; while (attempt < 3 && !Log.HasLoggedErrors) { var success = ExtractArchive(url, file, contentDir); if (!success && Log.HasLoggedErrors) { break; } if (!string.IsNullOrEmpty(embeddedArchive)) { string embeddedDir = Path.Combine(destinationDir, "embedded"); success = ExtractArchive(string.Format("{0}:{1}", url, embeddedArchive), Path.Combine(contentDir, embeddedArchive), embeddedDir); if (success) { contentDir = embeddedDir; break; } if (Log.HasLoggedErrors) { break; } if (!success) { Log.LogWarning("Expected File {0} does not exist. Trying to extract again.", Path.Combine(contentDir, embeddedArchive)); if (Directory.Exists(contentDir)) { Directory.Delete(contentDir, recursive: true); } } } else { break; } attempt++; } if (string.IsNullOrEmpty(contentDir) || !Directory.Exists(contentDir)) { if (createZipDirectory) { Directory.Delete(zipDir); } if (createDestinationDirectory) { Directory.Delete(destinationDir); } } return(contentDir); }
protected override string GenerateCommandLineCommands() { var cmd = new CommandLineBuilder(); // Add the JavaOptions if they are not null // These could be any of the additional options if (!string.IsNullOrEmpty(JavaOptions)) { cmd.AppendSwitch(JavaOptions); } // Add the specific -XmxN to override the default heap size for the JVM // N can be in the form of Nm or NGB (e.g 100m or 1GB ) cmd.AppendSwitchIfNotNull("-Xmx", JavaMaximumHeapSize); // See https://bugzilla.xamarin.com/show_bug.cgi?id=21096 cmd.AppendSwitch("-XX:-UseSplitVerifier"); // Arguments sent to java.exe cmd.AppendSwitchIfNotNull("-jar ", Path.Combine(MonoAndroidToolsDirectory, "jar2xml.jar")); foreach (var jar in SourceJars) { cmd.AppendSwitchIfNotNull("--jar=", Path.GetFullPath(jar.ItemSpec)); } var libraryProjectJars = MonoAndroidHelper.ExpandFiles(LibraryProjectJars); foreach (var jar in libraryProjectJars) { if (MonoAndroidHelper.IsEmbeddedReferenceJar(jar)) { cmd.AppendSwitchIfNotNull("--ref=", Path.GetFullPath(jar)); } else { cmd.AppendSwitchIfNotNull("--jar=", Path.GetFullPath(jar)); } } // Arguments sent to jar2xml var jarpath = Path.Combine(AndroidSdkDirectory, "platforms", "android-" + MonoAndroidHelper.GetPlatformApiLevelName(AndroidApiLevel), "android.jar"); cmd.AppendSwitchIfNotNull("--ref=", Path.GetFullPath(jarpath)); cmd.AppendSwitchIfNotNull("--out=", Path.GetFullPath(OutputFile)); if (ReferenceJars != null) { foreach (var jar in ReferenceJars) { cmd.AppendSwitchIfNotNull("--ref=", Path.GetFullPath(jar.ItemSpec)); } } if (DroidDocPaths != null) { foreach (var path in DroidDocPaths.Split(';')) { cmd.AppendSwitchIfNotNull("--droiddocpath=", Path.GetFullPath(path)); } } if (JavaDocPaths != null) { foreach (var path in JavaDocPaths.Split(';')) { cmd.AppendSwitchIfNotNull("--javadocpath=", Path.GetFullPath(path)); } } if (Java7DocPaths != null) { foreach (var path in Java7DocPaths.Split(';')) { cmd.AppendSwitchIfNotNull("--java7docpath=", Path.GetFullPath(path)); } } if (Java8DocPaths != null) { foreach (var path in Java8DocPaths.Split(';')) { cmd.AppendSwitchIfNotNull("--java8docpath=", Path.GetFullPath(path)); } } if (JavaDocs != null) { foreach (var doc in JavaDocs) { var opt = GetJavadocOption(doc.ItemSpec); if (opt != null) { cmd.AppendSwitchIfNotNull(opt, Path.GetFullPath(Path.GetDirectoryName(doc.ItemSpec))); } } } return(cmd.ToString()); }
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); using (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(); if (!result || Log.HasLoggedErrors) { if (File.Exists(CacheFile)) { File.Delete(CacheFile); } return(false); } 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); }
IEnumerable <Config> GetAotConfigs() { if (!Directory.Exists(AotOutputDirectory)) { Directory.CreateDirectory(AotOutputDirectory); } var sdkBinDirectory = MonoAndroidHelper.GetOSBinPath(); var abis = SupportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var abi in abis) { string aotCompiler = ""; string outdir = ""; string mtriple = ""; AndroidTargetArch arch; switch (abi) { case "armeabi-v7a": aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm"); outdir = Path.Combine(AotOutputDirectory, "armeabi-v7a"); mtriple = "armv7-linux-gnueabi"; arch = AndroidTargetArch.Arm; break; case "arm64": case "arm64-v8a": case "aarch64": aotCompiler = Path.Combine(sdkBinDirectory, "cross-arm64"); outdir = Path.Combine(AotOutputDirectory, "arm64-v8a"); mtriple = "aarch64-linux-android"; arch = AndroidTargetArch.Arm64; break; case "x86": aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86"); outdir = Path.Combine(AotOutputDirectory, "x86"); mtriple = "i686-linux-android"; arch = AndroidTargetArch.X86; break; case "x86_64": aotCompiler = Path.Combine(sdkBinDirectory, "cross-x86_64"); outdir = Path.Combine(AotOutputDirectory, "x86_64"); mtriple = "x86_64-linux-android"; arch = AndroidTargetArch.X86_64; break; // case "mips": default: throw new Exception("Unsupported Android target architecture ABI: " + abi); } if (!NdkUtil.ValidateNdkPlatform(Log, AndroidNdkDirectory, arch, enableLLVM:EnableLLVM)) { yield return(Config.Invalid); yield break; } if (!ValidateAotConfiguration(Log, arch, EnableLLVM)) { yield return(Config.Invalid); yield break; } outdir = Path.GetFullPath(outdir); if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } int level = GetNdkApiLevel(AndroidNdkDirectory, AndroidApiLevel, arch); string toolPrefix = NdkUtil.GetNdkToolPrefix(AndroidNdkDirectory, arch, level); var toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar)); var ldFlags = string.Empty; if (EnableLLVM) { string androidLibPath = string.Empty; try { androidLibPath = NdkUtil.GetNdkPlatformLibPath(AndroidNdkDirectory, arch, level); } catch (InvalidOperationException ex) { Diagnostic.Error(5101, ex.Message); } string toolchainLibDir; if (NdkUtil.UsingClangNDK) { toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath, arch); } else { toolchainLibDir = GetNdkToolchainLibraryDir(toolchainPath); } var libs = new List <string>(); if (NdkUtil.UsingClangNDK) { libs.Add($"-L{GetShortPath (toolchainLibDir)}"); libs.Add($"-L{GetShortPath (androidLibPath)}"); if (arch == AndroidTargetArch.Arm) { // Needed for -lunwind to work string compilerLibDir = Path.Combine(toolchainPath, "..", "sysroot", "usr", "lib", NdkUtil.GetArchDirName(arch)); libs.Add($"-L{GetShortPath (compilerLibDir)}"); } } libs.Add(GetShortPath(Path.Combine(toolchainLibDir, "libgcc.a"))); libs.Add(GetShortPath(Path.Combine(androidLibPath, "libc.so"))); libs.Add(GetShortPath(Path.Combine(androidLibPath, "libm.so"))); ldFlags = string.Join(";", libs); } foreach (var assembly in ResolvedAssemblies) { string outputFile = Path.Combine(outdir, string.Format("libaot-{0}.so", Path.GetFileName(assembly.ItemSpec))); string seqpointsFile = Path.Combine(outdir, string.Format("{0}.msym", Path.GetFileName(assembly.ItemSpec))); string tempDir = Path.Combine(outdir, Path.GetFileName(assembly.ItemSpec)); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } List <string> aotOptions = new List <string> (); if (Profiles != null && Profiles.Length > 0) { aotOptions.Add("profile-only"); foreach (var p in Profiles) { var fp = Path.GetFullPath(p.ItemSpec); aotOptions.Add($"profile={GetShortPath (fp)}"); } } if (!string.IsNullOrEmpty(AotAdditionalArguments)) { aotOptions.Add(AotAdditionalArguments); } if (sequencePointsMode == SequencePointsMode.Offline) { aotOptions.Add("msym-dir=" + GetShortPath(outdir)); } if (AotMode != AotMode.Normal) { aotOptions.Add(AotMode.ToString().ToLowerInvariant()); } aotOptions.Add("outfile=" + GetShortPath(outputFile)); aotOptions.Add("asmwriter"); aotOptions.Add("mtriple=" + mtriple); aotOptions.Add("tool-prefix=" + GetShortPath(toolPrefix)); aotOptions.Add("ld-flags=" + ldFlags); aotOptions.Add("llvm-path=" + GetShortPath(sdkBinDirectory)); aotOptions.Add("temp-path=" + GetShortPath(tempDir)); string aotOptionsStr = (EnableLLVM ? "--llvm " : "") + "--aot=" + string.Join(",", aotOptions); if (!string.IsNullOrEmpty(ExtraAotOptions)) { aotOptionsStr += (aotOptions.Count > 0 ? "," : "") + ExtraAotOptions; } // Due to a Monodroid MSBuild bug we can end up with paths to assemblies that are not in the intermediate // assembly directory (typically obj/assemblies). This can lead to problems with the Mono loader not being // able to find their dependency laters, since framework assemblies are stored in different directories. // This can happen when linking is disabled (AndroidLinkMode=None). Workaround this problem by resolving // the paths to the right assemblies manually. var resolvedPath = Path.GetFullPath(assembly.ItemSpec); var intermediateAssemblyPath = Path.Combine(IntermediateAssemblyDir, Path.GetFileName(assembly.ItemSpec)); if (LinkMode.ToLowerInvariant() == "none") { if (!resolvedPath.Contains(IntermediateAssemblyDir) && File.Exists(intermediateAssemblyPath)) { resolvedPath = intermediateAssemblyPath; } } var assembliesPath = Path.GetFullPath(Path.GetDirectoryName(resolvedPath)); var assemblyPath = QuoteFileName(Path.GetFullPath(resolvedPath)); yield return(new Config(assembliesPath, QuoteFileName(aotCompiler), aotOptionsStr, assemblyPath, outputFile)); } } }
void AddAssemblies(ZipArchiveEx apk, bool debug, bool compress, IDictionary <string, CompressedAssemblyInfo> compressedAssembliesInfo) { string sourcePath; AssemblyCompression.AssemblyData compressedAssembly = null; string compressedOutputDir = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(ApkOutputPath), "..", "lz4")); int count = 0; foreach (ITaskItem assembly in ResolvedUserAssemblies) { if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value) { Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' "); continue; } if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec)) { Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec); } sourcePath = CompressAssembly(assembly); // Add assembly var assemblyPath = GetAssemblyPath(assembly, frameworkAssembly: false); AddFileToArchiveIfNewer(apk, sourcePath, assemblyPath + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod); // Try to add config if exists var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config"); AddAssemblyConfigEntry(apk, assemblyPath, config); // Try to add symbols if Debug if (debug) { var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } } count++; if (count >= ZipArchiveEx.ZipFlushFilesLimit) { apk.Flush(); count = 0; } } count = 0; // Add framework assemblies foreach (ITaskItem assembly in ResolvedFrameworkAssemblies) { if (bool.TryParse(assembly.GetMetadata("AndroidSkipAddToPackage"), out bool value) && value) { Log.LogDebugMessage($"Skipping {assembly.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' "); continue; } if (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec)) { Log.LogCodedWarning("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec); } sourcePath = CompressAssembly(assembly); var assemblyPath = GetAssemblyPath(assembly, frameworkAssembly: true); AddFileToArchiveIfNewer(apk, sourcePath, assemblyPath + Path.GetFileName(assembly.ItemSpec), compressionMethod: UncompressedMethod); var config = Path.ChangeExtension(assembly.ItemSpec, "dll.config"); AddAssemblyConfigEntry(apk, assemblyPath, config); // Try to add symbols if Debug if (debug) { var symbols = Path.ChangeExtension(assembly.ItemSpec, "dll.mdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(symbols)) { AddFileToArchiveIfNewer(apk, symbols, assemblyPath + Path.GetFileName(symbols), compressionMethod: UncompressedMethod); } } count++; if (count >= ZipArchiveEx.ZipFlushFilesLimit) { apk.Flush(); count = 0; } } void EnsureCompressedAssemblyData(string sourcePath, uint descriptorIndex) { if (compressedAssembly == null) { compressedAssembly = new AssemblyCompression.AssemblyData(sourcePath, descriptorIndex); } else { compressedAssembly.SetData(sourcePath, descriptorIndex); } } string CompressAssembly(ITaskItem assembly) { if (!compress) { return(assembly.ItemSpec); } if (bool.TryParse(assembly.GetMetadata("AndroidSkipCompression"), out bool value) && value) { Log.LogDebugMessage($"Skipping compression of {assembly.ItemSpec} due to 'AndroidSkipCompression' == 'true' "); return(assembly.ItemSpec); } var key = CompressedAssemblyInfo.GetDictionaryKey(assembly); if (compressedAssembliesInfo.TryGetValue(key, out CompressedAssemblyInfo info) && info != null) { EnsureCompressedAssemblyData(assembly.ItemSpec, info.DescriptorIndex); string assemblyOutputDir; string subDirectory = assembly.GetMetadata("DestinationSubDirectory"); if (!String.IsNullOrEmpty(subDirectory)) { assemblyOutputDir = Path.Combine(compressedOutputDir, subDirectory); } else { assemblyOutputDir = compressedOutputDir; } AssemblyCompression.CompressionResult result = AssemblyCompression.Compress(compressedAssembly, assemblyOutputDir); if (result != AssemblyCompression.CompressionResult.Success) { switch (result) { case AssemblyCompression.CompressionResult.EncodingFailed: Log.LogMessage($"Failed to compress {assembly.ItemSpec}"); break; case AssemblyCompression.CompressionResult.InputTooBig: Log.LogMessage($"Input assembly {assembly.ItemSpec} exceeds maximum input size"); break; default: Log.LogMessage($"Unknown error compressing {assembly.ItemSpec}"); break; } return(assembly.ItemSpec); } return(compressedAssembly.DestinationPath); } else { Log.LogDebugMessage($"Assembly missing from {nameof (CompressedAssemblyInfo)}: {key}"); } return(assembly.ItemSpec); } }