Пример #1
0
        public void NoLLVMFailuresInWatchOS(string asm)
        {
            MTouch.AssertDeviceAvailable();

            // Run LLVM on every assembly we ship in watchOS, using the arguments we usually use when done from mtouch.
            var aot_compiler = Path.Combine(Configuration.BinDirXI, "armv7k-unknown-darwin-mono-sgen");
            var tmpdir       = Cache.CreateTemporaryDirectory();
            var llvm_path    = Path.Combine(Configuration.SdkRootXI, "LLVM", "bin");
            var env          = new Dictionary <string, string> {
                { "MONO_PATH", watchOSPath }
            };
            var arch     = "armv7k";
            var arch_dir = Path.Combine(tmpdir, arch);

            Directory.CreateDirectory(arch_dir);
            var args = new StringBuilder();

            args.Append("--debug ");
            args.Append("--llvm ");
            args.Append("-O=float32 ");
            args.Append($"--aot=mtriple={arch}-ios");
            args.Append($",data-outfile={Path.Combine (arch_dir, Path.GetFileNameWithoutExtension (asm) + ".aotdata." + arch)}");
            args.Append($",static,asmonly,direct-icalls,llvmonly,nodebug,dwarfdebug,direct-pinvoke");
            args.Append($",msym-dir={Path.Combine (arch_dir, Path.GetFileNameWithoutExtension (asm) + ".mSYM")}");
            args.Append($",llvm-path={llvm_path}");
            args.Append($",llvm-outfile={Path.Combine (arch_dir, Path.GetFileName (asm) + ".bc")} ");
            args.Append(Path.Combine(watchOSPath, asm));

            StringBuilder output      = new StringBuilder();
            var           rv          = ExecutionHelper.Execute(aot_compiler, args.ToString(), stdout: output, stderr: output, environmentVariables: env, timeout: TimeSpan.FromMinutes(5));
            var           llvm_failed = output.ToString().Split('\n').Where((v) => v.Contains("LLVM failed"));

            Console.WriteLine(output);

            int expected_exit_code = 0;

            if (known_llvm_failures.TryGetValue(asm, out var known_failures))
            {
                expected_exit_code = known_failures.Item1;
                Assert.AreEqual(expected_exit_code, rv, "AOT compilation");
                if (known_failures.Item2 != null)
                {
                    // Check if there are known failures for failures we've fixed
                    var known_inexistent_failures = known_failures.Item2.Where((v) => !llvm_failed.Contains(v));
                    Assert.IsEmpty(string.Join("\n", known_inexistent_failures), $"Redundant known failures: should be removed from dictionary for {asm}");
                    // Filter the known failures from the failed llvm lines.
                    llvm_failed = llvm_failed.Where((v) => !known_failures.Item2.Contains(v));
                }
            }

            Assert.AreEqual(expected_exit_code, rv, "AOT compilation");
            Assert.IsEmpty(string.Join("\n", llvm_failed), "LLVM failed");
        }
Пример #2
0
        public void VerifySymbols()
        {
            MTouch.AssertDeviceAvailable();

            var prohibited_symbols = new string [] { "_NSGetEnviron", "PKService", "SPPluginDelegate" };

            foreach (var symbol in prohibited_symbols)
            {
                var contents = ASCIIEncoding.ASCII.GetBytes(symbol);
                var sdk      = "iphoneos";            // we don't care about private symbols for simulator builds
                foreach (var static_lib in Directory.EnumerateFiles(Path.Combine(Configuration.MonoTouchRootDirectory, "SDKs", "MonoTouch." + sdk + ".sdk", "usr", "lib"), "*.a"))
                {
                    Assert.IsFalse(Contains(static_lib, contents), "Found \"{0}\" in {1}", symbol, static_lib);
                }
            }
        }
