Example #1
0
        static void Init()
        {
            string devroot = null, vplist = null, xcode = null;
            bool   foundSdk = false;

            SetInvalid();

            DeveloperRoot = Environment.GetEnvironmentVariable("MD_APPLE_SDK_ROOT");
            if (string.IsNullOrEmpty(DeveloperRoot))
            {
                DeveloperRoot = GetConfiguredSdkLocation();

                if (string.IsNullOrEmpty(DeveloperRoot) && File.Exists("/usr/bin/xcode-select"))
                {
                    var startInfo = new ProcessStartInfo("/usr/bin/xcode-select", "--print-path");
                    startInfo.RedirectStandardOutput = true;
                    startInfo.UseShellExecute        = false;

                    var process = new Process();
                    var stdout  = string.Empty;

                    try {
                        process.StartInfo           = startInfo;
                        process.OutputDataReceived += (sender, e) => stdout += e.Data;
                        process.Start();
                        process.WaitForExit();
                    } catch (Win32Exception) {
                        stdout = string.Empty;
                    }

                    stdout = stdout.Trim();

                    if (!string.IsNullOrEmpty(stdout) && Directory.Exists(stdout))
                    {
                        if (stdout.EndsWith("/Contents/Developer", StringComparison.Ordinal))
                        {
                            stdout = stdout.Substring(0, stdout.Length - "/Contents/Developer".Length);
                        }

                        DeveloperRoot = stdout;
                    }
                }
            }

            if (string.IsNullOrEmpty(DeveloperRoot))
            {
                foreach (var v in DefaultRoots)
                {
                    if (ValidateSdkLocation(v, out xcode, out vplist, out devroot))
                    {
                        foundSdk = true;
                        break;
                    }

                    LoggingService.LogInfo("A valid Xcode installation was not found at '{0}'", v);
                }
            }
            else if (!ValidateSdkLocation(DeveloperRoot, out xcode, out vplist, out devroot))
            {
                LoggingService.LogError("A valid Xcode installation was not found at the configured location: '{0}'", DeveloperRoot);
                SetInvalid();
                return;
            }
            else
            {
                foundSdk = true;
            }

            if (foundSdk)
            {
                XcodePath                 = xcode;
                DeveloperRoot             = devroot;
                DeveloperRootVersionPlist = vplist;
            }
            else
            {
                SetInvalid();
                return;
            }

            try {
                var plist = Path.Combine(XcodePath, "Contents", "Info.plist");

                if (!File.Exists(plist))
                {
                    SetInvalid();
                    return;
                }

                lastWritten = File.GetLastWriteTime(plist);

                XcodeVersion  = new Version(3, 2, 6);
                XcodeRevision = "0";

                // DTXCode was introduced after xcode 3.2.6 so it may not exist
                var dict = PDictionary.FromFile(plist);

                PString value;
                if (dict.TryGetValue("DTXcode", out value))
                {
                    DTXcode = value.Value;
                }

                if (dict.TryGetValue("CFBundleShortVersionString", out value))
                {
                    XcodeVersion = Version.Parse(value.Value);
                }

                if (dict.TryGetValue("CFBundleVersion", out value))
                {
                    XcodeRevision = value.Value;
                }

                LoggingService.LogInfo("Found Xcode, version {0} ({1}).", XcodeVersion, XcodeRevision);
                AnalyticsService.ReportContextProperty("XS.Core.SDK.Xcode.Version", XcodeVersion.ToString());
                IsValid = true;
            } catch (Exception ex) {
                LoggingService.LogError("Error loading Xcode information for prefix '" + DeveloperRoot + "'", ex);
                SetInvalid();
            }
        }
        public override bool Execute()
        {
            Log.LogTaskName("Archive");
            Log.LogTaskProperty("AppBundleDir", AppBundleDir);
            Log.LogTaskProperty("AppExtensionReferences", AppExtensionReferences);
            Log.LogTaskProperty("InsightsApiKey", InsightsApiKey);
            Log.LogTaskProperty("ITunesSourceFiles", ITunesSourceFiles);
            Log.LogTaskProperty("OutputPath", OutputPath);
            Log.LogTaskProperty("ProjectName", ProjectName);
            Log.LogTaskProperty("SigningKey", SigningKey);
            Log.LogTaskProperty("SolutionPath", SolutionPath);
            Log.LogTaskProperty("WatchAppReferences", WatchAppReferences);

            var archiveDir = CreateArchiveDirectory();

            try {
                var plist       = PDictionary.FromFile(Path.Combine(AppBundleDir.ItemSpec, "Info.plist"));
                var productsDir = Path.Combine(archiveDir, "Products");

                // Archive the OnDemandResources...
                var resourcesDestDir = Path.Combine(productsDir, "OnDemandResources");
                var resourcesSrcDir  = Path.Combine(OutputPath, "OnDemandResources");

                if (Directory.Exists(resourcesSrcDir))
                {
                    Ditto(resourcesSrcDir, resourcesDestDir);
                }

                // Archive the Applications...
                var appDestDir = Path.Combine(productsDir, "Applications", Path.GetFileName(AppBundleDir.ItemSpec));
                Ditto(AppBundleDir.ItemSpec, appDestDir);

                // Archive the dSYMs...
                if (Directory.Exists(DSYMDir))
                {
                    var destDir = Path.Combine(archiveDir, "dSYMs", Path.GetFileName(DSYMDir));
                    Ditto(DSYMDir, destDir);
                }

                // Archive the mSYMs...
                if (Directory.Exists(MSYMDir))
                {
                    var destDir = Path.Combine(archiveDir, "mSYMs", Path.GetFileName(MSYMDir));
                    Ditto(MSYMDir, destDir);
                }

                // Archive the Bitcode symbol maps
                var bcSymbolMaps = Directory.GetFiles(Path.GetDirectoryName(DSYMDir), "*.bcsymbolmap");
                if (bcSymbolMaps.Length > 0)
                {
                    var bcSymbolMapsDir = Path.Combine(archiveDir, "BCSymbolMaps");

                    Directory.CreateDirectory(bcSymbolMapsDir);

                    for (int i = 0; i < bcSymbolMaps.Length; i++)
                    {
                        File.Copy(bcSymbolMaps[i], Path.Combine(bcSymbolMapsDir, Path.GetFileName(bcSymbolMaps[i])));
                    }
                }

                if (AppExtensionReferences != null)
                {
                    // Archive the dSYMs, mSYMs, etc for each of the referenced App Extensions as well...
                    for (int i = 0; i < AppExtensionReferences.Length; i++)
                    {
                        ArchiveAppExtension(AppExtensionReferences[i], archiveDir);
                    }
                }

                if (WatchAppReferences != null)
                {
                    // Archive the dSYMs, mSYMs, etc for each of the referenced WatchOS2 Apps as well...
                    for (int i = 0; i < WatchAppReferences.Length; i++)
                    {
                        ArchiveWatchApp(WatchAppReferences[i], archiveDir);
                    }
                }

                if (ITunesSourceFiles != null)
                {
                    // Archive the iTunesMetadata.plist and iTunesArtwork files...
                    var iTunesMetadataDir = Path.Combine(archiveDir, "iTunesMetadata", Path.GetFileName(AppBundleDir.ItemSpec));
                    for (int i = 0; i < ITunesSourceFiles.Length; i++)
                    {
                        var archivedMetaFile = Path.Combine(iTunesMetadataDir, Path.GetFileName(ITunesSourceFiles[i].ItemSpec));

                        Directory.CreateDirectory(iTunesMetadataDir);
                        File.Copy(ITunesSourceFiles[i].ItemSpec, archivedMetaFile, true);
                    }
                }

                // Generate an archive Info.plist
                var arInfo = new PDictionary();
                // FIXME: figure out this value
                //arInfo.Add ("AppStoreFileSize", new PNumber (65535));

                var props = new PDictionary();
                props.Add("ApplicationPath", new PString(string.Format("Applications/{0}", Path.GetFileName(AppBundleDir.ItemSpec))));
                props.Add("CFBundleIdentifier", new PString(plist.GetCFBundleIdentifier()));
                if (plist.GetCFBundleShortVersionString() != null)
                {
                    props.Add("CFBundleShortVersionString", new PString(plist.GetCFBundleShortVersionString()));
                }
                else if (plist.GetCFBundleVersion() != null)
                {
                    props.Add("CFBundleShortVersionString", new PString(plist.GetCFBundleVersion()));
                }

                var iconFiles = plist.GetCFBundleIconFiles();
                var iconDict  = plist.GetCFBundleIcons();
                var icons     = new PArray();

                if (iconFiles != null)
                {
                    AddIconPaths(icons, iconFiles, Path.Combine(archiveDir, "Products"));
                }

                if (iconDict != null)
                {
                    var primary = iconDict.Get <PDictionary> (ManifestKeys.CFBundlePrimaryIcon);
                    if (primary != null && (iconFiles = primary.GetCFBundleIconFiles()) != null)
                    {
                        AddIconPaths(icons, iconFiles, Path.Combine(archiveDir, "Products"));
                    }
                }

                if (icons.Count > 0)
                {
                    props.Add("IconPaths", icons);
                }

                props.Add("SigningIdentity", new PString(SigningKey));

                arInfo.Add("ApplicationProperties", props);
                arInfo.Add("ArchiveVersion", new PNumber(2));
                arInfo.Add("CreationDate", new PDate(Now.ToUniversalTime()));
                arInfo.Add("Name", new PString(plist.GetCFBundleName() ?? plist.GetCFBundleDisplayName()));
                arInfo.Add("SchemeName", new PString(ProjectName));

                if (!string.IsNullOrEmpty(SolutionPath))
                {
                    arInfo.Add("SolutionName", new PString(Path.GetFileNameWithoutExtension(SolutionPath)));
                    arInfo.Add("SolutionPath", new PString(SolutionPath));
                }

                if (!string.IsNullOrEmpty(InsightsApiKey))
                {
                    arInfo.Add("InsightsApiKey", new PString(InsightsApiKey));
                }

                arInfo.Save(Path.Combine(archiveDir, "Info.plist"));

                ArchiveDir = archiveDir;
            } catch (Exception ex) {
                Log.LogErrorFromException(ex);
                Directory.Delete(archiveDir, true);
            }

            return(!Log.HasLoggedErrors);
        }
Example #3
0
        public override bool Execute()
        {
            Log.LogTaskName("CompileEntitlements");
            Log.LogTaskProperty("AppBundleDir", AppBundleDir);
            Log.LogTaskProperty("AppIdentifier", AppIdentifier);
            Log.LogTaskProperty("BundleIdentifier", BundleIdentifier);
            Log.LogTaskProperty("CompiledEntitlements", CompiledEntitlements);
            Log.LogTaskProperty("Entitlements", Entitlements);
            Log.LogTaskProperty("IsAppExtension", IsAppExtension);
            Log.LogTaskProperty("ProvisioningProfile", ProvisioningProfile);
            Log.LogTaskProperty("SdkVersion", SdkVersion);

            MobileProvision profile;
            PDictionary     template;
            PDictionary     compiled;
            PDictionary     archived;
            string          path;

            if (!string.IsNullOrEmpty(ProvisioningProfile))
            {
                if ((profile = GetMobileProvision(Platform, ProvisioningProfile)) == null)
                {
                    Log.LogError("Could not locate the provisioning profile with a UUID of {0}.", ProvisioningProfile);
                    return(false);
                }
            }
            else if (Platform == MobileProvisionPlatform.iOS)
            {
                Log.LogError("Provisioning Profiles are REQUIRED for iOS.");
                return(false);
            }
            else
            {
                profile = null;
            }

            if (!string.IsNullOrEmpty(Entitlements))
            {
                if (!File.Exists(Entitlements))
                {
                    Log.LogError("Entitlements.plist template '{0}' not found.", Entitlements);
                    return(false);
                }

                path = Entitlements;
            }
            else
            {
                path = DefaultEntitlementsPath;
            }

            try {
                template = PDictionary.FromFile(path);
            } catch (Exception ex) {
                Log.LogError("Error loading Entitlements.plist template '{0}': {1}", path, ex.Message);
                return(false);
            }

            compiled = GetCompiledEntitlements(profile, template);
            archived = GetArchivedExpandedEntitlements(template, compiled);

            try {
                Directory.CreateDirectory(Path.GetDirectoryName(CompiledEntitlements));
                WriteXcent(compiled, CompiledEntitlements);
            } catch (Exception ex) {
                Log.LogError("Error writing xcent file '{0}': {1}", CompiledEntitlements, ex.Message);
                return(false);
            }

            try {
                archived.Save(Path.Combine(EntitlementBundlePath, "archived-expanded-entitlements.xcent"), true);
            } catch (Exception ex) {
                Log.LogError("Error writing archived-expanded-entitlements.xcent file: {0}", ex.Message);
                return(false);
            }

            return(!Log.HasLoggedErrors);
        }
