Example #1
0
        public static string GetStoredCertificateName(IPhoneProject project, bool distribution)
        {
            var keys = project.UserProperties.GetValue <SigningKeyInformation> ("IPhoneSigningKeys");

            if (keys != null)
            {
                string key = distribution? keys.Distribution : keys.Developer;
                if (!String.IsNullOrEmpty(key))
                {
                    return(key);
                }
            }
            return(null);
        }
		public static BuildResult UpdateXibCodebehind (CodeBehindWriter writer, IPhoneProject project,
		                                               IEnumerable<ProjectFile> allFiles, bool forceRegen)
		{
			BuildResult result = null;
			var projWrite = File.GetLastWriteTime (project.FileName);
			
			foreach (var xibFile in allFiles.Where (x => x.FilePath.Extension == ".xib" && x.BuildAction == BuildAction.Page)) {
				var designerFile = GetDesignerFile (xibFile);
				if (designerFile == null)
					continue;
				
				try {
					var designerWrite = File.GetLastWriteTime (designerFile.FilePath);
					if (forceRegen || designerWrite < projWrite || designerWrite < File.GetLastWriteTime (xibFile.FilePath)) {
						GenerateDesignerCode (writer, xibFile, designerFile);
					}
				} catch (Exception ex) {
					result = result ?? new BuildResult ();
					result.AddError (xibFile.FilePath, 0, 0, null, ex.Message);
					LoggingService.LogError (String.Format ("Error generating code for xib file '{0}'", xibFile.FilePath), ex);
				}
			}
			return result;
		}
		static bool AddIconRelativeIfNotEmpty (IPhoneProject proj, PlistArray arr, FilePath iconFullPath, string name)
		{
			var icon = iconFullPath.ToRelative (proj.BaseDirectory).ToString ();
			if (string.IsNullOrEmpty (icon) || icon == ".")
				return false;
			arr.Add (null ?? icon);
			return true;
		}
		static bool AddIconRelativeIfNotEmpty (IPhoneProject proj, PlistArray arr, FilePath iconFullPath)
		{
			return AddIconRelativeIfNotEmpty (proj, arr, iconFullPath, null);
		}
		public IPhoneCodeBehind (IPhoneProject project) : base (project)
		{
		}
