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);
        }
Beispiel #3
0
        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);
        }
Beispiel #5
0
        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);
        }
Beispiel #7
0
        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);
        }