Example #4
0
        IEnumerable <ITaskItem> GetCodesignedFiles(ITaskItem item)
        {
            var codesignedFiles = new List <ITaskItem> ();

            if (Directory.Exists(item.ItemSpec))
            {
                var codeSignaturePath = Path.Combine(item.ItemSpec, CodeSignatureDirName);

                if (!Directory.Exists(codeSignaturePath))
                {
                    return(codesignedFiles);
                }

                codesignedFiles.AddRange(Directory.EnumerateFiles(codeSignaturePath).Select(x => new TaskItem(x)));

                var extension = Path.GetExtension(item.ItemSpec);

                if (extension == ".app" || extension == ".appex")
                {
                    var executableName = Path.GetFileName(item.ItemSpec);
                    var manifestPath   = Path.Combine(item.ItemSpec, "Info.plist");

                    if (File.Exists(manifestPath))
                    {
                        var bundleExecutable = PDictionary.FromFile(manifestPath).GetCFBundleExecutable();

                        if (!string.IsNullOrEmpty(bundleExecutable))
                        {
                            executableName = bundleExecutable;
                        }
                    }

                    var basePath = item.ItemSpec;

                    if (Directory.Exists(Path.Combine(basePath, MacOSDirName)))
                    {
                        basePath = Path.Combine(basePath, MacOSDirName);
                    }

                    var executablePath = Path.Combine(basePath, executableName);

                    if (File.Exists(executablePath))
                    {
                        codesignedFiles.Add(new TaskItem(executablePath));
                    }
                }
            }
            else if (File.Exists(item.ItemSpec))
            {
                codesignedFiles.Add(item);

                var dirName = Path.GetDirectoryName(item.ItemSpec);

                if (Path.GetExtension(dirName) == ".framework")
                {
                    codesignedFiles.AddRange(Directory.EnumerateFiles(Path.Combine(dirName, CodeSignatureDirName)).Select(x => new TaskItem(x)));
                }
            }

            return(codesignedFiles);
        }
Example #5
0
        protected override string GenerateCommandLineCommands()
        {
            var  args       = new CommandLineArgumentBuilder();
            var  actualArgs = new CommandLineArgumentBuilder();
            bool msym;

            args.AddLine("/verbose");

            if (Debug)
            {
                args.AddLine("/debug");
            }

            if (!string.IsNullOrEmpty(OutputPath))
            {
                args.AddQuotedLine("/output:" + Path.GetFullPath(OutputPath));
            }

            if (!string.IsNullOrEmpty(ApplicationName))
            {
                args.AddQuotedLine("/name:" + ApplicationName);
            }

            if (TargetFrameworkIdentifier == "Xamarin.Mac")
            {
                args.AddLine("/profile:Xamarin.Mac,Version=v2.0,Profile=Mobile");
            }
            else if (UseXamMacFullFramework)
            {
                args.AddLine($"/profile:Xamarin.Mac,Version={TargetFrameworkVersion},Profile=Full");
            }
            else
            {
                args.AddLine($"/profile:Xamarin.Mac,Version={TargetFrameworkVersion},Profile=System");
            }

            XamMacArch arch;

            if (!Enum.TryParse(Architecture, true, out arch))
            {
                arch = XamMacArch.Default;
            }

            if (arch == XamMacArch.Default)
            {
                arch = XamMacArch.x86_64;
            }

            if (arch.HasFlag(XamMacArch.i386))
            {
                args.AddLine("/arch:i386");
            }

            if (arch.HasFlag(XamMacArch.x86_64))
            {
                args.AddLine("/arch:x86_64");
            }

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

            args.AddLine(string.Format("--http-message-handler={0}", HttpClientHandler));

            if (AppManifest != null)
            {
                try {
                    var plist = PDictionary.FromFile(AppManifest.ItemSpec);

                    PString v;
                    string  minimumDeploymentTarget;

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

                    args.AddLine(string.Format("/minos={0}", minimumDeploymentTarget));
                }
                catch (Exception ex) {
                    Log.LogWarning(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "Error loading '{0}': {1}", AppManifest.ItemSpec, ex.Message);
                }
            }

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

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

            switch ((LinkMode ?? string.Empty).ToLower())
            {
            case "full":
                break;

            case "sdkonly":
                args.AddLine("/linksdkonly");
                break;

            case "platform":
                args.AddLine("/linkplatform");
                break;

            default:
                args.AddLine("/nolink");
                break;
            }

            if (!string.IsNullOrEmpty(AotMode) && AotMode != "None")
            {
                var aot = $"--aot:{AotMode.ToLower ()}";
                if (HybridAOT)
                {
                    aot += "|hybrid";
                }

                if (!string.IsNullOrEmpty(ExplicitAotAssemblies))
                {
                    aot += $",{ExplicitAotAssemblies}";
                }

                args.AddLine(aot);
            }

            if (!string.IsNullOrEmpty(I18n))
            {
                args.AddQuotedLine("/i18n:" + I18n);
            }

            if (ExplicitReferences != null)
            {
                foreach (var asm in ExplicitReferences)
                {
                    args.AddQuotedLine("/assembly:" + Path.GetFullPath(asm.ItemSpec));
                }
            }

            if (!string.IsNullOrEmpty(ApplicationAssembly.ItemSpec))
            {
                args.AddQuotedLine("/root-assembly:" + Path.GetFullPath(ApplicationAssembly.ItemSpec));
            }

            if (!string.IsNullOrWhiteSpace(ExtraArguments))
            {
                actualArgs.Add(ExtraArguments);
            }

            if (NativeReferences != null)
            {
                foreach (var nr in NativeReferences)
                {
                    args.AddQuotedLine("/native-reference:" + Path.GetFullPath(nr.ItemSpec));
                }
            }

            if (IsAppExtension)
            {
                args.AddQuotedLine("/extension");
            }

            args.AddQuotedLine("/sdkroot:" + SdkRoot);

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

                args.AddQuotedLine("--cache:" + Path.GetFullPath(IntermediateOutputPath));
            }

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

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

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

            // Use only the response file
            actualArgs.AddQuoted($"@{responseFile}");

            return(actualArgs.ToString());
        }
Example #6
0
        void ValidateAppExtension(string path, string mainBundleIdentifier, string mainShortVersionString, string mainVersion)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError(7003, path, MSBStrings.E7003, name);
                return;
            }

            var plist = PDictionary.FromFile(info);

            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError(7004, info, MSBStrings.E7004, name);
                return;
            }

            // The filename of the extension path is the extension's bundle identifier, which turns out ugly
            // in error messages. Try to get something more friendly-looking.
            name = plist.GetCFBundleDisplayName() ?? name;

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError(7005, info, MSBStrings.E7005, name);
            }

            if (!bundleIdentifier.StartsWith(mainBundleIdentifier + ".", StringComparison.Ordinal))
            {
                Log.LogError(7006, info, MSBStrings.E7006, name, bundleIdentifier, mainBundleIdentifier);
            }

            if (bundleIdentifier.EndsWith(".key", StringComparison.Ordinal))
            {
                Log.LogError(7007, info, MSBStrings.E7007, name, bundleIdentifier);
            }

            var shortVersionString = plist.GetCFBundleShortVersionString();

            if (string.IsNullOrEmpty(shortVersionString))
            {
                Log.LogError(7008, info, MSBStrings.E7008, name);
            }

            if (shortVersionString != mainShortVersionString)
            {
                Log.LogWarning(MSBStrings.W0071, name, shortVersionString, mainShortVersionString);
            }

            var version = plist.GetCFBundleVersion();

            if (string.IsNullOrEmpty(version))
            {
                Log.LogWarning(MSBStrings.W0072, name);
            }

            if (version != mainVersion)
            {
                Log.LogWarning(MSBStrings.W0073, name, version, mainVersion);
            }

            var extension = plist.Get <PDictionary> ("NSExtension");

            if (extension == null)
            {
                Log.LogError(7009, info, MSBStrings.E7009, name);
                return;
            }

            var extensionPointIdentifier = extension.GetString("NSExtensionPointIdentifier").Value;

            if (string.IsNullOrEmpty(extensionPointIdentifier))
            {
                Log.LogError(7010, info, MSBStrings.E7010, name);
                return;
            }

            // https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/SystemExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW9
            switch (extensionPointIdentifier)
            {
            case "com.apple.ui-services":                                    // iOS+OSX
            case "com.apple.services":                                       // iOS
            case "com.apple.keyboard-service":                               // iOS
            case "com.apple.fileprovider-ui":                                // iOS
            case "com.apple.fileprovider-nonui":                             // iOS
            case "com.apple.FinderSync":                                     // OSX
            case "com.apple.photo-editing":                                  // iOS
            case "com.apple.share-services":                                 // iOS+OSX
            case "com.apple.widget-extension":                               // iOS+OSX
            case "com.apple.Safari.content-blocker":                         // iOS
            case "com.apple.Safari.sharedlinks-service":                     // iOS
            case "com.apple.spotlight.index":                                // iOS
            case "com.apple.AudioUnit":                                      // iOS
            case "com.apple.AudioUnit-UI":                                   // iOS
            case "com.apple.tv-services":                                    // tvOS
            case "com.apple.broadcast-services":                             // iOS+tvOS
            case "com.apple.callkit.call-directory":                         // iOS
            case "com.apple.message-payload-provider":                       // iOS
            case "com.apple.intents-service":                                // iOS
            case "com.apple.intents-ui-service":                             // iOS
            case "com.apple.usernotifications.content-extension":            // iOS
            case "com.apple.usernotifications.service":                      // iOS
            case "com.apple.networkextension.packet-tunnel":                 // iOS+OSX
            case "com.apple.authentication-services-credential-provider-ui": // iOS
                break;

            case "com.apple.watchkit":             // iOS8.2
                Log.LogError(7069, info, MSBStrings.E7069 /* Xamarin.iOS 14+ does not support watchOS 1 apps. Please migrate your project to watchOS 2+. */);
                break;

            default:
                Log.LogWarning(MSBStrings.W0073, name, extensionPointIdentifier);
                break;
            }
        }
Example #7
0
        protected int Compile(ITaskItem[] items, string output, ITaskItem manifest)
        {
            var environment = new Dictionary <string, string> ();
            var args        = new CommandLineArgumentBuilder();

            if (!string.IsNullOrEmpty(SdkBinPath))
            {
                environment.Add("PATH", SdkBinPath);
            }

            if (!string.IsNullOrEmpty(SdkUsrPath))
            {
                environment.Add("XCODE_DEVELOPER_USR_PATH", SdkUsrPath);
            }

            args.Add("--errors", "--warnings", "--notices");
            args.Add("--output-format", "xml1");

            AppendCommandLineArguments(environment, args, items);

            if (Link)
            {
                args.Add("--link");
            }
            else if (UseCompilationDirectory)
            {
                args.Add("--compilation-directory");
            }
            else
            {
                args.Add("--compile");
            }

            args.AddQuoted(Path.GetFullPath(output));

            foreach (var item in items)
            {
                args.AddQuoted(item.GetMetadata("FullPath"));
            }

            var startInfo = GetProcessStartInfo(environment, GetFullPathToTool(), args.ToString());
            var errors    = new StringBuilder();
            int exitCode;

            try {
                Log.LogMessage(MessageImportance.Normal, "Tool {0} execution started with arguments: {1}", startInfo.FileName, startInfo.Arguments);

                using (var stdout = File.CreateText(manifest.ItemSpec)) {
                    using (var stderr = new StringWriter(errors)) {
                        using (var process = ProcessUtils.StartProcess(startInfo, stdout, stderr)) {
                            process.Wait();

                            exitCode = process.Result;
                        }
                    }

                    Log.LogMessage(MessageImportance.Low, "Tool {0} execution finished (exit code = {1}).", startInfo.FileName, exitCode);
                }
            } catch (Exception ex) {
                Log.LogError("Error executing tool '{0}': {1}", startInfo.FileName, ex.Message);
                File.Delete(manifest.ItemSpec);
                return(-1);
            }

            if (exitCode != 0)
            {
                // Note: ibtool or actool exited with an error. Dump everything we can to help the user
                // diagnose the issue and then delete the manifest log file so that rebuilding tries
                // again (in case of ibtool's infamous spurious errors).
                if (errors.Length > 0)
                {
                    Log.LogError(null, null, null, items[0].ItemSpec, 0, 0, 0, 0, "{0}", errors);
                }

                Log.LogError("{0} exited with code {1}", ToolName, exitCode);

                // Note: If the log file exists and is parseable, log those warnings/errors as well...
                if (File.Exists(manifest.ItemSpec))
                {
                    try {
                        var plist = PDictionary.FromFile(manifest.ItemSpec);

                        LogWarningsAndErrors(plist, items[0]);
                    } catch (Exception ex) {
                        Log.LogError("Failed to load {0} log file `{1}`: {2}", ToolName, manifest.ItemSpec, ex.Message);
                    }

                    File.Delete(manifest.ItemSpec);
                }
            }

            return(exitCode);
        }