Example #6
0
        void GenerateXCodeProject(IPhoneProject proj, IPhoneProjectConfiguration conf, ConfigurationSelector slnConf)
        {
            string mtouchPath = IPhoneUtility.GetMtouchPath(proj.TargetRuntime, proj.TargetFramework);

            var xcodeDir = conf.OutputDirectory.Combine("XcodeProject");

            if (!Directory.Exists(xcodeDir))
            {
                try {
                    Directory.CreateDirectory(xcodeDir);
                } catch (IOException ex) {
                    MessageService.ShowException(ex, "Failed to create directory '" + xcodeDir + "' for Xcode project");
                    return;
                }
            }

            var args = new System.Text.StringBuilder();

            args.AppendFormat("-xcode=\"{0}\" -v", xcodeDir);
            foreach (ProjectFile pf in proj.Files)
            {
                if (pf.BuildAction == BuildAction.Content)
                {
                    var rel = pf.ProjectVirtualPath;
                    args.AppendFormat(" -res=\"{0}\",\"{1}\"", pf.FilePath, rel);

                    //hack around mtouch 1.0 bug. create resource directories
                    string subdir = rel.ParentDirectory;
                    if (string.IsNullOrEmpty(subdir))
                    {
                        continue;
                    }
                    subdir = xcodeDir.Combine(subdir);
                    try {
                        if (!Directory.Exists(subdir))
                        {
                            Directory.CreateDirectory(subdir);
                        }
                    } catch (IOException ex) {
                        MessageService.ShowException(ex, "Failed to create directory '" + subdir + "' for Xcode project");
                        return;
                    }
                }
                else if (pf.BuildAction == BuildAction.Page)
                {
                    args.AppendFormat(" -res=\"{0}\"", pf.FilePath);
                }
            }

            args.AppendFormat(" -res=\"{0}\",\"Info.plist\"", conf.AppDirectory.Combine("Info.plist"));

            foreach (string asm in proj.GetReferencedAssemblies(slnConf).Distinct())
            {
                args.AppendFormat(" -r=\"{0}\"", asm);
            }

            IPhoneBuildExtension.AppendExtrasMtouchArgs(args, proj, conf);
            args.AppendFormat(" \"{0}\"", conf.CompiledOutputName);

            string argStr = args.ToString();

            var console = (IConsole)IdeApp.Workbench.ProgressMonitors.GetOutputProgressMonitor(
                GettextCatalog.GetString("Generate Xcode project"), MonoDevelop.Ide.Gui.Stock.RunProgramIcon, true, true);

            console.Log.WriteLine(mtouchPath + " " + argStr);
            Runtime.ProcessService.StartConsoleProcess(mtouchPath, argStr, conf.OutputDirectory, console, null);
        }
		static BuildResult GetIdentity (IProgressMonitor monitor, IPhoneProject proj, IPhoneProjectConfiguration conf,
		                                out IPhoneAppIdentity identity)
		{
			var result = new BuildResult ();
			identity = new IPhoneAppIdentity ();
			bool defaultID = string.IsNullOrEmpty (proj.BundleIdentifier);
			
			if (!defaultID)
				identity.BundleID = proj.BundleIdentifier;
			
			//treat empty as "developer automatic"
			if (string.IsNullOrEmpty (conf.CodesignKey)) {
				conf.CodesignKey = IPhoneProject.DEV_CERT_PREFIX;
			}
			
			IList<X509Certificate2> certs = null;
			if (conf.CodesignKey == IPhoneProject.DEV_CERT_PREFIX || conf.CodesignKey == IPhoneProject.DIST_CERT_PREFIX) {
				certs = Keychain.FindNamedSigningCertificates (x => x.StartsWith (conf.CodesignKey)).ToList ();
				if (certs.Count == 0) {
					result.AddError ("No valid iPhone code signing keys found in keychain.");
					return result;
				}
			} else {
				identity.SigningKey = Keychain.FindNamedSigningCertificates (x => x == conf.CodesignKey).FirstOrDefault ();
				if (identity.SigningKey == null) {
					result.AddError (string.Format ("iPhone code signing key '{0}' not found in keychain.", conf.CodesignKey));
					return result;
				}
				certs = new X509Certificate2[] { identity.SigningKey };
			}
			
			if (!string.IsNullOrEmpty (conf.CodesignProvision)) {
				//if the profile was installed by Xcode, we can determine the filename directly from the UUID
				//but if it was installed by iTunes, we need to search all profiles for the UUID.
				var file = MobileProvision.ProfileDirectory.Combine (conf.CodesignProvision).ChangeExtension (".mobileprovision");
				if (File.Exists (file)) {
					try {
						identity.Profile = MobileProvision.LoadFromFile (file);
					} catch (Exception ex) {
						string msg = "Could not read provisioning profile '" + file + "'.";
						monitor.ReportError (msg, ex);
						result.AddError (msg);
						return result;
					}
				} else {
					identity.Profile = MobileProvision.GetAllInstalledProvisions ()
						.Where (p => p.Uuid == conf.CodesignProvision).FirstOrDefault ();
				}
				
				if (identity.Profile == null) {
					result.AddError (string.Format ("The specified provisioning profile '{0}' could not be found", conf.CodesignProvision));
					return result;
				}
				
				var prof = identity.Profile; //capture ref for lambda
				identity.SigningKey = certs.Where (c => prof.DeveloperCertificates
				                           .Any (p => p.Thumbprint == c.Thumbprint)).FirstOrDefault ();
				if (identity.SigningKey == null) {
					result.AddError (string.Format ("No iPhone code signing key matches specified provisioning profile '{0}'.", conf.CodesignProvision));
					return result;
				}
				
				if (defaultID) {
					identity.BundleID = GetDefaultBundleID (proj, GetProfileBundleID (identity.Profile));
					result.AddWarning (string.Format ("Project does not have bundle identifier specified. Generated '{0}' to match provisioning profile.", identity.BundleID));
				}
				
				bool exact;
				identity.AppID = ConstructValidAppId (identity.Profile, identity.BundleID, out exact);
				if (identity.AppID == null) {
					result.AddError (string.Format (
						"Project bundle ID '{0}' does not match specified provisioning profile '{1}'", identity.BundleID, conf.CodesignProvision));
					return result;
				}
				return result;
			}
			
			var pairs = (from p in MobileProvision.GetAllInstalledProvisions ()
				from c in certs
				where p.DeveloperCertificates.Any (d => d.Thumbprint == c.Thumbprint)
				select new { Cert = c, Profile = p }).ToList ();
				
			if (pairs.Count == 0) {
				result.AddError ("No installed provisioning profiles match the installed iPhone code signing keys.");
				return result;
			}
			
			if (!defaultID) {
				//find a provisioning profile with compatible appid, preferring exact match
				foreach (var p in pairs) {
					bool exact;
					var id = ConstructValidAppId (p.Profile, identity.BundleID, out exact);
					if (id != null) {
						if (exact || identity.AppID == null) {
							identity.Profile = p.Profile;
							identity.SigningKey = p.Cert;
							identity.AppID = id;
						}
						if (exact)
							break;
					}
				}
			} else {
				//pick provisioning profile to provide appid and better default bundle ID, preferring star bundle IDs
				foreach (var p in pairs) {
					var suggestion = GetProfileBundleID (p.Profile);
					bool star = (suggestion != null) && suggestion.EndsWith ("*");
					if (star || identity.Profile == null) {
						identity.Profile = p.Profile;
						identity.SigningKey = p.Cert;
						identity.BundleID = GetDefaultBundleID (proj, suggestion);
						bool exact;
						identity.AppID = ConstructValidAppId (p.Profile, identity.BundleID, out exact);
					}
					if (star)
						break;
				}
				result.AddWarning (string.Format ("Project does not have bundle identifier specified. Generated '{0}' to match an installed provisioning profile.", identity.BundleID));
			}
			
			if (identity.Profile != null && identity.SigningKey != null && identity.AppID != null)
				return result;
			
			if (identity.SigningKey != null) {
				result.AddError (string.Format (
					"Bundle identifier '{0}' does not match any installed provisioning profile for selected signing identity '{0}'.",
					identity.BundleID, identity.SigningKey));
			} else {
				result.AddError (string.Format (
					"Bundle identifier '{0}' does not match any installed provisioning profile.",
					identity.BundleID));
			}
			return result;
		}
		static IEnumerable<FilePair> GetIconContentFiles (IPhoneSdkVersion sdkVersion, IPhoneProject proj,
			IPhoneProjectConfiguration conf)
		{
			bool v3_2_orNewer = sdkVersion >= IPhoneSdkVersion.V3_2;
			bool v4_0_orNewer = sdkVersion >= IPhoneSdkVersion.V4_0;
			bool supportsIPhone = (proj.SupportedDevices & TargetDevice.IPhone) != 0;
			bool supportsIPad = (proj.SupportedDevices & TargetDevice.IPad) != 0;
			var appDir = conf.AppDirectory;
			
			if (supportsIPhone && !proj.BundleIcon.IsNullOrEmpty)
					yield return new FilePair (proj.BundleIcon, appDir.Combine ("Icon.png"));
			
			if (!proj.BundleIconSpotlight.IsNullOrEmpty)
				yield return new FilePair (proj.BundleIconSpotlight, appDir.Combine ("Icon-Small.png"));
			
			if (v3_2_orNewer && supportsIPad) {
				if (!proj.BundleIconIPad.IsNullOrEmpty)
					yield return new FilePair (proj.BundleIconIPad, appDir.Combine ("Icon-72.png"));
				if (!proj.BundleIconIPadSpotlight.IsNullOrEmpty)
					yield return new FilePair (proj.BundleIconIPadSpotlight, appDir.Combine ("Icon-Small-50.png"));
			}
			
			if (supportsIPhone && v4_0_orNewer) {
				if (!proj.BundleIconHigh.IsNullOrEmpty)
					yield return new FilePair (proj.BundleIconHigh, appDir.Combine ("*****@*****.**"));
				if (!proj.BundleIconSpotlightHigh.IsNullOrEmpty)
					yield return new FilePair (proj.BundleIconSpotlightHigh, appDir.Combine ("*****@*****.**"));
			}
		}
		static string GetFilteredProjectName (IPhoneProject project)
		{
			var sb = new StringBuilder ();
			foreach (char c in project.Name)
				if (char.IsLetterOrDigit (c))
					sb.Append (c);
			return sb.Length > 0? sb.ToString ().ToLowerInvariant () : "application";
		}
		static string GetDefaultBundleID (IPhoneProject project, string suggestion)
		{
			if (string.IsNullOrEmpty (suggestion)) {
				return "com.yourcompany." + GetFilteredProjectName (project);
			} else if (suggestion.EndsWith ("*")) {
				return suggestion.Substring (0, suggestion.Length - 1) + GetFilteredProjectName (project);
			} else {
				return suggestion;
			}
		}
		static internal void AppendExtrasMtouchArgs (ProcessArgumentBuilder args, IPhoneSdkVersion sdkVersion, 
			IPhoneProject proj, IPhoneProjectConfiguration conf)
		{
			if (conf.MtouchDebug)
				args.Add ("-debug");
			
			switch (conf.MtouchLink) {
			case MtouchLinkMode.SdkOnly:
				args.Add ("-linksdkonly");
				break;
			case MtouchLinkMode.None:
				args.Add ("-nolink");
				break;
			case MtouchLinkMode.Full:
			default:
				break;
			}
			
			if (!string.IsNullOrEmpty (conf.MtouchI18n)) {
				args.AddQuotedFormat ("-i18n={0}", conf.MtouchI18n);
			}
			
			if (!sdkVersion.Equals (IPhoneSdkVersion.V3_0))
				args.AddQuotedFormat ("-sdk={0}", sdkVersion);
			
			if (conf.MtouchMinimumOSVersion != "3.0")
				args.AddQuotedFormat ("-targetver={0}", conf.MtouchMinimumOSVersion);
			
			
			AddExtraArgs (args, conf.MtouchExtraArgs, proj, conf);
		}
		internal ProcessStartInfo GetTool (string tool, IPhoneProject project, IProgressMonitor monitor,
		                                   out BuildResult error)
		{
			var toolPath = project.TargetRuntime.GetToolPath (project.TargetFramework, tool);
			if (String.IsNullOrEmpty (toolPath)) {
				var err = GettextCatalog.GetString ("Error: Unable to find '" + tool + "' tool.");
				monitor.ReportError (err, null);
				error = new BuildResult ();
				error.AddError (null, 0, 0, null, err);
				return null;
			}
			
			error = null;
			return new ProcessStartInfo (toolPath) {
				UseShellExecute = false,
				RedirectStandardError = true,
				RedirectStandardOutput = true,
			};
		}
		BuildResult UpdateInfoPlist (IProgressMonitor monitor, IPhoneProject proj, IPhoneProjectConfiguration conf,
		                             IPhoneAppIdentity identity, ProjectFile template, string plistOut)
		{
			return CreateMergedPlist (monitor, conf, template, plistOut, 
				(IPhoneProjectConfiguration config, PlistDocument doc) => 
			{
				var result = new BuildResult ();
				var dict = doc.Root as PlistDictionary;
				if (dict == null)
					doc.Root = dict = new PropertyList.PlistDictionary ();
				
				bool sim = conf.Platform != IPhoneProject.PLAT_IPHONE;
				
				SetIfNotPresent (dict, "CFBundleDevelopmentRegion",
					String.IsNullOrEmpty (proj.BundleDevelopmentRegion)? "English" : proj.BundleDevelopmentRegion);
				
				SetIfNotPresent (dict, "CFBundleDisplayName", proj.BundleDisplayName ?? proj.Name);
				SetIfNotPresent (dict, "CFBundleExecutable", conf.NativeExe.FileName);
				
				//iphone icons
				if ((proj.SupportedDevices & TargetDevice.IPhone) != 0) {
					if (!dict.ContainsKey ("CFBundleIconFile")) {
						var icon = proj.BundleIcon.ToRelative (proj.BaseDirectory);
						if (icon.IsNullOrEmpty || icon.ToString () == ".")
							result.AddWarning ("Application bundle icon has not been set");
						else
							dict ["CFBundleIconFile"] = icon.FileName;
					}
				}
				
				//ipad and universal icons
				if ((proj.SupportedDevices & TargetDevice.IPad) != 0 && !dict.ContainsKey ("CFBundleIconFiles")) {
					var arr = new PlistArray ();
					dict["CFBundleIconFiles"] = arr;
					//universal only
					if ((proj.SupportedDevices & TargetDevice.IPhone) != 0)
						AddRelativeIfNotEmpty (proj, arr, proj.BundleIcon);
					//ipad and universal
					AddRelativeIfNotEmpty (proj, arr, proj.BundleIconSpotlight);
					AddRelativeIfNotEmpty (proj, arr, proj.BundleIconIPadSpotlight);
					if (!AddRelativeIfNotEmpty (proj, arr, proj.BundleIconIPad))
						result.AddWarning ("iPad bundle icon has not been set");
				}
				
				SetIfNotPresent (dict, "CFBundleIdentifier", identity.BundleID);
				SetIfNotPresent (dict, "CFBundleInfoDictionaryVersion", "6.0");
				SetIfNotPresent (dict, "CFBundleName", proj.Name);
				SetIfNotPresent (dict, "CFBundlePackageType", "APPL");
				if (!sim)
					dict["CFBundleResourceSpecification"] = "ResourceRules.plist";
				SetIfNotPresent (dict, "CFBundleSignature", "????");
				SetIfNotPresent (dict,  "CFBundleSupportedPlatforms",
					new PropertyList.PlistArray () { sim? "iPhoneSimulator" : "iPhoneOS" });
				SetIfNotPresent (dict, "CFBundleVersion", proj.BundleVersion ?? "1.0");
				SetIfNotPresent (dict, "DTPlatformName", sim? "iphonesimulator" : "iphoneos");
				SetIfNotPresent (dict, "DTSDKName", (sim? "iphonesimulator" : "iphoneos")  + conf.MtouchSdkVersion);
				SetIfNotPresent (dict,  "LSRequiresIPhoneOS", true);
				if (proj.SupportedDevices != TargetDevice.IPhone)
					SetIfNotPresent (dict,  "UIDeviceFamily", GetSupportedDevices (proj.SupportedDevices));
				SetIfNotPresent (dict, "DTPlatformVersion", conf.MtouchSdkVersion);
				
				SetIfNotPresent (dict, "MinimumOSVersion", conf.MtouchMinimumOSVersion);
				
				SetNibProperty (dict, proj, proj.MainNibFile, "NSMainNibFile");
				if (proj.SupportedDevices == TargetDevice.IPhoneAndIPad)
					SetNibProperty (dict, proj, proj.MainNibFileIPad, "NSMainNibFile~ipad");
				
				return result;
			});
		}
		static internal void AppendExtrasMtouchArgs (StringBuilder args, IPhoneProject proj, IPhoneProjectConfiguration conf)
		{
			if (conf.MtouchDebug)
				args.Append (" -debug");
			
			switch (conf.MtouchLink) {
			case MtouchLinkMode.SdkOnly:
				args.Append (" -linksdkonly");
				break;
			case MtouchLinkMode.None:
				args.Append (" -nolink");
				break;
			case MtouchLinkMode.Full:
			default:
				break;
			}
			
			if (!string.IsNullOrEmpty (conf.MtouchI18n)) {
				args.Append (" -i18n=");
				args.Append (conf.MtouchI18n);
			}
			
			if (conf.MtouchSdkVersion != "3.0")
				args.AppendFormat (" -sdk=\"{0}\"", conf.MtouchSdkVersion);
			
			if (conf.MtouchMinimumOSVersion != "3.0")
				args.AppendFormat (" -targetver=\"{0}\"", conf.MtouchMinimumOSVersion);
			
			
			AppendExtraArgs (args, conf.MtouchExtraArgs, proj, conf);
		}
		static bool SetNibProperty (PlistDictionary dict, IPhoneProject proj, FilePath mainNibProp, string propName)
		{
			if (!dict.ContainsKey (propName)) {
				if (mainNibProp.IsNullOrEmpty) {
					return false;
				} else {
					string mainNib = mainNibProp.ToRelative (proj.BaseDirectory);
					if (mainNib.EndsWith (".nib") || mainNib.EndsWith (".xib"))
					    mainNib = mainNib.Substring (0, mainNib.Length - 4).Replace ('\\', '/');
					dict[propName] = mainNib;
				}
			}
			return true;
		}
		static BuildResult GenXcent (IProgressMonitor monitor, IPhoneSdkVersion sdkVersion, IPhoneProject proj, 
			IPhoneProjectConfiguration conf, IPhoneAppIdentity identity, out string xcentName)
		{
			xcentName = conf.CompiledOutputName.ChangeExtension (".xcent");
			
			monitor.BeginTask (GettextCatalog.GetString ("Processing entitlements file"), 0);
			
			string srcFile;
			
			if (!string.IsNullOrEmpty (conf.CodesignEntitlements)) {
				if (!File.Exists (conf.CodesignEntitlements))
					return BuildError ("Entitlements file \"" + conf.CodesignEntitlements + "\" not found.");
				srcFile = conf.CodesignEntitlements;
			} else {
				srcFile = "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS" + sdkVersion.ToString ()
					+ ".sdk/Entitlements.plist";
			}
			
			var doc = new PlistDocument ();
			try {
				doc.LoadFromXmlFile (srcFile);
			} catch (Exception ex) {
				monitor.Log.WriteLine (ex.ToString ());
				return BuildError ("Error loading entitlements source file '" + srcFile +"'.");
			}
			
			//insert the app ID into the plist at the beginning
			var oldDict = doc.Root as PlistDictionary;
			var newDict = new PlistDictionary ();
			doc.Root = newDict;
			newDict["application-identifier"] = identity.AppID;
			var keychainGroups = new PlistArray (new [] { identity.AppID } );
			newDict["keychain-access-groups"] = keychainGroups;
			
			//merge in the user's values
			foreach (var item in oldDict) {
				//FIXME: we currently ignore these items, and write our own, but maybe we should do substitutes
				//i.e. $(AppIdentifierPrefix)$(CFBundleIdentifier)
				if (item.Key == "application-identifier") {
					var str = item.Value as PlistString;
					if (str == null || string.IsNullOrEmpty (str.Value) || str.Value.Contains ('$'))
						continue;
				} else if (item.Key == "keychain-access-groups") {
					//special handling, merge into the array
					var keyArr = item.Value as PlistArray;
					foreach (var key in keyArr) {
						var str = key as PlistString;
						if (str != null && !string.IsNullOrEmpty (str.Value) && !str.Value.Contains ('$')) {
							keychainGroups.Add (str.Value);
						}
					}
					continue;
				}
				newDict[item.Key] = item.Value;
			}
			
			//merge in the settings from the provisioning profile, skipping some
			foreach (var item in identity.Profile.Entitlements)
				if (item.Key != "application-identifier" && item.Key != "keychain-access-groups")
					newDict[item.Key] = item.Value;
			
			try {
				WriteXcent (doc, xcentName);
			} catch (Exception ex) {
				monitor.Log.WriteLine (ex.ToString ());
				return BuildError ("Error writing entitlements file '" + xcentName +"'.");
			}
			
			monitor.EndTask ();
			return null;
		}
		internal ProcessStartInfo GetMTouch (IPhoneProject project, IProgressMonitor monitor, out BuildResult error)
		{
			return MacBuildUtilities.GetTool ("mtouch", project, monitor, out error);
		}
		static BuildResult SignAppBundle (IProgressMonitor monitor, IPhoneProject proj, IPhoneProjectConfiguration conf,
		                           X509Certificate2 key, string resRules, string xcent)
		{
			monitor.BeginTask (GettextCatalog.GetString ("Signing application"), 0);
			
			var args = new ProcessArgumentBuilder ();
			args.Add ("-v", "-f", "-s");
			args.AddQuoted (Keychain.GetCertificateCommonName (key));
			args.AddQuotedFormat ("--resource-rules={0}", resRules);
			args.Add ("--entitlements");
			args.AddQuoted (xcent);
			args.AddQuoted (conf.AppDirectory);
			
			AddExtraArgs (args, conf.CodesignExtraArgs, proj, conf);
				
			int signResultCode;
			var psi = new ProcessStartInfo ("codesign") {
				UseShellExecute = false,
				RedirectStandardError = true,
				RedirectStandardOutput = true,
				Arguments = args.ToString (),
			};
			
			monitor.Log.WriteLine ("codesign " + psi.Arguments);
			psi.EnvironmentVariables.Add ("CODESIGN_ALLOCATE",
				"/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate");
			string output;
			if ((signResultCode = MacBuildUtilities.ExecuteCommand (monitor, psi, out output)) != 0) {
				monitor.Log.WriteLine (output);
				return BuildError (string.Format ("Code signing failed with error code {0}. See output for details.", signResultCode));
			}
			monitor.EndTask ();
			
			return null;
		}
		static BuildResult ProcessPackaging (IProgressMonitor monitor, IPhoneSdkVersion sdkVersion, IPhoneProject proj,
			IPhoneProjectConfiguration conf, IPhoneAppIdentity identity)
		{
			//don't bother signing in the sim
			bool isDevice = conf.Platform == IPhoneProject.PLAT_IPHONE;
			if (!isDevice)
				return null;
			
			BuildResult result = new BuildResult ();
			
			var pkgInfo = conf.AppDirectory.Combine ("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);
			
			if (result.Append (CompressResources (monitor, conf)).ErrorCount > 0)
				return result;
			
			if (result.Append (EmbedProvisioningProfile (monitor, conf, identity.Profile)).ErrorCount > 0)
				return result;
			
			string xcent;
			if (result.Append (GenXcent (monitor, sdkVersion, proj, conf, identity, out xcent)).ErrorCount > 0)
				return result;
			
			string resRules;
			if (result.Append (PrepareResourceRules (monitor, sdkVersion, conf, out resRules)).ErrorCount > 0)
				return result;
			
			if (result.Append (SignAppBundle (monitor, proj, conf, identity.SigningKey, resRules, xcent)).ErrorCount > 0)
				return result;
			
			return result;
		}
		static internal void AppendExtrasMtouchArgs (ProcessArgumentBuilder args, IPhoneSdkVersion sdkVersion, 
			IPhoneProject proj, IPhoneProjectConfiguration conf)
		{
			if (conf.MtouchDebug)
				args.Add ("-debug");
			
			switch (conf.MtouchLink) {
			case MtouchLinkMode.SdkOnly:
				args.Add ("-linksdkonly");
				break;
			case MtouchLinkMode.None:
				args.Add ("-nolink");
				break;
			case MtouchLinkMode.Full:
			default:
				break;
			}
			
			if (!string.IsNullOrEmpty (conf.MtouchI18n)) {
				args.AddQuotedFormat ("-i18n={0}", conf.MtouchI18n);
			}
			
			if (!sdkVersion.Equals (IPhoneSdkVersion.V3_0))
				args.AddQuotedFormat ("-sdk={0}", sdkVersion);
			
			if (conf.MtouchMinimumOSVersion != "3.0")
				args.AddQuotedFormat ("-targetver={0}", conf.MtouchMinimumOSVersion);
			
			if (IPhoneFramework.MonoTouchVersion >= new IPhoneSdkVersion (3, 99)) {
				if (conf.MtouchUseSGen)
					args.Add ("--sgen");
				if (conf.MtouchUseLlvm) {
					args.Add ("--llvm");
					switch (conf.MtouchArch) {
					case MtouchArch.ARMv6_ARMv7:
						args.Add ("--fat");
						break;
					case MtouchArch.ARMv7:
						args.Add ("--armv7");
						break;
					}
					if (conf.MtouchArch != MtouchArch.ARMv6 && conf.MtouchUseThumb)
						args.Add ("--thumb");
				}
			}
			
			AddExtraArgs (args, conf.MtouchExtraArgs, proj, conf);
		}
		public static string GetStoredCertificateName (IPhoneProject project, bool distribution)
		{
			var keys = project.UserProperties.GetValue<SigningKeyInformation> ("IPhoneSigningKeys");
			if (keys != null) {
				string key = distribution? keys.Distribution : keys.Developer;
				if (!String.IsNullOrEmpty (key))
					return key;
			}
			return null;
		}
		static void AddExtraArgs (ProcessArgumentBuilder args, string extraArgs, IPhoneProject proj,
			IPhoneProjectConfiguration conf)
		{
			if (!string.IsNullOrEmpty (extraArgs)) {
				var customTags = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase) {
					{ "projectdir",   proj.BaseDirectory },
					{ "solutiondir",  proj.ParentSolution.BaseDirectory },
					{ "appbundledir", conf.AppDirectory },
					{ "targetpath",   conf.CompiledOutputName },
					{ "targetdir",    conf.CompiledOutputName.ParentDirectory },
					{ "targetname",   conf.CompiledOutputName.FileName },
					{ "targetext",    conf.CompiledOutputName.Extension },
				};
				args.Add (StringParserService.Parse (extraArgs, customTags));
			}
		}
