diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e558d463 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +target +work +.idea +test-* +*.iml diff --git a/README b/README index a674bf31..743e1d42 100644 --- a/README +++ b/README @@ -1,3 +1,8 @@ -This is a simple plugin for invoking xcode (iPhone, iPad, etc) builds from Hudson CI. +Xcode plugin +------------ -Please read the overview here: http://rayh.com.au/xcode-hudson-plugin +This plugin adds the ability to call Xcode command line tools to automate build and packaging iOS applications (iPhone, iPad, ...). +* More documentation about how to use it : https://wiki.jenkins-ci.org/display/JENKINS/Xcode+Plugin +* Report all issues or features requests in Jira : https://issues.jenkins-ci.org/browse/JENKINS/component/16124 + +Contact the Jenkins Community by [mail](http://jenkins-ci.org/content/mailing-lists) or [irc](http://jenkins-ci.org/content/chat) to have support. \ No newline at end of file diff --git a/pom.xml b/pom.xml index e91980c8..af4830d0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,68 +1,100 @@ - - 4.0.0 - - org.jvnet.hudson.plugins - plugin - 1.343 - ../pom.xml - - - - scm:git:git://github.com/rayh/xcode-hudson-plugin.git/tags/xcode-builder-0.1 - scm:git:git@github.com:rayh/xcode-hudson-plugin.git/tags/xcode-builder-0.1 - http://github.com/rayh/xcode-hudson-plugin/tags/xcode-builder-0.1 - - - - - - org.jvnet.wagon-svn - wagon-svn - 1.9 - - - - - org.apache.maven.plugins - maven-release-plugin - 2.0 - - - org.apache.maven.scm - maven-scm-provider-gitexe - 1.3 - - - - - - - - - rayhilton - Ray Yamamoto Hilton - ray.hilton@gmail.com - - - - hudson.plugins.xcode - xcode - 0.1-SNAPSHOT - XCode integration for Hudson CI - hpi + + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 1.399 + + + xcode-plugin + 1.3.1-SNAPSHOT + hpi + XCode integration + This plugin adds the ability to call Xcode command line tools to automate build and packaging iOS applications (iPhone, iPad, ...). + https://wiki.jenkins-ci.org/display/JENKINS/Xcode+Plugin + + + MIT + LICENSE.txt + repo + + + + + rayhilton + Ray Yamamoto Hilton + ray.hilton@gmail.com + + + aheritier + Arnaud Heritier + aheritier@apache.org + + + + scm:git:git@github.com:jenkinsci/xcode-plugin.git + scm:git:git@github.com:jenkinsci/xcode-plugin.git + https://github.com/jenkinsci/xcode-plugin + - - - m.g.o-public - http://maven.glassfish.org/content/groups/public/ - - - - - - m.g.o-public - http://maven.glassfish.org/content/groups/public/ - - + + + m.g.o-public + http://maven.glassfish.org/content/groups/public/ + + + + + m.g.o-public + http://maven.glassfish.org/content/groups/public/ + + + + + org.jenkins-ci.plugins + token-macro + 1.5.1 + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.2.1 + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.5 + + + + + diff --git a/src/main/java/au/com/rayh/AppFileFilter.java b/src/main/java/au/com/rayh/AppFileFilter.java index 40b139e4..aa915272 100644 --- a/src/main/java/au/com/rayh/AppFileFilter.java +++ b/src/main/java/au/com/rayh/AppFileFilter.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2011 Ray Yamamoto Hilton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + /* * To change this template, choose Tools | Templates * and open the template in the editor. diff --git a/src/main/java/au/com/rayh/XCodeBuildOutputParser.java b/src/main/java/au/com/rayh/XCodeBuildOutputParser.java index 463d6945..361a773c 100644 --- a/src/main/java/au/com/rayh/XCodeBuildOutputParser.java +++ b/src/main/java/au/com/rayh/XCodeBuildOutputParser.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2011 Ray Yamamoto Hilton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + /* * To change this template, choose Tools | Templates * and open the template in the editor. diff --git a/src/main/java/au/com/rayh/XCodeBuilder.java b/src/main/java/au/com/rayh/XCodeBuilder.java index b5133860..25e53e14 100644 --- a/src/main/java/au/com/rayh/XCodeBuilder.java +++ b/src/main/java/au/com/rayh/XCodeBuilder.java @@ -1,187 +1,338 @@ +/* + * The MIT License + * + * Copyright (c) 2011 Ray Yamamoto Hilton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + package au.com.rayh; + import com.google.common.collect.Lists; import hudson.EnvVars; -import hudson.Launcher; import hudson.Extension; import hudson.FilePath; -import hudson.util.FormValidation; +import hudson.Launcher; import hudson.model.AbstractBuild; -import hudson.model.BuildListener; import hudson.model.AbstractProject; -import hudson.tasks.Builder; +import hudson.model.BuildListener; import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Builder; +import hudson.util.FormValidation; import net.sf.json.JSONObject; +import org.apache.commons.lang.StringUtils; +import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; +import org.jenkinsci.plugins.tokenmacro.TokenMacro; import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; import javax.servlet.ServletException; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang.StringUtils; /** * @author Ray Hilton */ public class XCodeBuilder extends Builder { - private Boolean buildIpa; - private Boolean cleanBeforeBuild; - private Boolean updateBuildNumber; - private String configuration; - private String overrideMarketingNumber; - private String target; - private String sdk; - private String xcodeProjectPath; - private String xcodeProjectFile; - private String embeddedProfileFile; - private String versionNumberPattern; + /** + * @since 1.0 + */ + public final Boolean cleanBeforeBuild; + /** + * @since 1.3 + */ + public final Boolean cleanTestReports; + /** + * @since 1.0 + */ + public final String configuration; + /** + * @since 1.0 + */ + public final String target; + /** + * @since 1.0 + */ + public final String sdk; + /** + * @since 1.1 + */ + public final String symRoot; + /** + * @since 1.2 + */ + public final String configurationBuildDir; + /** + * @since 1.0 + */ + public final String xcodeProjectPath; + /** + * @since 1.0 + */ + public final String xcodeProjectFile; + /** + * @since 1.3 + */ + private String xcodebuildArguments; + /** + * @since 1.2 + */ + public final String xcodeSchema; + /** + * @since 1.2 + */ + public final String xcodeWorkspaceFile; + /** + * @since 1.0 + */ + public final String embeddedProfileFile; + /** + * @since 1.0 + */ + public final String cfBundleVersionValue; + /** + * @since 1.0 + */ + public final String cfBundleShortVersionStringValue; + /** + * @since 1.0 + */ + public final Boolean buildIpa; + /** + * @since 1.0 + */ + public final Boolean unlockKeychain; + /** + * @since 1.0 + */ + public final String keychainPath; + /** + * @since 1.0 + */ + public final String keychainPwd; // Fields in config.jelly must match the parameter names in the "DataBoundConstructor" @DataBoundConstructor - public XCodeBuilder(Boolean buildIpa, Boolean cleanBeforeBuild, Boolean updateBuildNumber, String configuration, String target, String sdk, String xcodeProjectPath, String xcodeProjectFile, String embeddedProfileFile, String versionNumberPattern, String overrideMarketingNumber) { + public XCodeBuilder(Boolean buildIpa, Boolean cleanBeforeBuild, Boolean cleanTestReports, String configuration, String target, String sdk, String xcodeProjectPath, String xcodeProjectFile, String xcodebuildArguments, String embeddedProfileFile, String cfBundleVersionValue, String cfBundleShortVersionStringValue, Boolean unlockKeychain, String keychainPath, String keychainPwd, String symRoot, String xcodeWorkspaceFile, String xcodeSchema, String configurationBuildDir) { this.buildIpa = buildIpa; this.sdk = sdk; this.target = target; this.cleanBeforeBuild = cleanBeforeBuild; - this.updateBuildNumber = updateBuildNumber; - this.overrideMarketingNumber = overrideMarketingNumber; + this.cleanTestReports = cleanTestReports; this.configuration = configuration; this.xcodeProjectPath = xcodeProjectPath; this.xcodeProjectFile = xcodeProjectFile; + this.xcodebuildArguments = xcodebuildArguments; + this.xcodeWorkspaceFile = xcodeWorkspaceFile; + this.xcodeSchema = xcodeSchema; this.embeddedProfileFile = embeddedProfileFile; - this.versionNumberPattern = versionNumberPattern; - } - - public String getVersionNumberPattern() { - return versionNumberPattern; - } - public String getSdk() { - return sdk; - } - - public String getTarget() { - return target; - } - - public String getConfiguration() { - return configuration; - } - - public Boolean getBuildIpa() { - return buildIpa; - } - - public Boolean getCleanBeforeBuild() { - return cleanBeforeBuild; - } - - public Boolean getUpdateBuildNumber() { - return updateBuildNumber; - } - - public String getOverrideMarketingNumber() { - return overrideMarketingNumber; - } - - public String getXcodeProjectPath() { - return xcodeProjectPath; - } - - public String getXcodeProjectFile() { - return xcodeProjectFile; - } - - public String getEmbeddedProfileFile() { - return embeddedProfileFile; + this.cfBundleVersionValue = cfBundleVersionValue; + this.cfBundleShortVersionStringValue = cfBundleShortVersionStringValue; + this.unlockKeychain = unlockKeychain; + this.keychainPath = keychainPath; + this.keychainPwd = keychainPwd; + this.symRoot = symRoot; + this.configurationBuildDir = configurationBuildDir; } @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { EnvVars envs = build.getEnvironment(listener); - FilePath projectRoot = build.getProject().getWorkspace(); + FilePath projectRoot = build.getWorkspace(); // check that the configured tools exist - if(!new FilePath(projectRoot.getChannel(), getDescriptor().xcodebuildPath()).exists()) { - listener.fatalError("Cannot find xcodebuild with the configured path {0}", getDescriptor().xcodebuildPath()); + if (!new FilePath(projectRoot.getChannel(), getDescriptor().getXcodebuildPath()).exists()) { + listener.fatalError(Messages.XCodeBuilder_xcodebuildNotFound(getDescriptor().getXcodebuildPath())); + return false; } - if(!new FilePath(projectRoot.getChannel(), getDescriptor().agvtoolPath()).exists()) { - listener.fatalError("Cannot find agvtool with the configured path {0}", getDescriptor().agvtoolPath()); + if (!new FilePath(projectRoot.getChannel(), getDescriptor().getAgvtoolPath()).exists()) { + listener.fatalError(Messages.XCodeBuilder_avgtoolNotFound(getDescriptor().getAgvtoolPath())); + return false; } // Set the working directory - if(!StringUtils.isEmpty(xcodeProjectPath)) { + if (!StringUtils.isEmpty(xcodeProjectPath)) { projectRoot = projectRoot.child(xcodeProjectPath); } - listener.getLogger().println("Working directory is " + projectRoot); - FilePath buildDirectory = projectRoot.child("build").child(configuration + "-iphoneos"); + listener.getLogger().println(Messages.XCodeBuilder_workingDir(projectRoot)); + + // Infer as best we can the build platform + String buildPlatform = "iphoneos"; + if (!StringUtils.isEmpty(sdk)) { + if (StringUtils.contains(sdk.toLowerCase(), "iphonesimulator")) { + // Building for the simulator + buildPlatform = "iphonesimulator"; + } + } + + // Set the build directory and the symRoot + // + String symRootValue = null; + if (!StringUtils.isEmpty(symRoot)) { + try { + // If not empty we use the Token Expansion to replace it + // https://wiki.jenkins-ci.org/display/JENKINS/Token+Macro+Plugin + symRootValue = TokenMacro.expandAll(build, listener, symRoot).trim(); + } catch (MacroEvaluationException e) { + listener.error(Messages.XCodeBuilder_symRootMacroError(e.getMessage())); + return false; + } + } + + String configurationBuildDirValue = null; + FilePath buildDirectory; + if (!StringUtils.isEmpty(configurationBuildDir)) { + try { + configurationBuildDirValue = TokenMacro.expandAll(build, listener, configurationBuildDir).trim(); + } catch (MacroEvaluationException e) { + listener.error(Messages.XCodeBuilder_configurationBuildDirMacroError(e.getMessage())); + return false; + } + } + + if (configurationBuildDirValue != null) { + // If there is a CONFIGURATION_BUILD_DIR, that overrides any use of SYMROOT. Does not require the build platform and the configuration. + buildDirectory = new FilePath(projectRoot.getChannel(), configurationBuildDirValue); + } else if (symRootValue != null) { + // If there is a SYMROOT specified, compute the build directory from that. + buildDirectory = new FilePath(projectRoot.getChannel(), symRootValue).child(configuration + "-" + buildPlatform); + } else { + // Assume its a build for the handset, not the simulator. + buildDirectory = projectRoot.child("build").child(configuration + "-" + buildPlatform); + } // XCode Version - int returnCode = launcher.launch().envs(envs).cmds(getDescriptor().xcodebuildPath(), "-version").stdout(listener).pwd(projectRoot).join(); - if(returnCode>0) return false; - - // Unlock keychain -// if(!StringUtils.isEmpty(keychainPassword)) { -// launcher.launch().envs(envs).cmds("security", "unlock-keychain", "-p", keychainPassword); -// } - - // Set build number - String artifactVersion = String.valueOf(build.getNumber()); - String versionNumber = artifactVersion; - if(!StringUtils.isEmpty(getVersionNumberPattern())) { - versionNumber = getVersionNumberPattern().replaceAll("\\{BUILD_NUMBER\\}", artifactVersion); - } - - if(updateBuildNumber) { - listener.getLogger().println("Updating version number (CFBundleVersion) to " + versionNumber); - //ByteArrayOutputStream output = new ByteArrayOutputStream(); - //returnCode = launcher.launch().envs(envs).cmds("agvtool", "mvers", "-terse1").stdout(output).pwd(projectRoot).join(); - //if(returnCode>0) { - - //} else { - // String marketingVersionNumber = output.toString().trim(); - // artifactVersion = marketingVersionNumber + "." + build.getNumber(); - // listener.getLogger().println("CFBundleShortVersionString is " + marketingVersionNumber + " so new CFBundleVersion will be " + artifactVersion); - //} - - returnCode = launcher.launch().envs(envs).cmds(getDescriptor().agvtoolPath(), "new-version", "-all", versionNumber ).stdout(listener).pwd(projectRoot).join(); - if(returnCode>0) { - listener.fatalError("Could not set the CFBundleVersion to " + versionNumber); + int returnCode = launcher.launch().envs(envs).cmds(getDescriptor().getXcodebuildPath(), "-version").stdout(listener).pwd(projectRoot).join(); + if (returnCode > 0) { + listener.fatalError(Messages.XCodeBuilder_xcodeVersionNotFound()); + return false; // We fail the build if XCode isn't deployed + } + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + // Try to read CFBundleShortVersionString from project + listener.getLogger().println(Messages.XCodeBuilder_fetchingCFBundleShortVersionString()); + String cfBundleShortVersionString = ""; + returnCode = launcher.launch().envs(envs).cmds(getDescriptor().getAgvtoolPath(), "mvers", "-terse1").stdout(output).pwd(projectRoot).join(); + // only use this version number if we found it + if (returnCode == 0) + cfBundleShortVersionString = output.toString().trim(); + if (StringUtils.isEmpty(cfBundleShortVersionString)) + listener.getLogger().println(Messages.XCodeBuilder_CFBundleShortVersionStringNotFound()); + else + listener.getLogger().println(Messages.XCodeBuilder_CFBundleShortVersionStringFound(cfBundleShortVersionString)); + listener.getLogger().println(Messages.XCodeBuilder_CFBundleShortVersionStringValue(cfBundleShortVersionString)); + + // Try to read CFBundleVersion from project + listener.getLogger().println(Messages.XCodeBuilder_fetchingCFBundleVersion()); + String cfBundleVersion = ""; + returnCode = launcher.launch().envs(envs).cmds(getDescriptor().getAgvtoolPath(), "vers", "-terse").stdout(output).pwd(projectRoot).join(); + // only use this version number if we found it + if (returnCode == 0) + cfBundleVersion = output.toString().trim(); + if (StringUtils.isEmpty(cfBundleVersion)) + listener.getLogger().println(Messages.XCodeBuilder_CFBundleVersionNotFound()); + else + listener.getLogger().println(Messages.XCodeBuilder_CFBundleVersionFound(cfBundleShortVersionString)); + listener.getLogger().println(Messages.XCodeBuilder_CFBundleVersionValue(cfBundleVersion)); + + // Update the Marketing version (CFBundleShortVersionString) + if (!StringUtils.isEmpty(cfBundleShortVersionStringValue)) { + try { + // If not empty we use the Token Expansion to replace it + // https://wiki.jenkins-ci.org/display/JENKINS/Token+Macro+Plugin + cfBundleShortVersionString = TokenMacro.expandAll(build, listener, cfBundleShortVersionStringValue); + listener.getLogger().println(Messages.XCodeBuilder_CFBundleShortVersionStringUpdate(cfBundleShortVersionString)); + returnCode = launcher.launch().envs(envs).cmds(getDescriptor().getAgvtoolPath(), "new-marketing-version", cfBundleShortVersionString).stdout(listener).pwd(projectRoot).join(); + if (returnCode > 0) { + listener.fatalError(Messages.XCodeBuilder_CFBundleShortVersionStringUpdateError(cfBundleShortVersionString)); + return false; + } + } catch (MacroEvaluationException e) { + listener.fatalError(Messages.XCodeBuilder_CFBundleShortVersionStringMacroError(e.getMessage())); + // Fails the build + return false; } -// } else { -// listener.getLogger().println("Fetching marketing version number (CFBundleShortVersionString)"); -// ByteArrayOutputStream output = new ByteArrayOutputStream(); -// returnCode = launcher.launch().envs(envs).cmds("agvtool", "vers", "-terse").stdout(output).pwd(projectRoot).join(); -// -// // only use this version number if we found it -// if(returnCode==0) -// artifactVersion = output.toString().trim(); - } - - if( false == StringUtils.isEmpty(overrideMarketingNumber) ) { - listener.getLogger().println("Updating marketing version to " + overrideMarketingNumber); - - returnCode = launcher.launch().envs(envs).cmds(getDescriptor().agvtoolPath(), "new-marketing-version", overrideMarketingNumber ).stdout(listener).pwd(projectRoot).join(); - if(returnCode>0) { - listener.fatalError("Could not set marketing version to " + overrideMarketingNumber); + } + + // Update the Technical version (CFBundleVersion) + if (!StringUtils.isEmpty(cfBundleVersionValue)) { + try { + // If not empty we use the Token Expansion to replace it + // https://wiki.jenkins-ci.org/display/JENKINS/Token+Macro+Plugin + cfBundleVersion = TokenMacro.expandAll(build, listener, cfBundleVersionValue); + listener.getLogger().println(Messages.XCodeBuilder_CFBundleVersionUpdate(cfBundleVersion)); + returnCode = launcher.launch().envs(envs).cmds(getDescriptor().getAgvtoolPath(), "new-version", "-all", cfBundleVersion).stdout(listener).pwd(projectRoot).join(); + if (returnCode > 0) { + listener.fatalError(Messages.XCodeBuilder_CFBundleVersionUpdateError(cfBundleVersion)); + return false; + } + } catch (MacroEvaluationException e) { + listener.fatalError(Messages.XCodeBuilder_CFBundleVersionMacroError(e.getMessage())); + // Fails the build + return false; } } + listener.getLogger().println(Messages.XCodeBuilder_CFBundleShortVersionStringUsed(cfBundleShortVersionString)); + listener.getLogger().println(Messages.XCodeBuilder_CFBundleVersionUsed(cfBundleVersion)); + // Clean build directories - if(cleanBeforeBuild) { - listener.getLogger().println("Cleaning build directory (" + projectRoot.child("build") + ")"); + if (cleanBeforeBuild) { + listener.getLogger().println(Messages.XCodeBuilder_cleaningBuildDir(buildDirectory.absolutize().getRemote())); buildDirectory.deleteRecursive(); } - + // remove test-reports and *.ipa - listener.getLogger().println("Cleaning up test-reports"); - projectRoot.child("test-reports").deleteRecursive(); + if (cleanTestReports != null && cleanTestReports) { + listener.getLogger().println(Messages.XCodeBuilder_cleaningTestReportsDir(projectRoot.child("test-reports").absolutize().getRemote())); + projectRoot.child("test-reports").deleteRecursive(); + } + + if (unlockKeychain != null && unlockKeychain) { + // Let's unlock the keychain + launcher.launch().envs(envs).cmds("/usr/bin/security", "list-keychains", "-s", keychainPath).stdout(listener).pwd(projectRoot).join(); + launcher.launch().envs(envs).cmds("/usr/bin/security", "default-keychain", "-d", "user", "-s", keychainPath).stdout(listener).pwd(projectRoot).join(); + if (StringUtils.isEmpty(keychainPwd)) + returnCode = launcher.launch().envs(envs).cmds("/usr/bin/security", "unlock-keychain", keychainPath).stdout(listener).pwd(projectRoot).join(); + else + returnCode = launcher.launch().envs(envs).cmds("/usr/bin/security", "unlock-keychain", "-p", keychainPwd, keychainPath).masks(false, false, false, true, false).stdout(listener).pwd(projectRoot).join(); + if (returnCode > 0) { + listener.fatalError(Messages.XCodeBuilder_unlockKeychainFailed()); + return false; + } + } // Build - StringBuilder xcodeReport = new StringBuilder("Going to invoke xcodebuild: "); + StringBuilder xcodeReport = new StringBuilder(Messages.XCodeBuilder_invokeXcodebuild()); XCodeBuildOutputParser reportGenerator = new XCodeBuildOutputParser(projectRoot, listener); - List commandLine = Lists.newArrayList(getDescriptor().xcodebuildPath()); - if(StringUtils.isEmpty(target)) { + List commandLine = Lists.newArrayList(getDescriptor().getXcodebuildPath()); + + // Prioritizing schema over target setting + if (!StringUtils.isEmpty(xcodeSchema)) { + commandLine.add("-scheme"); + commandLine.add(xcodeSchema); + xcodeReport.append(", scheme: ").append(xcodeSchema); + } else if (StringUtils.isEmpty(target)) { commandLine.add("-alltargets"); xcodeReport.append("target: ALL"); } else { @@ -189,8 +340,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis commandLine.add(target); xcodeReport.append("target: ").append(target); } - - if(!StringUtils.isEmpty(sdk)) { + + if (!StringUtils.isEmpty(sdk)) { commandLine.add("-sdk"); commandLine.add(sdk); xcodeReport.append(", sdk: ").append(sdk); @@ -198,7 +349,12 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis xcodeReport.append(", sdk: DEFAULT"); } - if(!StringUtils.isEmpty(xcodeProjectFile)) { + // Prioritizing workspace over project setting + if (!StringUtils.isEmpty(xcodeWorkspaceFile)) { + commandLine.add("-workspace"); + commandLine.add(xcodeWorkspaceFile + ".xcworkspace"); + xcodeReport.append(", workspace: ").append(xcodeWorkspaceFile); + } else if (!StringUtils.isEmpty(xcodeProjectFile)) { commandLine.add("-project"); commandLine.add(xcodeProjectFile); xcodeReport.append(", project: ").append(xcodeProjectFile); @@ -210,72 +366,105 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis commandLine.add(configuration); xcodeReport.append(", configuration: ").append(configuration); -// if (cleanBeforeBuild) { -// commandLine.add("clean"); -// xcodeReport.append(", clean: YES"); -// } else { -// xcodeReport.append(", clean: NO"); -// } + if (cleanBeforeBuild) { + commandLine.add("clean"); + xcodeReport.append(", clean: YES"); + } else { + xcodeReport.append(", clean: NO"); + } commandLine.add("build"); - + + if (!StringUtils.isEmpty(symRootValue)) { + commandLine.add("SYMROOT=" + symRootValue); + xcodeReport.append(", symRoot: ").append(symRootValue); + } else { + xcodeReport.append(", symRoot: DEFAULT"); + } + + // CONFIGURATION_BUILD_DIR + if (!StringUtils.isEmpty(configurationBuildDirValue)) { + commandLine.add("CONFIGURATION_BUILD_DIR=" + configurationBuildDirValue); + xcodeReport.append(", configurationBuildDir: ").append(configurationBuildDirValue); + } else { + xcodeReport.append(", configurationBuildDir: DEFAULT"); + } + + // Additional (custom) xcodebuild arguments + if (!StringUtils.isEmpty(xcodebuildArguments)) { + String[] parts = xcodebuildArguments.split("[ ]"); + for (String arg : parts) { + commandLine.add(arg); + } + } + listener.getLogger().println(xcodeReport.toString()); returnCode = launcher.launch().envs(envs).cmds(commandLine).stdout(reportGenerator.getOutputStream()).pwd(projectRoot).join(); - if(reportGenerator.getExitCode()!=0) return false; - if(returnCode>0) return false; + if (reportGenerator.getExitCode() != 0) return false; + if (returnCode > 0) return false; // Package IPA - if(buildIpa) { - listener.getLogger().println("Cleaning up previously generate .ipa files"); - for(FilePath path : buildDirectory.list("*.ipa")) { - path.delete(); + if (buildIpa) { + + if (buildDirectory.exists()) { + listener.getLogger().println(Messages.XCodeBuilder_cleaningIPA()); + for (FilePath path : buildDirectory.list("*.ipa")) { + path.delete(); + } + } else { + listener.getLogger().println(Messages.XCodeBuilder_NotExistingDirToCleanIPA(buildDirectory.absolutize().getRemote())); } - listener.getLogger().println("Packaging IPA"); + listener.getLogger().println(Messages.XCodeBuilder_packagingIPA()); List apps = buildDirectory.list(new AppFileFilter()); - for(FilePath app : apps) { - String baseName = app.getBaseName() + "-" + configuration + "-" + build.getProject().getName() + "-" + versionNumber; + for (FilePath app : apps) { + String version; + if (StringUtils.isEmpty(cfBundleShortVersionString) && StringUtils.isEmpty(cfBundleVersion)) + version = Integer.toString(build.getNumber()); + else if (StringUtils.isEmpty(cfBundleVersion)) + version = cfBundleShortVersionString; + else + version = cfBundleVersion; + + String baseName = app.getBaseName().replaceAll(" ", "_") + "-" + + configuration.replaceAll(" ", "_") + (StringUtils.isEmpty(version) ? "" : "-" + version); + FilePath ipaLocation = buildDirectory.child(baseName + ".ipa"); FilePath payload = buildDirectory.child("Payload"); payload.deleteRecursive(); payload.mkdirs(); - - listener.getLogger().println("Packaging " + app.getBaseName() + ".app => " + ipaLocation); + listener.getLogger().println("Packaging " + app.getBaseName() + ".app => " + ipaLocation.absolutize().getRemote()); List packageCommandLine = new ArrayList(); - packageCommandLine.add("/usr/bin/xcrun"); + packageCommandLine.add(getDescriptor().getXcrunPath()); packageCommandLine.add("-sdk"); - if(!StringUtils.isEmpty(sdk)) { + if (!StringUtils.isEmpty(sdk)) { packageCommandLine.add(sdk); } else { - packageCommandLine.add("iphoneos"); + packageCommandLine.add(buildPlatform); } - packageCommandLine.addAll(Lists.newArrayList("PackageApplication", "-v", app.toString(), "-o", ipaLocation.toString())); - if(!StringUtils.isEmpty(embeddedProfileFile)) { + packageCommandLine.addAll(Lists.newArrayList("PackageApplication", "-v", app.absolutize().getRemote(), "-o", ipaLocation.absolutize().getRemote())); + if (!StringUtils.isEmpty(embeddedProfileFile)) { packageCommandLine.add("--embed"); packageCommandLine.add(embeddedProfileFile); } - + returnCode = launcher.launch().envs(envs).stdout(listener).pwd(projectRoot).cmds(packageCommandLine).join(); - if(returnCode>0) { - listener.getLogger().println("Failed to build " + ipaLocation.getName()); + if (returnCode > 0) { + listener.getLogger().println("Failed to build " + ipaLocation.absolutize().getRemote()); continue; } // also zip up the symbols, if present - returnCode = launcher.launch().envs(envs).stdout(listener).pwd(buildDirectory).cmds("zip", "-r", "-T", "-y", baseName + "-dSYM.zip", app.getBaseName() + ".app.dSYM").join(); - if(returnCode>0) { - listener.getLogger().println("Failed to zip *.dSYM into " + baseName + "-dSYM.zip"); + returnCode = launcher.launch().envs(envs).stdout(listener).pwd(buildDirectory).cmds("zip", "-r", "-T", "-y", baseName + "-dSYM.zip", app.absolutize().getRemote() + ".dSYM").join(); + if (returnCode > 0) { + listener.getLogger().println(Messages.XCodeBuilder_zipFailed(baseName)); continue; } - //listener.getLogger().println("Copying to " + app.getBaseName() + ".ipa"); - //ipaLocation.copyTo(buildDirectory.child(app.getBaseName() + ".ipa")); - - payload.deleteRecursive(); } } @@ -285,37 +474,38 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener lis @Override public DescriptorImpl getDescriptor() { - return (DescriptorImpl)super.getDescriptor(); + return (DescriptorImpl) super.getDescriptor(); } @Extension public static final class DescriptorImpl extends BuildStepDescriptor { private String xcodebuildPath = "/usr/bin/xcodebuild"; private String agvtoolPath = "/usr/bin/agvtool"; + private String xcrunPath = "/usr/bin/xcrun"; - public FormValidation doCheckConfiguration(@QueryParameter String value) throws IOException, ServletException { + public FormValidation doCheckXcodebuildPath(@QueryParameter String value) throws IOException, ServletException { if (StringUtils.isEmpty(value)) { - return FormValidation.error("Please specify a configuration"); + return FormValidation.error(Messages.XCodeBuilder_xcodebuildPathNotSet()); } else { - // TODO: scan project file for specified configuration + // TODO: check that the file exists (and if an agent is used ?) } return FormValidation.ok(); } - public FormValidation doCheckXcodebuildPath(@QueryParameter String value) throws IOException, ServletException { - if (StringUtils.isEmpty(value)) { - return FormValidation.error("Please specify the path to the xcodebuild executable (usually /usr/bin/xcodebuild)"); - } else { - // TODO: check that the file exists + public FormValidation doCheckAgvtoolPath(@QueryParameter String value) throws IOException, ServletException { + if (StringUtils.isEmpty(value)) + return FormValidation.error(Messages.XCodeBuilder_agvtoolPathNotSet()); + else { + // TODO: check that the file exists (and if an agent is used ?) } return FormValidation.ok(); } - public FormValidation doCheckAgvtoolPath(@QueryParameter String value) throws IOException, ServletException { - if(StringUtils.isEmpty(value)) - return FormValidation.error("Please specify the path to the agvtool executable (usually /usr/bin/agvtool)"); + public FormValidation doCheckXcrunPath(@QueryParameter String value) throws IOException, ServletException { + if (StringUtils.isEmpty(value)) + return FormValidation.error(Messages.XCodeBuilder_xcrunPathNotSet()); else { - // TODO: check that the file exists + // TODO: check that the file exists (and if an agent is used ?) } return FormValidation.ok(); } @@ -326,33 +516,39 @@ public boolean isApplicable(Class aClass) { } public String getDisplayName() { - return "XCode"; + return Messages.XCodeBuilder_xcode(); } @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { - // To persist global configuration information, - // set that to properties and call save(). -// updateBuildNumber = formData.getBoolean("updateBuildNumber"); -// buildIpa = formData.getBoolean("buildIpa"); -// cleanBeforeBuild = formData.getBoolean("cleanBeforeBuild"); -// configuration = formData.getString("configuration"); - xcodebuildPath = formData.getString("xcodebuildPath"); - agvtoolPath = formData.getString("agvtoolPath"); - // ^Can also use req.bindJSON(this, formData); - // (easier when there are many fields; need set* methods for this, like setUseFrench) + req.bindJSON(this, formData); save(); return super.configure(req, formData); } - public String agvtoolPath() { + public String getAgvtoolPath() { return agvtoolPath; } - public String xcodebuildPath() { + public String getXcodebuildPath() { return xcodebuildPath; } + public String getXcrunPath() { + return xcrunPath; + } + + public void setXcodebuildPath(String xcodebuildPath) { + this.xcodebuildPath = xcodebuildPath; + } + + public void setAgvtoolPath(String agvtoolPath) { + this.agvtoolPath = agvtoolPath; + } + + public void setXcrunPath(String xcrunPath) { + this.xcrunPath = xcrunPath; + } } } diff --git a/src/main/java/au/com/rayh/report/TestCase.java b/src/main/java/au/com/rayh/report/TestCase.java index 9a440305..6c4edce2 100644 --- a/src/main/java/au/com/rayh/report/TestCase.java +++ b/src/main/java/au/com/rayh/report/TestCase.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2011 Ray Yamamoto Hilton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + package au.com.rayh.report; import java.util.ArrayList; diff --git a/src/main/java/au/com/rayh/report/TestFailure.java b/src/main/java/au/com/rayh/report/TestFailure.java index a5e3b1fd..e6a37408 100644 --- a/src/main/java/au/com/rayh/report/TestFailure.java +++ b/src/main/java/au/com/rayh/report/TestFailure.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2011 Ray Yamamoto Hilton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + package au.com.rayh.report; import javax.xml.bind.annotation.XmlAccessType; diff --git a/src/main/java/au/com/rayh/report/TestSuite.java b/src/main/java/au/com/rayh/report/TestSuite.java index 262456cb..5f133269 100644 --- a/src/main/java/au/com/rayh/report/TestSuite.java +++ b/src/main/java/au/com/rayh/report/TestSuite.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2011 Ray Yamamoto Hilton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + package au.com.rayh.report; import java.util.ArrayList; diff --git a/src/main/resources/au/com/rayh/Messages.properties b/src/main/resources/au/com/rayh/Messages.properties new file mode 100644 index 00000000..b5d363d8 --- /dev/null +++ b/src/main/resources/au/com/rayh/Messages.properties @@ -0,0 +1,58 @@ +# +# The MIT License +# +# Copyright (c) 2011 Ray Yamamoto Hilton +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +XCodeBuilder.xcodebuildNotFound=Cannot find xcodebuild with the configured path {0}. +XCodeBuilder.avgtoolNotFound=Cannot find agvtool with the configured path {0}. +XCodeBuilder.configurationBuildDirMacroError=Failure while expanding macros and variables for configurationBuildDir. Error: {0} +XCodeBuilder.symRootMacroError=Failure while expanding macros and variables for symRoot. Error: {0} +XCodeBuilder.unlockKeychainFailed=Unable to unlock the keychain. +XCodeBuilder.xcrunNotFound=Cannot find xcrun with the configured path {0}. +XCodeBuilder.cleaningBuildDir=Cleaning build directory: {0} +XCodeBuilder.cleaningIPA=Cleaning up previously generate .ipa files +XCodeBuilder.cleaningTestReportsDir=Cleaning up test-reports : {0} +XCodeBuilder.fetchingCFBundleShortVersionString=Fetching marketing version number (CFBundleShortVersionString) from project. +XCodeBuilder.fetchingCFBundleVersion=Fetching technical version number (CFBundleVersion) from project. +XCodeBuilder.invokeXcodebuild=Going to invoke xcodebuild: +XCodeBuilder.packagingIPA=Packaging IPA +XCodeBuilder.workingDir=Working directory is {0}. +XCodeBuilder.xcode=XCode +XCodeBuilder.xcodeVersionNotFound=Check your XCode installation. Jenkins cannot retrieve its version. +XCodeBuilder.CFBundleShortVersionStringNotFound=No marketing version found (CFBundleShortVersionString). +XCodeBuilder.CFBundleShortVersionStringFound=Found marketing version (CFBundleShortVersionString): {0}. +XCodeBuilder.CFBundleShortVersionStringMacroError=Failure while expanding macros and variables for CFBundleShortVersionString. Error: {0} +XCodeBuilder.CFBundleShortVersionStringUpdate=Updating marketing version (CFBundleShortVersionString) to: {0} +XCodeBuilder.CFBundleShortVersionStringUpdateError=Could not set CFBundleShortVersionString to: {0} +XCodeBuilder.CFBundleShortVersionStringUsed=Marketing version (CFBundleShortVersionString) used by Jenkins to produce the IPA: {0} +XCodeBuilder.CFBundleShortVersionStringValue=Marketing version (CFBundleShortVersionString) found in project configuration: {0}. +XCodeBuilder.CFBundleVersionFound=Found marketing version (CFBundleVersion): {0}. +XCodeBuilder.CFBundleVersionMacroError=Failure while expanding macros and variables for CFBundleVersion. Error: {0} +XCodeBuilder.CFBundleVersionNotFound=No marketing version found (CFBundleVersion) +XCodeBuilder.CFBundleVersionUpdate=Updating technical version (CFBundleVersion) to: {0} +XCodeBuilder.CFBundleVersionUpdateError=Could not set the CFBundleVersion to: {0} +XCodeBuilder.CFBundleVersionUsed=Technical version (CFBundleVersion) used by Jenkins to produce the IPA: {0} +XCodeBuilder.CFBundleVersionValue=Technical version (CFBundleVersion) found in project configuration: {0}. +XCodeBuilder.NotExistingDirToCleanIPA=Cannot remove *.ipa files from a non-existing directory: {0} +XCodeBuilder.agvtoolPathNotSet=Please specify the path to the agvtool executable (usually /usr/bin/agvtool) +XCodeBuilder.xcodebuildPathNotSet=Please specify the path to the xcodebuild executable (usually /usr/bin/xcodebuild) +XCodeBuilder.xcrunPathNotSet=Please specify the path to the xcrun executable (usually /usr/bin/xcrun) +XCodeBuilder.zipFailed=Failed to zip *.dSYM into {0}-dSYM.zip \ No newline at end of file diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly b/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly index b466bc13..9bfebf0b 100644 --- a/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly +++ b/src/main/resources/au/com/rayh/XCodeBuilder/config.jelly @@ -1,70 +1,127 @@ + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - - - - + + - - + + - - + + + + + + - - + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly b/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly index 0a4db592..3cc616bf 100644 --- a/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly +++ b/src/main/resources/au/com/rayh/XCodeBuilder/global.jelly @@ -1,12 +1,37 @@ + + - - - - + + + - - - + + + + + diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-buildIpa.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-buildIpa.html new file mode 100644 index 00000000..9bffd7a4 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-buildIpa.html @@ -0,0 +1,30 @@ + + +
+

