static ThemeSetting LoadThemeSetting(PDictionary dict)
        {
            string name     = null;
            var    scopes   = new List <string> ();
            var    settings = new Dictionary <string, string> ();

            PObject val;

            if (dict.TryGetValue("name", out val))
            {
                name = ((PString)val).Value;
            }
            if (dict.TryGetValue("scope", out val))
            {
                var scope = ((PString)val).Value;
                scopes.Add(scope);
            }
            if (dict.TryGetValue("settings", out val))
            {
                var settingsDictionary = val as PDictionary;
                foreach (var setting in settingsDictionary)
                {
                    settings.Add(setting.Key, ((PString)setting.Value).Value);
                }
            }

            return(new ThemeSetting(name, scopes, settings));
        }
        internal static TmSetting ReadPreferences(PDictionary dict)
        {
            string name     = null;
            var    scopes   = new List <StackMatchExpression> ();
            var    settings = new Dictionary <string, PObject> ();

            PObject val;

            if (dict.TryGetValue("name", out val))
            {
                name = ((PString)val).Value;
            }
            if (dict.TryGetValue("scope", out val))
            {
                foreach (var scope in ((PString)val).Value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    scopes.Add(StackMatchExpression.Parse(scope));
                }
            }
            if (dict.TryGetValue("settings", out val))
            {
                var settingsDictionary = val as PDictionary;
                foreach (var setting in settingsDictionary)
                {
                    settings.Add(setting.Key, setting.Value);
                }
            }

            return(new TmSetting(name, scopes, settings));
        }
Example #3
0
        public IList <IPhoneSdkVersion> GetKnownSdkVersions(PlatformName platform)
        {
            var         list = new List <IPhoneSdkVersion> ();
            var         key  = GetPlatformKey(platform);
            PDictionary knownVersions;

            if (versions.TryGetValue("KnownVersions", out knownVersions))
            {
                PArray array;

                if (knownVersions.TryGetValue(key, out array))
                {
                    foreach (var knownVersion in array.OfType <PString> ())
                    {
                        IPhoneSdkVersion version;

                        if (IPhoneSdkVersion.TryParse(knownVersion.Value, out version))
                        {
                            list.Add(version);
                        }
                    }
                }
            }

            return(list);
        }
        static string GetMinimumOSVersionLine(AdditionalText infoPlistFilePath)
        {
            PDictionary dict = PDictionary.FromFile(infoPlistFilePath.Path.ToString());

            if (dict.TryGetValue("MinimumOSVersion", out PString value) || dict.TryGetValue("LSMinimumSystemVersion", out value))
            {
                return(value.Value);
            }

            return(String.Empty); // should never reach here; placeholder value just in case
        }
Example #5
0
        public static bool GetBoolean(this PDictionary dict, string key)
        {
            PObject value;

            if (!dict.TryGetValue <PObject> (key, out value))
            {
                return(false);
            }

            var boolean = value as PBoolean;

            if (boolean != null)
            {
                return(boolean.Value);
            }

            var str = value as PString;

            if (str != null)
            {
                return(str.Value != null && string.Compare(str.Value, "yes", StringComparison.OrdinalIgnoreCase) == 0);
            }

            return(false);
        }
Example #6
0
        public static string GetConfiguredSdkLocation()
        {
            PDictionary plist;
            PString     value;
            bool        binary;

            try {
                if (File.Exists(SettingsPath))
                {
                    plist = PDictionary.FromFile(SettingsPath, out binary);
                }
                else
                {
                    plist = new PDictionary();
                }
            } catch (FileNotFoundException) {
                plist = new PDictionary();
            }

            if (!plist.TryGetValue("AppleSdkRoot", out value))
            {
                return(DefaultRoots[0]);
            }

            return(value.Value);
        }
        static bool LastModifiedChanged(PDictionary plist, DateTime mtime)
        {
            PDate lastModified;

            if (!plist.TryGetValue("LastModified", out lastModified))
            {
                return(true);
            }

            return(lastModified.Value < mtime);
        }
        static bool VersionChanged(PDictionary plist, int version)
        {
            PNumber value;

            if (!plist.TryGetValue("Version", out value))
            {
                return(true);
            }

            return(value.Value != version);
        }
Example #9
0
        static void CheckDictionary(PDictionary dict, PDictionary expected)
        {
            foreach (var kvp in expected)
            {
                PObject value;

                Assert.IsTrue(dict.TryGetValue(kvp.Key, out value), "Expected key '{0}'", kvp.Key);
                Assert.AreEqual(kvp.Value.Type, value.Type, "Type-mismatch for '{0}'", kvp.Key);

                CheckValue(value, kvp.Value);
            }
        }
Example #10
0
        static bool IsWatchExtension(PDictionary plist)
        {
            PDictionary extension;
            PString id;

            if (!plist.TryGetValue ("NSExtension", out extension))
                return false;

            if (!extension.TryGetValue ("NSExtensionPointIdentifier", out id))
                return false;

            return id.Value == "com.apple.watchkit";
        }
Example #11
0
        static bool IsMessagesExtension(PDictionary plist)
        {
            PDictionary extension;
            PString     id;

            if (!plist.TryGetValue("NSExtension", out extension))
            {
                return(false);
            }

            if (!extension.TryGetValue("NSExtensionPointIdentifier", out id))
            {
                return(false);
            }

            return(id.Value == "com.apple.message-payload-provider");
        }
Example #12
0
        static bool IsWatchExtension(PDictionary plist)
        {
            PDictionary extension;
            PString     id;

            if (!plist.TryGetValue("NSExtension", out extension))
            {
                return(false);
            }

            if (!extension.TryGetValue("NSExtensionPointIdentifier", out id))
            {
                return(false);
            }

            return(id.Value == "com.apple.watchkit");
        }
Example #13
0
        bool CheckSupportsFeature(string feature)
        {
            PArray features;

            if (!versions.TryGetValue("Features", out features))
            {
                features = CreateDefaultFeatures(Version);
                versions.Add("Features", features);
            }

            foreach (var item in features.OfType <PString> ().Select(x => x.Value))
            {
                if (feature == item)
                {
                    return(true);
                }
            }

            return(false);
        }
Example #14
0
        IEnumerable <ITaskItem> GetCompiledBundleResources(PDictionary output, string intermediateBundleDir)
        {
            var         pwd = PathUtils.ResolveSymbolicLinks(Environment.CurrentDirectory);
            PDictionary dict;
            PArray      array;

            if (output.TryGetValue(string.Format("com.apple.{0}.compilation-results", ToolName), out dict) && dict.TryGetValue("output-files", out array))
            {
                foreach (var path in array.OfType <PString> ().Select(x => x.Value))
                {
                    // don't include the generated plist files as BundleResources
                    if (path.EndsWith("partial-info.plist", StringComparison.Ordinal))
                    {
                        continue;
                    }

                    var vpath = PathUtils.AbsoluteToRelative(pwd, PathUtils.ResolveSymbolicLinks(path));
                    var item  = new TaskItem(vpath);

                    // Note: the intermediate bundle dir functions as a top-level bundle dir
                    var logicalName = PathUtils.AbsoluteToRelative(intermediateBundleDir, path);

                    if (logicalName.StartsWith("../OnDemandResources/", StringComparison.Ordinal))
                    {
                        logicalName = logicalName.Substring(3);

                        var outputPath = Path.Combine(OutputPath, logicalName);

                        item.SetMetadata("OutputPath", outputPath);
                    }

                    item.SetMetadata("LogicalName", logicalName);
                    item.SetMetadata("Optimize", "false");

                    yield return(item);
                }
            }

            yield break;
        }
Example #15
0
        public static IPhoneDeviceType GetUIDeviceFamily(this PDictionary dict, string key)
        {
            PObject value;

            if (!dict.TryGetValue(key, out value))
            {
                return(IPhoneDeviceType.NotSet);
            }

            var val = IPhoneDeviceType.NotSet;

            if (value is PArray)
            {
                foreach (var element in (PArray)value)
                {
                    var p = element as PString;
                    if (p != null)
                    {
                        val |= ParseDeviceTypeFromString(p);
                    }

                    var number = element as PNumber;
                    if (number != null)
                    {
                        val |= ParseDeviceFamilyFromNumber(number).ToDeviceType();
                    }
                }
            }
            else if (value is PNumber)
            {
                val |= ParseDeviceFamilyFromNumber((PNumber)value).ToDeviceType();
            }
            else if (value is PString)
            {
                val |= ParseDeviceTypeFromString((PString)value);
            }
            return(val);
        }