Example #23
0
		void GenerateXCodeProject (IPhoneProject proj, IPhoneProjectConfiguration conf, ConfigurationSelector slnConf)
		{
			string mtouchPath = IPhoneUtility.GetMtouchPath (proj.TargetRuntime, proj.TargetFramework);
			
			var xcodeDir = conf.OutputDirectory.Combine ("XcodeProject");
			if (!Directory.Exists (xcodeDir)) {
				try {
					Directory.CreateDirectory (xcodeDir);
				} catch (IOException ex) {
					MessageService.ShowException (ex, "Failed to create directory '" + xcodeDir +"' for Xcode project");
					return;
				}
			}
			
			var args = new ProcessArgumentBuilder ();
			args.AddQuotedFormat ("-xcode={0}", xcodeDir);
			args.Add ("-v");
			
			foreach (ProjectFile pf in proj.Files) {
				if (pf.BuildAction == BuildAction.Content) {
					var rel = pf.ProjectVirtualPath;
					args.AddQuotedFormat ("-res={0},{1}", pf.FilePath, rel);
					
					//hack around mtouch 1.0 bug. create resource directories
					string subdir = rel.ParentDirectory;
					if (string.IsNullOrEmpty (subdir))
						continue;
					subdir = xcodeDir.Combine (subdir);
					try {
						if (!Directory.Exists (subdir))
							Directory.CreateDirectory (subdir);
					} catch (IOException ex) {
						MessageService.ShowException (ex, "Failed to create directory '" + subdir +"' for Xcode project");
						return;
					}
				} else if (pf.BuildAction == BuildAction.Page) {
					args.AddQuotedFormat ("-res={0}", pf.FilePath);
				}
			}
			
			args.AddQuotedFormat ("-res={0},Info.plist", conf.AppDirectory.Combine ("Info.plist"));
			
			foreach (string asm in proj.GetReferencedAssemblies (slnConf).Distinct ())
				args.AddQuotedFormat ("-r={0}", asm);
			
			var sdkVersion = conf.MtouchSdkVersion.ResolveIfDefault ();
			if (!IPhoneFramework.SdkIsInstalled (sdkVersion))
				sdkVersion = IPhoneFramework.GetClosestInstalledSdk (sdkVersion);
			
			IPhoneBuildExtension.AppendExtrasMtouchArgs (args, sdkVersion, proj, conf);
			args.AddQuoted (conf.CompiledOutputName);
			
			string argStr = args.ToString ();
			
			var console = (IConsole) IdeApp.Workbench.ProgressMonitors.GetOutputProgressMonitor (
				GettextCatalog.GetString ("Generate Xcode project"), MonoDevelop.Ide.Gui.Stock.RunProgramIcon, true, true);
			console.Log.WriteLine (mtouchPath + " " + argStr);
			Runtime.ProcessService.StartConsoleProcess (mtouchPath, argStr, conf.OutputDirectory, console, null);
		}
		BuildResult UpdateInfoPlist (IProgressMonitor monitor, IPhoneSdkVersion sdkVersion, IPhoneProject proj,
			IPhoneProjectConfiguration conf, IPhoneAppIdentity identity, ProjectFile template, string 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 ();
				
				bool sim = conf.Platform != IPhoneProject.PLAT_IPHONE;
				bool v3_2_orNewer = sdkVersion >= IPhoneSdkVersion.V3_2;
				bool v3_1_orNewer = sdkVersion >= IPhoneSdkVersion.V3_1;
				bool v4_0_orNewer = sdkVersion >= IPhoneSdkVersion.V4_0;
				bool supportsIPhone = (proj.SupportedDevices & TargetDevice.IPhone) != 0;
				bool supportsIPad = (proj.SupportedDevices & TargetDevice.IPad) != 0;
				
				var sdkSettings = IPhoneFramework.GetSdkSettings (sdkVersion);
				var dtSettings = IPhoneFramework.GetDTSettings ();
				
				SetIfNotPresent (dict, "BuildMachineOSBuild", dtSettings.BuildMachineOSBuild);
				
				SetIfNotPresent (dict, "CFBundleDevelopmentRegion",
					String.IsNullOrEmpty (proj.BundleDevelopmentRegion)? "en" : proj.BundleDevelopmentRegion);
				
				SetIfNotPresent (dict, "CFBundleDisplayName", proj.BundleDisplayName ?? proj.Name);
				SetIfNotPresent (dict, "CFBundleExecutable", conf.NativeExe.FileName);
				
				// < 3.2 icon
				if (supportsIPhone) {
					if (!dict.ContainsKey ("CFBundleIconFile")) {
						var icon = proj.BundleIcon.ToRelative (proj.BaseDirectory);
						if (icon.IsNullOrEmpty || icon.ToString () == ".")
							result.AddWarning ("Application bundle icon has not been set (iPhone Application options panel)");
						else
							dict ["CFBundleIconFile"] = icon.FileName;
					}
				}
				
				//newer icons - see http://developer.apple.com/library/ios/#qa/qa2010/qa1686.html
				if (v3_2_orNewer && !dict.ContainsKey ("CFBundleIconFiles")) {
					var arr = new PlistArray ();
					dict["CFBundleIconFiles"] = arr;
					
					if (supportsIPhone)
						AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIcon, "Icon.png");
					
					if (v4_0_orNewer && supportsIPhone)
						if (!AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconHigh, "*****@*****.**"))
							result.AddWarning ("iPhone high res bundle icon has not been set (iPhone Application options panel)");
					
					if (supportsIPad)
						if (!AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconIPad, "Icon-72.png"))
							result.AddWarning ("iPad bundle icon has not been set (iPhone Application options panel)");
					
					AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconSpotlight, "Icon-Small.png");
					
					if (supportsIPad)
						AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconIPadSpotlight, "Icon-Small-50.png");
					
					if (v4_0_orNewer && supportsIPhone)
						AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconSpotlightHigh, "*****@*****.**");
				}
				
				SetIfNotPresent (dict, "CFBundleIdentifier", identity.BundleID);
				SetIfNotPresent (dict, "CFBundleInfoDictionaryVersion", "6.0");
				SetIfNotPresent (dict, "CFBundleName", proj.Name);
				SetIfNotPresent (dict, "CFBundlePackageType", "APPL");
				if (!sim)
					dict["CFBundleResourceSpecification"] = "ResourceRules.plist";
				SetIfNotPresent (dict, "CFBundleSignature", "????");
				SetIfNotPresent (dict,  "CFBundleSupportedPlatforms",
					new PlistArray () { sim? "iPhoneSimulator" : "iPhoneOS" });
				SetIfNotPresent (dict, "CFBundleVersion", proj.BundleVersion ?? "1.0");
				
				if (!sim) {
					SetIfNotPresent (dict, "DTCompiler", sdkSettings.DTCompiler);
					SetIfNotPresent (dict, "DTPlatformBuild", dtSettings.DTPlatformBuild);
					SetIfNotPresent (dict, "DTSDKBuild", sdkSettings.DTSDKBuild);
				}
				SetIfNotPresent (dict, "DTPlatformName", sim? "iphonesimulator" : "iphoneos");
				if (!sim) {
					SetIfNotPresent (dict, "DTPlatformVersion", dtSettings.DTPlatformVersion);
				}
				SetIfNotPresent (dict, "DTSDKName", sim? sdkSettings.AlternateSDK : sdkSettings.CanonicalName);
				if (!sim) {
					SetIfNotPresent (dict, "DTXcode", dtSettings.DTXcode);
					SetIfNotPresent (dict, "DTXcodeBuild", dtSettings.DTXcodeBuild);
				}
				
				SetIfNotPresent (dict,  "LSRequiresIPhoneOS", true);
				if (v3_2_orNewer)
					SetIfNotPresent (dict,  "UIDeviceFamily", GetSupportedDevices (proj.SupportedDevices));
				
				if (v3_1_orNewer) {
					if (conf.MtouchArch != MtouchArch.ARMv6_ARMv7) {
						var val = conf.MtouchArch == MtouchArch.ARMv6? "armv6" : "armv7";
						var key = "UIRequiredDeviceCapabilities";
						var caps = dict.TryGetValue (key) ?? (dict[key] = new PlistArray ());
						var a = caps as PlistArray;
						if (a != null) {
							a.Add (val);
						} else {
							var d = (PlistDictionary) caps;
							d[val] = new PlistBoolean (true);
						}
					}
				}
				
				SetIfNotPresent (dict, "MinimumOSVersion", conf.MtouchMinimumOSVersion);
				
				SetNibProperty (dict, proj, proj.MainNibFile, "NSMainNibFile");
				if (proj.SupportedDevices == TargetDevice.IPhoneAndIPad)
					SetNibProperty (dict, proj, proj.MainNibFileIPad, "NSMainNibFile~ipad");
				
				
				if (v3_2_orNewer) {
					if (!dict.ContainsKey (OrientationUtil.KEY)) {
						result.AddWarning ("Supported orientations have not been set (iPhone Application options panel)");
					} else {
						var val = OrientationUtil.Parse ((PlistArray)dict[OrientationUtil.KEY]);
						if (!OrientationUtil.IsValidPair (val))
							result.AddWarning ("Supported orientations are not matched pairs (Info.plist)");
						if (dict.ContainsKey (OrientationUtil.KEY_IPAD)) {
							var pad = OrientationUtil.Parse ((PlistArray)dict[OrientationUtil.KEY_IPAD]);
							if (pad != Orientation.None && !OrientationUtil.IsValidPair (pad))
								result.AddWarning ("iPad orientations are not matched pairs (Info.plist)");
						}
					}
				}   
				
				return result;
			});
		}
