Beispiel #1
0
        protected override string GenerateCommandLineCommands()
        {
            var args = new ProcessArgumentBuilder();
            TargetArchitecture architectures;
            bool msym;

            if (string.IsNullOrEmpty(Architectures) || !Enum.TryParse(Architectures, out architectures))
            {
                architectures = TargetArchitecture.Default;
            }

            if (architectures == TargetArchitecture.ARMv6)
            {
                Log.LogError("Target architecture ARMv6 is no longer supported in Xamarin.iOS. Please select a supported architecture.");
                return(null);
            }

            if (IsClassic && minimumOSVersion < IPhoneSdkVersion.V3_1 && architectures.HasFlag(TargetArchitecture.ARMv7))
            {
                Log.LogWarning(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "Deployment Target changed from iOS {0} to iOS 3.1 (minimum requirement for ARMv7)", minimumOSVersion);
                minimumOSVersion = IPhoneSdkVersion.V3_1;
            }

            if (!string.IsNullOrEmpty(IntermediateOutputPath))
            {
                Directory.CreateDirectory(IntermediateOutputPath);

                args.Add("--cache");
                args.AddQuoted(Path.GetFullPath(IntermediateOutputPath));
            }

            if (IsClassic || IPhoneSdks.MonoTouch.Version < new IPhoneSdkVersion(8, 5, 0))
            {
                args.Add("--nomanifest");
                args.Add("--nosign");
            }

            args.Add(SdkIsSimulator ? "--sim" : "--dev");
            args.AddQuoted(Path.GetFullPath(AppBundleDir));

            if (AppleSdkSettings.XcodeVersion.Major >= 5 && IPhoneSdks.MonoTouch.Version.CompareTo(new IPhoneSdkVersion(6, 3, 7)) < 0)
            {
                args.Add("--compiler", "clang");
            }

            args.Add("--executable");
            args.AddQuoted(ExecutableName);

            if (IsAppExtension)
            {
                args.Add("--extension");
            }

            if (Debug)
            {
                if (FastDev && IPhoneSdks.MonoTouch.SupportsFastDev)
                {
                    args.Add("--fastdev");
                }

                args.Add("--debug");
            }

            if (Profiling)
            {
                args.Add("--profiling");
            }

            if (LinkerDumpDependencies)
            {
                args.Add("--linkerdumpdependencies");
            }

            switch (LinkMode.ToLowerInvariant())
            {
            case "sdkonly": args.Add("--linksdkonly"); break;

            case "none":    args.Add("--nolink"); break;
            }

            if (!string.IsNullOrEmpty(I18n))
            {
                args.Add("--i18n");
                args.AddQuotedFormat(I18n);
            }

            args.Add("--sdkroot");
            args.AddQuoted(SdkRoot);

            args.Add("--sdk");
            args.AddQuoted(SdkVersion);

            if (!minimumOSVersion.IsUseDefault)
            {
                args.Add("--targetver");
                args.AddQuoted(minimumOSVersion.ToString());
            }

            if (UseFloat32 /* We want to compile 32-bit floating point code to use 32-bit floating point operations */)
            {
                args.Add("--aot-options=-O=float32");
            }

            if (IPhoneSdks.MonoTouch.SupportsGenericValueTypeSharing)
            {
                if (!EnableGenericValueTypeSharing)
                {
                    args.Add("--gsharedvt=false");
                }
            }

            if (LinkDescriptions != null)
            {
                foreach (var desc in LinkDescriptions)
                {
                    args.AddQuoted(string.Format("--xml={0}", desc.ItemSpec));
                }
            }

            if (EnableBitcode)
            {
                switch (Framework)
                {
                case PlatformFramework.WatchOS:
                    args.Add("--bitcode=full");
                    break;

                case PlatformFramework.TVOS:
                    args.Add("--bitcode=asmonly");
                    break;

                default:
                    throw new InvalidOperationException(string.Format("Bitcode is currently not supported on {0}.", Framework));
                }
            }

            if (!string.IsNullOrEmpty(HttpClientHandler))
            {
                args.Add(string.Format("--http-message-handler={0}", HttpClientHandler));
            }

            if (!string.IsNullOrEmpty(TLSProvider))
            {
                args.Add(string.Format("--tls-provider={0}", TLSProvider.ToLowerInvariant()));
            }

            string thumb = UseThumb && UseLlvm ? "+thumb2" : "";
            string llvm  = UseLlvm ? "+llvm" : "";
            string abi   = "";

            if (SdkIsSimulator)
            {
                if (architectures.HasFlag(TargetArchitecture.i386))
                {
                    abi += (abi.Length > 0 ? "," : "") + "i386";
                }

                if (architectures.HasFlag(TargetArchitecture.x86_64))
                {
                    abi += (abi.Length > 0 ? "," : "") + "x86_64";
                }

                if (string.IsNullOrEmpty(abi))
                {
                    architectures = TargetArchitecture.i386;
                    abi           = "i386";
                }
            }
            else
            {
                if (architectures == TargetArchitecture.Default)
                {
                    architectures = TargetArchitecture.ARMv7;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7" + llvm + thumb;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7s))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7s" + llvm + thumb;
                }

                if (architectures.HasFlag(TargetArchitecture.ARM64))
                {
                    // Note: ARM64 does not have thumb.
                    abi += (abi.Length > 0 ? "," : "") + "arm64" + llvm;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7k))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7k" + llvm;
                }

                if (string.IsNullOrEmpty(abi))
                {
                    abi = "armv7" + llvm + thumb;
                }
            }

            // Output the CompiledArchitectures
            CompiledArchitectures = architectures.ToString();

            args.Add("--abi=" + abi);

            // output symbols to preserve when stripping
            args.Add("--symbollist");
            args.AddQuoted(Path.GetFullPath(SymbolsList));

            // don't have mtouch generate the dsyms...
            args.Add("--dsym=no");

            if (!string.IsNullOrEmpty(ArchiveSymbols) && bool.TryParse(ArchiveSymbols.Trim(), out msym))
            {
                args.Add("--msym=" + (msym ? "yes" : "no"));
            }

            var gcc = new GccOptions();

            if (!string.IsNullOrEmpty(ExtraArgs))
            {
                var    extraArgs = ProcessArgumentBuilder.Parse(ExtraArgs);
                var    target    = MainAssembly.ItemSpec;
                string projectDir;

                if (ProjectDir.StartsWith("~/", StringComparison.Ordinal))
                {
                    // Note: Since the Visual Studio plugin doesn't know the user's home directory on the Mac build host,
                    // it simply uses paths relative to "~/". Expand these paths to their full path equivalents.
                    var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

                    projectDir = Path.Combine(home, ProjectDir.Substring(2));
                }
                else
                {
                    projectDir = ProjectDir;
                }

                var customTags = new Dictionary <string, string> (StringComparer.OrdinalIgnoreCase)
                {
                    { "projectdir", projectDir },
                    // Apparently msbuild doesn't propagate the solution path, so we can't get it.
                    // { "solutiondir",  proj.ParentSolution != null ? proj.ParentSolution.BaseDirectory : proj.BaseDirectory },
                    { "appbundledir", AppBundleDir },
                    { "targetpath", Path.Combine(Path.GetDirectoryName(target), Path.GetFileName(target)) },
                    { "targetdir", Path.GetDirectoryName(target) },
                    { "targetname", Path.GetFileName(target) },
                    { "targetext", Path.GetExtension(target) },
                };

                for (int i = 0; i < extraArgs.Length; i++)
                {
                    if (extraArgs[i] == "-gcc_flags" || extraArgs[i] == "--gcc_flags")
                    {
                        // user-defined -gcc_flags argument
                        if (i + 1 < extraArgs.Length && !string.IsNullOrEmpty(extraArgs[i + 1]))
                        {
                            var gccArgs = ProcessArgumentBuilder.Parse(extraArgs[i + 1]);

                            for (int j = 0; j < gccArgs.Length; j++)
                            {
                                gcc.Arguments.Add(StringParserService.Parse(gccArgs[j], customTags));
                            }
                        }

                        i++;
                    }
                    else
                    {
                        // other user-defined mtouch arguments
                        args.AddQuoted(StringParserService.Parse(extraArgs[i], customTags));
                    }
                }
            }

            BuildNativeReferenceFlags(gcc);
            BuildEntitlementFlags(gcc);

            foreach (var framework in gcc.Frameworks)
            {
                args.Add("-framework");
                args.AddQuoted(framework);
            }

            foreach (var framework in gcc.WeakFrameworks)
            {
                args.Add("-weak-framework");
                args.AddQuoted(framework);
            }

            if (gcc.Cxx)
            {
                args.Add("--cxx");
            }

            if (gcc.Arguments.Length > 0)
            {
                args.Add("--gcc_flags");
                args.AddQuoted(gcc.Arguments.ToString());
            }

            foreach (var asm in References)
            {
                args.Add("-r");
                if (IsFrameworkItem(asm))
                {
                    args.AddQuoted(ResolveFrameworkFile(asm.ItemSpec));
                }
                else
                {
                    args.AddQuoted(Path.GetFullPath(asm.ItemSpec));
                }
            }

            foreach (var ext in AppExtensionReferences)
            {
                args.Add("--app-extension");
                args.AddQuoted(Path.GetFullPath(ext.ItemSpec));
            }

            args.Add("--target-framework");
            args.Add(TargetFrameworkIdentifier + "," + TargetFrameworkVersion);

            args.AddQuoted(MainAssembly.ItemSpec);

            // We give the priority to the ExtraArgs to set the mtouch verbosity.
            if (string.IsNullOrEmpty(ExtraArgs) || (!string.IsNullOrEmpty(ExtraArgs) && !ExtraArgs.Contains("-q") && !ExtraArgs.Contains("-v")))
            {
                args.Add(GetVerbosityLevel(Verbosity));
            }

            if (!string.IsNullOrWhiteSpace(License))
            {
                args.Add(string.Format("--license={0}", License));
            }

            return(args.ToString());
        }
