void AddNativeLibraryToArchive(ZipArchiveEx apk, string abi, string filesystemPath, string inArchiveFileName) { string archivePath = $"lib/{abi}/{inArchiveFileName}"; Log.LogDebugMessage($"Adding native library: {filesystemPath} (APK path: {archivePath})"); apk.Archive.AddEntry(archivePath, File.OpenRead(filesystemPath), compressionMethod: GetCompressionMethod(archivePath)); }
void AddRuntimeLibraries(ZipArchiveEx apk, string [] supportedAbis) { bool use_shared_runtime = String.Equals(UseSharedRuntime, "true", StringComparison.OrdinalIgnoreCase); foreach (var abi in supportedAbis) { string library = string.Format("libmono-android.{0}.so", _Debug ? "debug" : "release"); AddNativeLibrary(apk, abi, library, "libmonodroid.so"); foreach (ITaskItem item in ApplicationSharedLibraries) { if (String.Compare(abi, item.GetMetadata("abi"), StringComparison.Ordinal) != 0) { continue; } AddNativeLibraryToArchive(apk, abi, item.ItemSpec, Path.GetFileName(item.ItemSpec)); } if (!use_shared_runtime) { // include the sgen AddNativeLibrary(apk, abi, "libmonosgen-2.0.so"); } AddBtlsLibs(apk, abi); AddProfilers(apk, abi); } }
void AddAssemblyConfigEntry(ZipArchiveEx apk, string assemblyPath, string configFile) { string inArchivePath = assemblyPath + Path.GetFileName(configFile); existingEntries.Remove(inArchivePath); if (!File.Exists(configFile)) { return; } CompressionMethod compressionMethod = UncompressedMethod; if (apk.SkipExistingFile(configFile, inArchivePath, compressionMethod)) { Log.LogDebugMessage($"Skipping {configFile} as the archive file is up to date."); return; } Log.LogDebugMessage($"Adding {configFile} as the archive file is out of date."); using (var source = File.OpenRead(configFile)) { var dest = new MemoryStream(); source.CopyTo(dest); dest.WriteByte(0); dest.Position = 0; apk.Archive.AddEntry(inArchivePath, dest, compressionMethod); } }
void AddBtlsLibs(ZipArchiveEx apk, string abi) { if (string.Compare("btls", TlsProvider, StringComparison.OrdinalIgnoreCase) == 0) { AddNativeLibrary(apk, abi, "libmono-btls-shared.so"); } // These are the other supported values // * "default": // * "legacy": }
void AddRuntimeLibraries (ZipArchiveEx apk, string [] supportedAbis) { foreach (var abi in supportedAbis) { foreach (ITaskItem item in ApplicationSharedLibraries) { if (String.Compare (abi, item.GetMetadata ("abi"), StringComparison.Ordinal) != 0) continue; AddNativeLibraryToArchive (apk, abi, item.ItemSpec, Path.GetFileName (item.ItemSpec)); } } }
public override bool Execute() { Log.LogDebugMessage("EmbeddedNativeLibraries Task"); Log.LogDebugMessage(" OutputDirectory: {0}", OutputDirectory); Log.LogDebugTaskItems(" EmbeddedNativeLibraries:", EmbeddedNativeLibraries); var outDirInfo = new DirectoryInfo(OutputDirectory); // Copy files into _NativeLibraryImportsDirectoryName (native_library_imports) dir. if (!outDirInfo.Exists) { outDirInfo.Create(); } foreach (var lib in EmbeddedNativeLibraries) { // seealso bug #3477 to find out why we use this method. var abi = MonoAndroidHelper.GetNativeLibraryAbi(lib); if (abi == null) { Log.LogWarning( subcategory: string.Empty, warningCode: "XA4300", helpKeyword: string.Empty, file: lib.ItemSpec, lineNumber: 0, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: "Native library '{0}' will not be bundled because it has an unsupported ABI.", messageArgs: new [] { lib.ItemSpec, } ); continue; } if (!outDirInfo.GetDirectories(abi).Any()) { outDirInfo.CreateSubdirectory(abi); } MonoAndroidHelper.CopyIfChanged(lib.ItemSpec, Path.Combine(OutputDirectory, abi, Path.GetFileName(lib.ItemSpec))); } var outpath = Path.Combine(outDirInfo.Parent.FullName, "__AndroidNativeLibraries__.zip"); if (Files.ArchiveZip(outpath, f => { using (var zip = new ZipArchiveEx(f)) { zip.AddDirectory(OutputDirectory, outDirInfo.Name); } })) { Log.LogDebugMessage("Saving contents to " + outpath); } return(true); }
void AddProfilers(ZipArchiveEx apk, string abi) { if (!string.IsNullOrEmpty(AndroidEmbedProfilers)) { foreach (var profiler in ParseProfilers(AndroidEmbedProfilers)) { var library = string.Format("libmono-profiler-{0}.so", profiler); AddNativeLibrary(apk, abi, library); } } }
bool AddFileToArchiveIfNewer (ZipArchiveEx apk, string file, string inArchivePath, CompressionMethod compressionMethod = CompressionMethod.Default) { existingEntries.Remove (inArchivePath); if (apk.SkipExistingFile (file, inArchivePath, compressionMethod)) { Log.LogDebugMessage ($"Skipping {file} as the archive file is up to date."); return false; } Log.LogDebugMessage ($"Adding {file} as the archive file is out of date."); apk.Archive.AddFile (file, inArchivePath, compressionMethod: compressionMethod); return true; }
/// <summary> /// aapt2 is putting AndroidManifest.xml in the root of the archive instead of at manifest/AndroidManifest.xml that bundletool expects. /// I see no way to change this behavior, so we can move the file for now: /// https://github.com/aosp-mirror/platform_frameworks_base/blob/e80b45506501815061b079dcb10bf87443bd385d/tools/aapt2/LoadedApk.h#L34 /// </summary> protected override void FixupArchive(ZipArchiveEx zip) { var entry = zip.Archive.ReadEntry("AndroidManifest.xml"); using (var stream = new MemoryStream()) { entry.Extract(stream); stream.Position = 0; zip.Archive.AddEntry("manifest/AndroidManifest.xml", stream); zip.Archive.DeleteEntry(entry); zip.Flush(); } }
void AddNativeLibrariesFromAssemblies(ZipArchiveEx apk, string supportedAbis) { int count = 0; var abis = supportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); using (var res = new DirectoryAssemblyResolver(this.CreateTaskLogger(), loadDebugSymbols: false)) { foreach (var assembly in EmbeddedNativeLibraryAssemblies) { res.Load(assembly.ItemSpec); } foreach (var assemblyPath in EmbeddedNativeLibraryAssemblies) { var assembly = res.GetAssembly(assemblyPath.ItemSpec); foreach (var mod in assembly.Modules) { var ressozip = mod.Resources.FirstOrDefault(r => r.Name == "__AndroidNativeLibraries__.zip") as EmbeddedResource; if (ressozip == null) { continue; } var data = ressozip.GetResourceData(); using (var ms = new MemoryStream(data)) { using (var zip = ZipArchive.Open(ms)) { foreach (var e in zip.Where(x => abis.Any(a => x.FullName.Contains($"/{a}/")))) { if (e.IsDirectory) { continue; } var key = e.FullName.Replace("native_library_imports", "lib"); if (apk.Archive.Any(k => k.FullName == key)) { Log.LogCodedWarning("4301", "Apk already contains the item {0}; ignoring.", key); continue; } using (var s = new MemoryStream()) { e.Extract(s); s.Position = 0; apk.Archive.AddEntry(s.ToArray(), key); } } } } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } } }
void AddNativeLibraryToArchive (ZipArchiveEx apk, string abi, string filesystemPath, string inArchiveFileName) { string archivePath = $"lib/{abi}/{inArchiveFileName}"; existingEntries.Remove (archivePath); CompressionMethod compressionMethod = GetCompressionMethod (archivePath); if (apk.SkipExistingFile (filesystemPath, archivePath, compressionMethod)) { Log.LogDebugMessage ($"Skipping {filesystemPath} (APK path: {archivePath}) as it is up to date."); return; } Log.LogDebugMessage ($"Adding native library: {filesystemPath} (APK path: {archivePath})"); apk.Archive.AddEntry (archivePath, File.OpenRead (filesystemPath), compressionMethod); }
public override bool RunTask() { var outDirInfo = new DirectoryInfo(OutputDirectory); // Copy files into _NativeLibraryImportsDirectoryName (native_library_imports) dir. if (!outDirInfo.Exists) { outDirInfo.Create(); } foreach (var lib in EmbeddedNativeLibraries) { // seealso bug #3477 to find out why we use this method. var abi = MonoAndroidHelper.GetNativeLibraryAbi(lib); if (abi == null) { Log.LogWarning( subcategory: string.Empty, warningCode: "XA4300", helpKeyword: string.Empty, file: lib.ItemSpec, lineNumber: 0, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: Properties.Resources.XA4300, messageArgs: new [] { lib.ItemSpec, } ); continue; } if (!outDirInfo.GetDirectories(abi).Any()) { outDirInfo.CreateSubdirectory(abi); } MonoAndroidHelper.CopyIfChanged(lib.ItemSpec, Path.Combine(OutputDirectory, abi, Path.GetFileName(lib.ItemSpec))); } var outpath = Path.Combine(outDirInfo.Parent.FullName, "__AndroidNativeLibraries__.zip"); if (Files.ArchiveZip(outpath, f => { using (var zip = new ZipArchiveEx(f)) { zip.AddDirectory(OutputDirectory, "native_library_imports"); } })) { Log.LogDebugMessage("Saving contents to " + outpath); } return(!Log.HasLoggedErrors); }
void AddAssemblyConfigEntry(ZipArchiveEx apk, string configFile) { if (!File.Exists(configFile)) { return; } using (var source = File.OpenRead(configFile)) { var dest = new MemoryStream(); source.CopyTo(dest); dest.WriteByte(0); dest.Position = 0; apk.Archive.AddEntry("assemblies/" + Path.GetFileName(configFile), dest, compressionMethod: CompressionMethod.Store); } }
void AddNativeLibrary(ZipArchiveEx apk, string abi, string filename, string inArchiveFileName = null) { string libPath = Path.Combine(MSBuildXamarinAndroidDirectory, "lib", abi); string path = Path.Combine(libPath, filename); if (PreferNativeLibrariesWithDebugSymbols) { string debugPath = Path.Combine(libPath, Path.ChangeExtension(filename, ".d.so")); if (File.Exists(debugPath)) { path = debugPath; } } AddNativeLibraryToArchive(apk, abi, path, inArchiveFileName ?? filename); }
public override bool Execute() { if (!Directory.Exists(ClassesOutputDirectory)) { Directory.CreateDirectory(ClassesOutputDirectory); } var result = base.Execute(); if (!result) { return(result); } // compress all the class files using (var zip = new ZipArchiveEx(Path.Combine(ClassesOutputDirectory, "..", "classes.zip"), FileMode.OpenOrCreate)) zip.AddDirectory(ClassesOutputDirectory, "", CompressionMethod.Store); return(result); }
/// <summary> /// aapt2 is putting AndroidManifest.xml in the root of the archive instead of at manifest/AndroidManifest.xml that bundletool expects. /// I see no way to change this behavior, so we can move the file for now: /// https://github.com/aosp-mirror/platform_frameworks_base/blob/e80b45506501815061b079dcb10bf87443bd385d/tools/aapt2/LoadedApk.h#L34 /// </summary> protected override void FixupArchive(ZipArchiveEx zip) { if (!zip.Archive.ContainsEntry("AndroidManifest.xml")) { Log.LogDebugMessage($"No AndroidManifest.xml. Skipping Fixup"); return; } var entry = zip.Archive.ReadEntry("AndroidManifest.xml"); Log.LogDebugMessage($"Fixing up AndroidManifest.xml to be manifest/AndroidManifest.xml."); using (var stream = new MemoryStream()) { entry.Extract(stream); stream.Position = 0; zip.Archive.AddEntry("manifest/AndroidManifest.xml", stream); zip.Archive.DeleteEntry(entry); zip.Flush(); } }
void AddRuntimeLibraries(ZipArchiveEx apk, string supportedAbis) { bool use_shared_runtime = String.Equals(UseSharedRuntime, "true", StringComparison.OrdinalIgnoreCase); var abis = supportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var abi in abis) { string library = string.Format("libmono-android.{0}.so", _Debug ? "debug" : "release"); AddNativeLibrary(apk, abi, library, "libmonodroid.so"); if (!use_shared_runtime) { // include the sgen AddNativeLibrary(apk, abi, "libmonosgen-2.0.so"); } AddBtlsLibs(apk, abi); AddProfilers(apk, abi); } }
void AddNativeLibrary(ZipArchiveEx apk, string abi, string filename, string inArchiveFileName = null) { string libPath = Path.Combine(MSBuildXamarinAndroidDirectory, "lib", abi); string path = Path.Combine(libPath, filename); if (PreferNativeLibrariesWithDebugSymbols) { string debugPath = Path.Combine(libPath, Path.ChangeExtension(filename, ".d.so")); if (File.Exists(debugPath)) { path = debugPath; } } string archivePath = string.Format("lib/{0}/{1}", abi, inArchiveFileName ?? filename); Log.LogDebugMessage($"Adding native library: {path} (APK path: {archivePath})"); apk.Archive.AddEntry(archivePath, File.OpenRead(path)); }
public override bool RunTask() { if (!Directory.Exists(ClassesOutputDirectory)) { Directory.CreateDirectory(ClassesOutputDirectory); } var result = base.RunTask(); if (!result) { return(result); } // compress all the class files if (!string.IsNullOrEmpty(ClassesZip)) { using (var zip = new ZipArchiveEx(ClassesZip, FileMode.OpenOrCreate)) { zip.AddDirectory(ClassesOutputDirectory, "", CompressionMethod.Store); } } return(result); }
private void AddGdbservers(ZipArchiveEx apk, ArchiveFileList files, string supportedAbis, AndroidDebugServer debugServer) { if (string.IsNullOrEmpty(AndroidNdkDirectory)) { return; } var sdkBinDirectory = MonoAndroidHelper.GetOSBinPath(); int count = 0; foreach (var sabi in supportedAbis.Split(new char[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)) { var arch = GdbPaths.GetArchFromAbi(sabi); var abi = GdbPaths.GetAbiFromArch(arch); if (abi == null) { continue; } var debugServerFile = GdbPaths.GetDebugServerFileName(debugServer); if (files.Any(f => string.Equals(Path.GetFileName(f.Item1), debugServerFile, StringComparison.Ordinal) && string.Equals(f.Item2, "lib/" + sabi, StringComparison.Ordinal))) { continue; } var entryName = string.Format("lib/{0}/{1}", sabi, debugServerFile); var debugServerPath = GdbPaths.GetDebugServerPath(debugServer, arch, AndroidNdkDirectory, sdkBinDirectory); if (!File.Exists(debugServerPath)) { continue; } Log.LogDebugMessage("Adding {0} debug server '{1}' to the APK as '{2}'", sabi, debugServerPath, entryName); apk.Archive.AddEntry(entryName, File.OpenRead(debugServerPath)); count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } }
public override bool RunTask() { var outDirInfo = new DirectoryInfo(OutputDirectory); // Copy files into _LibraryProjectImportsDirectoryName (library_project_imports) dir. if (!outDirInfo.Exists) { outDirInfo.Create(); } foreach (var sub in new string [] { "assets", "res", "java", "bin" }) { var subdirInfo = new DirectoryInfo(Path.Combine(outDirInfo.FullName, sub)); if (!subdirInfo.Exists) { subdirInfo.Create(); } } var compiledArchive = Path.Combine(FlatArchivesDirectory, "compiled.flata"); if (File.Exists(compiledArchive)) { Log.LogDebugMessage($"Coping: {compiledArchive} to {outDirInfo.FullName}"); MonoAndroidHelper.CopyIfChanged(compiledArchive, Path.Combine(outDirInfo.FullName, "compiled.flata")); } var dir_sep = new char [] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; if (AndroidAssets != null) { var dstsub = Path.Combine(outDirInfo.FullName, "assets"); if (!Directory.Exists(dstsub)) { Directory.CreateDirectory(dstsub); } foreach (var item in AndroidAssets) { var path = item.GetMetadata("Link"); path = !string.IsNullOrWhiteSpace(path) ? path : item.ItemSpec; var head = string.Join("\\", path.Split(dir_sep).TakeWhile(s => !s.Equals(MonoAndroidAssetsPrefix, StringComparison.OrdinalIgnoreCase))); path = head.Length == path.Length ? path : path.Substring((head.Length == 0 ? 0 : head.Length + Path.DirectorySeparatorChar) + MonoAndroidAssetsPrefix.Length).TrimStart(dir_sep); MonoAndroidHelper.CopyIfChanged(item.ItemSpec, Path.Combine(dstsub, path)); } } // resources folders are converted to the structure that aapt accepts. foreach (var srcsub in Directory.GetDirectories(ResourceDirectory)) { var dstsub = Path.Combine(outDirInfo.FullName, "res", Path.GetFileName(srcsub)); if (!Directory.Exists(dstsub)) { Directory.CreateDirectory(dstsub); } foreach (var file in Directory.GetFiles(srcsub)) { var filename = Path.GetFileName(file); MonoAndroidHelper.CopyIfChanged(file, Path.Combine(dstsub, Path.GetFileName(file))); } } if (RemovedAndroidResourceFiles != null) { foreach (var removedFile in RemovedAndroidResourceFiles) { var removed = Path.Combine(outDirInfo.FullName, removedFile.ItemSpec); if (File.Exists(removed)) { File.Delete(removed); Log.LogDebugMessage($"Removed: {removed}"); } } } if (AndroidJavaSources != null) { foreach (var item in AndroidJavaSources) { MonoAndroidHelper.CopyIfChanged(item.ItemSpec, Path.Combine(outDirInfo.FullName, item.ItemSpec)); } } if (AndroidJavaLibraries != null) { foreach (var item in AndroidJavaLibraries) { MonoAndroidHelper.CopyIfChanged(item.ItemSpec, Path.Combine(outDirInfo.FullName, item.ItemSpec)); } } var nameCaseMap = new StringWriter(); // add resource case mapping descriptor to the archive. if (AndroidResourcesInThisExactProject != null && AndroidResourcesInThisExactProject.Any()) { Log.LogMessage("writing __res_name_case_map.txt..."); foreach (var res in AndroidResourcesInThisExactProject) { nameCaseMap.WriteLine("{0};{1}", res.GetMetadata("LogicalName").Replace('\\', '/'), Path.Combine(Path.GetFileName(Path.GetDirectoryName(res.ItemSpec)), Path.GetFileName(res.ItemSpec)).Replace('\\', '/')); } File.WriteAllText(Path.Combine(outDirInfo.FullName, "__res_name_case_map.txt"), nameCaseMap.ToString()); } var outpath = Path.Combine(outDirInfo.Parent.FullName, "__AndroidLibraryProjects__.zip"); var fileMode = File.Exists(outpath) ? FileMode.Open : FileMode.CreateNew; if (Files.ArchiveZipUpdate(outpath, f => { using (var zip = new ZipArchiveEx(f, fileMode)) { zip.AddDirectory(outDirInfo.FullName, "library_project_imports"); if (RemovedAndroidResourceFiles != null) { foreach (var r in RemovedAndroidResourceFiles) { Log.LogDebugMessage($"Removed {r.ItemSpec} from {outpath}"); zip.RemoveFile("library_project_imports", r.ItemSpec); } } } })) { Log.LogDebugMessage("Saving contents to " + outpath); } return(!Log.HasLoggedErrors); }
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); } }
void ExecuteWithAbi(string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary <string, CompressedAssemblyInfo> compressedAssembliesInfo) { if (InterpreterEnabled) { foreach (string abi in supportedAbis) { if (String.Compare("x86", abi, StringComparison.OrdinalIgnoreCase) == 0) { Log.LogCodedError("XA0124", Properties.Resources.XA0124); return; } } } 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 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) { // NOTE: aapt2 is creating zip entries on Windows such as `assets\subfolder/asset2.txt` var entryName = entry.FullName; if (entryName.Contains("\\")) { entryName = entryName.Replace('\\', '/'); Log.LogDebugMessage($"Fixing up malformed entry `{entry.FullName}` -> `{entryName}`"); } Log.LogDebugMessage($"Deregistering item {entryName}"); existingEntries.Remove(entryName); if (lastWriteInput <= lastWriteOutput) { continue; } if (apk.Archive.ContainsEntry(entryName)) { ZipEntry e = apk.Archive.ReadEntry(entryName); // check the CRC values as the ModifiedDate is always 01/01/1980 in the aapt generated file. if (entry.CRC == e.CRC && entry.CompressedSize == e.CompressedSize) { Log.LogDebugMessage($"Skipping {entryName} from {apkInputPath} as its up to date."); continue; } } var ms = new MemoryStream(); entry.Extract(ms); Log.LogDebugMessage($"Refreshing {entryName} from {apkInputPath}"); apk.Archive.AddStream(ms, entryName, compressionMethod: entry.CompressionMethod); } } } apk.FixupWindowsPathSeparators((a, b) => Log.LogDebugMessage($"Fixing up malformed entry `{a}` -> `{b}`")); // 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, debug, compress, compressedAssembliesInfo); } 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.Replace(Path.DirectorySeparatorChar, '/')); existingEntries.Remove(item); CompressionMethod compressionMethod = GetCompressionMethod(file.filePath); if (apk.SkipExistingFile(file.filePath, item, compressionMethod)) { 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: compressionMethod); count++; if (count >= ZipArchiveEx.ZipFlushFilesLimit) { 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 stream = File.OpenRead(jarFile)) using (var jar = ZipArchive.Open(stream)) { 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; } if (apk.Archive.Any(e => e.FullName == path)) { Log.LogDebugMessage("Failed to add jar entry {0} from {1}: the same file already exists in the apk", name, Path.GetFileName(jarFile)); 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.ZipFlushFilesLimit) { 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); } }
protected virtual void FixupArchive(ZipArchiveEx zip) { }
void AddNativeLibrary(ZipArchiveEx apk, string abi, string filename) { var path = Path.Combine(MSBuildXamarinAndroidDirectory, "lib", abi, filename); apk.Archive.AddEntry(string.Format("lib/{0}/{1}", abi, filename), File.OpenRead(path)); }
public override bool RunTask() { if (LibraryProjectPropertiesFiles.Length == 0 && LibraryProjectZipFiles.Length == 0) { return(true); } var outDirInfo = new DirectoryInfo(OutputDirectory); // Copy files into _LibraryProjectImportsDirectoryName (library_project_imports) dir. if (!outDirInfo.Exists) { outDirInfo.Create(); } var projectsResolved = ResolveLibraryProjectReferences(LibraryProjectPropertiesFiles.Select(p => Path.GetFullPath(p.ItemSpec))); var imports = projectsResolved.Concat(LibraryProjectZipFiles.Select(p => p.ItemSpec)); foreach (var p in imports) { // note that imports could contain file name that neither of build items contains // (it may be one of those resolved references in project.properties). // Also non-zip files are now specified in full path. if (!LibraryProjectZipFiles.Any(l => l.ItemSpec == p)) { // project.properties var fileInfo = new FileInfo(p); if (!fileInfo.Exists) { throw new InvalidOperationException(String.Format("Library project properties file '{0}' does not exist.", p)); } var bindir = fileInfo.Directory.FullName; CopyLibraryContent(bindir, false); } else { // zip string tmpname = Path.Combine(Path.GetTempPath(), "monodroid_import_" + Guid.NewGuid().ToString()); try { Directory.CreateDirectory(tmpname); var archive = ZipArchive.Open(p, FileMode.Open); archive.ExtractAll(tmpname); if (!CopyLibraryContent(tmpname, p.EndsWith(".aar", StringComparison.OrdinalIgnoreCase))) { return(false); } } finally { Directory.Delete(tmpname, true); } } } var outpath = Path.Combine(outDirInfo.Parent.FullName, "__AndroidLibraryProjects__.zip"); if (Files.ArchiveZip(outpath, f => { using (var zip = new ZipArchiveEx(f)) { zip.AddDirectory(OutputDirectory, "library_project_imports"); } })) { Log.LogDebugMessage("Saving contents to " + outpath); } return(true); }
void AddEnvironment(ZipArchiveEx apk) { var environment = new StringWriter() { NewLine = "\n", }; if (EnableLLVM) { environment.WriteLine("mono.llvm=true"); } AotMode aotMode; if (AndroidAotMode != null && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode)) { environment.WriteLine("mono.aot={0}", aotMode.ToString().ToLowerInvariant()); } const string defaultLogLevel = "MONO_LOG_LEVEL=info"; const string defaultMonoDebug = "MONO_DEBUG=gen-compact-seq-points"; const string defaultHttpMessageHandler = "XA_HTTP_CLIENT_HANDLER_TYPE=System.Net.Http.HttpClientHandler, System.Net.Http"; const string defaultTlsProvider = "XA_TLS_PROVIDER=default"; string xamarinBuildId = string.Format("XAMARIN_BUILD_ID={0}", buildId); bool haveLogLevel = false; bool haveMonoDebug = false; bool havebuildId = false; bool haveHttpMessageHandler = false; bool haveTlsProvider = false; bool haveMonoGCParams = false; foreach (ITaskItem env in Environments ?? new TaskItem[0]) { environment.WriteLine("## Source File: {0}", env.ItemSpec); 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; } environment.WriteLine(lineToWrite); } } if (_Debug && !haveLogLevel) { environment.WriteLine(defaultLogLevel); } if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) { environment.WriteLine(defaultMonoDebug); } if (!havebuildId) { environment.WriteLine(xamarinBuildId); } if (!haveHttpMessageHandler) { environment.WriteLine(HttpClientHandlerType == null ? defaultHttpMessageHandler : $"XA_HTTP_CLIENT_HANDLER_TYPE={HttpClientHandlerType.Trim ()}"); } if (!haveTlsProvider) { environment.WriteLine(TlsProvider == null ? defaultTlsProvider : $"XA_TLS_PROVIDER={TlsProvider.Trim ()}"); } if (!haveMonoGCParams) { if (EnableSGenConcurrent) { environment.WriteLine("MONO_GC_PARAMS=major=marksweep-conc"); } else { environment.WriteLine("MONO_GC_PARAMS=major=marksweep"); } } apk.Archive.AddEntry("environment", environment.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); }
public override bool Execute() { if (IsApplication) { return(true); } Log.LogDebugMessage("CreateManagedLibraryResourceArchive Task"); Log.LogDebugMessage(" OutputDirectory: {0}", OutputDirectory); Log.LogDebugMessage(" ResourceDirectory: {0}", ResourceDirectory); Log.LogDebugTaskItems(" AndroidAssets:", AndroidAssets); Log.LogDebugTaskItems(" AndroidJavaSources:", AndroidJavaSources); Log.LogDebugTaskItems(" AndroidJavaLibraries:", AndroidJavaLibraries); var outDirInfo = new DirectoryInfo(OutputDirectory); // Copy files into _LibraryProjectImportsDirectoryName (library_project_imports) dir. if (!outDirInfo.Exists) { outDirInfo.Create(); } foreach (var sub in new string [] { "assets", "res", "java", "bin" }) { var subdirInfo = new DirectoryInfo(Path.Combine(outDirInfo.FullName, sub)); if (!subdirInfo.Exists) { subdirInfo.Create(); } } var dir_sep = new char [] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; if (AndroidAssets != null) { var dstsub = Path.Combine(outDirInfo.FullName, "assets"); if (!Directory.Exists(dstsub)) { Directory.CreateDirectory(dstsub); } foreach (var item in AndroidAssets) { var path = item.GetMetadata("Link"); path = !string.IsNullOrWhiteSpace(path) ? path : item.ItemSpec; var head = string.Join("\\", path.Split(dir_sep).TakeWhile(s => !s.Equals(MonoAndroidAssetsPrefix, StringComparison.OrdinalIgnoreCase))); path = head.Length == path.Length ? path : path.Substring((head.Length == 0 ? 0 : head.Length + Path.DirectorySeparatorChar) + MonoAndroidAssetsPrefix.Length).TrimStart(dir_sep); MonoAndroidHelper.CopyIfChanged(item.ItemSpec, Path.Combine(dstsub, path)); } } // resources folders are converted to the structure that aapt accepts. bool hasInvalidName = false; foreach (var srcsub in Directory.GetDirectories(ResourceDirectory)) { var dstsub = Path.Combine(outDirInfo.FullName, "res", Path.GetFileName(srcsub)); if (!Directory.Exists(dstsub)) { Directory.CreateDirectory(dstsub); } foreach (var file in Directory.GetFiles(srcsub)) { var filename = Path.GetFileName(file); MonoAndroidHelper.CopyIfChanged(file, Path.Combine(dstsub, Path.GetFileName(file))); } } if (hasInvalidName) { return(false); } if (AndroidJavaSources != null) { foreach (var item in AndroidJavaSources) { MonoAndroidHelper.CopyIfChanged(item.ItemSpec, Path.Combine(outDirInfo.FullName, item.ItemSpec)); } } if (AndroidJavaLibraries != null) { foreach (var item in AndroidJavaLibraries) { MonoAndroidHelper.CopyIfChanged(item.ItemSpec, Path.Combine(outDirInfo.FullName, item.ItemSpec)); } } var nameCaseMap = new StringWriter(); // add resource case mapping descriptor to the archive. if (AndroidResourcesInThisExactProject != null && AndroidResourcesInThisExactProject.Any()) { Log.LogMessage("writing __res_name_case_map.txt..."); foreach (var res in AndroidResourcesInThisExactProject) { nameCaseMap.WriteLine("{0};{1}", res.GetMetadata("LogicalName").Replace('\\', '/'), Path.Combine(Path.GetFileName(Path.GetDirectoryName(res.ItemSpec)), Path.GetFileName(res.ItemSpec)).Replace('\\', '/')); } File.WriteAllText(Path.Combine(OutputDirectory, "__res_name_case_map.txt"), nameCaseMap.ToString()); } var outpath = Path.Combine(outDirInfo.Parent.FullName, "__AndroidLibraryProjects__.zip"); if (Files.ArchiveZip(outpath, f => { using (var zip = new ZipArchiveEx(f)) { zip.AddDirectory(OutputDirectory, outDirInfo.Name); } })) { Log.LogDebugMessage("Saving contents to " + outpath); } return(true); }
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 (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec)) { Log.LogWarning($"{assembly.ItemSpec} is a reference assembly!"); } // Add assembly apk.Archive.AddFile(assembly.ItemSpec, GetTargetDirectory(assembly.ItemSpec) + "/" + Path.GetFileName(assembly.ItemSpec), compressionMethod: CompressionMethod.Store); // 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)) { apk.Archive.AddFile(symbols, "assemblies/" + Path.GetFileName(symbols), compressionMethod: CompressionMethod.Store); } symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(symbols)) { apk.Archive.AddFile(symbols, "assemblies/" + Path.GetFileName(symbols), compressionMethod: CompressionMethod.Store); } } 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 (MonoAndroidHelper.IsReferenceAssembly(assembly.ItemSpec)) { Log.LogWarning($"{assembly.ItemSpec} is a reference assembly!"); } apk.Archive.AddFile(assembly.ItemSpec, "assemblies/" + Path.GetFileName(assembly.ItemSpec), compressionMethod: CompressionMethod.Store); 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)) { apk.Archive.AddFile(symbols, "assemblies/" + Path.GetFileName(symbols), compressionMethod: CompressionMethod.Store); } symbols = Path.ChangeExtension(assembly.ItemSpec, "pdb"); if (File.Exists(symbols)) { apk.Archive.AddFile(symbols, "assemblies/" + Path.GetFileName(symbols), compressionMethod: CompressionMethod.Store); } } count++; if (count == ZipArchiveEx.ZipFlushLimit) { apk.Flush(); count = 0; } } }
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"); }