Example #25
0
 public IPhoneCodeBehind(IPhoneProject project) : base(project)
 {
 }
		BuildResult UpdateInfoPlist (IProgressMonitor monitor, IPhoneProject proj, IPhoneProjectConfiguration conf,
		                             IPhoneAppIdentity identity, ProjectFile template, string 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 ();
				
				bool sim = conf.Platform != IPhoneProject.PLAT_IPHONE;
				var sdkversion = IPhoneSdkVersion.Parse (conf.MtouchSdkVersion);
				bool v3_2_orNewer = sdkversion.CompareTo (IPhoneSdkVersion.V3_2) >= 0;
				bool v4_0_orNewer = sdkversion.CompareTo (IPhoneSdkVersion.V4_0) >= 0;
				bool supportsIPhone = (proj.SupportedDevices & TargetDevice.IPhone) != 0;
				bool supportsIPad = (proj.SupportedDevices & TargetDevice.IPad) != 0;
				
				SetIfNotPresent (dict, "CFBundleDevelopmentRegion",
					String.IsNullOrEmpty (proj.BundleDevelopmentRegion)? "English" : proj.BundleDevelopmentRegion);
				
				SetIfNotPresent (dict, "CFBundleDisplayName", proj.BundleDisplayName ?? proj.Name);
				SetIfNotPresent (dict, "CFBundleExecutable", conf.NativeExe.FileName);
				
				// < 3.2 icon
				if (supportsIPhone) {
					if (!dict.ContainsKey ("CFBundleIconFile")) {
						var icon = proj.BundleIcon.ToRelative (proj.BaseDirectory);
						if (icon.IsNullOrEmpty || icon.ToString () == ".")
							result.AddWarning ("Application bundle icon has not been set (iPhone Application options panel)");
						else
							dict ["CFBundleIconFile"] = icon.FileName;
					}
				}
				
				//newer icons
				if (v3_2_orNewer && !dict.ContainsKey ("CFBundleIconFiles")) {
					var arr = new PlistArray ();
					dict["CFBundleIconFiles"] = arr;
					
					if (supportsIPhone)
						AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIcon);
					
					AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconSpotlight, "Icon-Small.png");
					if (supportsIPad) {
						AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconIPadSpotlight, "Icon-Small-50.png");
						if (!AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconIPad))
							result.AddWarning ("iPad bundle icon has not been set (iPhone Application options panel)");
					}
					
					if (v4_0_orNewer) {
						if (supportsIPhone) {
							if (!AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconHigh))
								result.AddWarning ("iPhone high res bundle icon has not been set (iPhone Application options panel)");
							AddIconRelativeIfNotEmpty (proj, arr, proj.BundleIconSpotlightHigh, "*****@*****.**");
						}
					}
				}
				
				SetIfNotPresent (dict, "CFBundleIdentifier", identity.BundleID);
				SetIfNotPresent (dict, "CFBundleInfoDictionaryVersion", "6.0");
				SetIfNotPresent (dict, "CFBundleName", proj.Name);
				SetIfNotPresent (dict, "CFBundlePackageType", "APPL");
				if (!sim)
					dict["CFBundleResourceSpecification"] = "ResourceRules.plist";
				SetIfNotPresent (dict, "CFBundleSignature", "????");
				SetIfNotPresent (dict,  "CFBundleSupportedPlatforms",
					new PlistArray () { sim? "iPhoneSimulator" : "iPhoneOS" });
				SetIfNotPresent (dict, "CFBundleVersion", proj.BundleVersion ?? "1.0");
				SetIfNotPresent (dict, "DTPlatformName", sim? "iphonesimulator" : "iphoneos");
				SetIfNotPresent (dict, "DTSDKName", IPhoneFramework.GetDTSdkName (sdkversion, sim));
				SetIfNotPresent (dict,  "LSRequiresIPhoneOS", true);
				if (v3_2_orNewer)
					SetIfNotPresent (dict,  "UIDeviceFamily", GetSupportedDevices (proj.SupportedDevices));
				SetIfNotPresent (dict, "DTPlatformVersion", IPhoneFramework.DTPlatformVersion);
				
				SetIfNotPresent (dict, "MinimumOSVersion", conf.MtouchMinimumOSVersion);
				
				SetNibProperty (dict, proj, proj.MainNibFile, "NSMainNibFile");
				if (proj.SupportedDevices == TargetDevice.IPhoneAndIPad)
					SetNibProperty (dict, proj, proj.MainNibFileIPad, "NSMainNibFile~ipad");
				
				
				if (v3_2_orNewer) {
					if (!dict.ContainsKey (OrientationUtil.KEY)) {
						result.AddWarning ("Supported orientations have not been set (iPhone Application options panel)");
					} else {
						var val = OrientationUtil.Parse ((PlistArray)dict[OrientationUtil.KEY]);
						if (!OrientationUtil.IsValidPair (val))
							result.AddWarning ("Supported orientations are not matched pairs (Info.plist)");
						if (dict.ContainsKey (OrientationUtil.KEY_IPAD)) {
							var pad = OrientationUtil.Parse ((PlistArray)dict[OrientationUtil.KEY_IPAD]);
							if (pad != Orientation.None && !OrientationUtil.IsValidPair (pad))
								result.AddWarning ("iPad orientations are not matched pairs (Info.plist)");
						}
					}
				}   
				
				return result;
			});
		}