Example #8
0
        void ValidateWatchExtension(string path, string watchAppBundleIdentifier, string mainShortVersionString, string mainVersion)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError("The Watch Extension '{0}' does not contain an Info.plist", name);
                return;
            }

            var plist = PDictionary.FromFile(info);

            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError("The Watch Extension '{0}' does not specify a CFBundleIdentifier.", name);
                return;
            }

            // The filename of the extension path is the extension's bundle identifier, which turns out ugly
            // in error messages. Try to get something more friendly-looking.
            name = plist.GetCFBundleDisplayName() ?? name;

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError("The Watch Extension '{0}' does not specify a CFBundleExecutable", name);
            }

            if (!bundleIdentifier.StartsWith(watchAppBundleIdentifier + ".", StringComparison.Ordinal))
            {
                Log.LogError("The Watch Extension '{0}' has an invalid CFBundleIdentifier ({1}), it does not begin with the main app bundle's CFBundleIdentifier ({2}).", name, bundleIdentifier, watchAppBundleIdentifier);
            }

            if (bundleIdentifier.EndsWith(".key", StringComparison.Ordinal))
            {
                Log.LogError("The Watch Extension '{0}' has a CFBundleIdentifier ({1}) that ends with the illegal suffix \".key\".", name, bundleIdentifier);
            }

            var shortVersionString = plist.GetCFBundleShortVersionString();

            if (string.IsNullOrEmpty(shortVersionString))
            {
                Log.LogWarning("The Watch Extension '{0}' does not specify a CFBundleShortVersionString", name);
            }

            if (shortVersionString != mainShortVersionString)
            {
                Log.LogWarning("The Watch Extension '{0}' has a CFBundleShortVersionString ({1}) that does not match the main app bundle's CFBundleShortVersionString ({2})", name, shortVersionString, mainShortVersionString);
            }

            var version = plist.GetCFBundleVersion();

            if (string.IsNullOrEmpty(version))
            {
                Log.LogWarning("The Watch Extension '{0}' does not specify a CFBundleVersion", name);
            }

            if (version != mainVersion)
            {
                Log.LogWarning("The Watch Extension '{0}' has a CFBundleVersion ({1}) that does not match the main app bundle's CFBundleVersion ({2})", name, version, mainVersion);
            }

            var extension = plist.Get <PDictionary> ("NSExtension");

            if (extension == null)
            {
                Log.LogError("The Watch Extension '{0}' has an invalid Info.plist: it does not contain an NSExtension dictionary.", name);
                return;
            }

            var extensionPointIdentifier = extension.Get <PString> ("NSExtensionPointIdentifier");

            if (extensionPointIdentifier != null)
            {
                if (extensionPointIdentifier.Value != "com.apple.watchkit")
                {
                    Log.LogError("The Watch Extension '{0}' has an invalid Info.plist: the NSExtensionPointIdentifier must be \"com.apple.watchkit\".", name);
                }
            }
            else
            {
                Log.LogError("The Watch Extension '{0}' has an invalid Info.plist: the NSExtension dictionary must contain an NSExtensionPointIdentifier.", name);
            }

            PDictionary attributes;

            if (!extension.TryGetValue("NSExtensionAttributes", out attributes))
            {
                Log.LogError("The Watch Extension '{0}' has an invalid Info.plist: the NSExtension dictionary must contain NSExtensionAttributes.", name);
                return;
            }

            var appBundleIdentifier = attributes.Get <PString> ("WKAppBundleIdentifier");

            if (appBundleIdentifier != null)
            {
                if (appBundleIdentifier.Value != watchAppBundleIdentifier)
                {
                    Log.LogError("The Watch Extension '{0}' has an invalid WKAppBundleIdentifier value ('{1}'), it does not match the parent Watch App bundle's CFBundleIdentifier ('{2}').", name, appBundleIdentifier.Value, watchAppBundleIdentifier);
                }
            }
            else
            {
                Log.LogError("The Watch Extension '{0}' has an invalid Info.plist: the NSExtensionAttributes dictionary must contain a WKAppBundleIdentifier.", name);
            }

            PObject requiredDeviceCapabilities;

            if (plist.TryGetValue("UIRequiredDeviceCapabilities", out requiredDeviceCapabilities))
            {
                var requiredDeviceCapabilitiesDictionary = requiredDeviceCapabilities as PDictionary;
                var requiredDeviceCapabilitiesArray      = requiredDeviceCapabilities as PArray;

                if (requiredDeviceCapabilitiesDictionary != null)
                {
                    PBoolean watchCompanion;

                    if (requiredDeviceCapabilitiesDictionary.TryGetValue("watch-companion", out watchCompanion))
                    {
                        Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: the UIRequiredDeviceCapabilities dictionary should not contain the 'watch-companion' capability.", name);
                    }
                }
                else if (requiredDeviceCapabilitiesArray != null)
                {
                    if (requiredDeviceCapabilitiesArray.OfType <PString> ().Any(x => x.Value == "watch-companion"))
                    {
                        Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: the UIRequiredDeviceCapabilities array should not contain the 'watch-companion' capability.", name);
                    }
                }
            }
        }
Example #9
0
        void ValidateAppExtension(string path, string mainBundleIdentifier, string mainShortVersionString, string mainVersion)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError("The App Extension '{0}' does not contain an Info.plist", name);
                return;
            }

            var plist = PDictionary.FromFile(info);

            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError("The App Extension '{0}' does not specify a CFBundleIdentifier.", name);
                return;
            }

            // The filename of the extension path is the extension's bundle identifier, which turns out ugly
            // in error messages. Try to get something more friendly-looking.
            name = plist.GetCFBundleDisplayName() ?? name;

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError("The App Extension '{0}' does not specify a CFBundleExecutable", name);
            }

            if (!bundleIdentifier.StartsWith(mainBundleIdentifier + ".", StringComparison.Ordinal))
            {
                Log.LogError("The App Extension '{0}' has an invalid CFBundleIdentifier ({1}), it does not begin with the main app bundle's CFBundleIdentifier ({2}).", name, bundleIdentifier, mainBundleIdentifier);
            }

            if (bundleIdentifier.EndsWith(".key", StringComparison.Ordinal))
            {
                Log.LogError("The App Extension '{0}' has a CFBundleIdentifier ({1}) that ends with the illegal suffix \".key\".", name, bundleIdentifier);
            }

            var shortVersionString = plist.GetCFBundleShortVersionString();

            if (string.IsNullOrEmpty(shortVersionString))
            {
                Log.LogWarning("The App Extension '{0}' does not specify a CFBundleShortVersionString", name);
            }

            if (shortVersionString != mainShortVersionString)
            {
                Log.LogWarning("The App Extension '{0}' has a CFBundleShortVersionString ({1}) that does not match the main app bundle's CFBundleShortVersionString ({2})", name, shortVersionString, mainShortVersionString);
            }

            var version = plist.GetCFBundleVersion();

            if (string.IsNullOrEmpty(version))
            {
                Log.LogWarning("The App Extension '{0}' does not specify a CFBundleVersion", name);
            }

            if (version != mainVersion)
            {
                Log.LogWarning("The App Extension '{0}' has a CFBundleVersion ({1}) that does not match the main app bundle's CFBundleVersion ({2})", name, version, mainVersion);
            }

            var extension = plist.Get <PDictionary> ("NSExtension");

            if (extension == null)
            {
                Log.LogError("The App Extension '{0}' has an invalid Info.plist: it does not contain an NSExtension dictionary.", name);
                return;
            }

            var extensionPointIdentifier = extension.GetString("NSExtensionPointIdentifier").Value;

            if (string.IsNullOrEmpty(extensionPointIdentifier))
            {
                Log.LogError("The App Extension '{0}' has an invalid Info.plist: the NSExtension dictionary does not contain an NSExtensionPointIdentifier value.", name);
                return;
            }

            // https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/SystemExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW9
            switch (extensionPointIdentifier)
            {
            case "com.apple.ui-services":                         // iOS+OSX
            case "com.apple.services":                            // iOS
            case "com.apple.keyboard-service":                    // iOS
            case "com.apple.fileprovider-ui":                     // iOS
            case "com.apple.fileprovider-nonui":                  // iOS
            case "com.apple.FinderSync":                          // OSX
            case "com.apple.photo-editing":                       // iOS
            case "com.apple.share-services":                      // iOS+OSX
            case "com.apple.widget-extension":                    // iOS+OSX
            case "com.apple.Safari.content-blocker":              // iOS
            case "com.apple.Safari.sharedlinks-service":          // iOS
            case "com.apple.spotlight.index":                     // iOS
            case "com.apple.AudioUnit-UI":                        // iOS
            case "com.apple.tv-services":                         // tvOS
            case "com.apple.broadcast-services":                  // iOS+tvOS
            case "com.apple.callkit.call-directory":              // iOS
            case "com.apple.message-payload-provider":            // iOS
            case "com.apple.intents-service":                     // iOS
            case "com.apple.intents-ui-service":                  // iOS
            case "com.apple.usernotifications.content-extension": // iOS
            case "com.apple.usernotifications.service":           // iOS
                break;

            case "com.apple.watchkit":             // iOS8.2
                var attributes = extension.Get <PDictionary> ("NSExtensionAttributes");

                if (attributes == null)
                {
                    Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: The NSExtension dictionary does not contain an NSExtensionAttributes dictionary.", name);
                    return;
                }

                var wkAppBundleIdentifier = attributes.GetString("WKAppBundleIdentifier").Value;
                var apps = Directory.GetDirectories(path, "*.app");
                if (apps.Length == 0)
                {
                    Log.LogError("The WatchKit Extension '{0}' does not contain any watch apps.", name);
                }
                else if (apps.Length > 1)
                {
                    Log.LogError("The WatchKit Extension '{0}' contain more than one watch apps.", name);
                }
                else
                {
                    PObject requiredDeviceCapabilities;

                    if (plist.TryGetValue("UIRequiredDeviceCapabilities", out requiredDeviceCapabilities))
                    {
                        var requiredDeviceCapabilitiesDictionary = requiredDeviceCapabilities as PDictionary;
                        var requiredDeviceCapabilitiesArray      = requiredDeviceCapabilities as PArray;

                        if (requiredDeviceCapabilitiesDictionary != null)
                        {
                            PBoolean watchCompanion;

                            if (!requiredDeviceCapabilitiesDictionary.TryGetValue("watch-companion", out watchCompanion) || !watchCompanion.Value)
                            {
                                Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: the UIRequiredDeviceCapabilities dictionary must contain the 'watch-companion' capability with a value of 'true'.", name);
                            }
                        }
                        else if (requiredDeviceCapabilitiesArray != null)
                        {
                            if (!requiredDeviceCapabilitiesArray.OfType <PString> ().Any(x => x.Value == "watch-companion"))
                            {
                                Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: the UIRequiredDeviceCapabilities array must contain the 'watch-companion' capability.", name);
                            }
                        }
                        else
                        {
                            Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: the UIRequiredDeviceCapabilities key must be present and contain the 'watch-companion' capability.", name);
                        }
                    }
                    else
                    {
                        Log.LogError("The WatchKit Extension '{0}' has an invalid Info.plist: the UIRequiredDeviceCapabilities key must be present and contain the 'watch-companion' capability.", name);
                    }

                    ValidateWatchOS1App(apps[0], name, mainBundleIdentifier, wkAppBundleIdentifier);
                }
                break;

            default:
                Log.LogWarning("The App Extension '{0}' has an unrecognized NSExtensionPointIdentifier value ('{1}').", name, extensionPointIdentifier);
                break;
            }
        }