Beispiel #2
0
		static PlistDocument LoadSdkSettings (IPhoneSdkVersion sdk)
		{
			var doc = new PlistDocument ();
			doc.LoadFromXmlFile ("/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS" + sdk.ToString ()
			                     + ".sdk/SDKSettings.plist");
			return doc;
		}
Beispiel #3
0
		public static string GetDTSdkName (IPhoneSdkVersion sdk, bool sim)
		{
			var cache = sim? dtSdkNameSim : dtSdkName;
			string name;
			if (cache.TryGetValue (sdk.ToString (), out name))
				return name;
			
			string keyName = sim? "AlternateSDK" : "CanonicalName";
			var doc = LoadSdkSettings (sdk);
			var dict = (PlistDictionary) doc.Root;
			return cache[sdk.ToString ()] = ((PlistString) dict[keyName]).Value;
		}
        protected override string GenerateCommandLineCommands()
        {
            var           args          = GenerateCommandLineArguments();
            List <string> unescapedArgs = new List <string> ();

            TargetArchitecture architectures;

            if (string.IsNullOrEmpty(Architectures) || !Enum.TryParse(Architectures, out architectures))
            {
                architectures = TargetArchitecture.Default;
            }

            if (architectures == TargetArchitecture.ARMv6)
            {
                Log.LogError(MSBStrings.E0053);
                return(null);
            }

            args.AddQuotedLine((SdkIsSimulator ? "--sim=" : "--dev=") + Path.GetFullPath(AppBundleDir));

            if (AppleSdkSettings.XcodeVersion.Major >= 5 && IPhoneSdks.MonoTouch.Version.CompareTo(new IPhoneSdkVersion(6, 3, 7)) < 0)
            {
                args.AddLine("--compiler=clang");
            }

            args.AddQuotedLine($"--executable={ExecutableName}");

            if (IsAppExtension)
            {
                args.AddLine("--extension");
            }

            if (Debug)
            {
                if (FastDev && !SdkIsSimulator)
                {
                    args.AddLine("--fastdev");
                }
            }

            if (LinkerDumpDependencies)
            {
                args.AddLine("--linkerdumpdependencies");
            }

            if (!string.IsNullOrEmpty(Interpreter))
            {
                args.AddLine($"--interpreter={Interpreter}");
            }

            switch (LinkMode.ToLowerInvariant())
            {
            case "sdkonly": args.AddLine("--linksdkonly"); break;

            case "none":    args.AddLine("--nolink"); break;
            }

            args.AddQuotedLine($"--sdk={SdkVersion}");

            if (!minimumOSVersion.IsUseDefault)
            {
                args.AddQuotedLine($"--targetver={minimumOSVersion.ToString ()}");
            }

            if (UseFloat32 /* We want to compile 32-bit floating point code to use 32-bit floating point operations */)
            {
                args.AddLine("--aot-options=-O=float32");
            }
            else
            {
                args.AddLine("--aot-options=-O=-float32");
            }

            if (LinkDescriptions != null)
            {
                foreach (var desc in LinkDescriptions)
                {
                    args.AddQuotedLine($"--xml={desc.ItemSpec}");
                }
            }

            if (EnableBitcode)
            {
                switch (Platform)
                {
                case ApplePlatform.WatchOS:
                    args.AddLine("--bitcode=full");
                    break;

                case ApplePlatform.TVOS:
                    args.AddLine("--bitcode=asmonly");
                    break;

                default:
                    throw new InvalidOperationException(string.Format("Bitcode is currently not supported on {0}.", Platform));
                }
            }

            string thumb = UseThumb && UseLlvm ? "+thumb2" : "";
            string llvm  = UseLlvm ? "+llvm" : "";
            string abi   = "";

            if (SdkIsSimulator)
            {
                if (architectures.HasFlag(TargetArchitecture.i386))
                {
                    abi += (abi.Length > 0 ? "," : "") + "i386";
                }

                if (architectures.HasFlag(TargetArchitecture.x86_64))
                {
                    abi += (abi.Length > 0 ? "," : "") + "x86_64";
                }

                if (string.IsNullOrEmpty(abi))
                {
                    architectures = TargetArchitecture.i386;
                    abi           = "i386";
                }
            }
            else
            {
                if (architectures == TargetArchitecture.Default)
                {
                    architectures = TargetArchitecture.ARMv7;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7" + llvm + thumb;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7s))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7s" + llvm + thumb;
                }

                if (architectures.HasFlag(TargetArchitecture.ARM64))
                {
                    // Note: ARM64 does not have thumb.
                    abi += (abi.Length > 0 ? "," : "") + "arm64" + llvm;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7k))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7k" + llvm;
                }

                if (architectures.HasFlag(TargetArchitecture.ARM64_32))
                {
                    abi += (abi.Length > 0 ? "," : "") + "arm64_32" + llvm;
                }

                if (string.IsNullOrEmpty(abi))
                {
                    abi = "armv7" + llvm + thumb;
                }
            }

            // Output the CompiledArchitectures
            CompiledArchitectures = architectures.ToString();

            args.AddLine($"--abi={abi}");

            // output symbols to preserve when stripping
            args.AddQuotedLine($"--symbollist={Path.GetFullPath (SymbolsList)}");

            // don't have mtouch generate the dsyms...
            args.AddLine("--dsym=no");

            var gcc = new GccOptions();

            if (!string.IsNullOrEmpty(ExtraArgs))
            {
                var    extraArgs = CommandLineArgumentBuilder.Parse(ExtraArgs);
                var    target    = MainAssembly.ItemSpec;
                string projectDir;

                if (ProjectDir.StartsWith("~/", StringComparison.Ordinal))
                {
                    // Note: Since the Visual Studio plugin doesn't know the user's home directory on the Mac build host,
                    // it simply uses paths relative to "~/". Expand these paths to their full path equivalents.
                    var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

                    projectDir = Path.Combine(home, ProjectDir.Substring(2));
                }
                else
                {
                    projectDir = ProjectDir;
                }

                var customTags = new Dictionary <string, string> (StringComparer.OrdinalIgnoreCase)
                {
                    { "projectdir", projectDir },
                    // Apparently msbuild doesn't propagate the solution path, so we can't get it.
                    // { "solutiondir",  proj.ParentSolution != null ? proj.ParentSolution.BaseDirectory : proj.BaseDirectory },
                    { "appbundledir", AppBundleDir },
                    { "targetpath", Path.Combine(Path.GetDirectoryName(target), Path.GetFileName(target)) },
                    { "targetdir", Path.GetDirectoryName(target) },
                    { "targetname", Path.GetFileName(target) },
                    { "targetext", Path.GetExtension(target) },
                };

                for (int i = 0; i < extraArgs.Length; i++)
                {
                    var argument   = extraArgs[i];
                    int startIndex = 0;

                    while (argument.Length > startIndex && argument[startIndex] == '-')
                    {
                        startIndex++;
                    }

                    int endIndex = startIndex;
                    while (endIndex < argument.Length && argument[endIndex] != '=')
                    {
                        endIndex++;
                    }

                    int length = endIndex - startIndex;

                    if (length == 9 && string.CompareOrdinal(argument, startIndex, "gcc_flags", 0, 9) == 0)
                    {
                        // user-defined -gcc_flags argument
                        string flags = null;

                        if (endIndex < extraArgs[i].Length)
                        {
                            flags = Unquote(argument, endIndex + 1);
                        }
                        else if (i + 1 < extraArgs.Length)
                        {
                            flags = extraArgs[++i];
                        }

                        if (!string.IsNullOrEmpty(flags))
                        {
                            var gccArgs = CommandLineArgumentBuilder.Parse(flags);

                            for (int j = 0; j < gccArgs.Length; j++)
                            {
                                gcc.Arguments.Add(StringParserService.Parse(gccArgs[j], customTags));
                            }
                        }
                    }
                    else
                    {
                        // other user-defined mtouch arguments
                        unescapedArgs.Add(StringParserService.Parse(argument, customTags));
                    }
                }
            }

            BuildNativeReferenceFlags(gcc);
            BuildEntitlementFlags(gcc);

            foreach (var framework in gcc.Frameworks)
            {
                args.AddQuotedLine($"--framework={framework}");
            }

            foreach (var framework in gcc.WeakFrameworks)
            {
                args.AddQuotedLine($"--weak-framework={framework}");
            }

            if (gcc.Cxx)
            {
                args.AddLine("--cxx");
            }

            if (gcc.Arguments.Length > 0)
            {
                unescapedArgs.Add($"--gcc_flags={gcc.Arguments.ToString ()}");
            }

            foreach (var asm in References)
            {
                if (IsFrameworkItem(asm))
                {
                    args.AddQuotedLine($"--reference={ResolveFrameworkFile (asm.ItemSpec)}");
                }
                else
                {
                    args.AddQuotedLine($"--reference={Path.GetFullPath (asm.ItemSpec)}");
                }
            }

            foreach (var ext in AppExtensionReferences)
            {
                args.AddQuotedLine($"--app-extension={Path.GetFullPath (ext.ItemSpec)}");
            }

            if (!string.IsNullOrWhiteSpace(License))
            {
                args.AddLine($"--license={License}");
            }

            return(CreateResponseFile(args, unescapedArgs));
        }
