public void Aapt2CompileFixesUpErrors() { var path = Path.Combine(Root, "temp", "Aapt2CompileFixesUpErrors"); Directory.CreateDirectory(path); var resPath = Path.Combine(path, "res"); var archivePath = Path.Combine(path, "flata"); var flatFilePath = Path.Combine(path, "flat"); Directory.CreateDirectory(resPath); Directory.CreateDirectory(archivePath); Directory.CreateDirectory(Path.Combine(resPath, "values")); Directory.CreateDirectory(Path.Combine(resPath, "layout")); File.WriteAllText(Path.Combine(resPath, "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo'>foo</string</resources>"); File.WriteAllText(Path.Combine(resPath, "layout", "main.xml"), @"<?xml version='1.0' ?> <LinearLayout xmlns:android='http://schemas.android.com/apk/res/android' android:orientation='vertical' android:layout_width='fill_parent' android:layout_height='fill_parent' > <Button android:id='@+id/myButton' android:layout_width='fill_parent' android:layout_height='wrap_content' android:text='@string/hello' /> </LinearLayout> "); var errors = new List <BuildErrorEventArgs> (); var engine = new MockBuildEngine(TestContext.Out, errors); var directorySeperator = Path.DirectorySeparatorChar; var current = Directory.GetCurrentDirectory(); try { Directory.SetCurrentDirectory(path); MonoAndroidHelper.SaveResourceCaseMap(engine, new Dictionary <string, string> { { $"layout{directorySeperator}main.axml", $"Layout{directorySeperator}Main.xml" }, { $"values{directorySeperator}strings.xml", $"Values{directorySeperator}Strings.xml" }, }); var task = new Aapt2Compile { BuildEngine = engine, ToolPath = GetPathToAapt2(), ResourceDirectories = new ITaskItem [] { new TaskItem(resPath, new Dictionary <string, string> () { { "ResourceDirectory", resPath }, } ) }, FlatArchivesDirectory = archivePath, FlatFilesDirectory = flatFilePath, }; Assert.False(task.Execute(), "task should not have succeeded."); } finally { Directory.SetCurrentDirectory(current); } Assert.AreEqual(2, errors.Count, $"Two Error should have been raised. {string.Join (" ", errors.Select (e => e.Message))}"); Assert.AreEqual($"Resources{directorySeperator}Values{directorySeperator}Strings.xml", errors[0].File, $"`values{directorySeperator}strings.xml` should have been replaced with `Resources{directorySeperator}Values{directorySeperator}Strings.xml`"); Assert.AreEqual($"Resources{directorySeperator}Values{directorySeperator}Strings.xml", errors [1].File, $"`values{directorySeperator}strings.xml` should have been replaced with `Resources{directorySeperator}Values{directorySeperator}Strings.xml`"); Directory.Delete(Path.Combine(Root, path), recursive: true); }
static bool IsUserType(TypeDefinition type) { return(!MonoAndroidHelper.IsFrameworkAssembly(type.Module.Assembly.Name.Name + ".dll")); }
public static bool ExtractAll(ZipArchive zip, string destination, Action <int, int> progressCallback = null, Func <string, string> modifyCallback = null, Func <string, bool> deleteCallback = null, Func <string, bool> skipCallback = null) { int i = 0; int total = (int)zip.EntryCount; bool updated = false; var files = new HashSet <string> (); var memoryStream = MemoryStreamPool.Shared.Rent(); try { foreach (var entry in zip) { progressCallback?.Invoke(i++, total); if (entry.IsDirectory) { continue; } if (entry.FullName.Contains("/__MACOSX/") || entry.FullName.EndsWith("/__MACOSX", StringComparison.OrdinalIgnoreCase) || string.Equals(entry.FullName, ".DS_Store", StringComparison.OrdinalIgnoreCase) || entry.FullName.EndsWith("/.DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } if (skipCallback != null && skipCallback(entry.FullName)) { continue; } var fullName = modifyCallback?.Invoke(entry.FullName) ?? entry.FullName; var outfile = Path.GetFullPath(Path.Combine(destination, fullName)); files.Add(outfile); memoryStream.SetLength(0); //Reuse the stream entry.Extract(memoryStream); try { updated |= MonoAndroidHelper.CopyIfStreamChanged(memoryStream, outfile); } catch (PathTooLongException) { throw new PathTooLongException($"Could not extract \"{fullName}\" to \"{outfile}\". Path is too long."); } } } finally { MemoryStreamPool.Shared.Return(memoryStream); } if (Directory.Exists(destination)) { foreach (var file in Directory.GetFiles(destination, "*", SearchOption.AllDirectories)) { var outfile = Path.GetFullPath(file); if (outfile.Contains("/__MACOSX/") || outfile.EndsWith(".flat", StringComparison.OrdinalIgnoreCase) || outfile.EndsWith("files.cache", StringComparison.OrdinalIgnoreCase) || outfile.EndsWith("__AndroidLibraryProjects__.zip", StringComparison.OrdinalIgnoreCase) || outfile.EndsWith("/__MACOSX", StringComparison.OrdinalIgnoreCase) || outfile.EndsWith("/.DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } if (!files.Contains(outfile) && (deleteCallback?.Invoke(outfile) ?? true)) { File.Delete(outfile); updated = true; } } } return(updated); }
public static bool ExtractAll(ZipArchive zip, string destination, Action <int, int> progressCallback = null, Func <string, string> modifyCallback = null, Func <string, bool> deleteCallback = null, bool forceUpdate = true) { int i = 0; int total = (int)zip.EntryCount; bool updated = false; HashSet <string> files = new HashSet <string> (); foreach (var entry in zip) { progressCallback?.Invoke(i++, total); if (entry.FullName.Contains("/__MACOSX/") || entry.FullName.EndsWith("/__MACOSX", StringComparison.OrdinalIgnoreCase) || entry.FullName.EndsWith("/.DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } var fullName = modifyCallback?.Invoke(entry.FullName) ?? entry.FullName; if (entry.IsDirectory) { try { Directory.CreateDirectory(Path.Combine(destination, fullName)); } catch (NotSupportedException ex) { //NOTE: invalid paths, such as `:` on Windows can cause this throw new NotSupportedException($"Invalid zip entry `{fullName}` found in archive.", ex); } continue; } try { Directory.CreateDirectory(Path.Combine(destination, Path.GetDirectoryName(fullName))); } catch (NotSupportedException ex) { //NOTE: invalid paths, such as `:` on Windows can cause this throw new NotSupportedException($"Invalid zip entry `{fullName}` found in archive.", ex); } var outfile = Path.GetFullPath(Path.Combine(destination, fullName)); files.Add(outfile); var dt = File.Exists(outfile) ? File.GetLastWriteTimeUtc(outfile) : DateTime.MinValue; if (forceUpdate || entry.ModificationTime > dt) { var temp = Path.GetTempFileName(); try { using (var stream = File.Create(temp)) { entry.Extract(stream); } MonoAndroidHelper.CopyIfChanged(temp, outfile); } catch (PathTooLongException) { throw new PathTooLongException($"Could not extract \"{fullName}\" to \"{outfile}\". Path is too long."); } finally { File.Delete(temp); } updated = true; } } foreach (var file in Directory.GetFiles(destination, "*.*", SearchOption.AllDirectories)) { var outfile = Path.GetFullPath(file); if (outfile.Contains("/__MACOSX/") || outfile.EndsWith("__AndroidLibraryProjects__.zip", StringComparison.OrdinalIgnoreCase) || outfile.EndsWith("/__MACOSX", StringComparison.OrdinalIgnoreCase) || outfile.EndsWith("/.DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } if (!files.Contains(outfile) && !(deleteCallback?.Invoke(outfile) ?? true)) { File.Delete(outfile); updated = true; } } return(updated); }
bool DoExecute() { var results = new List <ITaskItem>(); string bundlepath = Path.Combine(TempOutputPath, "bundles"); if (!Directory.Exists(bundlepath)) { Directory.CreateDirectory(bundlepath); } else if (ProduceStub && Link) { 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); } var clb = new CommandLineBuilder(); var proc = new Process(); var outpath = Path.Combine(bundlepath, abi); int level = NdkUtil.GetMinimumApiLevelFor(arch, AndroidNdkDirectory); string windowsCompilerSwitches = NdkUtil.GetCompilerTargetParameters(AndroidNdkDirectory, arch, level); var compilerNoQuotes = NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "gcc", level); var compiler = $"\"{compilerNoQuotes}\" {windowsCompilerSwitches}".Trim(); if (ProduceStub) { if (!Directory.Exists(outpath)) { Directory.CreateDirectory(outpath); } 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("-v"); 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 gas = '"' + NdkUtil.GetNdkTool(AndroidNdkDirectory, arch, "as", level) + '"'; psi.EnvironmentVariables["CC"] = compiler; psi.EnvironmentVariables["AS"] = gas; Log.LogDebugMessage("CC=" + compiler); Log.LogDebugMessage("AS=" + gas); Log.LogDebugMessage("[mkbundle] " + psi.FileName + " " + clb); 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 ... if (Link) { 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); }