Example #10
0
        void Init()
        {
            string currentLocation = IsInstalled ? MmpPath : null;

            IsInstalled = false;
            versions    = null;

            FrameworkDirectory = "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current";
            var envFrameworkDir = Environment.GetEnvironmentVariable("XAMMAC_FRAMEWORK_PATH");

            if (envFrameworkDir != null && Directory.Exists(envFrameworkDir))
            {
                FrameworkDirectory = envFrameworkDir;
            }

            var versionPath = Path.Combine(FrameworkDirectory, "Version");

            if (File.Exists(versionPath))
            {
                Version = ReadVersion(versionPath);
                lastWriteTimes [versionPath] = File.GetLastWriteTimeUtc(versionPath);

                var path = Path.Combine(FrameworkDirectory, "Versions.plist");
                if (File.Exists(path))
                {
                    try {
                        versions = PDictionary.FromFile(path);
                    } catch {
                        LoggingService.LogWarning("Xamarin.Mac installation is corrupt: invalid Versions.plist at {0}.", path);
                    }
                }

                if (versions == null)
                {
                    versions = CreateDefaultVersionsPlist(Version);
                }
            }
            else
            {
                NotInstalled(versionPath, error: false);
                AnalyticsService.ReportSdkVersion("XS.Core.SDK.Mac.Version", string.Empty);
                return;
            }

            var paths = Version >= new MacOSXSdkVersion(1, 9, 0)
                                ? Detect2x()
                                : Detect1x();

            foreach (var path in paths)
            {
                if (!File.Exists(path))
                {
                    NotInstalled(path);
                    return;
                }

                lastWriteTimes [path] = File.GetLastWriteTimeUtc(path);
            }

            IsInstalled = true;
            LoggingService.LogInfo("Found Xamarin.Mac, version {0}.", Version);
            AnalyticsService.ReportSdkVersion("XS.Core.SDK.Mac.Version", Version.ToString());

            if (Changed != null && currentLocation != MmpPath)
            {
                Changed(this, EventArgs.Empty);
            }
        }
Example #11
0
        void ValidateWatchApp(string path, string mainBundleIdentifier, string mainShortVersionString, string mainVersion)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError("The Watch App '{0}' does not contain an Info.plist", name);
                return;
            }

            var plist            = PDictionary.FromFile(info);
            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError("The Watch App '{0}' does not specify a CFBundleIdentifier.", name);
                return;
            }

            if (!bundleIdentifier.StartsWith(mainBundleIdentifier + ".", StringComparison.Ordinal))
            {
                Log.LogError("The Watch App '{0}' has an invalid CFBundleIdentifier ({1}), it does not begin with the main app bundle's CFBundleIdentifier ({2}).", name, bundleIdentifier, mainBundleIdentifier);
            }

            var shortVersionString = plist.GetCFBundleShortVersionString();

            if (string.IsNullOrEmpty(shortVersionString))
            {
                Log.LogWarning("The Watch App '{0}' does not specify a CFBundleShortVersionString", name);
            }

            if (shortVersionString != mainShortVersionString)
            {
                Log.LogWarning("The Watch App '{0}' has a CFBundleShortVersionString ({1}) that does not match the main app bundle's CFBundleShortVersionString ({2})", name, shortVersionString, mainShortVersionString);
            }

            var version = plist.GetCFBundleVersion();

            if (string.IsNullOrEmpty(version))
            {
                Log.LogWarning("The Watch App '{0}' does not specify a CFBundleVersion", name);
            }

            if (version != mainVersion)
            {
                Log.LogWarning("The Watch App '{0}' has a CFBundleVersion ({1}) that does not match the main app bundle's CFBundleVersion ({2})", name, version, mainVersion);
            }

            var watchDeviceFamily = plist.GetUIDeviceFamily();

            if (watchDeviceFamily != IPhoneDeviceType.Watch)
            {
                Log.LogError("The Watch App '{0}' does not have a valid UIDeviceFamily value. Expected 'Watch (4)' but found '{1} ({2})'.", name, watchDeviceFamily.ToString(), (int)watchDeviceFamily);
            }

            var watchExecutable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(watchExecutable))
            {
                Log.LogError("The Watch App '{0}' does not specify a CFBundleExecutable", name);
            }

            var wkCompanionAppBundleIdentifier = plist.GetString("WKCompanionAppBundleIdentifier").Value;

            if (wkCompanionAppBundleIdentifier != mainBundleIdentifier)
            {
                Log.LogError("The Watch App '{0}' has an invalid WKCompanionAppBundleIdentifier value ('{1}'), it does not match the main app bundle's CFBundleIdentifier ('{2}').", name, wkCompanionAppBundleIdentifier, mainBundleIdentifier);
            }

            PBoolean watchKitApp;

            if (!plist.TryGetValue("WKWatchKitApp", out watchKitApp) || !watchKitApp.Value)
            {
                Log.LogError("The Watch App '{0}' has an invalid Info.plist: The WKWatchKitApp key must be present and have a value of 'true'.", name);
            }

            if (plist.ContainsKey("LSRequiresIPhoneOS"))
            {
                Log.LogError("The Watch App '{0}' has an invalid Info.plist: the LSRequiresIPhoneOS key must not be present.", name);
            }

            var pluginsDir = Path.Combine(path, "PlugIns");

            if (!Directory.Exists(pluginsDir))
            {
                Log.LogError("The Watch App '{0}' does not contain any Watch Extensions.", name);
                return;
            }

            int count = 0;

            foreach (var plugin in Directory.EnumerateDirectories(pluginsDir, "*.appex"))
            {
                ValidateWatchExtension(plugin, bundleIdentifier, shortVersionString, version);
                count++;
            }

            if (count == 0)
            {
                Log.LogError("The Watch App '{0}' does not contan a Watch Extension.", name);
            }
        }
        public override bool Execute()
        {
            PDictionary plist;

            Log.LogTaskName("CompileAppManifest");
            Log.LogTaskProperty("AppBundleName", AppBundleName);
            Log.LogTaskProperty("AppBundleDir", AppBundleDir);
            Log.LogTaskProperty("AppManifest", AppManifest);
            Log.LogTaskProperty("Architecture", Architecture);
            Log.LogTaskProperty("AssemblyName", AssemblyName);
            Log.LogTaskProperty("BundleIdentifier", BundleIdentifier);
            Log.LogTaskProperty("Debug", Debug);
            Log.LogTaskProperty("DebugIPAddresses", DebugIPAddresses);
            Log.LogTaskProperty("DefaultSdkVersion", DefaultSdkVersion);
            Log.LogTaskProperty("IsAppExtension", IsAppExtension);
            Log.LogTaskProperty("IsWatchApp", IsWatchApp);
            Log.LogTaskProperty("IsWatchExtension", IsWatchExtension);
            Log.LogTaskProperty("PartialAppManifests", PartialAppManifests);
            Log.LogTaskProperty("ResourceRules", ResourceRules);
            Log.LogTaskProperty("SdkPlatform", SdkPlatform);
            Log.LogTaskProperty("SdkIsSimulator", SdkIsSimulator);
            Log.LogTaskProperty("TargetFrameworkIdentifier", TargetFrameworkIdentifier);

            try {
                plist = PDictionary.FromFile(AppManifest);
            } catch (Exception ex) {
                LogAppManifestError("Error loading '{0}': {1}", AppManifest, ex.Message);
                return(false);
            }

            sdkVersion = IPhoneSdkVersion.Parse(DefaultSdkVersion);
            var text = plist.GetMinimumOSVersion();

            if (string.IsNullOrEmpty(text))
            {
                minimumOSVersion = sdkVersion;
            }
            else if (!IPhoneSdkVersion.TryParse(text, out minimumOSVersion))
            {
                LogAppManifestError("Could not parse MinimumOSVersion value '{0}'", text);
                return(false);
            }

            switch (Framework)
            {
            case PlatformFramework.iOS:
                IsIOS = true;
                break;

            case PlatformFramework.WatchOS:
                break;

            case PlatformFramework.TVOS:
                break;

            default:
                throw new InvalidOperationException(string.Format("Invalid framework: {0}", Framework));
            }

            return(Compile(plist));
        }
        public override bool Execute()
        {
            PDictionary metadata;

            if (ITunesMetadata != null)
            {
                if (ITunesMetadata.Length > 1)
                {
                    Log.LogError("Cannot have more than 1 iTunesMetadata.plist.");
                    return(false);
                }

                var path = ITunesMetadata[0].GetMetadata("FullPath");

                try {
                    metadata = PDictionary.FromFile(path);
                } catch (Exception ex) {
                    Log.LogError(null, null, null, path, 0, 0, 0, 0, "Error loading '{0}': {1}", path, ex.Message);
                    return(false);
                }
            }
            else
            {
                var         manifest = Path.Combine(AppBundleDir, "Info.plist");
                PDictionary plist;

                try {
                    plist = PDictionary.FromFile(manifest);
                } catch (Exception ex) {
                    Log.LogError(null, null, null, manifest, 0, 0, 0, 0, "Error loading '{0}': {1}", manifest, ex.Message);
                    return(false);
                }

                var displayName   = plist.GetCFBundleDisplayName();
                var bundleVersion = plist.GetCFBundleVersion();

                metadata = new PDictionary();

                metadata.Add("genre", new PString("Application"));
                if (bundleVersion != null)
                {
                    metadata.Add("bundleVersion", (PString)bundleVersion);
                }
                if (displayName != null)
                {
                    metadata.Add("itemName", (PString)displayName);
                }
                metadata.Add("kind", (PString)"software");
                if (displayName != null)
                {
                    metadata.Add("playlistName", (PString)displayName);
                }
                metadata.Add("softwareIconNeedsShine", (PBoolean)true);
                metadata.Add("softwareVersionBundleId", (PString)plist.GetCFBundleIdentifier());
            }

            Directory.CreateDirectory(Path.GetDirectoryName(OutputPath.ItemSpec));
            metadata.Save(OutputPath.ItemSpec, true);

            return(!Log.HasLoggedErrors);
        }
 protected override string ReadManifest() => PDictionary.FromFile(ManifestOutputPath).ToXml();
Example #15
0
        void ValidateWatchApp(string path, string mainBundleIdentifier, string mainShortVersionString, string mainVersion)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError(7014, path, MSBStrings.E7014, name);
                return;
            }

            var plist            = PDictionary.FromFile(info);
            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError(7015, info, MSBStrings.E7015, name);
                return;
            }

            if (!bundleIdentifier.StartsWith(mainBundleIdentifier + ".", StringComparison.Ordinal))
            {
                Log.LogError(7016, info, MSBStrings.E7016, name, bundleIdentifier, mainBundleIdentifier);
            }

            var shortVersionString = plist.GetCFBundleShortVersionString();

            if (string.IsNullOrEmpty(shortVersionString))
            {
                Log.LogWarning(MSBStrings.W0075, name);
            }

            if (shortVersionString != mainShortVersionString)
            {
                Log.LogWarning(MSBStrings.W0076, name, shortVersionString, mainShortVersionString);
            }

            var version = plist.GetCFBundleVersion();

            if (string.IsNullOrEmpty(version))
            {
                Log.LogWarning(MSBStrings.W0077, name);
            }

            if (version != mainVersion)
            {
                Log.LogWarning(MSBStrings.W0078, name, version, mainVersion);
            }

            var watchDeviceFamily = plist.GetUIDeviceFamily();

            if (watchDeviceFamily != IPhoneDeviceType.Watch)
            {
                Log.LogError(7017, info, MSBStrings.E7017, name, watchDeviceFamily.ToString(), (int)watchDeviceFamily);
            }

            var watchExecutable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(watchExecutable))
            {
                Log.LogError(7018, info, MSBStrings.E7018, name);
            }

            var wkCompanionAppBundleIdentifier = plist.GetString("WKCompanionAppBundleIdentifier").Value;

            if (wkCompanionAppBundleIdentifier != mainBundleIdentifier)
            {
                Log.LogError(7019, info, MSBStrings.E7019, name, wkCompanionAppBundleIdentifier, mainBundleIdentifier);
            }

            PBoolean watchKitApp;

            if (!plist.TryGetValue("WKWatchKitApp", out watchKitApp) || !watchKitApp.Value)
            {
                Log.LogError(7020, info, MSBStrings.E7020, name);
            }

            if (plist.ContainsKey("LSRequiresIPhoneOS"))
            {
                Log.LogError(7021, info, MSBStrings.E7021, name);
            }

            var pluginsDir = Path.Combine(path, "PlugIns");

            if (!Directory.Exists(pluginsDir))
            {
                Log.LogError(7022, path, MSBStrings.E7022, name);
                return;
            }

            int count = 0;

            foreach (var plugin in Directory.EnumerateDirectories(pluginsDir, "*.appex"))
            {
                ValidateWatchExtension(plugin, bundleIdentifier, shortVersionString, version);
                count++;
            }

            if (count == 0)
            {
                Log.LogError(7022, pluginsDir, MSBStrings.E7022_A, name);
            }
        }