Beispiel #5
0
		public static bool SdkIsInstalled (IPhoneSdkVersion version)
		{
			return SdkIsInstalled (version.ToString ());
		}
Beispiel #6
0
        protected override string GenerateCommandLineCommands()
        {
            var           args          = new CommandLineArgumentBuilder();
            List <string> unescapedArgs = new List <string> ();

            TargetArchitecture architectures;
            bool msym;

            if (string.IsNullOrEmpty(Architectures) || !Enum.TryParse(Architectures, out architectures))
            {
                architectures = TargetArchitecture.Default;
            }

            if (architectures == TargetArchitecture.ARMv6)
            {
                Log.LogError("Target architecture ARMv6 is no longer supported in Xamarin.iOS. Please select a supported architecture.");
                return(null);
            }

            if (!string.IsNullOrEmpty(IntermediateOutputPath))
            {
                Directory.CreateDirectory(IntermediateOutputPath);

                args.AddQuotedLine($"--cache={Path.GetFullPath (IntermediateOutputPath)}");
            }

            args.AddQuotedLine((SdkIsSimulator ? "--sim=" : "--dev=") + Path.GetFullPath(AppBundleDir));

            if (AppleSdkSettings.XcodeVersion.Major >= 5 && IPhoneSdks.MonoTouch.Version.CompareTo(new IPhoneSdkVersion(6, 3, 7)) < 0)
            {
                args.AddLine("--compiler=clang");
            }

            args.AddQuotedLine($"--executable={ExecutableName}");

            if (IsAppExtension)
            {
                args.AddLine("--extension");
            }

            if (Debug)
            {
                if (FastDev && !SdkIsSimulator)
                {
                    args.AddLine("--fastdev");
                }

                args.AddLine("--debug");
            }

            if (Profiling)
            {
                args.AddLine("--profiling");
            }

            if (LinkerDumpDependencies)
            {
                args.AddLine("--linkerdumpdependencies");
            }

            if (EnableSGenConc)
            {
                args.AddLine("--sgen-conc");
            }

            if (!string.IsNullOrEmpty(Interpreter))
            {
                args.Add($"--interpreter={Interpreter}");
            }

            switch (LinkMode.ToLowerInvariant())
            {
            case "sdkonly": args.AddLine("--linksdkonly"); break;

            case "none":    args.AddLine("--nolink"); break;
            }

            if (!string.IsNullOrEmpty(I18n))
            {
                args.AddQuotedLine($"--i18n={I18n}");
            }

            args.AddQuotedLine($"--sdkroot={SdkRoot}");

            args.AddQuotedLine($"--sdk={SdkVersion}");

            if (!minimumOSVersion.IsUseDefault)
            {
                args.AddQuotedLine($"--targetver={minimumOSVersion.ToString ()}");
            }

            if (UseFloat32 /* We want to compile 32-bit floating point code to use 32-bit floating point operations */)
            {
                args.AddLine("--aot-options=-O=float32");
            }
            else
            {
                args.AddLine("--aot-options=-O=-float32");
            }

            if (!EnableGenericValueTypeSharing)
            {
                args.AddLine("--gsharedvt=false");
            }

            if (LinkDescriptions != null)
            {
                foreach (var desc in LinkDescriptions)
                {
                    args.AddQuotedLine($"--xml={desc.ItemSpec}");
                }
            }

            if (EnableBitcode)
            {
                switch (Framework)
                {
                case PlatformFramework.WatchOS:
                    args.AddLine("--bitcode=full");
                    break;

                case PlatformFramework.TVOS:
                    args.AddLine("--bitcode=asmonly");
                    break;

                default:
                    throw new InvalidOperationException(string.Format("Bitcode is currently not supported on {0}.", Framework));
                }
            }

            if (!string.IsNullOrEmpty(HttpClientHandler))
            {
                args.AddLine($"--http-message-handler={HttpClientHandler}");
            }

            string thumb = UseThumb && UseLlvm ? "+thumb2" : "";
            string llvm  = UseLlvm ? "+llvm" : "";
            string abi   = "";

            if (SdkIsSimulator)
            {
                if (architectures.HasFlag(TargetArchitecture.i386))
                {
                    abi += (abi.Length > 0 ? "," : "") + "i386";
                }

                if (architectures.HasFlag(TargetArchitecture.x86_64))
                {
                    abi += (abi.Length > 0 ? "," : "") + "x86_64";
                }

                if (string.IsNullOrEmpty(abi))
                {
                    architectures = TargetArchitecture.i386;
                    abi           = "i386";
                }
            }
            else
            {
                if (architectures == TargetArchitecture.Default)
                {
                    architectures = TargetArchitecture.ARMv7;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7" + llvm + thumb;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7s))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7s" + llvm + thumb;
                }

                if (architectures.HasFlag(TargetArchitecture.ARM64))
                {
                    // Note: ARM64 does not have thumb.
                    abi += (abi.Length > 0 ? "," : "") + "arm64" + llvm;
                }

                if (architectures.HasFlag(TargetArchitecture.ARMv7k))
                {
                    abi += (abi.Length > 0 ? "," : "") + "armv7k" + llvm;
                }

                if (string.IsNullOrEmpty(abi))
                {
                    abi = "armv7" + llvm + thumb;
                }
            }

            // Output the CompiledArchitectures
            CompiledArchitectures = architectures.ToString();

            args.AddLine($"--abi={abi}");

            // output symbols to preserve when stripping
            args.AddQuotedLine($"--symbollist={Path.GetFullPath (SymbolsList)}");

            // don't have mtouch generate the dsyms...
            args.AddLine("--dsym=no");

            if (!string.IsNullOrEmpty(ArchiveSymbols) && bool.TryParse(ArchiveSymbols.Trim(), out msym))
            {
                args.AddLine($"--msym={(msym ? "yes" : "no")}");
            }

            var gcc = new GccOptions();

            if (!string.IsNullOrEmpty(ExtraArgs))
            {
                var    extraArgs = CommandLineArgumentBuilder.Parse(ExtraArgs);
                var    target    = MainAssembly.ItemSpec;
                string projectDir;

                if (ProjectDir.StartsWith("~/", StringComparison.Ordinal))
                {
                    // Note: Since the Visual Studio plugin doesn't know the user's home directory on the Mac build host,
                    // it simply uses paths relative to "~/". Expand these paths to their full path equivalents.
                    var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

                    projectDir = Path.Combine(home, ProjectDir.Substring(2));
                }
                else
                {
                    projectDir = ProjectDir;
                }

                var customTags = new Dictionary <string, string> (StringComparer.OrdinalIgnoreCase)
                {
                    { "projectdir", projectDir },
                    // Apparently msbuild doesn't propagate the solution path, so we can't get it.
                    // { "solutiondir",  proj.ParentSolution != null ? proj.ParentSolution.BaseDirectory : proj.BaseDirectory },
                    { "appbundledir", AppBundleDir },
                    { "targetpath", Path.Combine(Path.GetDirectoryName(target), Path.GetFileName(target)) },
                    { "targetdir", Path.GetDirectoryName(target) },
                    { "targetname", Path.GetFileName(target) },
                    { "targetext", Path.GetExtension(target) },
                };

                for (int i = 0; i < extraArgs.Length; i++)
                {
                    var argument   = extraArgs[i];
                    int startIndex = 0;

                    while (argument.Length > startIndex && argument[startIndex] == '-')
                    {
                        startIndex++;
                    }

                    int endIndex = startIndex;
                    while (endIndex < argument.Length && argument[endIndex] != '=')
                    {
                        endIndex++;
                    }

                    int length = endIndex - startIndex;

                    if (length == 9 && string.CompareOrdinal(argument, startIndex, "gcc_flags", 0, 9) == 0)
                    {
                        // user-defined -gcc_flags argument
                        string flags = null;

                        if (endIndex < extraArgs[i].Length)
                        {
                            flags = Unquote(argument, endIndex + 1);
                        }
                        else if (i + 1 < extraArgs.Length)
                        {
                            flags = extraArgs[++i];
                        }

                        if (!string.IsNullOrEmpty(flags))
                        {
                            var gccArgs = CommandLineArgumentBuilder.Parse(flags);

                            for (int j = 0; j < gccArgs.Length; j++)
                            {
                                gcc.Arguments.Add(StringParserService.Parse(gccArgs[j], customTags));
                            }
                        }
                    }
                    else
                    {
                        // other user-defined mtouch arguments
                        unescapedArgs.Add(StringParserService.Parse(argument, customTags));
                    }
                }
            }

            BuildNativeReferenceFlags(gcc);
            BuildEntitlementFlags(gcc);

            foreach (var framework in gcc.Frameworks)
            {
                args.AddQuotedLine($"--framework={framework}");
            }

            foreach (var framework in gcc.WeakFrameworks)
            {
                args.AddQuotedLine($"--weak-framework={framework}");
            }

            if (gcc.Cxx)
            {
                args.AddLine("--cxx");
            }

            if (gcc.Arguments.Length > 0)
            {
                unescapedArgs.Add($"--gcc_flags={gcc.Arguments.ToString ()}");
            }

            foreach (var asm in References)
            {
                if (IsFrameworkItem(asm))
                {
                    args.AddQuotedLine($"-r={ResolveFrameworkFile (asm.ItemSpec)}");
                }
                else
                {
                    args.AddQuotedLine($"-r={Path.GetFullPath (asm.ItemSpec)}");
                }
            }

            foreach (var ext in AppExtensionReferences)
            {
                args.AddQuotedLine($"--app-extension={Path.GetFullPath (ext.ItemSpec)}");
            }

            args.AddLine($"--target-framework={TargetFrameworkIdentifier},{TargetFrameworkVersion}");

            args.AddQuotedLine($"--root-assembly={Path.GetFullPath (MainAssembly.ItemSpec)}");

            // We give the priority to the ExtraArgs to set the mtouch verbosity.
            if (string.IsNullOrEmpty(ExtraArgs) || (!string.IsNullOrEmpty(ExtraArgs) && !ExtraArgs.Contains("-q") && !ExtraArgs.Contains("-v")))
            {
                args.AddLine(GetVerbosityLevel(Verbosity));
            }

            if (!string.IsNullOrWhiteSpace(License))
            {
                args.AddLine($"--license={License}");
            }

            // Generate a response file
            var responseFile = Path.GetFullPath(ResponseFilePath);

            if (File.Exists(responseFile))
            {
                File.Delete(responseFile);
            }

            try {
                using (var fs = File.Create(responseFile)) {
                    using (var writer = new StreamWriter(fs))
                        writer.Write(args);
                }
            } catch (Exception ex) {
                Log.LogWarning("Failed to create response file '{0}': {1}", responseFile, ex);
            }

            // Some arguments can not safely go in the response file and are
            // added separately. They must go _after_ the response file
            // as they may override options passed in the response file
            var actualArgs = new CommandLineArgumentBuilder();

            actualArgs.AddQuoted($"@{responseFile}");

            foreach (var arg in unescapedArgs)
            {
                actualArgs.AddQuoted(arg);
            }

            return(actualArgs.ToString());
        }
        bool Compile(PDictionary plist)
        {
            var currentSDK = IPhoneSdks.GetSdk(Framework);

            if (!currentSDK.SdkIsInstalled(sdkVersion, SdkIsSimulator))
            {
                Log.LogError(null, null, null, null, 0, 0, 0, 0, "The {0} SDK for '{1}' is not installed.", Framework, sdkVersion);
                return(false);
            }

            supportedDevices = plist.GetUIDeviceFamily();

            if (!IsWatchApp)
            {
                var version = IPhoneSdks.MonoTouch.ExtendedVersion;
                // This key is our supported way of determining if an app
                // was built with Xamarin, so it needs to be present in all apps.

                var dict = new PDictionary();
                dict.Add("Version", new PString(string.Format("{0} ({1}: {2})", version.Version, version.Branch, version.Hash)));
                plist.Add("com.xamarin.ios", dict);
            }

            var sdkSettings = currentSDK.GetSdkSettings(sdkVersion, SdkIsSimulator);
            var dtSettings  = currentSDK.GetDTSettings();

            SetValue(plist, ManifestKeys.BuildMachineOSBuild, dtSettings.BuildMachineOSBuild);
            plist.SetIfNotPresent(ManifestKeys.CFBundleDevelopmentRegion, "en");

            plist.SetIfNotPresent(ManifestKeys.CFBundleExecutable, AssemblyName);
            if (IsIOS)
            {
                var executable = plist.GetCFBundleExecutable();
                if (executable.EndsWith(".app", StringComparison.Ordinal))
                {
                    LogAppManifestError("The executable (CFBundleExecutable) name ({0}) cannot end with '.app', because iOS may fail to launch the app.", executable);
                }
            }

            if (IsIOS)
            {
                if (minimumOSVersion < IPhoneSdkVersion.V5_0 && plist.GetUIMainStoryboardFile(true) != null)
                {
                    LogAppManifestError("Applications using a storyboard as the Main Interface must have a deployment target greater than 5.0");
                }

                if (!plist.ContainsKey(ManifestKeys.CFBundleName))
                {
                    plist [ManifestKeys.CFBundleName] = plist.ContainsKey(ManifestKeys.CFBundleDisplayName) ? plist.GetString(ManifestKeys.CFBundleDisplayName).Clone() : new PString(AppBundleName);
                }
            }
            else
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleName, AppBundleName);
            }

            plist.SetIfNotPresent(ManifestKeys.CFBundleIdentifier, BundleIdentifier);
            plist.SetIfNotPresent(ManifestKeys.CFBundleInfoDictionaryVersion, "6.0");
            plist.SetIfNotPresent(ManifestKeys.CFBundlePackageType, IsAppExtension ? "XPC!" : "APPL");
            if (!string.IsNullOrEmpty(ResourceRules))
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleResourceSpecification, Path.GetFileName(ResourceRules));
            }
            plist.SetIfNotPresent(ManifestKeys.CFBundleSignature, "????");
            if (!plist.ContainsKey(ManifestKeys.CFBundleSupportedPlatforms))
            {
                plist[ManifestKeys.CFBundleSupportedPlatforms] = new PArray {
                    SdkPlatform
                }
            }
            ;
            plist.SetIfNotPresent(ManifestKeys.CFBundleVersion, "1.0");
            plist.SetIfNotPresent(ManifestKeys.CFBundleShortVersionString, plist.GetCFBundleVersion());

            if (!SdkIsSimulator)
            {
                SetValue(plist, "DTCompiler", sdkSettings.DTCompiler);
                SetValue(plist, "DTPlatformBuild", dtSettings.DTPlatformBuild);
                SetValue(plist, "DTSDKBuild", sdkSettings.DTSDKBuild);
            }

            plist.SetIfNotPresent("DTPlatformName", SdkPlatform.ToLowerInvariant());
            if (!SdkIsSimulator)
            {
                SetValue(plist, "DTPlatformVersion", dtSettings.DTPlatformVersion);
            }

            var sdkName = sdkSettings.CanonicalName;

            // older sdksettings didn't have a canonicalname for sim
            if (SdkIsSimulator && string.IsNullOrEmpty(sdkName))
            {
                var deviceSdkSettings = currentSDK.GetSdkSettings(sdkVersion, false);
                sdkName = deviceSdkSettings.AlternateSDK;
            }
            SetValue(plist, "DTSDKName", sdkName);

            if (!SdkIsSimulator)
            {
                SetValue(plist, "DTXcode", AppleSdkSettings.DTXcode);
                SetValue(plist, "DTXcodeBuild", dtSettings.DTXcodeBuild);
            }

            SetDeviceFamily(plist);

            plist.SetIfNotPresent(ManifestKeys.MinimumOSVersion, minimumOSVersion.ToString());

            if (IsWatchExtension && Debug)
            {
                SetAppTransportSecurity(plist);
            }

            // Remove any Xamarin Studio specific keys
            plist.Remove(ManifestKeys.XSLaunchImageAssets);
            plist.Remove(ManifestKeys.XSAppIconAssets);

            // Merge with any partial plists generated by the Asset Catalog compiler...
            MergePartialPlistTemplates(plist);

            if (IsIOS)
            {
                Validation(plist);
            }

            CompiledAppManifest = new TaskItem(Path.Combine(AppBundleDir, "Info.plist"));
            plist.Save(CompiledAppManifest.ItemSpec, true, true);

            return(!Log.HasLoggedErrors);
        }

        void SetDeviceFamily(PDictionary plist)
        {
            switch (Framework)
            {
            case PlatformFramework.iOS:
                SetIOSDeviceFamily(plist);
                break;

            case PlatformFramework.WatchOS:
                plist.SetUIDeviceFamily(IPhoneDeviceType.Watch);
                break;

            case PlatformFramework.TVOS:
                plist.SetUIDeviceFamily(IPhoneDeviceType.TV);
                break;
            }
        }

        void SetIOSDeviceFamily(PDictionary plist)
        {
            if (IsWatchApp)
            {
                if (SdkIsSimulator)
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.IPhone | IPhoneDeviceType.Watch);
                }
                else
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.Watch);
                }
            }
            else
            {
                if (!IsAppExtension)
                {
                    plist.SetIfNotPresent(ManifestKeys.LSRequiresIPhoneOS, true);
                }

                if (minimumOSVersion >= IPhoneSdkVersion.V3_2 && supportedDevices == IPhoneDeviceType.NotSet)
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.IPhone);
                }
            }
        }

        void SetAppTransportSecurity(PDictionary plist)
        {
            // Debugging over http has a couple of gotchas:
            // * We can't use https, because that requires a valid server certificate,
            //   which we can't ensure.
            //   It would also require a hostname for the mac, which it might not have either.
            // * NSAppTransportSecurity/NSExceptionDomains does not allow exceptions based
            //   on IP address (only hostname).
            // * Which means the only way to make sure watchOS allows connections from
            //   the app on device to the mac is to disable App Transport Security altogether.
            // Good news: watchOS 3 will apparently not apply ATS when connecting
            // directly to IP addresses, which means we won't have to do this at all
            // (sometime in the future).

            PDictionary ats;

            if (!plist.TryGetValue(ManifestKeys.NSAppTransportSecurity, out ats))
            {
                plist.Add(ManifestKeys.NSAppTransportSecurity, ats = new PDictionary());
            }

            if (ats.GetBoolean(ManifestKeys.NSAllowsArbitraryLoads))
            {
                Log.LogMessage(MessageImportance.Low, "All http loads are already allowed.");
            }
            else
            {
                Log.LogMessage(MessageImportance.Low, "Allowed arbitrary HTTP loads to support debugging.");
                ats.SetBooleanOrRemove(ManifestKeys.NSAllowsArbitraryLoads, true);
            }
        }

        void Validation(PDictionary plist)
        {
            var supportsIPhone = (supportedDevices & IPhoneDeviceType.IPhone) != 0 ||
                                 supportedDevices == IPhoneDeviceType.NotSet;
            var supportsIPad = (supportedDevices & IPhoneDeviceType.IPad) != 0;

            // Validation...
            if (!IsAppExtension && sdkVersion >= IPhoneSdkVersion.V3_2)
            {
                IPhoneOrientation orientation;

                if (supportsIPhone)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(false);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning("Supported iPhone orientations have not been set");
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning("Supported iPhone orientations are not matched pairs");
                    }
                }

                if (supportsIPad)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(true);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning("Supported iPad orientations have not been set");
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning("Supported iPad orientations are not matched pairs");
                    }
                }
            }
        }
    }
        bool Compile(PDictionary plist)
        {
            var currentSDK = IPhoneSdks.GetSdk(Framework);

            if (!currentSDK.SdkIsInstalled(sdkVersion, SdkIsSimulator))
            {
                Log.LogError(null, null, null, null, 0, 0, 0, 0, "The {0} SDK for '{1}' is not installed.", Framework, sdkVersion);
                return(false);
            }

            supportedDevices = plist.GetUIDeviceFamily();

            if (!IsWatchApp)
            {
                var version = IPhoneSdks.MonoTouch.ExtendedVersion;
                // This key is our supported way of determining if an app
                // was built with Xamarin, so it needs to be present in all apps.

                var dict = new PDictionary();
                dict.Add("Version", new PString(string.Format("{0} ({1}: {2})", version.Version, version.Branch, version.Hash)));
                plist.Add("com.xamarin.ios", dict);
            }

            var sdkSettings = currentSDK.GetSdkSettings(sdkVersion, SdkIsSimulator);
            var dtSettings  = currentSDK.GetDTSettings();

            SetValue(plist, ManifestKeys.BuildMachineOSBuild, dtSettings.BuildMachineOSBuild);
            // We have an issue here, this is for consideration by the platform:
            // CFLocaleCopyCurrent(), used in the mono code to get the current locale (locale.c line 421), will return the value of the application's CFBundleDevelopmentRegion Info.plist key if all of the following conditions are true:
            //
            // * CFBundleDevelopmentRegion is present in the Info.plist
            // * The CFBundleDevelopmentRegion language is in the list of preferred languages on the iOS device, but isn't the first one
            // * There are no localized resources (i.e. no .lproj directory) in the app for the first preferred locale
            //
            // This differs from iOS 10 where the presence of the CFBundleDevelopmentRegion key had no effect. Commenting this line out, ensures that CurrentCulture is correct and behaves like the iOS 10 version.
            // plist.SetIfNotPresent (ManifestKeys.CFBundleDevelopmentRegion, "en");

            plist.SetIfNotPresent(ManifestKeys.CFBundleExecutable, AssemblyName);
            if (IsIOS)
            {
                var executable = plist.GetCFBundleExecutable();
                if (executable.EndsWith(".app", StringComparison.Ordinal))
                {
                    LogAppManifestError("The executable (CFBundleExecutable) name ({0}) cannot end with '.app', because iOS may fail to launch the app.", executable);
                }
            }

            if (IsIOS)
            {
                if (minimumOSVersion < IPhoneSdkVersion.V5_0 && plist.GetUIMainStoryboardFile(true) != null)
                {
                    LogAppManifestError("Applications using a storyboard as the Main Interface must have a deployment target greater than 5.0");
                }

                if (!plist.ContainsKey(ManifestKeys.CFBundleName))
                {
                    plist [ManifestKeys.CFBundleName] = plist.ContainsKey(ManifestKeys.CFBundleDisplayName) ? plist.GetString(ManifestKeys.CFBundleDisplayName).Clone() : new PString(AppBundleName);
                }
            }
            else
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleName, AppBundleName);
            }

            plist.SetIfNotPresent(ManifestKeys.CFBundleIdentifier, BundleIdentifier);
            plist.SetIfNotPresent(ManifestKeys.CFBundleInfoDictionaryVersion, "6.0");
            plist.SetIfNotPresent(ManifestKeys.CFBundlePackageType, IsAppExtension ? "XPC!" : "APPL");
            if (!string.IsNullOrEmpty(ResourceRules))
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleResourceSpecification, Path.GetFileName(ResourceRules));
            }
            plist.SetIfNotPresent(ManifestKeys.CFBundleSignature, "????");
            if (!plist.ContainsKey(ManifestKeys.CFBundleSupportedPlatforms))
            {
                plist[ManifestKeys.CFBundleSupportedPlatforms] = new PArray {
                    SdkPlatform
                }
            }
            ;
            plist.SetIfNotPresent(ManifestKeys.CFBundleVersion, "1.0");
            plist.SetIfNotPresent(ManifestKeys.CFBundleShortVersionString, plist.GetCFBundleVersion());

            string dtCompiler        = null;
            string dtPlatformBuild   = null;
            string dtSDKBuild        = null;
            string dtPlatformName    = null;
            string dtPlatformVersion = null;
            string dtXcode           = null;
            string dtXcodeBuild      = null;

            if (!SdkIsSimulator)
            {
                dtCompiler      = sdkSettings.DTCompiler;
                dtPlatformBuild = dtSettings.DTPlatformBuild;
                dtSDKBuild      = sdkSettings.DTSDKBuild;
            }

            dtPlatformName = SdkPlatform.ToLowerInvariant();
            if (!SdkIsSimulator)
            {
                dtPlatformVersion = dtSettings.DTPlatformVersion;
            }

            var dtSDKName = sdkSettings.CanonicalName;

            // older sdksettings didn't have a canonicalname for sim
            if (SdkIsSimulator && string.IsNullOrEmpty(dtSDKName))
            {
                var deviceSdkSettings = currentSDK.GetSdkSettings(sdkVersion, false);
                dtSDKName = deviceSdkSettings.AlternateSDK;
            }

            if (!SdkIsSimulator)
            {
                dtXcode      = AppleSdkSettings.DTXcode;
                dtXcodeBuild = dtSettings.DTXcodeBuild;
            }

            if (UseFakeWatchOS4_3Sdk)
            {
                // This is a workaround for https://github.com/xamarin/xamarin-macios/issues/4810
                if (Framework == PlatformFramework.WatchOS)
                {
                    if (dtPlatformBuild != null)
                    {
                        dtPlatformBuild = "15T212";
                    }
                    if (dtPlatformVersion != null)
                    {
                        dtPlatformVersion = "4.3";
                    }
                    if (dtSDKBuild != null)
                    {
                        dtSDKBuild = "15T212";
                    }
                    if (dtSDKName != null)
                    {
                        dtSDKName = "watchos4.3";
                    }
                    if (dtXcode != null)
                    {
                        dtXcode = "0940";
                    }
                    if (dtXcodeBuild != null)
                    {
                        dtXcodeBuild = "9F1027a";
                    }
                }
                else
                {
                    Log.LogWarning("Can only fake the watchOS 4.3 SDK when building for watchOS.");
                }
            }

            SetValueIfNotNull(plist, "DTCompiler", dtCompiler);
            SetValueIfNotNull(plist, "DTPlatformBuild", dtPlatformBuild);
            SetValueIfNotNull(plist, "DTSDKBuild", dtSDKBuild);
            plist.SetIfNotPresent("DTPlatformName", dtPlatformName);
            SetValueIfNotNull(plist, "DTPlatformVersion", dtPlatformVersion);
            SetValue(plist, "DTSDKName", dtSDKName);
            SetValueIfNotNull(plist, "DTXcode", dtXcode);
            SetValueIfNotNull(plist, "DTXcodeBuild", dtXcodeBuild);

            SetDeviceFamily(plist);

            plist.SetIfNotPresent(ManifestKeys.MinimumOSVersion, minimumOSVersion.ToString());

            if (IsWatchExtension)
            {
                // Note: Only watchOS1 Extensions target Xamarin.iOS
                if (Framework == PlatformFramework.iOS)
                {
                    PObject value;

                    if (!plist.TryGetValue(ManifestKeys.UIRequiredDeviceCapabilities, out value))
                    {
                        var capabilities = new PArray();
                        capabilities.Add(new PString("watch-companion"));

                        plist.Add(ManifestKeys.UIRequiredDeviceCapabilities, capabilities);
                    }
                    else if (value is PDictionary)
                    {
                        var capabilities = (PDictionary)value;

                        if (!capabilities.ContainsKey("watch-companion"))
                        {
                            capabilities.Add("watch-companion", new PBoolean(true));
                        }
                    }
                    else
                    {
                        var  capabilities = (PArray)value;
                        bool exists       = false;

                        foreach (var capability in capabilities.OfType <PString> ())
                        {
                            if (capability.Value != "watch-companion")
                            {
                                continue;
                            }

                            exists = true;
                            break;
                        }

                        if (!exists)
                        {
                            capabilities.Add(new PString("watch-companion"));
                        }
                    }
                }

                if (Debug)
                {
                    SetAppTransportSecurity(plist);
                }
            }

            // Remove any Xamarin Studio specific keys
            plist.Remove(ManifestKeys.XSLaunchImageAssets);
            plist.Remove(ManifestKeys.XSAppIconAssets);

            // Merge with any partial plists generated by the Asset Catalog compiler...
            MergePartialPlistTemplates(plist);

            SetRequiredArchitectures(plist);

            if (IsIOS)
            {
                Validation(plist);
            }

            CompiledAppManifest = new TaskItem(Path.Combine(AppBundleDir, "Info.plist"));
            plist.Save(CompiledAppManifest.ItemSpec, true, true);

            return(!Log.HasLoggedErrors);
        }

        void SetValueIfNotNull(PDictionary dict, string key, string value)
        {
            if (value == null)
            {
                return;
            }
            SetValue(dict, key, value);
        }

        void SetRequiredArchitectures(PDictionary plist)
        {
            PObject capabilities;

            if (plist.TryGetValue(ManifestKeys.UIRequiredDeviceCapabilities, out capabilities))
            {
                if (capabilities is PArray)
                {
                    var architectureValues = new HashSet <string> (new[] { "armv6", "armv7", "arm64" });
                    var array = (PArray)capabilities;

                    // Remove any architecture values
                    for (int i = 0; i < array.Count; i++)
                    {
                        var value = array[i] as PString;

                        if (value == null || !architectureValues.Contains(value.Value))
                        {
                            continue;
                        }

                        array.RemoveAt(i);
                    }

                    // If-and-only-if the TargetArchitecture is a single architecture, set it as a required device capability
                    switch (architectures)
                    {
                    case TargetArchitecture.ARM64:
                        array.Add(new PString("arm64"));
                        break;

                    case TargetArchitecture.ARMv7:
                        array.Add(new PString("armv7"));
                        break;
                    }
                }
                else if (capabilities is PDictionary)
                {
                    var dict = (PDictionary)capabilities;

                    switch (architectures)
                    {
                    case TargetArchitecture.ARM64:
                        dict["arm64"] = new PBoolean(true);
                        dict.Remove("armv6");
                        dict.Remove("armv7");
                        break;

                    case TargetArchitecture.ARMv7:
                        dict["armv7"] = new PBoolean(true);
                        dict.Remove("armv6");
                        dict.Remove("arm64");
                        break;

                    default:
                        dict.Remove("armv6");
                        dict.Remove("armv7");
                        dict.Remove("arm64");
                        break;
                    }
                }
            }
            else
            {
                var array = new PArray();

                // If-and-only-if the TargetArchitecture is a single architecture, set it as a required device capability
                switch (architectures)
                {
                case TargetArchitecture.ARM64:
                    array.Add(new PString("arm64"));
                    break;

                case TargetArchitecture.ARMv7:
                    array.Add(new PString("armv7"));
                    break;
                }

                if (array.Count > 0)
                {
                    plist.Add(ManifestKeys.UIRequiredDeviceCapabilities, array);
                }
            }
        }

        void SetDeviceFamily(PDictionary plist)
        {
            switch (Framework)
            {
            case PlatformFramework.iOS:
                SetIOSDeviceFamily(plist);
                break;

            case PlatformFramework.WatchOS:
                plist.SetUIDeviceFamily(IPhoneDeviceType.Watch);
                break;

            case PlatformFramework.TVOS:
                plist.SetUIDeviceFamily(IPhoneDeviceType.TV);
                break;
            }
        }

        void SetIOSDeviceFamily(PDictionary plist)
        {
            if (IsWatchApp)
            {
                if (SdkIsSimulator)
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.IPhone | IPhoneDeviceType.Watch);
                }
                else
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.Watch);
                }
            }
            else
            {
                if (!IsAppExtension)
                {
                    plist.SetIfNotPresent(ManifestKeys.LSRequiresIPhoneOS, true);
                }

                if (minimumOSVersion >= IPhoneSdkVersion.V3_2 && supportedDevices == IPhoneDeviceType.NotSet)
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.IPhone);
                }
            }
        }

        void SetAppTransportSecurity(PDictionary plist)
        {
            // Debugging over http has a couple of gotchas:
            // * We can't use https, because that requires a valid server certificate,
            //   which we can't ensure.
            //   It would also require a hostname for the mac, which it might not have either.
            // * NSAppTransportSecurity/NSExceptionDomains does not allow exceptions based
            //   on IP address (only hostname).
            // * Which means the only way to make sure watchOS allows connections from
            //   the app on device to the mac is to disable App Transport Security altogether.
            // Good news: watchOS 3 will apparently not apply ATS when connecting
            // directly to IP addresses, which means we won't have to do this at all
            // (sometime in the future).

            PDictionary ats;

            if (!plist.TryGetValue(ManifestKeys.NSAppTransportSecurity, out ats))
            {
                plist.Add(ManifestKeys.NSAppTransportSecurity, ats = new PDictionary());
            }

            if (ats.GetBoolean(ManifestKeys.NSAllowsArbitraryLoads))
            {
                Log.LogMessage(MessageImportance.Low, "All http loads are already allowed.");
            }
            else
            {
                Log.LogMessage(MessageImportance.Low, "Allowed arbitrary HTTP loads to support debugging.");
                ats.SetBooleanOrRemove(ManifestKeys.NSAllowsArbitraryLoads, true);
            }
        }

        void Validation(PDictionary plist)
        {
            var supportsIPhone = (supportedDevices & IPhoneDeviceType.IPhone) != 0 ||
                                 supportedDevices == IPhoneDeviceType.NotSet;
            var supportsIPad = (supportedDevices & IPhoneDeviceType.IPad) != 0;

            // Validation...
            if (!IsAppExtension && sdkVersion >= IPhoneSdkVersion.V3_2)
            {
                IPhoneOrientation orientation;

                if (supportsIPhone)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(false);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning("Supported iPhone orientations have not been set");
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning("Supported iPhone orientations are not matched pairs");
                    }
                }

                if (supportsIPad)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(true);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning("Supported iPad orientations have not been set");
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning("Supported iPad orientations are not matched pairs");
                    }
                }
            }
        }
    }