Example #16
0
        public static IPhoneDeviceCapabilities GetUIRequiredDeviceCapabilities(this PDictionary dict)
        {
            var         capabilities = IPhoneDeviceCapabilities.None;
            PDictionary dictionary;
            PObject     value;
            PArray      array;

            if (!dict.TryGetValue(ManifestKeys.UIRequiredDeviceCapabilities, out value))
            {
                return(capabilities);
            }

            if ((dictionary = value as PDictionary) != null)
            {
                foreach (var kvp in dictionary)
                {
                    var required = kvp.Value as PBoolean;

                    if (required == null || !required.Value)
                    {
                        continue;
                    }

                    capabilities |= kvp.Key.ToDeviceCapability();
                }
            }
            else if ((array = value as PArray) != null)
            {
                foreach (var capability in array.OfType <PString> ().Select(x => x.Value))
                {
                    capabilities |= capability.ToDeviceCapability();
                }
            }

            return(capabilities);
        }
        public override bool Execute()
        {
            var  manifestPath            = Path.Combine(AppBundleDir.ItemSpec, "AssetPackManifestTemplate.plist");
            var  onDemandResourcesPath   = Path.Combine(AppBundleDir.ItemSpec, "OnDemandResources.plist");
            var  onDemandResourcesDir    = Path.Combine(OutputPath, "OnDemandResources");
            var  onDemandResourcesStamp  = File.GetLastWriteTime(onDemandResourcesPath);
            var  initialInstallTags      = new HashSet <string> (AssetPackUtils.ParseTags(InitialInstallTags));
            var  prefetchOrder           = AssetPackUtils.ParseTags(PrefetchOrder);
            var  manifestStamp           = File.GetLastWriteTime(manifestPath);
            var  onDemandResources       = new PDictionary();
            var  requestTags             = new PDictionary();
            bool updateOnDemandResources = false;
            var  assetPacks     = new PDictionary();
            var  manifest       = new PDictionary();
            var  resources      = new PArray();
            bool updateManifest = false;

            Log.LogTaskName("CreateAssetPackManifest");
            Log.LogTaskProperty("AppBundleDir", AppBundleDir);
            Log.LogTaskProperty("InitialInstallTags", InitialInstallTags);
            Log.LogTaskProperty("OutputPath", OutputPath);
            Log.LogTaskProperty("PrefetchOrder", PrefetchOrder);

            if (!Directory.Exists(onDemandResourcesDir))
            {
                return(!Log.HasLoggedErrors);
            }

            onDemandResources.Add("NSBundleResourceRequestAssetPacks", assetPacks);
            onDemandResources.Add("NSBundleResourceRequestTags", requestTags);

            manifest.Add("resources", resources);

            foreach (var dir in Directory.EnumerateDirectories(onDemandResourcesDir))
            {
                var         path = Path.Combine(dir, "Info.plist");
                PDictionary info;

                if (!File.Exists(path))
                {
                    continue;
                }

                var mtime = File.GetLastWriteTime(path);

                updateOnDemandResources = updateOnDemandResources || mtime > onDemandResourcesStamp;
                updateManifest          = updateManifest || mtime > manifestStamp;

                try {
                    info = PDictionary.FromFile(path);
                } catch {
                    continue;
                }

                var  bundleIdentifier   = info.GetCFBundleIdentifier();
                var  primaryContentHash = new PDictionary();
                var  resource           = new PDictionary();
                var  items = new PArray();
                long size  = 0;

                // update OnDemandResources.plist:NSBundleResourceRequestAssetPacks
                foreach (var file in Directory.EnumerateFiles(dir))
                {
                    var name = Path.GetFileName(file);

                    if (name != "Info.plist")
                    {
                        items.Add(new PString(name));
                    }

                    size += new FileInfo(file).Length;
                }

                assetPacks.Add(bundleIdentifier, items);

                // update OnDemandResources.plist:NSBundleResourceRequestTags
                var tags     = info.GetArray("Tags").OfType <PString> ().Select(x => x.Value);
                var priority = double.NaN;

                foreach (var tag in tags)
                {
                    PDictionary dict;
                    PArray      packs;

                    if (initialInstallTags.Contains(tag))
                    {
                        priority = 1.0f;
                    }
                    else
                    {
                        for (int i = 0; i < prefetchOrder.Length; i++)
                        {
                            if (tag == prefetchOrder[i])
                            {
                                var value = GetDownloadPriority(i, prefetchOrder.Length);

                                priority = double.IsNaN(priority) ? value : Math.Max(priority, value);
                                break;
                            }
                        }
                    }

                    if (!requestTags.TryGetValue(tag, out dict))
                    {
                        dict = new PDictionary();
                        dict.Add("NSAssetPacks", new PArray());

                        requestTags.Add(tag, dict);
                    }

                    packs = dict.GetArray("NSAssetPacks");

                    packs.Add(new PString(bundleIdentifier));
                }

                // update AssetPackManifestTemplate.plist
                resource.Add("URL", new PString("http://127.0.0.1" + Path.GetFullPath(dir)));
                resource.Add("bundleKey", new PString(bundleIdentifier));

                if (!double.IsNaN(priority))
                {
                    resource.Add("downloadPriority", new PReal(priority));
                }

                resource.Add("isStreamable", new PBoolean(true));
                primaryContentHash.Add("hash", mtime.ToString("yyyy-MM-dd HH:mm:ss.000"));
                primaryContentHash.Add("strategy", "modtime");
                resource.Add("primaryContentHash", primaryContentHash);
                resource.Add("uncompressedSize", new PNumber((int)((size + 8191) & ~8191)));

                resources.Add(resource);
            }

            if (updateOnDemandResources)
            {
                try {
                    onDemandResources.Save(onDemandResourcesPath, true, true);
                } catch (Exception ex) {
                    Log.LogError("Error saving `{0}': {1}", onDemandResourcesPath, ex.Message);
                }
            }

            if (updateManifest)
            {
                try {
                    manifest.Save(manifestPath, true, true);
                } catch (Exception ex) {
                    Log.LogError("Error saving `{0}': {1}", manifestPath, ex.Message);
                }
            }

            return(!Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            Log.LogTaskName("IBTool");
            Log.LogTaskProperty("AppManifest", AppManifest);
            Log.LogTaskProperty("InterfaceDefinitions", InterfaceDefinitions);
            Log.LogTaskProperty("IntermediateOutputPath", IntermediateOutputPath);
            Log.LogTaskProperty("IsWatchApp", IsWatchApp);
            Log.LogTaskProperty("IsWatch2App", IsWatch2App);
            Log.LogTaskProperty("IsAppExtension", IsAppExtension);
            Log.LogTaskProperty("ProjectDir", ProjectDir);
            Log.LogTaskProperty("ResourcePrefix", ResourcePrefix);
            Log.LogTaskProperty("SdkBinPath", SdkBinPath);
            Log.LogTaskProperty("SdkPlatform", SdkPlatform);
            Log.LogTaskProperty("SdkRoot", SdkRoot);
            Log.LogTaskProperty("SdkVersion", SdkVersion);

            if (IsWatchApp && AppleSdkSettings.XcodeVersion < new Version(6, 2))
            {
                Log.LogError("Watch apps/extensions require Xcode 6.2 or later. The current Xcode version is {0}", AppleSdkSettings.XcodeVersion);

                return(!Log.HasLoggedErrors);
            }

            var  ibtoolManifestDir = Path.Combine(IntermediateOutputPath, ToolName + "-manifests");
            var  ibtoolOutputDir   = Path.Combine(IntermediateOutputPath, ToolName);
            var  bundleResources   = new List <ITaskItem> ();
            var  outputManifests   = new List <ITaskItem> ();
            var  compiled          = new List <ITaskItem> ();
            bool changed           = false;

            if (InterfaceDefinitions.Length > 0)
            {
                if (AppManifest != null)
                {
                    plist = PDictionary.FromFile(AppManifest.ItemSpec);
                    PString value;

                    if (!plist.TryGetValue(MinimumDeploymentTargetKey, out value) || string.IsNullOrEmpty(value.Value))
                    {
                        minimumDeploymentTarget = SdkVersion;
                    }
                    else
                    {
                        minimumDeploymentTarget = value.Value;
                    }
                }
                else
                {
                    minimumDeploymentTarget = SdkVersion;
                }

                Directory.CreateDirectory(ibtoolManifestDir);
                Directory.CreateDirectory(ibtoolOutputDir);
            }

            foreach (var item in InterfaceDefinitions)
            {
                var       bundleName = GetBundleRelativeOutputPath(item);
                var       manifest = new TaskItem(Path.Combine(ibtoolManifestDir, bundleName));
                var       manifestDir = Path.GetDirectoryName(manifest.ItemSpec);
                var       resourceTags = item.GetMetadata("ResourceTags");
                string    rpath, outputDir;
                ITaskItem output;
                int       rc;

                if (!File.Exists(item.ItemSpec))
                {
                    Log.LogError(null, null, null, item.ItemSpec, 0, 0, 0, 0, "The file '{0}' does not exist.", item.ItemSpec);
                    continue;
                }

                rpath     = Path.Combine(ibtoolOutputDir, bundleName);
                outputDir = Path.GetDirectoryName(rpath);
                output    = new TaskItem(rpath);

                output.SetMetadata("LogicalName", bundleName);
                output.SetMetadata("Optimize", "false");

                if (!string.IsNullOrEmpty(resourceTags))
                {
                    output.SetMetadata("ResourceTags", resourceTags);
                }

                compiled.Add(output);

                if (UseCompilationDirectory)
                {
                    // Note: When using --compilation-directory, we need to specify the output path as the parent directory
                    output          = new TaskItem(output);
                    output.ItemSpec = Path.GetDirectoryName(output.ItemSpec);
                    output.SetMetadata("LogicalName", Path.GetDirectoryName(bundleName));
                }

                if (!ManifestExists(manifest.ItemSpec) || File.GetLastWriteTime(manifest.ItemSpec) < File.GetLastWriteTime(item.ItemSpec))
                {
                    Directory.CreateDirectory(manifestDir);
                    Directory.CreateDirectory(outputDir);

                    if ((rc = Compile(new [] { item }, output, manifest)) != 0)
                    {
                        if (File.Exists(manifest.ItemSpec))
                        {
                            try {
                                var log = PDictionary.FromFile(manifest.ItemSpec);

                                LogWarningsAndErrors(log, item);
                            } catch {
                                Log.LogError("ibtool exited with code {0}", rc);
                            }

                            File.Delete(manifest.ItemSpec);
                        }

                        return(false);
                    }

                    changed = true;
                }
                else
                {
                    Log.LogMessage(MessageImportance.Low, "Skipping `{0}' as the output file, `{1}', is newer.", item.ItemSpec, manifest.ItemSpec);
                }

                try {
                    var dict = PDictionary.FromFile(manifest.ItemSpec);

                    LogWarningsAndErrors(dict, item);
                } catch (Exception ex) {
                    Log.LogError("Failed to load output manifest for {0}: {1}", ToolName, ex.Message);
                    if (File.Exists(manifest.ItemSpec))
                    {
                        Log.LogError("Output manifest contents: {0}", File.ReadAllText(manifest.ItemSpec));
                    }
                    continue;
                }

                if (!UseCompilationDirectory)
                {
                    bundleResources.AddRange(GetCompiledBundleResources(output));
                }

                outputManifests.Add(manifest);
            }

            if (InterfaceDefinitions.Length > 0 && UseCompilationDirectory)
            {
                var output = new TaskItem(ibtoolOutputDir);
                output.SetMetadata("LogicalName", "");

                if (!CanLinkStoryboards)
                {
                    bundleResources.AddRange(GetCompiledBundleResources(output));
                }
            }

            if (CanLinkStoryboards && compiled.Count > 0)
            {
                var linkOutputDir = Path.Combine(IntermediateOutputPath, ToolName + "-link");
                var manifest      = new TaskItem(Path.Combine(ibtoolManifestDir, "link"));
                var output        = new TaskItem(linkOutputDir);

                if (changed)
                {
                    if (Directory.Exists(output.ItemSpec))
                    {
                        Directory.Delete(output.ItemSpec, true);
                    }

                    if (File.Exists(manifest.ItemSpec))
                    {
                        File.Delete(manifest.ItemSpec);
                    }

                    Directory.CreateDirectory(Path.GetDirectoryName(manifest.ItemSpec));
                    Directory.CreateDirectory(output.ItemSpec);

                    Link = true;

                    if (Compile(compiled.ToArray(), output, manifest) != 0)
                    {
                        if (File.Exists(manifest.ItemSpec))
                        {
                            File.Delete(manifest.ItemSpec);
                        }

                        return(false);
                    }
                }

                output = new TaskItem(linkOutputDir);
                output.SetMetadata("LogicalName", "");

                bundleResources.AddRange(GetCompiledBundleResources(output));
                outputManifests.Add(manifest);
            }

            BundleResources = bundleResources.ToArray();
            OutputManifests = outputManifests.ToArray();

            Log.LogTaskProperty("BundleResources Output", BundleResources);
            Log.LogTaskProperty("OutputManifests Output", OutputManifests);

            return(!Log.HasLoggedErrors);
        }
Example #19
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");

            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);

            if (IsWatchExtension) {
                PObject capabilities;

                if (!plist.TryGetValue (ManifestKeys.UIRequiredDeviceCapabilities, out capabilities))
                    plist[ManifestKeys.UIRequiredDeviceCapabilities] = capabilities = new PArray ();
            }

            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;
        }
        protected override bool Compile(PDictionary plist)
        {
            var currentSDK = Sdks.GetAppleSdk(Platform);

            sdkVersion = AppleSdkVersion.Parse(DefaultSdkVersion);
            if (!currentSDK.SdkIsInstalled(sdkVersion, SdkIsSimulator))
            {
                Log.LogError(null, null, null, null, 0, 0, 0, 0, MSBStrings.E0013, Platform, sdkVersion);
                return(false);
            }

            supportedDevices = plist.GetUIDeviceFamily();

            if (!IsWatchApp)
            {
                var version = Sdks.XamIOS.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.GetAppleDTSettings();

            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");

            if (IsIOS)
            {
                var executable = plist.GetCFBundleExecutable();
                if (executable.EndsWith(".app", StringComparison.Ordinal))
                {
                    LogAppManifestError(MSBStrings.E0014, executable);
                }
            }

            if (!string.IsNullOrEmpty(ResourceRules))
            {
                plist.SetIfNotPresent(ManifestKeys.CFBundleResourceSpecification, Path.GetFileName(ResourceRules));
            }
            if (!plist.ContainsKey(ManifestKeys.CFBundleSupportedPlatforms))
            {
                plist[ManifestKeys.CFBundleSupportedPlatforms] = new PArray {
                    SdkPlatform
                }
            }
            ;

            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;
            }

            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);

            if (IsWatchExtension)
            {
                // Note: Only watchOS1 Extensions target Xamarin.iOS
                if (Platform == ApplePlatform.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);
                }
            }

            SetRequiredArchitectures(plist);

            if (IsIOS)
            {
                Validation(plist);
            }

            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 (Platform)
            {
            case ApplePlatform.iOS:
                SetIOSDeviceFamily(plist);
                break;

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

            case ApplePlatform.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 (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, MSBStrings.M0017);
            }
            else
            {
                Log.LogMessage(MessageImportance.Low, MSBStrings.M0018);
                ats.SetBooleanOrRemove(ManifestKeys.NSAllowsArbitraryLoads, true);
            }
        }

        void Validation(PDictionary plist)
        {
            if (!Validate)
            {
                return;
            }

            var supportsIPhone = (supportedDevices & IPhoneDeviceType.IPhone) != 0 ||
                                 supportedDevices == IPhoneDeviceType.NotSet;
            var supportsIPad = (supportedDevices & IPhoneDeviceType.IPad) != 0;

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

                if (supportsIPhone)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(false);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning(MSBStrings.W0019);
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning(MSBStrings.W0020);
                    }
                }

                if (supportsIPad)
                {
                    orientation = plist.GetUISupportedInterfaceOrientations(true);
                    if (orientation == IPhoneOrientation.None)
                    {
                        LogAppManifestWarning(MSBStrings.W0021);
                    }
                    else if (!orientation.IsValidPair())
                    {
                        LogAppManifestWarning(MSBStrings.W0022);
                    }
                }
            }
        }
    }