Example #16
0
        void ValidateWatchOS1App(string path, string extensionName, string mainBundleIdentifier, string wkAppBundleIdentifier)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError("The Watch App '{0}' does not contain an Info.plist", name);
                return;
            }

            var plist            = PDictionary.FromFile(info);
            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError("The Watch App '{0}' does not specify a CFBundleIdentifier.", name);
                return;
            }

            var deviceFamily = plist.GetUIDeviceFamily();
            IPhoneDeviceType expectedDeviceFamily;
            string           expectedDeviceFamilyString;

            if (SdkIsSimulator)
            {
                expectedDeviceFamily       = IPhoneDeviceType.Watch | IPhoneDeviceType.IPhone;
                expectedDeviceFamilyString = "IPhone, Watch (1, 4)";
            }
            else
            {
                expectedDeviceFamily       = IPhoneDeviceType.Watch;
                expectedDeviceFamilyString = "Watch (4)";
            }

            if (deviceFamily != expectedDeviceFamily)
            {
                Log.LogError("The Watch App '{0}' does not have a valid UIDeviceFamily value. Expected '{1}' but found '{2} ({3})'.", name, expectedDeviceFamilyString, deviceFamily.ToString(), (int)deviceFamily);
            }

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError("The Watch App '{0}' does not specify a CFBundleExecutable", name);
            }

            if (bundleIdentifier != wkAppBundleIdentifier)
            {
                Log.LogError("The WatchKit Extension '{0}' has an invalid WKAppBundleIdentifier value ('{1}'), it does not match the Watch App's CFBundleIdentifier ('{2}').", extensionName, wkAppBundleIdentifier, bundleIdentifier);
            }

            var companionAppBundleIdentifier = plist.Get <PString> ("WKCompanionAppBundleIdentifier");

            if (companionAppBundleIdentifier != null)
            {
                if (companionAppBundleIdentifier.Value != mainBundleIdentifier)
                {
                    Log.LogError("The Watch App '{0}' has an invalid WKCompanionAppBundleIdentifier value ('{1}'), it does not match the main app bundle's CFBundleIdentifier ('{2}').", name, companionAppBundleIdentifier.Value, mainBundleIdentifier);
                }
            }
            else
            {
                Log.LogError("The Watch App '{0}' has an invalid Info.plist: the WKCompanionAppBundleIdentifier must exist and must match the main app bundle's CFBundleIdentifier.", name);
            }

            if (plist.ContainsKey("LSRequiresIPhoneOS"))
            {
                Log.LogError("The Watch App '{0}' has an invalid Info.plist: the LSRequiresIPhoneOS key must not be present.", name);
            }
        }
Example #17
0
        void ValidateWatchExtension(string path, string watchAppBundleIdentifier, string mainShortVersionString, string mainVersion)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            var info = Path.Combine(path, "Info.plist");

            if (!File.Exists(info))
            {
                Log.LogError(7023, path, MSBStrings.E7023, name);
                return;
            }

            var plist = PDictionary.FromFile(info);

            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError(7024, info, MSBStrings.E7024, name);
                return;
            }

            // The filename of the extension path is the extension's bundle identifier, which turns out ugly
            // in error messages. Try to get something more friendly-looking.
            name = plist.GetCFBundleDisplayName() ?? name;

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError(7025, info, MSBStrings.E7025, name);
            }

            if (!bundleIdentifier.StartsWith(watchAppBundleIdentifier + ".", StringComparison.Ordinal))
            {
                Log.LogError(7026, info, MSBStrings.E7026, name, bundleIdentifier, watchAppBundleIdentifier);
            }

            if (bundleIdentifier.EndsWith(".key", StringComparison.Ordinal))
            {
                Log.LogError(7027, info, MSBStrings.E7027, name, bundleIdentifier);
            }

            var shortVersionString = plist.GetCFBundleShortVersionString();

            if (string.IsNullOrEmpty(shortVersionString))
            {
                Log.LogWarning(MSBStrings.W0079, name);
            }

            if (shortVersionString != mainShortVersionString)
            {
                Log.LogWarning(MSBStrings.W0080, name, shortVersionString, mainShortVersionString);
            }

            var version = plist.GetCFBundleVersion();

            if (string.IsNullOrEmpty(version))
            {
                Log.LogWarning(MSBStrings.W0081, name);
            }

            if (version != mainVersion)
            {
                Log.LogWarning(MSBStrings.W0082, name, version, mainVersion);
            }

            var extension = plist.Get <PDictionary> ("NSExtension");

            if (extension == null)
            {
                Log.LogError(7028, info, MSBStrings.E7028, name);
                return;
            }

            var extensionPointIdentifier = extension.Get <PString> ("NSExtensionPointIdentifier");

            if (extensionPointIdentifier != null)
            {
                if (extensionPointIdentifier.Value != "com.apple.watchkit")
                {
                    Log.LogError(7029, info, MSBStrings.E7029, name);
                }
            }
            else
            {
                Log.LogError(7029, info, MSBStrings.E7029_A, name);
            }

            PDictionary attributes;

            if (!extension.TryGetValue("NSExtensionAttributes", out attributes))
            {
                Log.LogError(7030, info, MSBStrings.E7030, name);
                return;
            }

            var appBundleIdentifier = attributes.Get <PString> ("WKAppBundleIdentifier");

            if (appBundleIdentifier != null)
            {
                if (appBundleIdentifier.Value != watchAppBundleIdentifier)
                {
                    Log.LogError(7031, info, MSBStrings.E7031, name, appBundleIdentifier.Value, watchAppBundleIdentifier);
                }
            }
            else
            {
                Log.LogError(7031, info, MSBStrings.E7031_A, name);
            }

            PObject requiredDeviceCapabilities;

            if (plist.TryGetValue("UIRequiredDeviceCapabilities", out requiredDeviceCapabilities))
            {
                var requiredDeviceCapabilitiesDictionary = requiredDeviceCapabilities as PDictionary;
                var requiredDeviceCapabilitiesArray      = requiredDeviceCapabilities as PArray;

                if (requiredDeviceCapabilitiesDictionary != null)
                {
                    PBoolean watchCompanion;

                    if (requiredDeviceCapabilitiesDictionary.TryGetValue("watch-companion", out watchCompanion))
                    {
                        Log.LogError(7032, info, MSBStrings.E7032, name);
                    }
                }
                else if (requiredDeviceCapabilitiesArray != null)
                {
                    if (requiredDeviceCapabilitiesArray.OfType <PString> ().Any(x => x.Value == "watch-companion"))
                    {
                        Log.LogError(7032, info, MSBStrings.E7032_A, name);
                    }
                }
            }
        }
Example #18
0
        public override bool Execute()
        {
            Log.LogTaskName("ValidateAppBundle");
            Log.LogTaskProperty("AppBundlePath", Path.GetFullPath(AppBundlePath));
            Log.LogTaskProperty("SdkIsSimulator", SdkIsSimulator);
            Log.LogTaskProperty("TargetFrameworkIdentifier", TargetFrameworkIdentifier);

            var mainInfoPath = Path.Combine(AppBundlePath, "Info.plist");

            if (!File.Exists(mainInfoPath))
            {
                Log.LogError("The app bundle {0} does not contain an Info.plist.", AppBundlePath);
                return(false);
            }

            var plist = PDictionary.FromFile(mainInfoPath);

            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError("{0} does not specify a CFBundleIdentifier.", mainInfoPath);
                return(false);
            }

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError("{0} does not specify a CFBundleExecutable", mainInfoPath);
            }

            var supportedPlatforms = plist.GetArray(ManifestKeys.CFBundleSupportedPlatforms);
            var platform           = string.Empty;

            if (supportedPlatforms == null || supportedPlatforms.Count == 0)
            {
                Log.LogError("{0} does not specify a CFBundleSupportedPlatforms.", mainInfoPath);
            }
            else
            {
                platform = (PString)supportedPlatforms[0];
            }

            // Validate UIDeviceFamily
            var deviceTypes    = plist.GetUIDeviceFamily();
            var deviceFamilies = deviceTypes.ToDeviceFamily();

            AppleDeviceFamily[] validFamilies = null;

            switch (Framework)
            {
            case PlatformFramework.iOS:
                validFamilies = new AppleDeviceFamily[] {
                    AppleDeviceFamily.IPhone,
                    AppleDeviceFamily.IPad,
                    AppleDeviceFamily.Watch
                };
                break;

            case PlatformFramework.WatchOS:
                validFamilies = new AppleDeviceFamily[] { AppleDeviceFamily.Watch };
                break;

            case PlatformFramework.TVOS:
                validFamilies = new AppleDeviceFamily[] { AppleDeviceFamily.TV };
                break;

            default:
                Log.LogError("Invalid framework: {0}", Framework);
                break;
            }

            if (validFamilies != null)
            {
                if (validFamilies.Length == 0)
                {
                    Log.LogError("{0} does not specify a UIDeviceFamily.", mainInfoPath);
                }
                else
                {
                    foreach (var family in deviceFamilies)
                    {
                        if (Array.IndexOf(validFamilies, family) == -1)
                        {
                            Log.LogError("{0} is invalid: the UIDeviceFamily key must contain a value for '{1}'.", mainInfoPath, family);
                        }
                    }
                }
            }

            var mainShortVersionString = plist.GetCFBundleShortVersionString();
            var mainVersion            = plist.GetCFBundleVersion();

            if (Directory.Exists(Path.Combine(AppBundlePath, "PlugIns")))
            {
                foreach (var plugin in Directory.GetDirectories(Path.Combine(AppBundlePath, "PlugIns"), "*.appex"))
                {
                    ValidateAppExtension(plugin, bundleIdentifier, mainShortVersionString, mainVersion);
                }
            }

            if (Directory.Exists(Path.Combine(AppBundlePath, "Watch")))
            {
                foreach (var watchApp in Directory.GetDirectories(Path.Combine(AppBundlePath, "Watch"), "*.app"))
                {
                    ValidateWatchApp(watchApp, bundleIdentifier, mainShortVersionString, mainVersion);
                }
            }

            return(!Log.HasLoggedErrors);
        }