Пример #3
0
        public void InvalidStructOffset()
        {
            MTouch.AssertDeviceAvailable();

            var str      = "invalid struct offset";
            var contents = ASCIIEncoding.ASCII.GetBytes(str);

            foreach (var sdk in new string [] { "iphoneos", "iphonesimulator" })
            {
                foreach (var ext in new string [] { "dylib", "a" })
                {
                    var fn = Path.Combine(Configuration.MonoTouchRootDirectory, "SDKs", "MonoTouch." + sdk + ".sdk", "usr", "lib", "libmonosgen-2.0." + ext);
                    Assert.IsFalse(Contains(fn, contents), "Found \"{0}\" in {1}", str, fn);
                }
            }
        }
Пример #4
0
        protected override void BuildArguments(IList <string> sb)
        {
            base.BuildArguments(sb);

            switch (Action.Value)
            {
            case MTouchAction.None:
                break;

            case MTouchAction.BuildDev:
                MTouch.AssertDeviceAvailable();
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Add("--dev");
                sb.Add(AppPath);
                break;

            case MTouchAction.BuildSim:
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Add("--sim");
                sb.Add(AppPath);
                break;

            case MTouchAction.LaunchSim:
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Add("--launchsim");
                sb.Add(AppPath);
                break;

            default:
                throw new NotImplementedException();
            }

            if (FastDev.HasValue && FastDev.Value)
            {
                sb.Add("--fastdev");
            }

            if (PackageMdb.HasValue)
            {
                sb.Add($"--package-mdb:{(PackageMdb.Value ? "true" : "false")}");
            }

            if (NoStrip.HasValue && NoStrip.Value)
            {
                sb.Add("--nostrip");
            }

            if (NoSymbolStrip != null)
            {
                if (NoSymbolStrip.Length == 0)
                {
                    sb.Add("--nosymbolstrip");
                }
                else
                {
                    sb.Add($"--nosymbolstrip:{NoSymbolStrip}");
                }
            }

            if (!string.IsNullOrEmpty(SymbolList))
            {
                sb.Add($"--symbollist={SymbolList}");
            }

            if (MSym.HasValue)
            {
                sb.Add($"--msym:{(MSym.Value ? "true" : "false")}");
            }

            if (DSym.HasValue)
            {
                sb.Add($"--dsym:{(DSym.Value ? "true" : "false")}");
            }

            foreach (var appext in AppExtensions)
            {
                sb.Add($"--app-extension");
                sb.Add(appext.AppPath);
            }

            foreach (var framework in Frameworks)
            {
                sb.Add($"--framework");
                sb.Add(framework);
            }

            if (!string.IsNullOrEmpty(Mono))
            {
                sb.Add($"--mono:{Mono}");
            }

            if (Dlsym.HasValue)
            {
                sb.Add($"--dlsym:{(Dlsym.Value ? "true" : "false")}");
            }
            else if (!string.IsNullOrEmpty(DlsymString))
            {
                sb.Add($"--dlsym:{DlsymString}");
            }

            if (!string.IsNullOrEmpty(Executable))
            {
                sb.Add("--executable");
                sb.Add(Executable);
            }

            switch (SymbolMode)
            {
            case MTouchSymbolMode.Ignore:
                sb.Add("--dynamic-symbol-mode=ignore");
                break;

            case MTouchSymbolMode.Code:
                sb.Add("--dynamic-symbol-mode=code");
                break;

            case MTouchSymbolMode.Default:
                sb.Add("--dynamic-symbol-mode=default");
                break;

            case MTouchSymbolMode.Linker:
                sb.Add("--dynamic-symbol-mode=linker");
                break;

            case MTouchSymbolMode.Unspecified:
                break;

            default:
                throw new NotImplementedException();
            }

            if (NoFastSim.HasValue && NoFastSim.Value)
            {
                sb.Add("--nofastsim");
            }

            if (!string.IsNullOrEmpty(Device))
            {
                sb.Add($"--device:{Device}");
            }

            if (!string.IsNullOrEmpty(LLVMOptimizations))
            {
                sb.Add($"--llvm-opt={LLVMOptimizations}");
            }

            if (Bitcode != MTouchBitcode.Unspecified)
            {
                sb.Add($"--bitcode:{Bitcode.ToString ().ToLower ()}");
            }

            foreach (var abt in AssemblyBuildTargets)
            {
                sb.Add($"--assembly-build-target={abt}");
            }

            if (!string.IsNullOrEmpty(AotArguments))
            {
                sb.Add($"--aot:{AotArguments}");
            }

            if (!string.IsNullOrEmpty(AotOtherArguments))
            {
                sb.Add($"--aot-options:{AotOtherArguments}");
            }
        }
