Example #1
0
        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);
        }
Example #2
0
 static bool IsUserType(TypeDefinition type)
 {
     return(!MonoAndroidHelper.IsFrameworkAssembly(type.Module.Assembly.Name.Name + ".dll"));
 }
Example #3
0
        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);
        }
Example #4
0
        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);
        }
Example #5
0
        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);
        }