Exemple #1
0
		public ProjectFile GetInfoPlist ()
		{
			var name = BaseDirectory.Combine ("Info.plist");
			var pf = Files.GetFile (name);
			if (pf != null)
				return pf;
			
			var doc = new PlistDocument ();
			doc.Root = new PlistDictionary ();
			doc.WriteToFile (name);
			return AddFile (name);
		}
		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, true);
				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;
		}
		public void Store (IPhoneProject proj)
		{
			proj.BundleDevelopmentRegion = NullIfEmpty (devRegionEntry.Text);
			proj.BundleIdentifier = NullIfEmpty (bundleIdEntry.Text);
			proj.BundleVersion = NullIfEmpty (bundleVersionEntry.Text);
			proj.BundleDisplayName = NullIfEmpty (displayNameEntry.Text);
			proj.MainNibFile = NullIfEmpty (mainNibPicker.SelectedFile);
			proj.MainNibFileIPad = NullIfEmpty (iPadNibPicker.SelectedFile);
			
			proj.BundleIcon = NullIfEmpty (iphoneIconPicker.SelectedFile);
			proj.BundleIconHigh = NullIfEmpty (iphoneIconHighPicker.SelectedFile);
			proj.BundleIconIPad = NullIfEmpty (ipadIconPicker.SelectedFile);
			proj.BundleIconSpotlight = NullIfEmpty (settingsIconPicker.SelectedFile);
			proj.BundleIconSpotlightHigh = NullIfEmpty (settingsIconHighPicker.SelectedFile);
			proj.BundleIconIPadSpotlight = NullIfEmpty (ipadSpotlightIconPicker.SelectedFile);
			
			proj.SupportedDevices = SupportedDevices;
			
			if (badPlist)
				return;
			try {
				var pf = proj.GetInfoPlist ();
				var doc = new PlistDocument ();
				doc.LoadFromXmlFile (pf.FilePath);
				var dict = doc.Root as PlistDictionary;
				if (dict == null)
					doc.Root = dict = new PlistDictionary ();
				
				var orientations = SaveOrientationsCombo (supportedOrientationsCombo);
				if (orientations != null)
					dict [OrientationUtil.KEY] = orientations;
				else
					dict.Remove (OrientationUtil.KEY);
				
				var iPadOrientations = SaveOrientationsCombo (iPadOrientationsCombo);
				if (proj.SupportedDevices == TargetDevice.IPhoneAndIPad && iPadOrientations != null)
					dict [OrientationUtil.KEY_IPAD] = iPadOrientations;
				else
					dict.Remove (OrientationUtil.KEY_IPAD);
				
				doc.WriteToFile (pf.FilePath);
			} catch (Exception ex) {
				badPlist = true;
				MonoDevelop.Ide.MessageService.ShowException (ex, "Error saving Info.plist.");
			}
		}
		public static BuildResult CreateMergedPlist (IProgressMonitor monitor, 
			ProjectFile template, string outPath,
			Func<PlistDocument,BuildResult> merge)
		{
			var result = new BuildResult ();
			
			var doc = new PlistDocument ();
			if (template != null) {
				try {
					doc.LoadFromXmlFile (template.FilePath);
				} catch (Exception ex) {
					if (ex is XmlException)
						result.AddError (template.FilePath, ((XmlException)ex).LineNumber,
						                 ((XmlException)ex).LinePosition, null, ex.Message);
					else
						result.AddError (template.FilePath, 0, 0, null, ex.Message);
					monitor.ReportError (GettextCatalog.GetString ("Could not load file '{0}': {1}",
					                                               template.FilePath, ex.Message), null);
					return result;
				}
			}
			
			try {
				if (result.Append (merge (doc)).ErrorCount > 0)
					return result;
			} catch (Exception ex) {
				result.AddError ("Error merging Info.plist: " + ex.Message);
				LoggingService.LogError ("Error merging Info.plist", ex);
				return result;
			}
			
			try {
				EnsureDirectoryForFile (outPath);
				doc.WriteToFile (outPath);
			} catch (Exception ex) {
				result.AddError (outPath, 0, 0, null, ex.Message);
				monitor.ReportError (GettextCatalog.GetString ("Could not write file '{0}'", outPath), ex);
			}
			return result;
		}
		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.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 {
				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 ();
					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");
					var psi = new ProcessStartInfo (mmpPath, args.ToString ());
					monitor.Log.WriteLine ("mmp " + psi.Arguments);
					
					string err;
					if (MacBuildUtilities.ExecuteCommand (monitor, psi, out err) != 0) {
						monitor.Log.WriteLine (err);
						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);
					
					var args = new ProcessArgumentBuilder ();
					args.Add ("-v", "-f", "-s");
					args.AddQuoted (bundleKey, workingApp);
					
					var psi = new ProcessStartInfo ("codesign", args.ToString ());
					monitor.Log.WriteLine ("codesign " + psi.Arguments);
					
					string err;
					if (MacBuildUtilities.ExecuteCommand (monitor, psi, out err) != 0) {
						monitor.Log.WriteLine (err);
						monitor.ReportError ("Signing failed", null);
						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", "--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 ());
					monitor.Log.WriteLine ("productbuild " + psi.Arguments);
					
					string err;
					if (MacBuildUtilities.ExecuteCommand (monitor, psi, out err) != 0) {
						monitor.Log.WriteLine (err);
						monitor.ReportError ("Package creation failed", 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;
		}