Пример #5
0
        string BuildArguments(MTouchAction action)
        {
            var sb       = new StringBuilder();
            var isDevice = false;

            switch (action)
            {
            case MTouchAction.None:
                break;

            case MTouchAction.BuildDev:
                MTouch.AssertDeviceAvailable();
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                isDevice = true;
                sb.Append(" --dev ").Append(StringUtils.Quote(AppPath));
                break;

            case MTouchAction.BuildSim:
                isDevice = false;
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Append(" --sim ").Append(StringUtils.Quote(AppPath));
                break;

            case MTouchAction.LaunchSim:
                isDevice = false;
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Append(" --launchsim ").Append(StringUtils.Quote(AppPath));
                break;

            default:
                throw new NotImplementedException();
            }

            if (SdkRoot == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(SdkRoot))
            {
                sb.Append(" --sdkroot ").Append(StringUtils.Quote(SdkRoot));
            }
            else
            {
                sb.Append(" --sdkroot ").Append(StringUtils.Quote(Configuration.xcode_root));
            }

            sb.Append(" ").Append(GetVerbosity());

            if (Sdk == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(Sdk))
            {
                sb.Append(" --sdk ").Append(Sdk);
            }
            else
            {
                sb.Append(" --sdk ").Append(MTouch.GetSdkVersion(Profile));
            }

            if (TargetVer == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(TargetVer))
            {
                sb.Append(" --targetver ").Append(TargetVer);
            }

            if (Debug.HasValue && Debug.Value)
            {
                sb.Append(" --debug");
            }

            if (FastDev.HasValue && FastDev.Value)
            {
                sb.Append(" --fastdev");
            }

            if (PackageMdb.HasValue)
            {
                sb.Append(" --package-mdb:").Append(PackageMdb.Value ? "true" : "false");
            }

            if (NoStrip.HasValue && NoStrip.Value)
            {
                sb.Append(" --nostrip");
            }

            if (NoSymbolStrip != null)
            {
                if (NoSymbolStrip.Length == 0)
                {
                    sb.Append(" --nosymbolstrip");
                }
                else
                {
                    sb.Append(" --nosymbolstrip:").Append(NoSymbolStrip);
                }
            }

            if (Profiling.HasValue)
            {
                sb.Append(" --profiling:").Append(Profiling.Value ? "true" : "false");
            }

            if (!string.IsNullOrEmpty(SymbolList))
            {
                sb.Append(" --symbollist=").Append(StringUtils.Quote(SymbolList));
            }

            if (MSym.HasValue)
            {
                sb.Append(" --msym:").Append(MSym.Value ? "true" : "false");
            }

            if (DSym.HasValue)
            {
                sb.Append(" --dsym:").Append(DSym.Value ? "true" : "false");
            }

            if (Extension == true)
            {
                sb.Append(" --extension");
            }

            foreach (var appext in AppExtensions)
            {
                sb.Append(" --app-extension ").Append(StringUtils.Quote(appext.AppPath));
            }

            foreach (var framework in Frameworks)
            {
                sb.Append(" --framework ").Append(StringUtils.Quote(framework));
            }

            if (!string.IsNullOrEmpty(Mono))
            {
                sb.Append(" --mono:").Append(StringUtils.Quote(Mono));
            }

            if (!string.IsNullOrEmpty(GccFlags))
            {
                sb.Append(" --gcc_flags ").Append(StringUtils.Quote(GccFlags));
            }

            if (!string.IsNullOrEmpty(HttpMessageHandler))
            {
                sb.Append(" --http-message-handler=").Append(StringUtils.Quote(HttpMessageHandler));
            }

            if (Dlsym.HasValue)
            {
                sb.Append(" --dlsym:").Append(Dlsym.Value ? "true" : "false");
            }

            if (References != null)
            {
                foreach (var r in References)
                {
                    sb.Append(" -r:").Append(StringUtils.Quote(r));
                }
            }

            if (!string.IsNullOrEmpty(Executable))
            {
                sb.Append(" --executable ").Append(StringUtils.Quote(Executable));
            }

            if (!string.IsNullOrEmpty(RootAssembly))
            {
                sb.Append(" ").Append(StringUtils.Quote(RootAssembly));
            }

            if (TargetFramework == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(TargetFramework))
            {
                sb.Append(" --target-framework ").Append(TargetFramework);
            }
            else if (!NoPlatformAssemblyReference)
            {
                // make the implicit default the way tests have been running until now, and at the same time the very minimum to make apps build.
                switch (Profile)
                {
                case Profile.iOS:
                    sb.Append(" -r:").Append(StringUtils.Quote(Configuration.XamarinIOSDll));
                    break;

                case Profile.tvOS:
                case Profile.watchOS:
                    sb.Append(" --target-framework ").Append(MTouch.GetTargetFramework(Profile));
                    sb.Append(" -r:").Append(StringUtils.Quote(MTouch.GetBaseLibrary(Profile)));
                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            if (Abi == None)
            {
                // add nothing
            }
            else if (!string.IsNullOrEmpty(Abi))
            {
                sb.Append(" --abi ").Append(Abi);
            }
            else
            {
                switch (Profile)
                {
                case Profile.iOS:
                    break;                     // not required

                case Profile.tvOS:
                    sb.Append(isDevice ? " --abi arm64" : " --abi x86_64");
                    break;

                case Profile.watchOS:
                    sb.Append(isDevice ? " --abi armv7k" : " --abi i386");
                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            switch (Linker)
            {
            case MTouchLinker.LinkAll:
            case MTouchLinker.Unspecified:
                break;

            case MTouchLinker.DontLink:
                sb.Append(" --nolink");
                break;

            case MTouchLinker.LinkSdk:
                sb.Append(" --linksdkonly");
                break;

            default:
                throw new NotImplementedException();
            }

            if (Optimize != null)
            {
                foreach (var opt in Optimize)
                {
                    sb.Append(" --optimize:").Append(opt);
                }
            }

            switch (SymbolMode)
            {
            case MTouchSymbolMode.Ignore:
                sb.Append(" --dynamic-symbol-mode=ignore");
                break;

            case MTouchSymbolMode.Code:
                sb.Append(" --dynamic-symbol-mode=code");
                break;

            case MTouchSymbolMode.Default:
                sb.Append(" --dynamic-symbol-mode=default");
                break;

            case MTouchSymbolMode.Linker:
                sb.Append(" --dynamic-symbol-mode=linker");
                break;

            case MTouchSymbolMode.Unspecified:
                break;

            default:
                throw new NotImplementedException();
            }

            if (NoFastSim.HasValue && NoFastSim.Value)
            {
                sb.Append(" --nofastsim");
            }

            switch (Registrar)
            {
            case MTouchRegistrar.Unspecified:
                break;

            case MTouchRegistrar.Dynamic:
                sb.Append(" --registrar:dynamic");
                break;

            case MTouchRegistrar.Static:
                sb.Append(" --registrar:static");
                break;

            default:
                throw new NotImplementedException();
            }

            if (I18N != I18N.None)
            {
                sb.Append(" --i18n ");
                int count = 0;
                if ((I18N & I18N.CJK) == I18N.CJK)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("cjk");
                }
                if ((I18N & I18N.MidEast) == I18N.MidEast)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("mideast");
                }
                if ((I18N & I18N.Other) == I18N.Other)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("other");
                }
                if ((I18N & I18N.Rare) == I18N.Rare)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("rare");
                }
                if ((I18N & I18N.West) == I18N.West)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("west");
                }
            }

            if (!string.IsNullOrEmpty(Cache))
            {
                sb.Append(" --cache ").Append(StringUtils.Quote(Cache));
            }

            if (!string.IsNullOrEmpty(Device))
            {
                sb.Append(" --device:").Append(StringUtils.Quote(Device));
            }

            if (!string.IsNullOrEmpty(LLVMOptimizations))
            {
                sb.Append(" --llvm-opt=").Append(StringUtils.Quote(LLVMOptimizations));
            }

            if (CustomArguments != null)
            {
                foreach (var arg in CustomArguments)
                {
                    sb.Append(" ").Append(arg);
                }
            }

            if (NoWarn != null)
            {
                if (NoWarn.Length > 0)
                {
                    sb.Append(" --nowarn:");
                    foreach (var code in NoWarn)
                    {
                        sb.Append(code).Append(',');
                    }
                    sb.Length--;
                }
                else
                {
                    sb.Append(" --nowarn");
                }
            }

            if (WarnAsError != null)
            {
                if (WarnAsError.Length > 0)
                {
                    sb.Append(" --warnaserror:");
                    foreach (var code in WarnAsError)
                    {
                        sb.Append(code).Append(',');
                    }
                    sb.Length--;
                }
                else
                {
                    sb.Append(" --warnaserror");
                }
            }

            if (Bitcode != MTouchBitcode.Unspecified)
            {
                sb.Append(" --bitcode:").Append(Bitcode.ToString().ToLower());
            }

            foreach (var abt in AssemblyBuildTargets)
            {
                sb.Append(" --assembly-build-target=").Append(StringUtils.Quote(abt));
            }

            if (!string.IsNullOrEmpty(AotArguments))
            {
                sb.Append(" --aot:").Append(StringUtils.Quote(AotArguments));
            }

            if (!string.IsNullOrEmpty(AotOtherArguments))
            {
                sb.Append(" --aot-options:").Append(StringUtils.Quote(AotOtherArguments));
            }

            if (LinkSkip?.Length > 0)
            {
                foreach (var ls in LinkSkip)
                {
                    sb.Append(" --linkskip:").Append(StringUtils.Quote(ls));
                }
            }

            if (XmlDefinitions?.Length > 0)
            {
                foreach (var xd in XmlDefinitions)
                {
                    sb.Append(" --xml:").Append(StringUtils.Quote(xd));
                }
            }

            if (!string.IsNullOrEmpty(ResponseFile))
            {
                sb.Append(" @").Append(StringUtils.Quote(ResponseFile));
            }

            return(sb.ToString());
        }