Beispiel #9
0
        bool Compile(PDictionary plist)
        {
            var currentSDK = IPhoneSdks.GetSdk(Framework);

            if (!currentSDK.SdkIsInstalled(sdkVersion, SdkIsSimulator))
            {
                Log.LogError(null, null, null, null, 0, 0, 0, 0, "The {0} SDK for '{1}' is not installed.", Framework, sdkVersion);
                return(false);
            }

            supportedDevices = plist.GetUIDeviceFamily();

            if (!IsWatchApp)
            {
                var version = IPhoneSdks.MonoTouch.ExtendedVersion;
                // This key is our supported way of determining if an app
                // was built with Xamarin, so it needs to be present in all apps.

                var dict = new PDictionary();
                dict.Add("Version", new PString(string.Format("{0} ({1}: {2})", version.Version, version.Branch, version.Hash)));
                plist.Add("com.xamarin.ios", dict);
            }

            var sdkSettings = currentSDK.GetSdkSettings(sdkVersion, SdkIsSimulator);
            var dtSettings  = currentSDK.GetDTSettings();

            SetValue(plist, ManifestKeys.BuildMachineOSBuild, dtSettings.BuildMachineOSBuild);
            plist.SetIfNotPresent(ManifestKeys.CFBundleDevelopmentRegion, "en");

            plist.SetIfNotPresent(ManifestKeys.CFBundleExecutable, AssemblyName);
            if (IsIOS)
            {
                var executable = plist.GetCFBundleExecutable();
                if (executable.EndsWith(".app", StringComparison.Ordinal))
                {
                    LogAppManifestError("The executable (CFBundleExecutable) name ({0}) cannot end with '.app', because iOS may fail to launch the app.", executable);
                }
            }

            if (IsIOS)
            {
                if (minimumOSVersion < IPhoneSdkVersion.V5_0 && plist.GetUIMainStoryboardFile(true) != null)
                {
                    LogAppManifestError("Applications using a storyboard as the Main Interface must have a deployment target greater than 5.0");
                }

                if (!plist.ContainsKey(ManifestKeys.CFBundleName))
                {
                    plist [ManifestKeys.CFBundleName] = plist.ContainsKey(ManifestKeys.CFBundleDisplayName) ? plist.GetString(ManifestKeys.CFBundleDisplayName).Clone() : new PString(AppBundleName);
                }
            }
            else
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleName, AppBundleName);
            }

            plist.SetIfNotPresent(ManifestKeys.CFBundleIdentifier, BundleIdentifier);
            plist.SetIfNotPresent(ManifestKeys.CFBundleInfoDictionaryVersion, "6.0");
            plist.SetIfNotPresent(ManifestKeys.CFBundlePackageType, IsAppExtension ? "XPC!" : "APPL");
            if (!string.IsNullOrEmpty(ResourceRules))
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleResourceSpecification, Path.GetFileName(ResourceRules));
            }
            plist.SetIfNotPresent(ManifestKeys.CFBundleSignature, "????");
            if (!plist.ContainsKey(ManifestKeys.CFBundleSupportedPlatforms))
            {
                plist[ManifestKeys.CFBundleSupportedPlatforms] = new PArray {
                    SdkPlatform
                }
            }
            ;
            plist.SetIfNotPresent(ManifestKeys.CFBundleVersion, "1.0");
            plist.SetIfNotPresent(ManifestKeys.CFBundleShortVersionString, plist.GetCFBundleVersion());

            if (!SdkIsSimulator)
            {
                SetValue(plist, "DTCompiler", sdkSettings.DTCompiler);
                SetValue(plist, "DTPlatformBuild", dtSettings.DTPlatformBuild);
                SetValue(plist, "DTSDKBuild", sdkSettings.DTSDKBuild);
            }

            plist.SetIfNotPresent("DTPlatformName", SdkPlatform.ToLowerInvariant());
            if (!SdkIsSimulator)
            {
                SetValue(plist, "DTPlatformVersion", dtSettings.DTPlatformVersion);
            }

            var sdkName = sdkSettings.CanonicalName;

            // older sdksettings didn't have a canonicalname for sim
            if (SdkIsSimulator && string.IsNullOrEmpty(sdkName))
            {
                var deviceSdkSettings = currentSDK.GetSdkSettings(sdkVersion, false);
                sdkName = deviceSdkSettings.AlternateSDK;
            }
            SetValue(plist, "DTSDKName", sdkName);

            if (!SdkIsSimulator)
            {
                SetValue(plist, "DTXcode", AppleSdkSettings.DTXcode);
                SetValue(plist, "DTXcodeBuild", dtSettings.DTXcodeBuild);
            }

            SetDeviceFamily(plist);

            plist.SetIfNotPresent(ManifestKeys.MinimumOSVersion, minimumOSVersion.ToString());

            // Remove any Xamarin Studio specific keys
            plist.Remove(ManifestKeys.XSLaunchImageAssets);
            plist.Remove(ManifestKeys.XSAppIconAssets);

            // Merge with any partial plists generated by the Asset Catalog compiler...
            MergePartialPlistTemplates(plist);

            if (IsIOS)
            {
                Validation(plist);
            }

            CompiledAppManifest = new TaskItem(Path.Combine(AppBundleDir, "Info.plist"));
            plist.Save(CompiledAppManifest.ItemSpec, true, true);

            return(!Log.HasLoggedErrors);
        }

        void SetDeviceFamily(PDictionary plist)
        {
            switch (Framework)
            {
            case PlatformFramework.iOS:
                SetIOSDeviceFamily(plist);
                break;

            case PlatformFramework.WatchOS:
                plist.SetUIDeviceFamily(IPhoneDeviceType.Watch);
                break;

            case PlatformFramework.TVOS:
                plist.SetUIDeviceFamily(IPhoneDeviceType.TV);
                break;
            }
        }

        void SetIOSDeviceFamily(PDictionary plist)
        {
            if (IsWatchApp)
            {
                if (SdkIsSimulator)
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.IPhone | IPhoneDeviceType.Watch);
                }
                else
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.Watch);
                }
            }
            else
            {
                if (!IsAppExtension)
                {
                    plist.SetIfNotPresent(ManifestKeys.LSRequiresIPhoneOS, true);
                }

                if (minimumOSVersion >= IPhoneSdkVersion.V3_2 && supportedDevices == IPhoneDeviceType.NotSet)
                {
                    plist.SetUIDeviceFamily(IPhoneDeviceType.IPhone);
                }
            }
        }

        void Validation(PDictionary plist)
        {
            var supportsIPhone = (supportedDevices & IPhoneDeviceType.IPhone) != 0 ||
                                 supportedDevices == IPhoneDeviceType.NotSet;
            var supportsIPad = (supportedDevices & IPhoneDeviceType.IPad) != 0;

            // Validation...
            if (!IsAppExtension && sdkVersion >= IPhoneSdkVersion.V3_2)
            {
                IPhoneOrientation orientation;

                if (supportsIPhone)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(false);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning("Supported iPhone orientations have not been set");
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning("Supported iPhone orientations are not matched pairs");
                    }
                }

                if (supportsIPad)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(true);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning("Supported iPad orientations have not been set");
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning("Supported iPad orientations are not matched pairs");
                    }
                }
            }
        }
    }
		public static DTSdkSettings GetSdkSettings (IPhoneSdkVersion sdk)
		{
			DTSdkSettings settings;
			if (sdkSettingsCache.TryGetValue (sdk.ToString (), out settings))
				return settings;
			
			settings = new DTSdkSettings ();
			var doc = new PlistDocument ();
			doc.LoadFromXmlFile (GetSdkPlistFilename (sdk.ToString ()));
			var dict = (PlistDictionary) doc.Root;
			
			settings.AlternateSDK = ((PlistString)dict["AlternateSDK"]).Value;
			settings.CanonicalName = ((PlistString)dict["CanonicalName"]).Value;
			var props = (PlistDictionary) dict["DefaultProperties"];
			settings.DTCompiler = ((PlistString)props["GCC_VERSION"]).Value;
			
			var sdkPath = GetSdkPath (sdk.ToString ());
			var file = sdkPath + "/System/Library/CoreServices/SystemVersion.plist";
			settings.DTPlatformBuild = GrabRootString (file, "ProductBuildVersion");
			
			sdkSettingsCache[sdk.ToString ()] = settings;
			return settings;
		}
		static BuildResult PrepareResourceRules (IProgressMonitor monitor, IPhoneSdkVersion sdkVersion, IPhoneProjectConfiguration conf, out string resRulesFile)
		{
			resRulesFile = conf.AppDirectory.Combine ("ResourceRules.plist");
			
			monitor.BeginTask (GettextCatalog.GetString ("Preparing resources rules"), 0);
			
			if (File.Exists (resRulesFile))
				File.Delete (resRulesFile);
			
			string resRulesSrc = String.IsNullOrEmpty (conf.CodesignResourceRules)
				? "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS"
					+ sdkVersion.ToString () + ".sdk/ResourceRules.plist"
				: (string) conf.CodesignResourceRules;
			if (File.Exists (resRulesSrc)) {
				File.Copy (resRulesSrc, resRulesFile, true);
			} else {
				return BuildError ("Resources rules file \"" + conf.CodesignResourceRules + "\" not found.");
			}
			
			monitor.EndTask ();
			return null;
		}
		static BuildResult GenXcent (IProgressMonitor monitor, IPhoneSdkVersion sdkVersion, IPhoneProject proj, 
			IPhoneProjectConfiguration conf, IPhoneAppIdentity identity, out string xcentName)
		{
			xcentName = conf.CompiledOutputName.ChangeExtension (".xcent");
			
			monitor.BeginTask (GettextCatalog.GetString ("Processing entitlements file"), 0);
			
			string srcFile;
			
			if (!string.IsNullOrEmpty (conf.CodesignEntitlements)) {
				if (!File.Exists (conf.CodesignEntitlements))
					return BuildError ("Entitlements file \"" + conf.CodesignEntitlements + "\" not found.");
				srcFile = conf.CodesignEntitlements;
			} else {
				srcFile = "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS" + sdkVersion.ToString ()
					+ ".sdk/Entitlements.plist";
			}
			
			var doc = new PlistDocument ();
			try {
				doc.LoadFromXmlFile (srcFile);
			} catch (Exception ex) {
				monitor.Log.WriteLine (ex.ToString ());
				return BuildError ("Error loading entitlements source file '" + srcFile +"'.");
			}
			
			//insert the app ID into the plist at the beginning
			var oldDict = doc.Root as PlistDictionary;
			var newDict = new PlistDictionary ();
			doc.Root = newDict;
			newDict["application-identifier"] = identity.AppID;
			var keychainGroups = new PlistArray (new [] { identity.AppID } );
			newDict["keychain-access-groups"] = keychainGroups;
			
			//merge in the user's values
			foreach (var item in oldDict) {
				//FIXME: we currently ignore these items, and write our own, but maybe we should do substitutes
				//i.e. $(AppIdentifierPrefix)$(CFBundleIdentifier)
				if (item.Key == "application-identifier") {
					var str = item.Value as PlistString;
					if (str == null || string.IsNullOrEmpty (str.Value) || str.Value.Contains ('$'))
						continue;
				} else if (item.Key == "keychain-access-groups") {
					//special handling, merge into the array
					var keyArr = item.Value as PlistArray;
					foreach (var key in keyArr) {
						var str = key as PlistString;
						if (str != null && !string.IsNullOrEmpty (str.Value) && !str.Value.Contains ('$')) {
							keychainGroups.Add (str.Value);
						}
					}
					continue;
				}
				newDict[item.Key] = item.Value;
			}
			
			//merge in the settings from the provisioning profile, skipping some
			foreach (var item in identity.Profile.Entitlements)
				if (item.Key != "application-identifier" && item.Key != "keychain-access-groups")
					newDict[item.Key] = item.Value;
			
			try {
				WriteXcent (doc, xcentName);
			} catch (Exception ex) {
				monitor.Log.WriteLine (ex.ToString ());
				return BuildError ("Error writing entitlements file '" + xcentName +"'.");
			}
			
			monitor.EndTask ();
			return null;
		}