+ Checking this option will create a .ipa for each .app found in the build directory.
+ An .ipa is basically a zipped up .app.
+ This is quite handy for distributing ad-hoc builds to testers as they can just double-click the .ipa and it will import into iTunes.

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-cfBundleShortVersionStringValue.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-cfBundleShortVersionStringValue.html new file mode 100644 index 00000000..39dc41f5 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-cfBundleShortVersionStringValue.html @@ -0,0 +1,30 @@ + + +
+

+ This will set the CFBundleShortVersionString to the specified string.
+ Supports all macros and also environment and build variables from the Token Macro Plugin.
+

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-cfBundleVersionValue.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-cfBundleVersionValue.html new file mode 100644 index 00000000..0a21f7ae --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-cfBundleVersionValue.html @@ -0,0 +1,33 @@ + + +
+

+ This will set the CFBundleVersion to the specified string.
+ Supports all macros and also environment and build variables from the Token Macro Plugin.
+ For example the value ${BUILD_NUMBER} will be replaced with the current build number.
+ We advice you to generate a unique value for each build if you want for example deploy it into a private store.
+ In that case, for example, you can use : ${JOB_NAME}-${BUILD_NUMBER} +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-cleanBeforeBuild.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-cleanBeforeBuild.html new file mode 100644 index 00000000..9843f97b --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-cleanBeforeBuild.html @@ -0,0 +1,30 @@ + + +
+