Example #19
0
        public override bool Execute()
        {
            var mainInfoPath = PlatformFrameworkHelper.GetAppManifestPath(Platform, AppBundlePath);

            if (!File.Exists(mainInfoPath))
            {
                Log.LogError(7040, AppBundlePath, MSBStrings.E7040, AppBundlePath);
                return(false);
            }

            var plist = PDictionary.FromFile(mainInfoPath);

            var bundleIdentifier = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(bundleIdentifier))
            {
                Log.LogError(7041, mainInfoPath, MSBStrings.E7041, mainInfoPath);
                return(false);
            }

            var executable = plist.GetCFBundleExecutable();

            if (string.IsNullOrEmpty(executable))
            {
                Log.LogError(7042, mainInfoPath, MSBStrings.E7042, mainInfoPath);
            }

            var supportedPlatforms = plist.GetArray(ManifestKeys.CFBundleSupportedPlatforms);
            var platform           = string.Empty;

            if (supportedPlatforms == null || supportedPlatforms.Count == 0)
            {
                Log.LogError(7043, mainInfoPath, MSBStrings.E7043, mainInfoPath);
            }
            else
            {
                platform = (PString)supportedPlatforms[0];
            }

            // Validate UIDeviceFamily
            var deviceTypes    = plist.GetUIDeviceFamily();
            var deviceFamilies = deviceTypes.ToDeviceFamily();

            AppleDeviceFamily[]  validFamilies    = null;
            AppleDeviceFamily [] requiredFamilies = null;

            switch (Platform)
            {
            case ApplePlatform.MacCatalyst:
                requiredFamilies = new AppleDeviceFamily [] {
                    AppleDeviceFamily.IPad,
                };
                goto case ApplePlatform.iOS;

            case ApplePlatform.iOS:
                validFamilies = new AppleDeviceFamily[] {
                    AppleDeviceFamily.IPhone,
                    AppleDeviceFamily.IPad,
                };
                break;

            case ApplePlatform.WatchOS:
                validFamilies = new AppleDeviceFamily[] { AppleDeviceFamily.Watch };
                break;

            case ApplePlatform.TVOS:
                validFamilies = new AppleDeviceFamily[] { AppleDeviceFamily.TV };
                break;

            default:
                Log.LogError("Invalid platform: {0}", Platform);
                break;
            }

            if (validFamilies != null)
            {
                if (validFamilies.Length == 0)
                {
                    Log.LogError(7044, mainInfoPath, MSBStrings.E7044, mainInfoPath);
                }
                else
                {
                    foreach (var family in deviceFamilies)
                    {
                        if (Array.IndexOf(validFamilies, family) == -1)
                        {
                            Log.LogError(7044, mainInfoPath, MSBStrings.E7044_A, mainInfoPath, family);
                        }
                    }
                }
            }

            if (requiredFamilies != null)
            {
                foreach (var family in requiredFamilies)
                {
                    if (!deviceFamilies.Contains(family))
                    {
                        Log.LogError(7044, mainInfoPath, MSBStrings.E7044_A, mainInfoPath, family);
                    }
                }
            }

            var mainShortVersionString = plist.GetCFBundleShortVersionString();
            var mainVersion            = plist.GetCFBundleVersion();

            if (Directory.Exists(Path.Combine(AppBundlePath, "PlugIns")))
            {
                foreach (var plugin in Directory.GetDirectories(Path.Combine(AppBundlePath, "PlugIns"), "*.appex"))
                {
                    ValidateAppExtension(plugin, bundleIdentifier, mainShortVersionString, mainVersion);
                }
            }

            if (Directory.Exists(Path.Combine(AppBundlePath, "Watch")))
            {
                foreach (var watchApp in Directory.GetDirectories(Path.Combine(AppBundlePath, "Watch"), "*.app"))
                {
                    ValidateWatchApp(watchApp, bundleIdentifier, mainShortVersionString, mainVersion);
                }
            }

            return(!Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            MobileProvisionPlatform platform;
            MobileProvision         profile;
            PDictionary             template;
            PDictionary             compiled;
            PDictionary             archived;
            string path;
            bool   save;

            switch (SdkPlatform)
            {
            case "AppleTVSimulator":
            case "AppleTVOS":
                platform = MobileProvisionPlatform.tvOS;
                break;

            case "iPhoneSimulator":
            case "WatchSimulator":
            case "iPhoneOS":
            case "WatchOS":
                platform = MobileProvisionPlatform.iOS;
                break;

            case "MacOSX":
                platform = MobileProvisionPlatform.MacOS;
                break;

            default:
                Log.LogError("Unknown SDK platform: {0}", SdkPlatform);
                return(false);
            }

            if (!string.IsNullOrEmpty(ProvisioningProfile))
            {
                if ((profile = GetMobileProvision(platform, ProvisioningProfile)) == null)
                {
                    Log.LogError("Could not locate the provisioning profile with a Name or UUID of {0}.", ProvisioningProfile);
                    return(false);
                }
            }
            else
            {
                profile = null;
            }

            if (!string.IsNullOrEmpty(Entitlements))
            {
                if (!File.Exists(Entitlements))
                {
                    Log.LogError("Entitlements.plist template '{0}' not found.", Entitlements);
                    return(false);
                }

                path = Entitlements;
            }
            else
            {
                path = DefaultEntitlementsPath;
            }

            try {
                template = PDictionary.FromFile(path);
            } catch (Exception ex) {
                Log.LogError("Error loading Entitlements.plist template '{0}': {1}", path, ex.Message);
                return(false);
            }

            compiled = GetCompiledEntitlements(profile, template);
            archived = GetArchivedExpandedEntitlements(template, compiled);

            try {
                Directory.CreateDirectory(Path.GetDirectoryName(CompiledEntitlements));
                WriteXcent(compiled, CompiledEntitlements);
            } catch (Exception ex) {
                Log.LogError("Error writing xcent file '{0}': {1}", CompiledEntitlements, ex.Message);
                return(false);
            }

            path = Path.Combine(EntitlementBundlePath, "archived-expanded-entitlements.xcent");

            if (File.Exists(path))
            {
                var plist = PDictionary.FromFile(path);
                var src   = archived.ToXml();
                var dest  = plist.ToXml();

                save = src != dest;
            }
            else
            {
                save = true;
            }

            if (save)
            {
                try {
                    archived.Save(path, true);
                } catch (Exception ex) {
                    Log.LogError("Error writing archived-expanded-entitlements.xcent file: {0}", ex.Message);
                    return(false);
                }
            }

            return(!Log.HasLoggedErrors);
        }
Example #21
0
        public override bool Execute()
        {
            var    intermediate          = Path.Combine(IntermediateOutputPath, ToolName);
            var    intermediateBundleDir = Path.Combine(intermediate, "bundle");
            var    intermediateCloneDir  = Path.Combine(intermediate, "cloned-assets");
            var    manifest         = new TaskItem(Path.Combine(intermediate, "asset-manifest.plist"));
            var    bundleResources  = new List <ITaskItem> ();
            var    outputManifests  = new List <ITaskItem> ();
            var    catalogs         = new List <ITaskItem> ();
            var    unique           = new HashSet <string> ();
            string bundleIdentifier = null;
            var    knownSpecs       = new HashSet <string> ();
            var    clones           = new HashSet <string> ();
            var    items            = new List <ITaskItem> ();
            var    specs            = new PArray();

            switch (SdkPlatform)
            {
            case "iPhoneSimulator":
            case "iPhoneOS":
            case "MacOSX":
            case "WatchSimulator":
            case "WatchOS":
            case "AppleTVSimulator":
            case "AppleTVOS":
                break;

            default:
                Log.LogError("Unrecognized platform: {0}", SdkPlatform);
                return(false);
            }

            if (AppManifest != null)
            {
                try {
                    plist = PDictionary.FromFile(AppManifest.ItemSpec);
                } catch (Exception ex) {
                    Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "{0}", ex.Message);
                    return(false);
                }

                bundleIdentifier = plist.GetCFBundleIdentifier();
            }

            for (int i = 0; i < ImageAssets.Length; i++)
            {
                var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, ImageAssets[i], !string.IsNullOrEmpty(SessionId));

                // Ignore MacOS .DS_Store files...
                if (Path.GetFileName(vpath).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc)
                var catalog = Path.GetDirectoryName(vpath);

                // keep walking up the directory structure until we get to the .xcassets directory
                while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets")
                {
                    catalog = Path.GetDirectoryName(catalog);
                }

                if (string.IsNullOrEmpty(catalog))
                {
                    Log.LogWarning(null, null, null, ImageAssets[i].ItemSpec, 0, 0, 0, 0, "Asset not part of an asset catalog: {0}", ImageAssets[i].ItemSpec);
                    continue;
                }

                if (ImageAssets[i].GetMetadata("Link") != null)
                {
                    // Note: if any of the files within a catalog are linked, we'll have to clone the *entire* catalog
                    clones.Add(catalog);
                    continue;
                }

                // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these
                if (Path.GetFileName(vpath) != "Contents.json")
                {
                    continue;
                }

                items.Add(ImageAssets[i]);
            }

            // clone any *.xcassets dirs that need cloning
            if (clones.Count > 0)
            {
                if (Directory.Exists(intermediateCloneDir))
                {
                    Directory.Delete(intermediateCloneDir, true);
                }

                Directory.CreateDirectory(intermediateCloneDir);

                items.Clear();

                for (int i = 0; i < ImageAssets.Length; i++)
                {
                    var       vpath = BundleResource.GetVirtualProjectPath(ProjectDir, ImageAssets[i], !string.IsNullOrEmpty(SessionId));
                    var       clone = false;
                    ITaskItem item;

                    // Ignore MacOS .DS_Store files...
                    if (Path.GetFileName(vpath).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    foreach (var catalog in clones)
                    {
                        if (vpath.Length > catalog.Length && vpath[catalog.Length] == '/' && vpath.StartsWith(catalog, StringComparison.Ordinal))
                        {
                            clone = true;
                            break;
                        }
                    }

                    if (clone)
                    {
                        var src = ImageAssets[i].GetMetadata("FullPath");

                        if (!File.Exists(src))
                        {
                            Log.LogError(null, null, null, src, 0, 0, 0, 0, "File not found: {0}", src);
                            return(false);
                        }

                        var dest = Path.Combine(intermediateCloneDir, vpath);
                        var dir  = Path.GetDirectoryName(dest);

                        Directory.CreateDirectory(dir);

                        File.Copy(src, dest, true);

                        // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these
                        if (Path.GetFileName(vpath) != "Contents.json")
                        {
                            continue;
                        }

                        item = new TaskItem(dest);
                        ImageAssets[i].CopyMetadataTo(item);
                        item.SetMetadata("Link", vpath);
                    }
                    else
                    {
                        // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these
                        if (Path.GetFileName(vpath) != "Contents.json")
                        {
                            continue;
                        }

                        item = ImageAssets[i];
                    }

                    items.Add(item);
                }
            }

            // Note: `items` contains only the Contents.json files at this point
            for (int i = 0; i < items.Count; i++)
            {
                var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, items[i], !string.IsNullOrEmpty(SessionId));
                var path  = items[i].GetMetadata("FullPath");

                // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc)
                var catalog = Path.GetDirectoryName(vpath);
                path = Path.GetDirectoryName(path);

                // keep walking up the directory structure until we get to the .xcassets directory
                while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets")
                {
                    catalog = Path.GetDirectoryName(catalog);
                    path    = Path.GetDirectoryName(path);
                }

                if (unique.Add(catalog))
                {
                    var item = new TaskItem(path);
                    item.SetMetadata("Link", catalog);

                    catalogs.Add(item);
                }

                if (AppleSdkSettings.XcodeVersion.Major >= 7 && !string.IsNullOrEmpty(bundleIdentifier) && SdkPlatform != "WatchSimulator")
                {
                    var text = File.ReadAllText(items[i].ItemSpec);

                    if (string.IsNullOrEmpty(text))
                    {
                        continue;
                    }

                    JsonObject json;
                    JsonValue  value;

                    try {
                        json = (JsonObject)JsonValue.Parse(text);
                    } catch (ArgumentException ex) {
                        // ... At line ###, column ###
                        int line = 0, column = 0;
                        int index, endIndex;

                        var message = ex.Message;
                        if (message.EndsWith(".", StringComparison.Ordinal))
                        {
                            message = message.Substring(0, message.Length - 1);
                        }
                        if ((index = message.IndexOf("At line ", StringComparison.Ordinal)) != -1)
                        {
                            index += "At line ".Length;

                            if ((endIndex = message.IndexOf(", column ", index, StringComparison.Ordinal)) != -1)
                            {
                                var columnBuf = message.Substring(endIndex + ", column ".Length);
                                var lineBuf   = message.Substring(index, endIndex - index);

                                int.TryParse(columnBuf, out column);
                                int.TryParse(lineBuf, out line);
                            }
                        }

                        Log.LogError(null, null, null, items[i].ItemSpec, line, column, line, column, "{0}", ex.Message);
                        return(false);
                    } catch (InvalidCastException) {
                        Log.LogError(null, null, null, items[i].ItemSpec, 0, 0, 0, 0, "Invalid json.");
                        return(false);
                    }

                    if (!json.TryGetValue("properties", out value) || value.JsonType != JsonType.Object)
                    {
                        continue;
                    }

                    var properties = (JsonObject)value;

                    if (!properties.TryGetValue("on-demand-resource-tags", out value) || value.JsonType != JsonType.Array)
                    {
                        continue;
                    }

                    var    resourceTags = (JsonArray)value;
                    var    tags         = new HashSet <string> ();
                    string hash;

                    foreach (var tag in resourceTags)
                    {
                        if (tag.JsonType == JsonType.String)
                        {
                            tags.Add((string)tag);
                        }
                    }

                    var tagList = tags.ToList();
                    tagList.Sort();

                    var assetDir = AssetPackUtils.GetAssetPackDirectory(intermediate, bundleIdentifier, tagList, out hash);

                    if (knownSpecs.Add(hash))
                    {
                        var assetpack = new PDictionary();
                        var ptags     = new PArray();

                        Directory.CreateDirectory(assetDir);

                        for (int j = 0; j < tagList.Count; j++)
                        {
                            ptags.Add(new PString(tagList[j]));
                        }

                        assetpack.Add("bundle-id", new PString(string.Format("{0}.asset-pack-{1}", bundleIdentifier, hash)));
                        assetpack.Add("bundle-path", new PString(Path.GetFullPath(assetDir)));
                        assetpack.Add("tags", ptags);
                        specs.Add(assetpack);
                    }
                }
            }

            if (catalogs.Count == 0)
            {
                // There are no (supported?) asset catalogs
                return(!Log.HasLoggedErrors);
            }

            partialAppManifest = new TaskItem(Path.Combine(intermediate, "partial-info.plist"));

            if (specs.Count > 0)
            {
                outputSpecs = Path.Combine(intermediate, "output-specifications.plist");
                specs.Save(outputSpecs, true);
            }

            Directory.CreateDirectory(intermediateBundleDir);

            // Note: Compile() will set the PartialAppManifest property if it is used...
            if ((Compile(catalogs.ToArray(), intermediateBundleDir, manifest)) != 0)
            {
                return(false);
            }

            if (PartialAppManifest != null && !File.Exists(PartialAppManifest.GetMetadata("FullPath")))
            {
                Log.LogError("Partial Info.plist file was not generated: {0}", PartialAppManifest.GetMetadata("FullPath"));
            }

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

                LogWarningsAndErrors(manifestOutput, catalogs[0]);

                bundleResources.AddRange(GetCompiledBundleResources(manifestOutput, intermediateBundleDir));
                outputManifests.Add(manifest);
            } catch (Exception ex) {
                Log.LogError("Failed to load {0} log file `{1}`: {2}", ToolName, manifest.ItemSpec, ex.Message);
            }

            foreach (var assetpack in specs.OfType <PDictionary> ())
            {
                var path       = Path.Combine(assetpack.GetString("bundle-path").Value, "Info.plist");
                var bundlePath = PathUtils.AbsoluteToRelative(intermediate, path);
                var outputPath = Path.Combine(OutputPath, bundlePath);
                var rpath      = Path.Combine(intermediate, bundlePath);
                var dict       = new PDictionary();

                dict.SetCFBundleIdentifier(assetpack.GetString("bundle-id").Value);
                dict.Add("Tags", assetpack.GetArray("tags").Clone());

                dict.Save(path, true, true);

                var item = new TaskItem(rpath);
                item.SetMetadata("LogicalName", bundlePath);
                item.SetMetadata("OutputPath", outputPath);
                item.SetMetadata("Optimize", "false");

                bundleResources.Add(item);
            }

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

            return(!Log.HasLoggedErrors);
        }