Example #21
0
        public override bool Execute()
        {
            var manifestPath = Path.Combine (AppBundleDir.ItemSpec, "AssetPackManifestTemplate.plist");
            var onDemandResourcesPath = Path.Combine (AppBundleDir.ItemSpec, "OnDemandResources.plist");
            var onDemandResourcesDir = Path.Combine (OutputPath, "OnDemandResources");
            var onDemandResourcesStamp = File.GetLastWriteTime (onDemandResourcesPath);
            var initialInstallTags = new HashSet<string> (AssetPackUtils.ParseTags (InitialInstallTags));
            var prefetchOrder = AssetPackUtils.ParseTags (PrefetchOrder);
            var manifestStamp = File.GetLastWriteTime (manifestPath);
            var onDemandResources = new PDictionary ();
            var requestTags = new PDictionary ();
            bool updateOnDemandResources = false;
            var assetPacks = new PDictionary ();
            var manifest = new PDictionary ();
            var resources = new PArray ();
            bool updateManifest = false;

            Log.LogTaskName ("CreateAssetPackManifest");
            Log.LogTaskProperty ("AppBundleDir", AppBundleDir);
            Log.LogTaskProperty ("InitialInstallTags", InitialInstallTags);
            Log.LogTaskProperty ("OutputPath", OutputPath);
            Log.LogTaskProperty ("PrefetchOrder", PrefetchOrder);

            if (!Directory.Exists (onDemandResourcesDir))
                return !Log.HasLoggedErrors;

            onDemandResources.Add ("NSBundleResourceRequestAssetPacks", assetPacks);
            onDemandResources.Add ("NSBundleResourceRequestTags", requestTags);

            manifest.Add ("resources", resources);

            foreach (var dir in Directory.EnumerateDirectories (onDemandResourcesDir)) {
                var path = Path.Combine (dir, "Info.plist");
                PDictionary info;

                if (!File.Exists (path))
                    continue;

                var mtime = File.GetLastWriteTime (path);

                updateOnDemandResources = updateOnDemandResources || mtime > onDemandResourcesStamp;
                updateManifest = updateManifest || mtime > manifestStamp;

                try {
                    info = PDictionary.FromFile (path);
                } catch {
                    continue;
                }

                var bundleIdentifier = info.GetCFBundleIdentifier ();
                var primaryContentHash = new PDictionary ();
                var resource = new PDictionary ();
                var items = new PArray ();
                long size = 0;

                // update OnDemandResources.plist:NSBundleResourceRequestAssetPacks
                foreach (var file in Directory.EnumerateFiles (dir)) {
                    var name = Path.GetFileName (file);

                    if (name != "Info.plist")
                        items.Add (new PString (name));

                    size += new FileInfo (file).Length;
                }

                assetPacks.Add (bundleIdentifier, items);

                // update OnDemandResources.plist:NSBundleResourceRequestTags
                var tags = info.GetArray ("Tags").OfType<PString> ().Select (x => x.Value);
                var priority = double.NaN;

                foreach (var tag in tags) {
                    PDictionary dict;
                    PArray packs;

                    if (initialInstallTags.Contains (tag)) {
                        priority = 1.0f;
                    } else {
                        for (int i = 0; i < prefetchOrder.Length; i++) {
                            if (tag == prefetchOrder[i]) {
                                var value = GetDownloadPriority (i, prefetchOrder.Length);

                                priority = double.IsNaN (priority) ? value : Math.Max (priority, value);
                                break;
                            }
                        }
                    }

                    if (!requestTags.TryGetValue (tag, out dict)) {
                        dict = new PDictionary ();
                        dict.Add ("NSAssetPacks", new PArray ());

                        requestTags.Add (tag, dict);
                    }

                    packs = dict.GetArray ("NSAssetPacks");

                    packs.Add (new PString (bundleIdentifier));
                }

                // update AssetPackManifestTemplate.plist
                resource.Add ("URL", new PString ("http://127.0.0.1" + Path.GetFullPath (dir)));
                resource.Add ("bundleKey", new PString (bundleIdentifier));

                if (!double.IsNaN (priority))
                    resource.Add ("downloadPriority", new PReal (priority));

                resource.Add ("isStreamable", new PBoolean (true));
                primaryContentHash.Add ("hash", mtime.ToString ("yyyy-MM-dd HH:mm:ss.000"));
                primaryContentHash.Add ("strategy", "modtime");
                resource.Add ("primaryContentHash", primaryContentHash);
                resource.Add ("uncompressedSize", new PNumber ((int) ((size + 8191) & ~8191)));

                resources.Add (resource);
            }

            if (updateOnDemandResources) {
                try {
                    onDemandResources.Save (onDemandResourcesPath, true, true);
                } catch (Exception ex) {
                    Log.LogError ("Error saving `{0}': {1}", onDemandResourcesPath, ex.Message);
                }
            }

            if (updateManifest) {
                try {
                    manifest.Save (manifestPath, true, true);
                } catch (Exception ex) {
                    Log.LogError ("Error saving `{0}': {1}", manifestPath, ex.Message);
                }
            }

            return !Log.HasLoggedErrors;
        }
