BuildResult MergeInfoPlist(IProgressMonitor monitor, MonoMacProject proj, MonoMacProjectConfiguration conf, ProjectFile template, FilePath plistOut) { return(MacBuildUtilities.CreateMergedPlist(monitor, template, plistOut, (PlistDocument doc) => { var result = new BuildResult(); var dict = doc.Root as PlistDictionary; if (dict == null) { doc.Root = dict = new PlistDictionary(); } //required keys that the user is likely to want to modify SetIfNotPresent(dict, "CFBundleName", proj.Name); SetIfNotPresent(dict, "CFBundleIdentifier", "com.yourcompany." + proj.Name); SetIfNotPresent(dict, "CFBundleShortVersionString", proj.Version); SetIfNotPresent(dict, "CFBundleVersion", "1"); SetIfNotPresent(dict, "LSMinimumSystemVersion", "10.6"); SetIfNotPresent(dict, "CFBundleDevelopmentRegion", "English"); //required keys that the user probably should not modify dict["CFBundleExecutable"] = conf.LaunchScript.FileName; SetIfNotPresent(dict, "CFBundleInfoDictionaryVersion", "6.0"); SetIfNotPresent(dict, "CFBundlePackageType", "APPL"); SetIfNotPresent(dict, "CFBundleSignature", "????"); return result; })); }
protected override bool GetNeedsBuilding(SolutionEntityItem item, ConfigurationSelector configuration) { if (base.GetNeedsBuilding(item, configuration)) { return(true); } var proj = item as MonoMacProject; if (proj == null || proj.CompileTarget != CompileTarget.Exe) { return(false); } var conf = (MonoMacProjectConfiguration)configuration.GetConfiguration(item); //all content files if (GetCopyFiles(proj, configuration, conf).Where(NeedsBuilding).Any()) { return(true); } if (PropertyService.IsMac) { //Interface Builder files var resDir = conf.AppDirectory.Combine("Contents", "Resources"); if (MacBuildUtilities.GetIBFilePairs(proj.Files, resDir).Any(NeedsBuilding)) { return(true); } } //the Info.plist var plistOut = conf.AppDirectory.Combine("Contents", "Info.plist"); var appInfoIn = proj.Files.GetFile(proj.BaseDirectory.Combine("Info.plist")); if (new FilePair(proj.FileName, plistOut).NeedsBuilding() || (appInfoIn != null && new FilePair(appInfoIn.FilePath, plistOut).NeedsBuilding())) { return(true); } //launch script var ls = conf.LaunchScript; if (!File.Exists(ls)) { return(true); } //pkginfo if (!File.Exists(conf.AppDirectory.Combine("Contents", "PkgInfo"))) { return(true); } return(false); }
static bool Sign(IProgressMonitor monitor, string bundleKey, string path) { var args = new ProcessArgumentBuilder(); args.Add("-v", "-f", "-s"); args.AddQuoted(bundleKey, path); var psi = new ProcessStartInfo("codesign", args.ToString()); if (MacBuildUtilities.ExecuteBuildCommand(monitor, psi) != 0) { monitor.ReportError("Signing failed", null); return(false); } return(true); }
public static BuildResult CompileXibFiles(IProgressMonitor monitor, IEnumerable <ProjectFile> files, FilePath outputRoot) { var result = new BuildResult(); var ibfiles = GetIBFilePairs(files, outputRoot).Where(NeedsBuilding).ToList(); if (ibfiles.Count > 0) { monitor.BeginTask(GettextCatalog.GetString("Compiling interface definitions"), 0); foreach (var file in ibfiles) { file.EnsureOutputDirectory(); var args = new ProcessArgumentBuilder(); args.Add("--errors", "--warnings", "--notices", "--output-format", "human-readable-text"); args.AddQuoted(file.Input); args.Add("--compile"); args.AddQuoted(file.Output); var ibtoolPath = AppleSdkSettings.DeveloperRoot.Combine("usr", "bin", "ibtool"); var psi = new ProcessStartInfo(ibtoolPath, args.ToString()); monitor.Log.WriteLine(psi.FileName + " " + psi.Arguments); int code; try { code = MacBuildUtilities.ExecuteBuildCommand(monitor, psi); } catch (System.ComponentModel.Win32Exception ex) { LoggingService.LogError("Error running ibtool", ex); result.AddError(null, 0, 0, null, "ibtool not found. Please ensure the Apple SDK is installed."); return(result); } if (monitor.IsCancelRequested) { return(result); } if (code != 0) { result.AddError(null, 0, 0, null, "ibtool returned error code " + code); return(result); } } monitor.EndTask(); } return(result); }
public static bool BuildPackage(IProgressMonitor monitor, MonoMacProject project, ConfigurationSelector conf, MonoMacPackagingSettings settings, FilePath target) { string bundleKey = settings.BundleSigningKey; string packageKey = settings.PackageSigningKey; if (settings.SignBundle || (settings.CreatePackage && settings.SignPackage)) { var identities = Keychain.GetAllSigningIdentities(); if (string.IsNullOrEmpty(bundleKey)) { bundleKey = identities.FirstOrDefault(k => k.StartsWith(MonoMacPackagingSettingsWidget.APPLICATION_PREFIX)); if (string.IsNullOrEmpty(bundleKey)) { monitor.ReportError("Did not find default app signing key", null); return(false); } else if (!identities.Any(k => k == bundleKey)) { monitor.ReportError("Did not find app signing key in keychain", null); return(false); } } if (string.IsNullOrEmpty(packageKey)) { packageKey = identities.FirstOrDefault(k => k.StartsWith(MonoMacPackagingSettingsWidget.INSTALLER_PREFIX)); if (string.IsNullOrEmpty(packageKey)) { monitor.ReportError("Did not find default package signing key", null); return(false); } else if (!identities.Any(k => k == packageKey)) { monitor.ReportError("Did not find package signing key in keychain", null); return(false); } } } if (project.NeedsBuilding(conf)) { BuildResult res = project.Build(monitor, conf); if (res.ErrorCount > 0) { foreach (BuildError e in res.Errors) { monitor.ReportError(e.ToString(), null); } monitor.ReportError(GettextCatalog.GetString("The project failed to build."), null); return(false); } } var cfg = (MonoMacProjectConfiguration)project.GetConfiguration(conf); FilePath tempDir = "/tmp/monomac-build-" + DateTime.Now.Ticks; FilePath workingApp = tempDir.Combine(cfg.AppDirectory.FileName); try { //user will have agreed to overwrite when they picked the target if (Directory.Exists(target)) { Directory.Delete(target); } else if (File.Exists(target)) { File.Delete(target); } monitor.BeginTask(GettextCatalog.GetString("Creating app bundle"), 0); var files = Directory.GetFiles(cfg.AppDirectory, "*", SearchOption.AllDirectories); HashSet <string> createdDirs = new HashSet <string> (); foreach (FilePath f in files) { var rel = f.ToRelative(cfg.AppDirectory); var parentDir = rel.ParentDirectory; if (settings.IncludeMono) { if (parentDir.IsNullOrEmpty || parentDir == "." || parentDir == "Contents/MacOS") { continue; } var ext = rel.Extension; if (ext == ".mdb" || ext == ".exe" || ext == ".dll") { continue; } } if (monitor.IsCancelRequested) { return(false); } if (createdDirs.Add(parentDir)) { Directory.CreateDirectory(workingApp.Combine(parentDir)); } monitor.Log.WriteLine(rel); File.Copy(f, workingApp.Combine(rel)); } monitor.EndTask(); if (settings.IncludeMono) { monitor.BeginTask(GettextCatalog.GetString("Merging Mono into app bundle"), 0); var args = new ProcessArgumentBuilder(); switch (settings.LinkerMode) { case MonoMacLinkerMode.LinkNone: args.Add("--nolink"); break; case MonoMacLinkerMode.LinkFramework: args.Add("--linksdkonly"); break; case MonoMacLinkerMode.LinkAll: // nothing break; } args.Add("-o"); args.AddQuoted(tempDir); args.Add("-n"); args.AddQuoted(cfg.AppName); var assemblies = project.GetReferencedAssemblies(conf, true); foreach (var a in assemblies) { args.Add("-a"); args.AddQuoted(a); } args.AddQuoted(cfg.CompiledOutputName); string mmpPath = Mono.Addins.AddinManager.CurrentAddin.GetFilePath("mmp"); //FIXME: workaround for Mono.Addins losing the executable bit during packaging var mmpInfo = new Mono.Unix.UnixFileInfo(mmpPath); if ((mmpInfo.FileAccessPermissions & Mono.Unix.FileAccessPermissions.UserExecute) == 0) { mmpInfo.FileAccessPermissions |= Mono.Unix.FileAccessPermissions.UserExecute; } var psi = new ProcessStartInfo(mmpPath, args.ToString()); if (MacBuildUtilities.ExecuteBuildCommand(monitor, psi) != 0) { monitor.ReportError("Merging Mono failed", null); return(false); } var plistFile = workingApp.Combine("Contents", "Info.plist"); var plistDoc = new PlistDocument(); plistDoc.LoadFromXmlFile(plistFile); ((PlistDictionary)plistDoc.Root)["MonoBundleExecutable"] = cfg.CompiledOutputName.FileName; plistDoc.WriteToFile(plistFile); monitor.EndTask(); } //TODO: verify bundle details if for app store? if (settings.SignBundle) { monitor.BeginTask(GettextCatalog.GetString("Signing app bundle"), 0); // Sign any dynamic libraries, before we sign the bundle var dylibs = Directory.GetFiles(workingApp, "*.dylib", SearchOption.AllDirectories); foreach (var dylib in dylibs) { if (!Sign(monitor, bundleKey, dylib)) { return(false); } } if (!Sign(monitor, bundleKey, workingApp)) { return(false); } monitor.EndTask(); } if (settings.CreatePackage) { monitor.BeginTask(GettextCatalog.GetString("Creating installer"), 0); var args = new ProcessArgumentBuilder(); args.Add("--component"); args.AddQuoted(workingApp); args.Add("/Applications"); if (settings.SignPackage) { args.Add("--sign"); args.AddQuoted(packageKey); } if (!settings.ProductDefinition.IsNullOrEmpty) { args.Add("--product"); args.AddQuoted(settings.ProductDefinition); } args.AddQuoted(target); var psi = new ProcessStartInfo("productbuild", args.ToString()); try { if (MacBuildUtilities.ExecuteBuildCommand(monitor, psi) != 0) { monitor.ReportError("Package creation failed", null); return(false); } } catch (System.ComponentModel.Win32Exception) { monitor.ReportError("productbuild not found", null); return(false); } monitor.EndTask(); } else { Directory.Move(workingApp, target); } } finally { try { if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, true); } } catch (Exception ex) { LoggingService.LogError("Error removing temp directory", ex); } } return(true); }
protected override BuildResult Build(IProgressMonitor monitor, SolutionEntityItem item, ConfigurationSelector configuration) { var proj = item as MonoMacProject; if (proj == null || proj.CompileTarget != CompileTarget.Exe) { return(base.Build(monitor, item, configuration)); } var conf = (MonoMacProjectConfiguration)configuration.GetConfiguration(item); var resDir = conf.AppDirectory.Combine("Contents", "Resources"); var appDir = conf.AppDirectory; //make sure the codebehind files are updated before building var res = MacBuildUtilities.UpdateCodeBehind(monitor, proj.CodeBehindGenerator, proj.Files); if (res.ErrorCount > 0) { return(res); } res = res.Append(base.Build(monitor, item, configuration)); if (res.ErrorCount > 0) { return(res); } //copy exe, mdb, refs, copy-to-output, Content files to Resources var filesToCopy = GetCopyFiles(proj, configuration, conf).Where(NeedsBuilding).ToList(); if (filesToCopy.Count > 0) { monitor.BeginTask("Copying resource files to app bundle", filesToCopy.Count); foreach (var f in filesToCopy) { f.EnsureOutputDirectory(); File.Copy(f.Input, f.Output, true); monitor.Log.WriteLine("Copied {0}", f.Output.ToRelative(appDir)); monitor.Step(1); } monitor.EndTask(); } if (!PropertyService.IsMac) { res.AddWarning("Cannot compile xib files on non-Mac platforms"); } else { //Interface Builder files if (res.Append(MacBuildUtilities.CompileXibFiles(monitor, proj.Files, resDir)).ErrorCount > 0) { return(res); } } //info.plist var plistOut = conf.AppDirectory.Combine("Contents", "Info.plist"); var appInfoIn = proj.Files.GetFile(proj.BaseDirectory.Combine("Info.plist")); if (new FilePair(proj.FileName, plistOut).NeedsBuilding() || (appInfoIn != null && new FilePair(appInfoIn.FilePath, plistOut).NeedsBuilding())) { if (res.Append(MergeInfoPlist(monitor, proj, conf, appInfoIn, plistOut)).ErrorCount > 0) { return(res); } } //launch script var ls = conf.LaunchScript; if (!File.Exists(ls)) { if (!Directory.Exists(ls.ParentDirectory)) { Directory.CreateDirectory(ls.ParentDirectory); } var src = AddinManager.CurrentAddin.GetFilePath("MonoMacLaunchScript.sh"); File.Copy(src, ls, true); var fi = new UnixFileInfo(ls); fi.FileAccessPermissions |= FileAccessPermissions.UserExecute | FileAccessPermissions.GroupExecute | FileAccessPermissions.OtherExecute; } //pkginfo var pkgInfo = conf.AppDirectory.Combine("Contents", "PkgInfo"); if (!File.Exists(pkgInfo)) { using (var f = File.OpenWrite(pkgInfo)) f.Write(new byte [] { 0X41, 0X50, 0X50, 0X4C, 0x3f, 0x3f, 0x3f, 0x3f }, 0, 8); // "APPL???" } return(res); }
protected override BuildResult Build(IProgressMonitor monitor, SolutionEntityItem item, ConfigurationSelector configuration) { var proj = item as MonoMacProject; if (proj == null || proj.CompileTarget != CompileTarget.Exe) { return(base.Build(monitor, item, configuration)); } var conf = (MonoMacProjectConfiguration)configuration.GetConfiguration(item); var resDir = conf.AppDirectory.Combine("Contents", "Resources"); var appDir = conf.AppDirectory; //make sure the codebehind files are updated before building var res = MacBuildUtilities.UpdateCodeBehind(monitor, proj.CodeBehindGenerator, proj.Files); if (res.ErrorCount > 0) { return(res); } res = res.Append(base.Build(monitor, item, configuration)); if (res.ErrorCount > 0) { return(res); } //copy exe, mdb, refs, copy-to-output, Content files to Resources var filesToCopy = GetCopyFiles(proj, configuration, conf).Where(NeedsBuilding).ToList(); if (filesToCopy.Count > 0) { monitor.BeginTask("Copying resource files to app bundle", filesToCopy.Count); foreach (var f in filesToCopy) { f.EnsureOutputDirectory(); File.Copy(f.Input, f.Output, true); monitor.Log.WriteLine("Copied {0}", f.Output.ToRelative(appDir)); monitor.Step(1); } monitor.EndTask(); } //FIXME: only do this check if there are actually xib files if (!PropertyService.IsMac) { res.AddWarning("Cannot compile xib files on non-Mac platforms"); } else { //Interface Builder files if (res.Append(MacBuildUtilities.CompileXibFiles(monitor, proj.Files, resDir)).ErrorCount > 0) { return(res); } } //info.plist var plistOut = conf.AppDirectory.Combine("Contents", "Info.plist"); var appInfoIn = proj.Files.GetFile(proj.BaseDirectory.Combine("Info.plist")); if (new FilePair(proj.FileName, plistOut).NeedsBuilding() || (appInfoIn != null && new FilePair(appInfoIn.FilePath, plistOut).NeedsBuilding())) { if (res.Append(MergeInfoPlist(monitor, proj, conf, appInfoIn, plistOut)).ErrorCount > 0) { return(res); } } if (PropertyService.IsWindows) { res.AddWarning("Cannot create app bundle on Windows"); } else { //launch script var macOSDir = appDir.Combine("Contents", "MacOS"); CopyExecutableFile(AddinManager.CurrentAddin.GetFilePath("MonoMacLaunchScript.sh"), conf.LaunchScript); CopyExecutableFile(AddinManager.CurrentAddin.GetFilePath("mono-version-check"), macOSDir.Combine("mono-version-check")); var si = new UnixSymbolicLinkInfo(appDir.Combine(conf.AppName)); if (!si.Exists) { si.CreateSymbolicLinkTo("/Library/Frameworks/Mono.framework/Versions/Current/bin/mono"); } } //pkginfo var pkgInfo = conf.AppDirectory.Combine("Contents", "PkgInfo"); if (!File.Exists(pkgInfo)) { using (var f = File.OpenWrite(pkgInfo)) f.Write(new byte [] { 0X41, 0X50, 0X50, 0X4C, 0x3f, 0x3f, 0x3f, 0x3f }, 0, 8); // "APPL???" } return(res); }