Example #22
0
        public void CreateIpa()
        {
            if (Platform == "iPhoneSimulator")
            {
                return;                 // this is a device-only test.
            }
            const string hostAppName = "MyWatchApp";
//			string extensionName = "MyWatchKitExtension";
            const string configuration = "AppStore";

            var mtouchPaths = SetupProjectPaths(hostAppName, "../", true, Platform, configuration);
            var proj        = SetupProject(Engine, mtouchPaths.ProjectCSProjPath);

            AppBundlePath = mtouchPaths.AppBundlePath;

            Engine.GlobalProperties.SetProperty("Platform", Platform);
            Engine.GlobalProperties.SetProperty("BuildIpa", "true");
            Engine.GlobalProperties.SetProperty("IpaIncludeArtwork", "true");
            Engine.GlobalProperties.SetProperty("CodesignProvision", "Automatic");              // Provisioning profile
            Engine.GlobalProperties.SetProperty("CodesignKey", "iPhone Developer");
            Engine.GlobalProperties.SetProperty("Configuration", configuration);

            RunTarget(proj, "Clean");
            Assert.IsFalse(Directory.Exists(AppBundlePath), "{1}: App bundle exists after cleanup: {0} ", AppBundlePath, Platform);

            proj = SetupProject(Engine, mtouchPaths.ProjectCSProjPath);
            RunTarget(proj, "Build");

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

            Assert.IsTrue(plist.ContainsKey("CFBundleExecutable"));
            Assert.IsTrue(plist.ContainsKey("CFBundleVersion"));
            Assert.IsNotEmpty(((PString)plist["CFBundleExecutable"]).Value);
            Assert.IsNotEmpty(((PString)plist["CFBundleVersion"]).Value);

            var ipaOutputDir        = Directory.EnumerateDirectories(mtouchPaths.ProjectBinPath, hostAppName + " *").FirstOrDefault();
            var ipaPath             = Path.Combine(ipaOutputDir, hostAppName + ".ipa");
            var payloadPath         = "Payload/";
            var watchkitSupportPath = "WatchKitSupport/";

            Assert.IsTrue(File.Exists(ipaPath));

            var startInfo = new ProcessStartInfo("/usr/bin/zipinfo", "-1 \"" + ipaPath + "\"");

            startInfo.RedirectStandardOutput = true;
            startInfo.UseShellExecute        = false;
            var process = new Process();

            process.StartInfo = startInfo;
            process.Start();
            var output = process.StandardOutput.ReadToEnd();

            process.WaitForExit();

            var lines = output.Split(new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

            Assert.Contains(payloadPath, lines, payloadPath + " does not exist");
            Assert.Contains(watchkitSupportPath, lines, watchkitSupportPath + " does not exist");

            string wkPath = "WatchKitSupport/WK";

            Assert.Contains(wkPath, lines, wkPath + " does not exist");

            var ipaIncludeArtwork = proj.GetEvaluatedProperty("IpaIncludeArtwork");

            Assert.IsTrue(output.Contains("iTunesMetadata.plist"), string.Format("The ipa should contain at least one iTunesMetadata.plist file if we are using an AppStore config and IpaIncludeArtwork is true. IpaIncludeArtwork: {0}", ipaIncludeArtwork));
        }
        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);
        }
        public override bool Execute()
        {
            var archiveDir = CreateArchiveDirectory();

            try {
                var plist       = PDictionary.FromFile(PlatformFrameworkHelper.GetAppManifestPath(Platform, AppBundleDir.ItemSpec));
                var productsDir = Path.Combine(archiveDir, "Products");

                // Archive the OnDemandResources...
                var resourcesDestDir = Path.Combine(productsDir, "OnDemandResources");
                var resourcesSrcDir  = Path.Combine(OutputPath, "OnDemandResources");

                if (Directory.Exists(resourcesSrcDir))
                {
                    Ditto(resourcesSrcDir, resourcesDestDir);
                }

                // Archive the Applications...
                var appDestDir = Path.Combine(productsDir, "Applications", Path.GetFileName(AppBundleDir.ItemSpec));
                Ditto(AppBundleDir.ItemSpec, appDestDir);

                // Archive the main dSYM...
                ArchiveDSym(DSYMDir, archiveDir);

                // for each `.dylib` (file) inside `MonoBundle` there could be a corresponding `.dSYM` - e.g. when using an AOT mode
                foreach (var dylib in Directory.GetFiles(UserDylibPath, "*.dylib"))
                {
                    var dsym = Path.Combine(AppBundleDir.ItemSpec, "..", Path.GetFileName(dylib) + ".dSYM");
                    ArchiveDSym(dsym, archiveDir);
                }

                // for each user framework (directory) that is bundled inside the app we must also archive their dSYMs, if available
                var fks = UserFrameworksPath;
                if (Directory.Exists(fks))
                {
                    foreach (var fx in Directory.GetDirectories(fks, "*.framework"))
                    {
                        var dsym = Path.Combine(AppBundleDir.ItemSpec, "..", Path.GetFileName(fx) + ".dSYM");
                        ArchiveDSym(dsym, archiveDir);
                    }
                }

                // Archive the mSYMs...
                ArchiveMSym(MSYMDir, archiveDir);

                // Archive the Bitcode symbol maps
                var bcSymbolMaps = Directory.GetFiles(Path.GetDirectoryName(DSYMDir), "*.bcsymbolmap");
                if (bcSymbolMaps.Length > 0)
                {
                    var bcSymbolMapsDir = Path.Combine(archiveDir, "BCSymbolMaps");

                    Directory.CreateDirectory(bcSymbolMapsDir);

                    for (int i = 0; i < bcSymbolMaps.Length; i++)
                    {
                        File.Copy(bcSymbolMaps [i], Path.Combine(bcSymbolMapsDir, Path.GetFileName(bcSymbolMaps [i])));
                    }
                }

                if (AppExtensionReferences != null)
                {
                    // Archive the dSYMs, mSYMs, etc for each of the referenced App Extensions as well...
                    for (int i = 0; i < AppExtensionReferences.Length; i++)
                    {
                        ArchiveAppExtension(AppExtensionReferences [i], archiveDir);
                    }
                }

                if (WatchAppReferences != null)
                {
                    // Archive the dSYMs, mSYMs, etc for each of the referenced WatchOS2 Apps as well...
                    for (int i = 0; i < WatchAppReferences.Length; i++)
                    {
                        ArchiveWatchApp(WatchAppReferences [i], archiveDir);
                    }
                }

                if (ITunesSourceFiles != null)
                {
                    // Archive the iTunesMetadata.plist and iTunesArtwork files...
                    var iTunesMetadataDir = Path.Combine(archiveDir, "iTunesMetadata", Path.GetFileName(AppBundleDir.ItemSpec));
                    for (int i = 0; i < ITunesSourceFiles.Length; i++)
                    {
                        var archivedMetaFile = Path.Combine(iTunesMetadataDir, Path.GetFileName(ITunesSourceFiles [i].ItemSpec));

                        Directory.CreateDirectory(iTunesMetadataDir);
                        File.Copy(ITunesSourceFiles [i].ItemSpec, archivedMetaFile, true);
                    }
                }

                // Generate an archive Info.plist
                var arInfo = new PDictionary();
                // FIXME: figure out this value
                //arInfo.Add ("AppStoreFileSize", new PNumber (65535));
                var props = new PDictionary();
                props.Add("ApplicationPath", new PString(string.Format("Applications/{0}", Path.GetFileName(AppBundleDir.ItemSpec))));
                props.Add("CFBundleIdentifier", new PString(plist.GetCFBundleIdentifier()));

                var version = plist.GetCFBundleShortVersionString();
                var build   = plist.GetCFBundleVersion();
                props.Add("CFBundleShortVersionString", new PString(version ?? (build ?? "1.0")));
                props.Add("CFBundleVersion", new PString(build ?? "1.0"));

                var iconFiles = plist.GetCFBundleIconFiles();
                var iconDict  = plist.GetCFBundleIcons();
                var icons     = new PArray();

                if (iconFiles != null)
                {
                    AddIconPaths(icons, iconFiles, Path.Combine(archiveDir, "Products"));
                }

                if (iconDict != null)
                {
                    var primary = iconDict.Get <PDictionary> (ManifestKeys.CFBundlePrimaryIcon);
                    if (primary != null && (iconFiles = primary.GetCFBundleIconFiles()) != null)
                    {
                        AddIconPaths(icons, iconFiles, Path.Combine(archiveDir, "Products"));
                    }
                }

                if (icons.Count > 0)
                {
                    props.Add("IconPaths", icons);
                }

                props.Add("SigningIdentity", SigningKey);

                arInfo.Add("ApplicationProperties", props);
                arInfo.Add("ArchiveVersion", new PNumber(2));
                arInfo.Add("CreationDate", new PDate(Now.ToUniversalTime()));
                arInfo.Add("Name", new PString(plist.GetCFBundleName() ?? plist.GetCFBundleDisplayName()));

                if (!string.IsNullOrEmpty(ProjectGuid))
                {
                    arInfo.Add("ProjectGuid", new PString(ProjectGuid));
                }

                if (!string.IsNullOrEmpty(ProjectTypeGuids))
                {
                    arInfo.Add("ProjectTypeGuids", new PString(ProjectTypeGuids));
                }

                if (!string.IsNullOrEmpty(SolutionPath))
                {
                    arInfo.Add("SolutionName", new PString(Path.GetFileNameWithoutExtension(SolutionPath)));
                    arInfo.Add("SolutionPath", new PString(SolutionPath));
                }

                if (!string.IsNullOrEmpty(InsightsApiKey))
                {
                    arInfo.Add("InsightsApiKey", new PString(InsightsApiKey));
                }

                arInfo.Save(Path.Combine(archiveDir, "Info.plist"));
                ArchiveDir = archiveDir;
            } catch (Exception ex) {
                Log.LogErrorFromException(ex);
                Directory.Delete(archiveDir, true);
            }

            return(!Log.HasLoggedErrors);
        }
        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 #26