+ This will delete the build directories before invoking the build. This will force the rebuilding of ALL + dependencies and can make large projects take a lot longer. +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-configuration.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-configuration.html new file mode 100644 index 00000000..81be5509 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-configuration.html @@ -0,0 +1,30 @@ + + +
+

+ This is the name of the configuration as defined in the XCode project.
+ By default there are Debug and Release configurations. +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-configurationBuildDir.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-configurationBuildDir.html new file mode 100644 index 00000000..3e0d2e8c --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-configurationBuildDir.html @@ -0,0 +1,35 @@ + + +
+

The value to use for CONFIGURATION_BUILD_DIR setting. You only need to supply this value if you want the product + of the XCode build to be in a location other than the one specified in project settings and this job 'SYMROOT' + parameter.
+ Supports all macros and also environment and + build variables from + the Token Macro Plugin.
+ For example you can use the value :
+

${WORKSPACE}/build

+

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-embeddedProfileFile.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-embeddedProfileFile.html new file mode 100644 index 00000000..3af33629 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-embeddedProfileFile.html @@ -0,0 +1,29 @@ + + +
+

+ The relative path to the mobileprovision to embed, leave blank for no embedded profile. +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-keychainPath.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-keychainPath.html new file mode 100644 index 00000000..156b156d --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-keychainPath.html @@ -0,0 +1,27 @@ + + +
+

