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