Пример #6
0
        string BuildArguments(MTouchAction action)
        {
            var sb       = new StringBuilder();
            var isDevice = false;

            switch (action)
            {
            case MTouchAction.None:
                break;

            case MTouchAction.BuildDev:
                MTouch.AssertDeviceAvailable();
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                isDevice = true;
                sb.Append(" --dev ").Append(MTouch.Quote(AppPath));
                break;

            case MTouchAction.BuildSim:
                isDevice = false;
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Append(" --sim ").Append(MTouch.Quote(AppPath));
                break;

            case MTouchAction.LaunchSim:
                isDevice = false;
                if (AppPath == null)
                {
                    throw new Exception("No AppPath specified.");
                }
                sb.Append(" --launchsim ").Append(MTouch.Quote(AppPath));
                break;

            default:
                throw new NotImplementedException();
            }

            if (SdkRoot == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(SdkRoot))
            {
                sb.Append(" --sdkroot ").Append(MTouch.Quote(SdkRoot));
            }
            else
            {
                sb.Append(" --sdkroot ").Append(MTouch.Quote(Configuration.xcode_root));
            }

            sb.Append(" ").Append(GetVerbosity());

            if (Sdk == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(Sdk))
            {
                sb.Append(" --sdk ").Append(Sdk);
            }
            else
            {
                sb.Append(" --sdk ").Append(MTouch.GetSdkVersion(Profile));
            }

            if (TargetVer == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(TargetVer))
            {
                sb.Append(" --targetver ").Append(TargetVer);
            }

            if (Debug.HasValue && Debug.Value)
            {
                sb.Append(" --debug");
            }

            if (FastDev.HasValue && FastDev.Value)
            {
                sb.Append(" --fastdev");
            }

            if (Extension == true)
            {
                sb.Append(" --extension");
            }

            foreach (var appext in AppExtensions)
            {
                sb.Append(" --app-extension ").Append(MTouch.Quote(appext));
            }

            foreach (var framework in Frameworks)
            {
                sb.Append(" --framework ").Append(MTouch.Quote(framework));
            }

            if (!string.IsNullOrEmpty(HttpMessageHandler))
            {
                sb.Append(" --http-message-handler=").Append(MTouch.Quote(HttpMessageHandler));
            }

            if (Dlsym.HasValue)
            {
                sb.Append(" --dlsym:").Append(Dlsym.Value ? "true" : "false");
            }

            if (References != null)
            {
                foreach (var r in References)
                {
                    sb.Append(" -r:").Append(MTouch.Quote(r));
                }
            }

            if (!string.IsNullOrEmpty(Executable))
            {
                sb.Append(" ").Append(MTouch.Quote(Executable));
            }

            if (TargetFramework == None)
            {
                // do nothing
            }
            else if (!string.IsNullOrEmpty(TargetFramework))
            {
                sb.Append(" --target-framework ").Append(TargetFramework);
            }
            else if (!NoPlatformAssemblyReference)
            {
                // make the implicit default the way tests have been running until now, and at the same time the very minimum to make apps build.
                switch (Profile)
                {
                case MTouch.Profile.Unified:
                    sb.Append(" -r:").Append(MTouch.Quote(Configuration.XamarinIOSDll));
                    break;

                case MTouch.Profile.TVOS:
                case MTouch.Profile.WatchOS:
                    sb.Append(" --target-framework ").Append(MTouch.GetTargetFramework(Profile));
                    sb.Append(" -r:").Append(MTouch.Quote(MTouch.GetBaseLibrary(Profile)));
                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            if (!string.IsNullOrEmpty(Abi))
            {
                sb.Append(" --abi ").Append(Abi);
            }
            else
            {
                switch (Profile)
                {
                case MTouch.Profile.Unified:
                    break;                     // not required

                case MTouch.Profile.TVOS:
                    sb.Append(isDevice ? " --abi arm64" : " --abi x86_64");
                    break;

                case MTouch.Profile.WatchOS:
                    sb.Append(isDevice ? " --abi armv7k" : " --abi i386");
                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            switch (Linker)
            {
            case MTouchLinker.LinkAll:
            case MTouchLinker.Unspecified:
                break;

            case MTouchLinker.DontLink:
                sb.Append(" --nolink");
                break;

            case MTouchLinker.LinkSdk:
                sb.Append(" --linksdkonly");
                break;

            default:
                throw new NotImplementedException();
            }

            if (NoFastSim.HasValue && NoFastSim.Value)
            {
                sb.Append(" --nofastsim");
            }

            switch (Registrar)
            {
            case MTouchRegistrar.Unspecified:
                break;

            case MTouchRegistrar.Dynamic:
                sb.Append(" --registrar:dynamic");
                break;

            case MTouchRegistrar.Static:
                sb.Append(" --registrar:static");
                break;

            default:
                throw new NotImplementedException();
            }

            if (I18N != I18N.None)
            {
                sb.Append(" --i18n ");
                int count = 0;
                if ((I18N & I18N.CJK) == I18N.CJK)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("cjk");
                }
                if ((I18N & I18N.MidEast) == I18N.MidEast)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("mideast");
                }
                if ((I18N & I18N.Other) == I18N.Other)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("other");
                }
                if ((I18N & I18N.Rare) == I18N.Rare)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("rare");
                }
                if ((I18N & I18N.West) == I18N.West)
                {
                    sb.Append(count++ == 0 ? string.Empty : ",").Append("west");
                }
            }

            if (!string.IsNullOrEmpty(Cache))
            {
                sb.Append(" --cache ").Append(MTouch.Quote(Cache));
            }

            if (!string.IsNullOrEmpty(Device))
            {
                sb.Append(" --device:").Append(MTouch.Quote(Device));
            }

            return(sb.ToString());
        }