The path of the keychain to use to retrieve certificates to sign the package (default : ${HOME}/Library/Keychains/login.keychain).

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-keychainPwd.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-keychainPwd.html new file mode 100644 index 00000000..f033e17e --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-keychainPwd.html @@ -0,0 +1,27 @@ + + +
+

The password of the keychain to use to retrieve certificates to sign the package.

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-sdk.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-sdk.html new file mode 100644 index 00000000..c4d926df --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-sdk.html @@ -0,0 +1,32 @@ + + +
+

+ You only need to supply this value if you want to specify the SDK to build against. If empty, the SDK will be + determined by XCode. If you wish to run OCUnit tests, you will need to use the iPhone Simulator's SDK, for + example: +

+
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.1.sdk/
+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-symRoot.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-symRoot.html new file mode 100644 index 00000000..4ab13473 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-symRoot.html @@ -0,0 +1,35 @@ + + +
+

+ You only need to supply this value if you want to specify the SYMROOT path to use.
+ If empty, the default SYMROOT path will be used (it could be different depending of your Xcode version).
+ Supports all macros and also environment and build + variables from the Token + Macro Plugin.
+ For example you can use the value :
+

+
${WORKSPACE}/symroot
+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-target.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-target.html new file mode 100644 index 00000000..903248d6 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-target.html @@ -0,0 +1,32 @@ + + +
+