Example #22
0
        static bool IsWatchAppExtension(ITaskItem appex, PDictionary plist, out string watchAppBundleDir)
        {
            PString expectedBundleIdentifier, bundleIdentifier, extensionPoint;
            PDictionary extension, attributes;

            watchAppBundleDir = null;

            if (!plist.TryGetValue ("NSExtension", out extension))
                return false;

            if (!extension.TryGetValue ("NSExtensionPointIdentifier", out extensionPoint))
                return false;

            if (extensionPoint.Value != "com.apple.watchkit")
                return false;

            // Okay, we've found the WatchKit App Extension...
            if (!extension.TryGetValue ("NSExtensionAttributes", out attributes))
                return false;

            if (!attributes.TryGetValue ("WKAppBundleIdentifier", out expectedBundleIdentifier))
                return false;

            var pwd = PathUtils.ResolveSymbolicLink (Environment.CurrentDirectory);

            // Scan the *.app subdirectories to find the WatchApp bundle...
            foreach (var bundle in Directory.GetDirectories (appex.ItemSpec, "*.app")) {
                if (!File.Exists (Path.Combine (bundle, "Info.plist")))
                    continue;

                plist = PDictionary.FromFile (Path.Combine (bundle, "Info.plist"));

                if (!plist.TryGetValue ("CFBundleIdentifier", out bundleIdentifier))
                    continue;

                if (bundleIdentifier.Value != expectedBundleIdentifier.Value)
                    continue;

                watchAppBundleDir = PathUtils.AbsoluteToRelative (pwd, PathUtils.ResolveSymbolicLink (bundle));

                return true;
            }

            return false;
        }