0
        public override bool Execute()
        {
            PDictionary plist;
            PString     value;

            try {
                plist = PDictionary.FromFile(AppManifest.ItemSpec);
            } catch (Exception ex) {
                Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, MSBStrings.E0055, ex.Message);
                return(false);
            }

//			deviceType = plist.GetUIDeviceFamily ();

            if (plist.TryGetValue(ManifestKeys.MinimumOSVersion, out value))
            {
                if (!IPhoneSdkVersion.TryParse(value.Value, out minimumOSVersion))
                {
                    Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, MSBStrings.E0011, value);
                    return(false);
                }
            }
            else
            {
                switch (Platform)
                {
                case ApplePlatform.iOS:
                    IPhoneSdkVersion sdkVersion;
                    if (!IPhoneSdkVersion.TryParse(SdkVersion, out sdkVersion))
                    {
                        Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, MSBStrings.E0056, SdkVersion);
                        return(false);
                    }

                    minimumOSVersion = sdkVersion;
                    break;

                case ApplePlatform.WatchOS:
                case ApplePlatform.TVOS:
                    minimumOSVersion = IPhoneSdkVersion.UseDefault;
                    break;

                default:
                    throw new InvalidOperationException(string.Format("Invalid framework: {0}", Platform));
                }
            }

            Directory.CreateDirectory(AppBundleDir);

            var executableLastWriteTime = default(DateTime);
            var executable = Path.Combine(AppBundleDir, ExecutableName);

            if (File.Exists(executable))
            {
                executableLastWriteTime = File.GetLastWriteTimeUtc(executable);
            }

            var result = base.Execute();

            CopiedFrameworks = GetCopiedFrameworks();

            if (File.Exists(executable) && File.GetLastWriteTimeUtc(executable) != executableLastWriteTime)
            {
                NativeExecutable = new TaskItem(executable);
            }

            return(result);
        }
        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 #28
0
        protected override string GenerateCommandLineCommands()
        {
            var  args = new ProcessArgumentBuilder();
            bool msym;

            args.Add("/verbose");

            if (Debug)
            {
                args.Add("/debug");
            }

            if (!string.IsNullOrEmpty(OutputPath))
            {
                args.AddQuoted("/output:" + Path.GetFullPath(OutputPath));
            }

            if (!string.IsNullOrEmpty(ApplicationName))
            {
                args.AddQuoted("/name:" + ApplicationName);
            }

            if (TargetFrameworkIdentifier == "Xamarin.Mac")
            {
                args.Add("/profile:Xamarin.Mac");
            }
            else if (TargetFrameworkVersion.StartsWith("v", StringComparison.Ordinal))
            {
                args.Add("/profile:" + TargetFrameworkVersion.Substring(1));
            }

            XamMacArch arch;

            if (!Enum.TryParse(Architecture, true, out arch))
            {
                arch = XamMacArch.Default;
            }

            if (arch == XamMacArch.Default)
            {
                arch = XamMacArch.x86_64;
            }

            if (arch.HasFlag(XamMacArch.i386))
            {
                args.Add("/arch:i386");
            }

            if (arch.HasFlag(XamMacArch.x86_64))
            {
                args.Add("/arch:x86_64");
            }

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

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

            if (AppManifest != null)
            {
                try {
                    var plist = PDictionary.FromFile(AppManifest.ItemSpec);

                    PString v;
                    string  minimumDeploymentTarget;

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

                    args.Add(string.Format("/minos={0}", minimumDeploymentTarget));
                }
                catch (Exception ex) {
                    Log.LogWarning(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "Error loading '{0}': {1}", AppManifest.ItemSpec, ex.Message);
                }
            }

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

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

            switch ((LinkMode ?? string.Empty).ToLower())
            {
            case "full":
                break;

            case "sdkonly":
                args.Add("/linksdkonly");
                break;

            default:
                args.Add("/nolink");
                break;
            }

            if (!string.IsNullOrEmpty(I18n))
            {
                args.AddQuoted("/i18n:" + I18n);
            }

            if (ExplicitReferences != null)
            {
                foreach (var asm in ExplicitReferences)
                {
                    args.AddQuoted("/assembly:" + Path.GetFullPath(asm.ItemSpec));
                }
            }

            if (!string.IsNullOrEmpty(ApplicationAssembly.ItemSpec))
            {
                args.AddQuoted(Path.GetFullPath(ApplicationAssembly.ItemSpec));
            }

            if (!string.IsNullOrWhiteSpace(ExtraArguments))
            {
                args.Add(ExtraArguments);
            }

            if (NativeReferences != null)
            {
                foreach (var nr in NativeReferences)
                {
                    args.AddQuoted("/native-reference:" + Path.GetFullPath(nr.ItemSpec));
                }
            }

            if (IsAppExtension)
            {
                args.AddQuoted("/extension");
            }

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

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

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

            return(args.ToString());
        }
Example #29
0
        public override bool Execute()
        {
            MobileProvisionPlatform platform;
            MobileProvision         profile;
            PDictionary             template;
            PDictionary             compiled;
            PDictionary             archived;
            string path;

            switch (SdkPlatform)
            {
            case "AppleTVSimulator":
            case "AppleTVOS":
                platform = MobileProvisionPlatform.tvOS;
                break;

            case "iPhoneSimulator":
            case "WatchSimulator":
            case "iPhoneOS":
            case "WatchOS":
                platform = MobileProvisionPlatform.iOS;
                break;

            case "MacOSX":
                platform = MobileProvisionPlatform.MacOS;
                break;

            case "MacCatalyst":
                platform = MobileProvisionPlatform.MacOS;
                break;

            default:
                Log.LogError(MSBStrings.E0048, SdkPlatform);
                return(false);
            }

            if (!string.IsNullOrEmpty(ProvisioningProfile))
            {
                if ((profile = GetMobileProvision(platform, ProvisioningProfile)) == null)
                {
                    Log.LogError(MSBStrings.E0049, ProvisioningProfile);
                    return(false);
                }
            }
            else
            {
                profile = null;
            }

            if (!string.IsNullOrEmpty(Entitlements))
            {
                if (!File.Exists(Entitlements))
                {
                    Log.LogError(MSBStrings.E0112, Entitlements);
                    return(false);
                }

                path = Entitlements;
            }
            else
            {
                path = DefaultEntitlementsPath;
            }

            try {
                template = PDictionary.FromFile(path);
            } catch (Exception ex) {
                Log.LogError(MSBStrings.E0113, path, ex.Message);
                return(false);
            }

            compiled = GetCompiledEntitlements(profile, template);
            archived = GetArchivedExpandedEntitlements(template, compiled);

            try {
                Directory.CreateDirectory(Path.GetDirectoryName(CompiledEntitlements.ItemSpec));
                WriteXcent(compiled, CompiledEntitlements.ItemSpec);
            } catch (Exception ex) {
                Log.LogError(MSBStrings.E0114, CompiledEntitlements, ex.Message);
                return(false);
            }

            SaveArchivedExpandedEntitlements(archived);

            if (SdkIsSimulator)
            {
                if (compiled.Count > 0)
                {
                    EntitlementsInExecutable = CompiledEntitlements;
                }
            }
            else
            {
                EntitlementsInSignature = CompiledEntitlements;
            }

            return(!Log.HasLoggedErrors);
        }
Example #30
0
        public override bool Execute()
        {
            PDictionary              plist;
            IList <MobileProvision>  profiles;
            IList <X509Certificate2> certs;
            List <CodeSignIdentity>  pairs;

            var type     = GetProvisioningDistributionType();
            var platform = MobileProvisionPlatform.iOS;
            var identity = new CodeSignIdentity();

            hotRestartClient = new HotRestartClient();

            try {
                plist = PDictionary.FromFile(AppManifest);
            } catch (Exception ex) {
                Log.LogError(null, null, null, AppManifest, 0, 0, 0, 0, "Error loading '{0}': {1}", AppManifest, ex.Message);

                return(false);
            }

            identity.BundleId = plist.GetCFBundleIdentifier();

            if (string.IsNullOrEmpty(identity.BundleId))
            {
                if (GenerateApplicationManifest && !string.IsNullOrEmpty(ApplicationId))
                {
                    identity.BundleId = ApplicationId;
                }
                else
                {
                    Log.LogError(null, null, null, AppManifest, 0, 0, 0, 0, "{0} does not define CFBundleIdentifier", AppManifest);

                    return(false);
                }
            }

            DetectedBundleId = identity.BundleId;

            var appDisplayName = plist.GetCFBundleDisplayName();

            if (string.IsNullOrEmpty(appDisplayName))
            {
                if (GenerateApplicationManifest && !string.IsNullOrEmpty(ApplicationTitle))
                {
                    appDisplayName = ApplicationTitle;
                }
            }

            DetectedAppDisplayName = appDisplayName;

            if (!TryGetSigningCertificates(out certs, false))
            {
                return(false);
            }

            Log.LogMessage(MessageImportance.Low, "Available certificates:");

            foreach (var cert in certs)
            {
                Log.LogMessage(MessageImportance.Low, "    {0}", GetCertificateCommonName(cert));
            }

            if (!IsAutoCodeSignProfile(ProvisioningProfile))
            {
                identity.Profile = MobileProvisionIndex.GetMobileProvision(platform, ProvisioningProfile);

                if (identity.Profile == null)
                {
                    Log.LogError("The specified " + PlatformName + " provisioning profile '{0}' could not be found. Please enable Automatic Provisioning from the iOS Bundle Signing page.", ProvisioningProfile);
                    return(false);
                }

                var profile = identity.Profile;                 // capture ref for lambda

                if (certs.Count > 0)
                {
                    identity.SigningKey = certs.FirstOrDefault(c => profile.DeveloperCertificates.Any(p => p.Thumbprint == c.Thumbprint));
                    if (identity.SigningKey == null)
                    {
                        Log.LogError("No " + PlatformName + " signing identities match the specified provisioning profile '{0}'.", ProvisioningProfile);
                        return(false);
                    }
                }

                identity.AppId = ConstructValidAppId(identity.Profile, identity.BundleId);

                if (identity.AppId == null)
                {
                    Log.LogError(null, null, null, AppManifest, 0, 0, 0, 0, "Project bundle identifier '{0}' does not match specified provisioning profile '{1}'. Please enable Automatic Provisioning from the iOS Bundle Signing page.", identity.BundleId, ProvisioningProfile);
                    return(false);
                }

                if (identity.SigningKey != null)
                {
                    codesignCommonName      = GetCertificateCommonName(identity.SigningKey);
                    DetectedCodeSigningPath = Path.Combine(CertificatesPath, $"{identity.SigningKey.SerialNumber}.p12");
                }

                provisioningProfileName = identity.Profile.Name;

                DetectedAppId = identity.AppId;
                DetectedProvisioningProfileId   = identity.Profile.Uuid;
                DetectedProvisioningProfilePath = Path.Combine(ProfilesPath, $"{DetectedProvisioningProfileId}.mobileprovision");

                ReportDetectedCodesignInfo();

                return(!Log.HasLoggedErrors);
            }

            if ((profiles = GetProvisioningProfiles(platform, type, identity, certs)) == null)
            {
                return(false);
            }

            if ((pairs = GetCodeSignIdentityPairs(profiles, certs)) == null)
            {
                return(false);
            }

            identity = GetBestMatch(pairs, identity);

            if (identity.Profile != null && identity.AppId != null)
            {
                codesignCommonName = identity.SigningKey != null?GetCertificateCommonName(identity.SigningKey) : null;

                provisioningProfileName = identity.Profile.Name;

                DetectedAppId           = identity.AppId;
                DetectedCodeSigningPath = identity.SigningKey != null?Path.Combine(CertificatesPath, $"{identity.SigningKey.SerialNumber}.p12") : string.Empty;

                DetectedProvisioningProfileId   = identity.Profile.Uuid;
                DetectedProvisioningProfilePath = Path.Combine(ProfilesPath, $"{DetectedProvisioningProfileId}.mobileprovision");

                ReportDetectedCodesignInfo();
            }
            else
            {
                if (identity.SigningKey != null)
                {
                    Log.LogError("Bundle identifier '{0}' does not match any installed provisioning profile for selected signing identity '{0}'. Please enable Automatic Provisioning from the iOS Bundle Signing page.", identity.BundleId, identity.SigningKey);
                }
                else
                {
                    Log.LogError("Bundle identifier '{0}' does not match any installed provisioning profile. Please enable Automatic Provisioning from the iOS Bundle Signing page.", identity.BundleId);
                }
            }

            return(!Log.HasLoggedErrors);
        }