+ The target to build. If left empty, this will build all targets in the project.
+ If you wish to build your binary and the unit test module, it is best to do this as two separate steps each with + their own target.
+ This was, the iPhone Simulator SDK can be specified for the unit tests. +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-unlockKeychain.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-unlockKeychain.html new file mode 100644 index 00000000..0aa6c145 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-unlockKeychain.html @@ -0,0 +1,28 @@ + + +
+

+ Automatically unlock the keychain before signing the archive?

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeProjectFile.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeProjectFile.html new file mode 100644 index 00000000..6d461481 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeProjectFile.html @@ -0,0 +1,30 @@ + + +
+

+ If there is more than one XCode project file in the project path, you will need to specify the file name of the project you wish to build.
+ If you need to build all project, you will need to create an XCode build step for each one manually. +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeProjectPath.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeProjectPath.html new file mode 100644 index 00000000..ccaa7553 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeProjectPath.html @@ -0,0 +1,30 @@ + + +
+

+ This is the relative path from the workspace to the directory that contains the XCode project file. + You only need to supply this value if the XCode project you wish to build is not in the root of the workspace. +

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeSchema.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeSchema.html new file mode 100644 index 00000000..ea1496a9 --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeSchema.html @@ -0,0 +1,28 @@ + + +
+

Only needed if you want to compile for a specific schema instead of a target. + It takes precedence over 'Xcode Configuration' setting and this job 'target' parameter.

+
diff --git a/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeWorkspaceFile.html b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeWorkspaceFile.html new file mode 100644 index 00000000..944f95fa --- /dev/null +++ b/src/main/resources/au/com/rayh/XCodeBuilder/help-xcodeWorkspaceFile.html @@ -0,0 +1,28 @@ + + +
+

Only needed if you want to compile a workspace instead of a project. + It takes precedence over 'Xcode Project File' setting and this job 'configuration' parameter."

+
diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly index b3c739b3..1442ed2f 100644 --- a/src/main/resources/index.jelly +++ b/src/main/resources/index.jelly @@ -1,3 +1,27 @@ + +