Example #23
0
        protected void LogWarningsAndErrors(PDictionary plist, ITaskItem file)
        {
            PDictionary dictionary;
            PString message;
            PArray array;

            if (plist.TryGetValue (string.Format ("com.apple.{0}.document.notices", ToolName), out array)) {
                foreach (var item in array.OfType<PDictionary> ()) {
                    if (item.TryGetValue ("message", out message))
                        Log.LogMessage (MessageImportance.Low, "{0}", message.Value);
                }
            }

            if (plist.TryGetValue (string.Format ("com.apple.{0}.document.warnings", ToolName), out array)) {
                foreach (var item in array.OfType<PDictionary> ()) {
                    if (item.TryGetValue ("message", out message))
                        Log.LogWarning (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                }
            }

            if (plist.TryGetValue (string.Format ("com.apple.{0}.document.errors", ToolName), out array)) {
                foreach (var item in array.OfType<PDictionary> ()) {
                    if (item.TryGetValue ("message", out message))
                        Log.LogError (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                }
            }

            //Trying to parse document warnings and erros using a PDictionary first since it's what ibtool is returning when building a storyboard.
            if (plist.TryGetValue (string.Format ("com.apple.{0}.document.notices", ToolName), out dictionary)) {
                foreach (var valuePair in dictionary) {
                    array = valuePair.Value as PArray;
                    foreach (var item in array.OfType<PDictionary> ()) {
                        if (item.TryGetValue ("message", out message))
                            Log.LogMessage (MessageImportance.Low, "{0}", message.Value);
                    }
                }
            }

            if (plist.TryGetValue (string.Format ("com.apple.{0}.document.warnings", ToolName), out dictionary)) {
                foreach (var valuePair in dictionary) {
                    array = valuePair.Value as PArray;
                    foreach (var item in array.OfType<PDictionary> ()) {
                        if (item.TryGetValue ("message", out message))
                            Log.LogWarning (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                    }
                }
            }

            if (plist.TryGetValue (string.Format ("com.apple.{0}.document.errors", ToolName), out dictionary)) {
                foreach (var valuePair in dictionary) {
                    array = valuePair.Value as PArray;
                    foreach (var item in array.OfType<PDictionary> ()) {
                        if (item.TryGetValue ("message", out message))
                            Log.LogError (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                    }
                }
            }

            if (plist.TryGetValue (string.Format ("com.apple.{0}.errors", ToolName), out array)) {
                foreach (var item in array.OfType<PDictionary> ()) {
                    if (item.TryGetValue ("description", out message))
                        Log.LogError (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                }
            }

            if (plist.TryGetValue (string.Format ("com.apple.{0}.notices", ToolName), out array)) {
                foreach (var item in array.OfType<PDictionary> ()) {
                    if (item.TryGetValue ("description", out message))
                        Log.LogError (ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                }
            }
        }
        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");

            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);

            if (IsWatchExtension)
            {
                PObject capabilities;

                if (!plist.TryGetValue(ManifestKeys.UIRequiredDeviceCapabilities, out capabilities))
                {
                    plist[ManifestKeys.UIRequiredDeviceCapabilities] = capabilities = new PArray();
                }
            }

            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");
                    }
                }
            }
        }
    }
Example #25
0
        void Load(PDictionary doc)
        {
            PArray prefixes;

            if (doc.TryGetValue("ApplicationIdentifierPrefix", out prefixes) && prefixes != null)
            {
                ApplicationIdentifierPrefix = GetStrings(prefixes);
            }

            if (doc.TryGetValue("TeamIdentifier", out prefixes) && prefixes != null)
            {
                TeamIdentifierPrefix = GetStrings(prefixes);
            }

            PDate creationDate;

            if (doc.TryGetValue("CreationDate", out creationDate) && creationDate != null)
            {
                CreationDate = creationDate.Value;
            }

            PArray devCerts;

            if (doc.TryGetValue("DeveloperCertificates", out devCerts) && devCerts != null)
            {
                DeveloperCertificates = GetCertificates(devCerts);
            }

            PDictionary entitlements;

            if (doc.TryGetValue("Entitlements", out entitlements) && entitlements != null)
            {
                // Note: we clone the entitlements so that the runtime can garbage collect the 'doc' dictionary
                // (which may be rather huge if it contains a massive list of developer certs, for example)
                Entitlements = (PDictionary)entitlements.Clone();
            }

            PDate expirationDate;

            if (doc.TryGetValue("ExpirationDate", out expirationDate) && expirationDate != null)
            {
                ExpirationDate = expirationDate.Value;
            }

            PString name;

            if (doc.TryGetValue("Name", out name) && name != null)
            {
                Name = name.Value;
            }

            PArray array;

            if (doc.TryGetValue("Platform", out array) && array != null)
            {
                var platforms = new List <MobileProvisionPlatform> ();

                foreach (var platform in array.OfType <PString> ())
                {
                    switch (platform)
                    {
                    case "OSX": platforms.Add(MobileProvisionPlatform.MacOS); break;

                    case "tvOS": platforms.Add(MobileProvisionPlatform.tvOS); break;

                    case "iOS": platforms.Add(MobileProvisionPlatform.iOS); break;
                    }
                }

                Platforms = platforms.ToArray();
            }

            PArray provDevs;

            if (doc.TryGetValue("ProvisionedDevices", out provDevs) && provDevs != null)
            {
                ProvisionedDevices = GetStrings(provDevs);
            }

            PBoolean provisionsAllDevices;

            if (doc.TryGetValue("ProvisionsAllDevices", out provisionsAllDevices) && provisionsAllDevices != null)
            {
                ProvisionsAllDevices = provisionsAllDevices.Value;
            }

            PNumber ttl;

            if (doc.TryGetValue("TimeToLive", out ttl) && ttl != null)
            {
                TimeToLive = ttl.Value;
            }

            PString uuid;

            if (doc.TryGetValue("UUID", out uuid) && uuid != null)
            {
                Uuid = uuid.Value;
            }

            PNumber version;

            if (doc.TryGetValue("Version", out version) && version != null)
            {
                Version = version.Value;
            }
        }
        static bool IsWatchAppExtension(ITaskItem appex, PDictionary plist, out string watchAppBundleDir)
        {
            PString     expectedBundleIdentifier, bundleIdentifier, extensionPoint;
            PDictionary extension, attributes;

            watchAppBundleDir = null;

            if (!plist.TryGetValue("NSExtension", out extension))
            {
                return(false);
            }

            if (!extension.TryGetValue("NSExtensionPointIdentifier", out extensionPoint))
            {
                return(false);
            }

            if (extensionPoint.Value != "com.apple.watchkit")
            {
                return(false);
            }

            // Okay, we've found the WatchKit App Extension...
            if (!extension.TryGetValue("NSExtensionAttributes", out attributes))
            {
                return(false);
            }

            if (!attributes.TryGetValue("WKAppBundleIdentifier", out expectedBundleIdentifier))
            {
                return(false);
            }

            var pwd = PathUtils.ResolveSymbolicLink(Environment.CurrentDirectory);

            // Scan the *.app subdirectories to find the WatchApp bundle...
            foreach (var bundle in Directory.GetDirectories(appex.ItemSpec, "*.app"))
            {
                if (!File.Exists(Path.Combine(bundle, "Info.plist")))
                {
                    continue;
                }

                plist = PDictionary.FromFile(Path.Combine(bundle, "Info.plist"));

                if (!plist.TryGetValue("CFBundleIdentifier", out bundleIdentifier))
                {
                    continue;
                }

                if (bundleIdentifier.Value != expectedBundleIdentifier.Value)
                {
                    continue;
                }

                watchAppBundleDir = PathUtils.AbsoluteToRelative(pwd, PathUtils.ResolveSymbolicLink(bundle));

                return(true);
            }

            return(false);
        }
        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");
                    }
                }
            }
        }
    }
Example #28
0
        protected virtual PDictionary GetCompiledEntitlements(MobileProvision profile, PDictionary template)
        {
            var entitlements = new PDictionary();

            if (profile != null && MergeProfileEntitlements)
            {
                // start off with the settings from the provisioning profile
                foreach (var item in profile.Entitlements)
                {
                    if (!AllowedProvisioningKeys.Contains(item.Key))
                    {
                        continue;
                    }

                    var value = item.Value;

                    if (item.Key == "com.apple.developer.icloud-container-environment")
                    {
                        value = new PString("Development");
                    }
                    else if (value is PDictionary)
                    {
                        value = MergeEntitlementDictionary((PDictionary)value, profile);
                    }
                    else if (value is PString)
                    {
                        value = MergeEntitlementString((PString)value, profile, item.Key == ApplicationIdentifierKey);
                    }
                    else if (value is PArray)
                    {
                        value = MergeEntitlementArray((PArray)value, profile);
                    }
                    else
                    {
                        value = value.Clone();
                    }

                    if (value != null)
                    {
                        entitlements.Add(item.Key, value);
                    }
                }
            }

            // merge in the user's values
            foreach (var item in template)
            {
                var value = item.Value;

                if (item.Key == "com.apple.developer.ubiquity-container-identifiers" ||
                    item.Key == "com.apple.developer.icloud-container-identifiers" ||
                    item.Key == "com.apple.developer.icloud-container-environment" ||
                    item.Key == "com.apple.developer.icloud-services")
                {
                    if (profile == null)
                    {
                        Log.LogWarning(null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0110, item.Key);
                    }
                    else if (!profile.Entitlements.ContainsKey(item.Key))
                    {
                        Log.LogWarning(null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0111, item.Key);
                    }
                }
                else if (item.Key == ApplicationIdentifierKey)
                {
                    var str = value as PString;

                    // Ignore ONLY if it is empty, otherwise take the user's value
                    if (str == null || string.IsNullOrEmpty(str.Value))
                    {
                        continue;
                    }
                }

                if (value is PDictionary)
                {
                    value = MergeEntitlementDictionary((PDictionary)value, profile);
                }
                else if (value is PString)
                {
                    value = MergeEntitlementString((PString)value, profile, item.Key == ApplicationIdentifierKey);
                }
                else if (value is PArray)
                {
                    value = MergeEntitlementArray((PArray)value, profile);
                }
                else
                {
                    value = value.Clone();
                }

                if (value != null)
                {
                    entitlements[item.Key] = value;
                }
            }

            switch (Platform)
            {
            case ApplePlatform.MacOSX:
            case ApplePlatform.MacCatalyst:
                if (Debug && entitlements.TryGetValue("com.apple.security.app-sandbox", out PBoolean sandbox) && sandbox.Value)
                {
                    entitlements ["com.apple.security.network.client"] = new PBoolean(true);
                }
                break;
            }

            return(entitlements);
        }
        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");
                    }
                }
            }
        }
    }
Example #30
0
        protected override void AppendCommandLineArguments(IDictionary <string, string> environment, ProcessArgumentBuilder args, ITaskItem[] items)
        {
            string minimumDeploymentTarget;

            if (plist != null)
            {
                PString value;

                if (!plist.TryGetValue(MinimumDeploymentTargetKey, out value) || string.IsNullOrEmpty(value.Value))
                {
                    minimumDeploymentTarget = SdkVersion;
                }
                else
                {
                    minimumDeploymentTarget = value.Value;
                }

                var assetDirs = new HashSet <string> (items.Select(x => BundleResource.GetVirtualProjectPath(ProjectDir, x, !string.IsNullOrEmpty(SessionId))));

                if (plist.TryGetValue(ManifestKeys.XSAppIconAssets, out value) && !string.IsNullOrEmpty(value.Value))
                {
                    int    index    = value.Value.IndexOf(".xcassets" + Path.DirectorySeparatorChar, StringComparison.Ordinal);
                    string assetDir = null;
                    var    rpath    = value.Value;

                    if (index != -1)
                    {
                        assetDir = rpath.Substring(0, index + ".xcassets".Length);
                    }

                    if (assetDirs != null && assetDirs.Contains(assetDir))
                    {
                        var assetName = Path.GetFileNameWithoutExtension(rpath);

                        if (PartialAppManifest == null)
                        {
                            args.Add("--output-partial-info-plist");
                            args.AddQuoted(partialAppManifest.GetMetadata("FullPath"));

                            PartialAppManifest = partialAppManifest;
                        }

                        args.Add("--app-icon");
                        args.AddQuoted(assetName);

                        if (IsMessagesExtension(plist))
                        {
                            args.Add("--product-type com.apple.product-type.app-extension.messages");
                        }
                    }
                }

                if (plist.TryGetValue(ManifestKeys.XSLaunchImageAssets, out value) && !string.IsNullOrEmpty(value.Value))
                {
                    int    index    = value.Value.IndexOf(".xcassets" + Path.DirectorySeparatorChar, StringComparison.Ordinal);
                    string assetDir = null;
                    var    rpath    = value.Value;

                    if (index != -1)
                    {
                        assetDir = rpath.Substring(0, index + ".xcassets".Length);
                    }

                    if (assetDirs != null && assetDirs.Contains(assetDir))
                    {
                        var assetName = Path.GetFileNameWithoutExtension(rpath);

                        if (PartialAppManifest == null)
                        {
                            args.Add("--output-partial-info-plist");
                            args.AddQuoted(partialAppManifest.GetMetadata("FullPath"));

                            PartialAppManifest = partialAppManifest;
                        }

                        args.Add("--launch-image");
                        args.AddQuoted(assetName);
                    }
                }

                if (plist.TryGetValue(ManifestKeys.CLKComplicationGroup, out value) && !string.IsNullOrEmpty(value.Value))
                {
                    args.Add("--complication", value);
                }
            }
            else
            {
                minimumDeploymentTarget = SdkVersion;
            }

            if (OptimizePNGs)
            {
                args.Add("--compress-pngs");
            }

            if (AppleSdkSettings.XcodeVersion.Major >= 7)
            {
                if (!string.IsNullOrEmpty(outputSpecs))
                {
                    args.Add("--enable-on-demand-resources", EnableOnDemandResources ? "YES" : "NO");
                }

                if (!string.IsNullOrEmpty(DeviceModel))
                {
                    args.Add("--filter-for-device-model", DeviceModel);
                }

                if (!string.IsNullOrEmpty(DeviceOSVersion))
                {
                    args.Add("--filter-for-device-os-version", DeviceOSVersion);
                }

                if (!string.IsNullOrEmpty(outputSpecs))
                {
                    args.Add("--asset-pack-output-specifications");
                    args.AddQuoted(Path.GetFullPath(outputSpecs));
                }
            }

            if (plist != null)
            {
                foreach (var targetDevice in GetTargetDevices(plist))
                {
                    args.Add("--target-device", targetDevice);
                }
            }

            args.Add("--minimum-deployment-target", minimumDeploymentTarget);

            switch (SdkPlatform)
            {
            case "iPhoneSimulator":
                args.Add("--platform", IsWatchApp ? "watchsimulator" : "iphonesimulator");
                break;

            case "iPhoneOS":
                args.Add("--platform", IsWatchApp ? "watchos" : "iphoneos");
                break;

            case "MacOSX":
                args.Add("--platform", "macosx");
                break;

            case "WatchSimulator":
                args.Add("--platform", "watchsimulator");
                break;

            case "WatchOS":
                args.Add("--platform", "watchos");
                break;

            case "AppleTVSimulator":
                args.Add("--platform", "appletvsimulator");
                break;

            case "AppleTVOS":
                args.Add("--platform", "appletvos");
                break;
            }
        }
Example #31
0
        public override bool Execute()
        {
            if (IsWatchApp && AppleSdkSettings.XcodeVersion < new Version(6, 2))
            {
                Log.LogError("Watch apps/extensions require Xcode 6.2 or later. The current Xcode version is {0}", AppleSdkSettings.XcodeVersion);

                return(!Log.HasLoggedErrors);
            }

            var  ibtoolManifestDir = Path.Combine(IntermediateOutputPath, "ibtool-manifests");
            var  ibtoolOutputDir   = Path.Combine(IntermediateOutputPath, "ibtool");
            var  outputManifests   = new List <ITaskItem> ();
            var  compiled          = new List <ITaskItem> ();
            bool changed;

            if (InterfaceDefinitions.Length > 0)
            {
                if (AppManifest != null)
                {
                    plist = PDictionary.FromFile(AppManifest.ItemSpec);
                    PString value;

                    if (!plist.TryGetValue(MinimumDeploymentTargetKey, out value) || string.IsNullOrEmpty(value.Value))
                    {
                        minimumDeploymentTarget = SdkVersion;
                    }
                    else
                    {
                        minimumDeploymentTarget = value.Value;
                    }
                }
                else
                {
                    minimumDeploymentTarget = SdkVersion;
                }

                Directory.CreateDirectory(ibtoolManifestDir);
                Directory.CreateDirectory(ibtoolOutputDir);

                if (!CompileInterfaceDefinitions(ibtoolManifestDir, ibtoolOutputDir, compiled, outputManifests, out changed))
                {
                    return(false);
                }

                if (CanLinkStoryboards)
                {
                    var storyboards = new List <ITaskItem> ();
                    var linked      = new List <ITaskItem> ();
                    var unique      = new HashSet <string> ();

                    for (int i = 0; i < compiled.Count; i++)
                    {
                        // pretend that non-storyboardc items (e.g. *.nib) are already 'linked'
                        if (compiled[i].ItemSpec.EndsWith(".storyboardc", StringComparison.Ordinal))
                        {
                            var interfaceDefinition = compiled[i].GetMetadata("InterfaceDefinition");
                            unique.Add(interfaceDefinition);
                            storyboards.Add(compiled[i]);
                            continue;
                        }

                        // just pretend any *nib's have already been 'linked'...
                        compiled[i].RemoveMetadata("InterfaceDefinition");
                        linked.Add(compiled[i]);
                    }

                    // only link the storyboards if there are multiple unique storyboards
                    if (unique.Count > 1)
                    {
                        var linkOutputDir = Path.Combine(IntermediateOutputPath, "ibtool-link");

                        if (!LinkStoryboards(ibtoolManifestDir, linkOutputDir, storyboards, linked, outputManifests, changed))
                        {
                            return(false);
                        }

                        compiled = linked;
                    }
                }
                else
                {
                    for (int i = 0; i < compiled.Count; i++)
                    {
                        compiled[i].RemoveMetadata("InterfaceDefinition");
                    }
                }
            }

            var bundleResources = new List <ITaskItem> ();

            foreach (var compiledItem in compiled)
            {
                if (Directory.Exists(compiledItem.ItemSpec))
                {
                    bundleResources.AddRange(GetBundleResources(compiledItem));
                }
                else if (File.Exists(compiledItem.ItemSpec))
                {
                    bundleResources.Add(compiledItem);
                }
            }

            BundleResources = bundleResources.ToArray();
            OutputManifests = outputManifests.ToArray();

            Log.LogTaskProperty("BundleResources Output", BundleResources);
            Log.LogTaskProperty("OutputManifests Output", OutputManifests);

            return(!Log.HasLoggedErrors);
        }
Example #32
0
        protected override void AppendCommandLineArguments(IDictionary <string, string> environment, CommandLineArgumentBuilder args, ITaskItem[] items)
        {
            if (plist != null)
            {
                PString value;

                var assetDirs = new HashSet <string> (items.Select(x => BundleResource.GetVirtualProjectPath(ProjectDir, x, !string.IsNullOrEmpty(SessionId))));

                if (plist.TryGetValue(ManifestKeys.XSAppIconAssets, out value) && !string.IsNullOrEmpty(value.Value))
                {
                    int    index    = value.Value.IndexOf(".xcassets" + Path.DirectorySeparatorChar, StringComparison.Ordinal);
                    string assetDir = null;
                    var    rpath    = value.Value;

                    if (index != -1)
                    {
                        assetDir = rpath.Substring(0, index + ".xcassets".Length);
                    }

                    if (assetDirs != null && assetDirs.Contains(assetDir))
                    {
                        var assetName = Path.GetFileNameWithoutExtension(rpath);

                        if (PartialAppManifest == null)
                        {
                            args.Add("--output-partial-info-plist");
                            args.AddQuoted(partialAppManifest.GetMetadata("FullPath"));

                            PartialAppManifest = partialAppManifest;
                        }

                        args.Add("--app-icon");
                        args.AddQuoted(assetName);

                        if (IsMessagesExtension(plist))
                        {
                            args.Add("--product-type com.apple.product-type.app-extension.messages");
                        }
                    }
                }

                if (plist.TryGetValue(ManifestKeys.XSLaunchImageAssets, out value) && !string.IsNullOrEmpty(value.Value))
                {
                    int    index    = value.Value.IndexOf(".xcassets" + Path.DirectorySeparatorChar, StringComparison.Ordinal);
                    string assetDir = null;
                    var    rpath    = value.Value;

                    if (index != -1)
                    {
                        assetDir = rpath.Substring(0, index + ".xcassets".Length);
                    }

                    if (assetDirs != null && assetDirs.Contains(assetDir))
                    {
                        var assetName = Path.GetFileNameWithoutExtension(rpath);

                        if (PartialAppManifest == null)
                        {
                            args.Add("--output-partial-info-plist");
                            args.AddQuoted(partialAppManifest.GetMetadata("FullPath"));

                            PartialAppManifest = partialAppManifest;
                        }

                        args.Add("--launch-image");
                        args.AddQuoted(assetName);
                    }
                }

                if (plist.TryGetValue(ManifestKeys.CLKComplicationGroup, out value) && !string.IsNullOrEmpty(value.Value))
                {
                    args.Add("--complication", value);
                }
            }

            if (OptimizePNGs)
            {
                args.Add("--compress-pngs");
            }

            if (AppleSdkSettings.XcodeVersion.Major >= 7)
            {
                if (!string.IsNullOrEmpty(outputSpecs))
                {
                    args.Add("--enable-on-demand-resources", EnableOnDemandResources ? "YES" : "NO");
                }

                if (!string.IsNullOrEmpty(DeviceModel))
                {
                    args.Add("--filter-for-device-model", DeviceModel);
                }

                if (!string.IsNullOrEmpty(DeviceOSVersion))
                {
                    args.Add("--filter-for-device-os-version", DeviceOSVersion);
                }

                if (!string.IsNullOrEmpty(outputSpecs))
                {
                    args.Add("--asset-pack-output-specifications");
                    args.AddQuoted(Path.GetFullPath(outputSpecs));
                }
            }

            if (Platform == ApplePlatform.MacCatalyst)
            {
                args.Add("--ui-framework-family");
                args.Add("uikit");
            }

            if (plist != null)
            {
                foreach (var targetDevice in GetTargetDevices(plist))
                {
                    args.Add("--target-device", targetDevice);
                }
            }

            args.Add("--minimum-deployment-target", MinimumOSVersion);

            var platform = PlatformUtils.GetTargetPlatform(SdkPlatform, IsWatchApp);

            if (platform != null)
            {
                args.Add("--platform", platform);
            }
        }
Example #33
0
        IEnumerable<ITaskItem> GetCompiledBundleResources(PDictionary output, string intermediateBundleDir)
        {
            var pwd = PathUtils.ResolveSymbolicLink (Environment.CurrentDirectory);
            PDictionary dict;
            PArray array;

            if (output.TryGetValue (string.Format ("com.apple.{0}.compilation-results", ToolName), out dict) && dict.TryGetValue ("output-files", out array)) {
                foreach (var path in array.OfType<PString> ().Select (x => x.Value)) {
                    // don't include the generated plist files as BundleResources
                    if (path.EndsWith ("partial-info.plist", StringComparison.Ordinal))
                        continue;

                    var vpath = PathUtils.AbsoluteToRelative (pwd, PathUtils.ResolveSymbolicLink (path));
                    var item = new TaskItem (vpath);

                    // Note: the intermediate bundle dir functions as a top-level bundle dir
                    var logicalName = PathUtils.AbsoluteToRelative (intermediateBundleDir, path);

                    if (logicalName.StartsWith ("../OnDemandResources/", StringComparison.Ordinal)) {
                        logicalName = logicalName.Substring (3);

                        var outputPath = Path.Combine (OutputPath, logicalName);

                        item.SetMetadata ("OutputPath", outputPath);
                    }

                    item.SetMetadata ("LogicalName", logicalName);
                    item.SetMetadata ("Optimize", "false");

                    yield return item;
                }
            }

            yield break;
        }
Example #34
0
        protected void LogWarningsAndErrors(PDictionary plist, ITaskItem file)
        {
            PDictionary dictionary;
            PString     message;
            PArray      array;

            if (plist.TryGetValue(string.Format("com.apple.{0}.document.notices", ToolName), out array))
            {
                foreach (var item in array.OfType <PDictionary> ())
                {
                    if (item.TryGetValue("message", out message))
                    {
                        Log.LogMessage(MessageImportance.Low, "{0}", message.Value);
                    }
                }
            }

            if (plist.TryGetValue(string.Format("com.apple.{0}.document.warnings", ToolName), out array))
            {
                foreach (var item in array.OfType <PDictionary> ())
                {
                    if (item.TryGetValue("message", out message))
                    {
                        Log.LogWarning(ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                    }
                }
            }

            if (plist.TryGetValue(string.Format("com.apple.{0}.document.errors", ToolName), out array))
            {
                foreach (var item in array.OfType <PDictionary> ())
                {
                    if (item.TryGetValue("message", out message))
                    {
                        Log.LogError(ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                    }
                }
            }

            //Trying to parse document warnings and erros using a PDictionary first since it's what ibtool is returning when building a storyboard.
            if (plist.TryGetValue(string.Format("com.apple.{0}.document.notices", ToolName), out dictionary))
            {
                foreach (var valuePair in dictionary)
                {
                    array = valuePair.Value as PArray;
                    foreach (var item in array.OfType <PDictionary> ())
                    {
                        if (item.TryGetValue("message", out message))
                        {
                            Log.LogMessage(MessageImportance.Low, "{0}", message.Value);
                        }
                    }
                }
            }

            if (plist.TryGetValue(string.Format("com.apple.{0}.document.warnings", ToolName), out dictionary))
            {
                foreach (var valuePair in dictionary)
                {
                    array = valuePair.Value as PArray;
                    foreach (var item in array.OfType <PDictionary> ())
                    {
                        if (item.TryGetValue("message", out message))
                        {
                            Log.LogWarning(ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                        }
                    }
                }
            }

            if (plist.TryGetValue(string.Format("com.apple.{0}.document.errors", ToolName), out dictionary))
            {
                foreach (var valuePair in dictionary)
                {
                    array = valuePair.Value as PArray;
                    foreach (var item in array.OfType <PDictionary> ())
                    {
                        if (item.TryGetValue("message", out message))
                        {
                            Log.LogError(ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                        }
                    }
                }
            }

            if (plist.TryGetValue(string.Format("com.apple.{0}.errors", ToolName), out array))
            {
                foreach (var item in array.OfType <PDictionary> ())
                {
                    if (item.TryGetValue("description", out message))
                    {
                        Log.LogError(ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                    }
                }
            }

            if (plist.TryGetValue(string.Format("com.apple.{0}.notices", ToolName), out array))
            {
                foreach (var item in array.OfType <PDictionary> ())
                {
                    if (item.TryGetValue("description", out message))
                    {
                        Log.LogError(ToolName, null, null, file.ItemSpec, 0, 0, 0, 0, "{0}", message.Value);
                    }
                }
            }
        }
Example #35
0
        public override bool Execute()
        {
            Log.LogTaskName ("IBTool");
            Log.LogTaskProperty ("AppManifest", AppManifest);
            Log.LogTaskProperty ("InterfaceDefinitions", InterfaceDefinitions);
            Log.LogTaskProperty ("IntermediateOutputPath", IntermediateOutputPath);
            Log.LogTaskProperty ("IsWatchApp", IsWatchApp);
            Log.LogTaskProperty ("IsWatch2App", IsWatch2App);
            Log.LogTaskProperty ("ProjectDir", ProjectDir);
            Log.LogTaskProperty ("ResourcePrefix", ResourcePrefix);
            Log.LogTaskProperty ("SdkBinPath", SdkBinPath);
            Log.LogTaskProperty ("SdkPlatform", SdkPlatform);
            Log.LogTaskProperty ("SdkRoot", SdkRoot);
            Log.LogTaskProperty ("SdkVersion", SdkVersion);

            if (IsWatchApp && AppleSdkSettings.XcodeVersion < new Version (6, 2)) {
                Log.LogError ("Watch apps/extensions require Xcode 6.2 or later. The current Xcode version is {0}", AppleSdkSettings.XcodeVersion);

                return !Log.HasLoggedErrors;
            }

            var ibtoolManifestDir = Path.Combine (IntermediateOutputPath, ToolName + "-manifests");
            var ibtoolOutputDir = Path.Combine (IntermediateOutputPath, ToolName);
            var linkOutputDir = Path.Combine (IntermediateOutputPath, ToolName + "-link");
            var bundleResources = new List<ITaskItem> ();
            var outputManifests = new List<ITaskItem> ();

            if (InterfaceDefinitions.Length > 0) {
                if (AppManifest != null) {
                    plist = PDictionary.FromFile (AppManifest.ItemSpec);
                    PString value;

                    if (!plist.TryGetValue (MinimumDeploymentTargetKey, out value) || string.IsNullOrEmpty (value.Value))
                        minimumDeploymentTarget = SdkVersion;
                    else
                        minimumDeploymentTarget = value.Value;
                } else {
                    minimumDeploymentTarget = SdkVersion;
                }

                Directory.CreateDirectory (ibtoolManifestDir);
                Directory.CreateDirectory (ibtoolOutputDir);
            }

            foreach (var item in InterfaceDefinitions) {
                var bundleName = GetBundleRelativeOutputPath (item);
                var manifest = new TaskItem (Path.Combine (ibtoolManifestDir, bundleName));
                var manifestDir = Path.GetDirectoryName (manifest.ItemSpec);
                var resourceTags = item.GetMetadata ("ResourceTags");
                string rpath, outputDir;
                ITaskItem output;
                int rc;

                if (!File.Exists (item.ItemSpec)) {
                    Log.LogError (null, null, null, item.ItemSpec, 0, 0, 0, 0, "The file '{0}' does not exist.", item.ItemSpec);
                    continue;
                }

                if (UseCompilationDirectory) {
                    rpath = Path.Combine (ibtoolOutputDir, Path.GetDirectoryName (bundleName));
                    output = new TaskItem (rpath);
                    outputDir = rpath;

                    output.SetMetadata ("LogicalName", Path.GetDirectoryName (bundleName));
                } else {
                    rpath = Path.Combine (ibtoolOutputDir, bundleName);
                    outputDir = Path.GetDirectoryName (rpath);
                    output = new TaskItem (rpath);

                    output.SetMetadata ("LogicalName", bundleName);
                }

                output.SetMetadata ("Optimize", "false");

                if (!string.IsNullOrEmpty (resourceTags))
                    output.SetMetadata ("ResourceTags", resourceTags);

                if (!ManifestExists (manifest.ItemSpec) || File.GetLastWriteTime (manifest.ItemSpec) < File.GetLastWriteTime (item.ItemSpec)) {
                    Directory.CreateDirectory (manifestDir);
                    Directory.CreateDirectory (outputDir);

                    if ((rc = Compile (new [] { item }, output, manifest)) != 0) {
                        if (File.Exists (manifest.ItemSpec)) {
                            try {
                                var log = PDictionary.FromFile (manifest.ItemSpec);

                                LogWarningsAndErrors (log, item);
                            } catch {
                                Log.LogError ("ibtool exited with code {0}", rc);
                            }

                            File.Delete (manifest.ItemSpec);
                        }

                        return false;
                    }
                } else {
                    Log.LogMessage (MessageImportance.Low, "Skipping `{0}' as the output file, `{1}', is newer.", item.ItemSpec, manifest.ItemSpec);
                }

                try {
                    var dict = PDictionary.FromFile (manifest.ItemSpec);

                    LogWarningsAndErrors (dict, item);
                } catch (Exception ex) {
                    Log.LogError ("Failed to load output manifest for {0}: {1}", ToolName, ex.Message);
                    if (File.Exists (manifest.ItemSpec))
                        Log.LogError ("Output manifest contents: {0}", File.ReadAllText (manifest.ItemSpec));
                    continue;
                }

                if (!UseCompilationDirectory)
                    bundleResources.AddRange (GetCompiledBundleResources (output));

                outputManifests.Add (manifest);
            }

            if (InterfaceDefinitions.Length > 0 && UseCompilationDirectory) {
                var output = new TaskItem (ibtoolOutputDir);
                output.SetMetadata ("LogicalName", "");

                bundleResources.AddRange (GetCompiledBundleResources (output));
            }

            if (IsWatch2App) {
                Link = true;
                if (InterfaceDefinitions.Length > 0) {
                    var linkItems = new List<ITaskItem> ();
                    foreach (var item in InterfaceDefinitions) {
                        var linkInput = new TaskItem (item);
                        linkInput.ItemSpec = Path.Combine (ibtoolOutputDir, Path.GetFileName (item.ItemSpec) + "c");
                        linkItems.Add (linkInput);
                    }

                    var output = new TaskItem (linkOutputDir);
                    var manifest = new TaskItem (Path.Combine (ibtoolManifestDir, "link"));

                    Directory.CreateDirectory (Path.GetDirectoryName (manifest.ItemSpec));
                    Directory.CreateDirectory (output.ItemSpec);

                    if (Compile (linkItems.ToArray (), output, manifest) != 0) {
                        if (File.Exists (manifest.ItemSpec))
                            File.Delete (manifest.ItemSpec);

                        return false;
                    }

                    output = new TaskItem (linkOutputDir);
                    output.SetMetadata ("LogicalName", "");
                    bundleResources.AddRange (GetCompiledBundleResources (output));

                    outputManifests.Add (manifest);
                }
            }

            BundleResources = bundleResources.ToArray ();
            OutputManifests = outputManifests.ToArray ();

            Log.LogTaskProperty ("BundleResources Output", BundleResources);
            Log.LogTaskProperty ("OutputManifests Output", OutputManifests);

            return !Log.HasLoggedErrors;
        }