aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJake Petroules <jake.petroules@petroules.com>2015-06-09 23:53:05 -0700
committerJake Petroules <jake.petroules@petroules.com>2015-07-29 19:18:55 +0000
commitef5992bb58473bca8c019f2eba9faa0048aa0c05 (patch)
treeafa5f222f3d72be13f904b0fdfcdea80a4aaef29
parent248ed3e0e0e809f8994c2e8983db830fbc2bdb21 (diff)
Add code signing support for OS X and iOS.
Change-Id: Ia85f68692974288780484fa47a896d66f16bc11b Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
-rw-r--r--doc/reference/modules/xcode-module.qdoc44
-rw-r--r--share/qbs/modules/bundle/BundleModule.qbs36
-rw-r--r--share/qbs/modules/cpp/CppModule.qbs5
-rw-r--r--share/qbs/modules/cpp/gcc.js13
-rw-r--r--share/qbs/modules/cpp/ios-gcc.qbs34
-rw-r--r--share/qbs/modules/xcode/xcode.js51
-rw-r--r--share/qbs/modules/xcode/xcode.qbs48
7 files changed, 203 insertions, 28 deletions
diff --git a/doc/reference/modules/xcode-module.qdoc b/doc/reference/modules/xcode-module.qdoc
index 1969ef9ae..ac5a36f19 100644
--- a/doc/reference/modules/xcode-module.qdoc
+++ b/doc/reference/modules/xcode-module.qdoc
@@ -56,6 +56,11 @@
directory of the Xcode installation at its default location in /Applications.
Corresponds to the \c DEVELOPER_DIR environment variable.
\row
+ \li provisioningProfile
+ \li string
+ \li undefined
+ \li Name or UUID of the provisioning profile to embed in the product.
+ \row
\li sdk
\li string
\li \c{"macosx"} on OS X, \c{"iphoneos"} on iOS, \c{"iphonesimulator"} on iOS Simulator
@@ -64,6 +69,23 @@
\c{"10.10"}), or a platform identifier (i.e. \c{"macosx"}) in which case the latest SDK
available for that platform will be used.
\row
+ \li signingIdentity
+ \li string
+ \li undefined
+ \li Search string used to find the certificate to sign the product. This does not have to be
+ a full certificate name like "Mac Developer: John Doe (XXXXXXXXXX)", and can instead be
+ a partial string like "Mac Developer".
+ The search string should generally be one of the following:
+ \li 3rd Party Mac Developer Application
+ \li 3rd Party Mac Developer Installer
+ \li Developer ID Application
+ \li Developer ID Installer
+ \li iPhone Developer
+ \li iPhone Distribution
+ \li Mac Developer
+ Complete documentation for the existing certificate types can be found at:
+ https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW41
+ \row
\li targetDevices
\li stringList
\li \c{["mac"]} on OS X; {["iphone", "ipad"]} on iOS
@@ -71,6 +93,28 @@
"mac", and for iOS it can be one or both of "iphone" and "ipad".
\endtable
+ \section1 Advanced Properties
+
+ \table
+ \header
+ \li Property
+ \li Type
+ \li Default
+ \li Description
+ \row
+ \li provisioningProfilePath
+ \li path
+ \li determined by \c{xcode.provisioningProfilesPath} and \c{xcode.provisioningProfile}
+ \li Path of the provisioning profile to embed in the product. Generally you should not set
+ this property and instead use \c{xcode.provisioningProfile} to automatically
+ determine the provisioning profile's path based on its name or UUID.
+ \row
+ \li provisioningProfilesPath
+ \li path
+ \li \c{"~/Library/MobileDevice/Provisioning Profiles"}
+ \li Path to directory containing provisioning profiles installed on the system.
+ \endtable
+
\section1 Read-only Properties
\table
diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs
index c553946d8..d1dee8e14 100644
--- a/share/qbs/modules/bundle/BundleModule.qbs
+++ b/share/qbs/modules/bundle/BundleModule.qbs
@@ -426,7 +426,7 @@ Module {
condition: qbs.targetOS.contains("darwin")
multiplex: true
inputs: ["infoplist", "pkginfo", "hpp",
- "icns", "resourcerules", "ipa",
+ "icns", "resourcerules",
"compiled_nib", "compiled_storyboard", "compiled_assetcatalog"]
outputFileTags: ["bundle",
@@ -610,12 +610,36 @@ Module {
if (cmd.sources && cmd.sources.length)
commands.push(cmd);
- if (product.type.contains("application") && product.moduleProperty("qbs", "hostOS").contains("darwin")) {
+ if (product.moduleProperty("qbs", "hostOS").contains("darwin")) {
for (i in bundles) {
- cmd = new Command(ModUtils.moduleProperty(product, "lsregisterPath"),
- ["-f", bundles[i].filePath]);
- cmd.description = "register " + ModUtils.moduleProperty(product, "bundleName");
- commands.push(cmd);
+ var actualSigningIdentity = product.moduleProperty("xcode", "actualSigningIdentity");
+ var codesignDisplayName = product.moduleProperty("xcode", "actualSigningIdentityDisplayName");
+ if (actualSigningIdentity) {
+ // If this is a framework, we need to sign its versioned directory
+ var subpath = "";
+ var frameworkVersion = ModUtils.moduleProperty(product, "frameworkVersion");
+ if (frameworkVersion) {
+ subpath = ModUtils.moduleProperty(product, "contentsFolderPath");
+ subpath = subpath.substring(subpath.indexOf(ModUtils.moduleProperty("qbs", "pathSeparator")));
+ }
+
+ cmd = new Command(product.moduleProperty("xcode", "codesignPath"),
+ (product.moduleProperty("xcode", "codesignFlags") || [])
+ .concat(["--force", "--sign", actualSigningIdentity,
+ bundles[i].filePath + subpath]));
+ cmd.description = "codesign "
+ + ModUtils.moduleProperty(product, "bundleName")
+ + " using " + codesignDisplayName
+ + " (" + actualSigningIdentity + ")";
+ commands.push(cmd);
+ }
+
+ if (product.type.contains("application")) {
+ cmd = new Command(ModUtils.moduleProperty(product, "lsregisterPath"),
+ ["-f", bundles[i].filePath]);
+ cmd.description = "register " + ModUtils.moduleProperty(product, "bundleName");
+ commands.push(cmd);
+ }
}
}
diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs
index 3443f1c7b..cc382c0b2 100644
--- a/share/qbs/modules/cpp/CppModule.qbs
+++ b/share/qbs/modules/cpp/CppModule.qbs
@@ -239,11 +239,6 @@ Module {
property stringList platformLinkerFlags
// OS X and iOS properties
- property bool buildIpa: !qbs.targetOS.contains('ios-simulator')
-
- property string signingIdentity
- property path provisioningProfile
-
property bool automaticReferenceCounting
PropertyOptions {
name: "automaticReferenceCounting"
diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js
index ce92771a5..a8af8353b 100644
--- a/share/qbs/modules/cpp/gcc.js
+++ b/share/qbs/modules/cpp/gcc.js
@@ -667,6 +667,19 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
}
}
+ var actualSigningIdentity = product.moduleProperty("xcode", "actualSigningIdentity");
+ var codesignDisplayName = product.moduleProperty("xcode", "actualSigningIdentityDisplayName");
+ if (actualSigningIdentity && !product.moduleProperty("bundle", "isBundle")) {
+ cmd = new Command(product.moduleProperty("xcode", "codesignPath"),
+ ["--force", "--sign", actualSigningIdentity,
+ primaryOutput.filePath]);
+ cmd.description = "codesign "
+ + primaryOutput.fileName
+ + " using " + codesignDisplayName
+ + " (" + actualSigningIdentity + ")";
+ commands.push(cmd);
+ }
+
return commands;
}
diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs
index 93df91c9c..c6ad1f8da 100644
--- a/share/qbs/modules/cpp/ios-gcc.qbs
+++ b/share/qbs/modules/cpp/ios-gcc.qbs
@@ -31,6 +31,7 @@
import qbs 1.0
import qbs.DarwinTools
import qbs.File
+import qbs.FileInfo
import qbs.ModUtils
DarwinGCC {
@@ -78,36 +79,35 @@ DarwinGCC {
}
Rule {
- condition: product.moduleProperty("cpp", "buildIpa")
- multiplex: true
- inputs: ["application", "infoplist", "pkginfo", "resourcerules", "compiled_nib"]
+ inputsFromDependencies: ["bundle"]
Artifact {
- filePath: product.destinationDirectory + "/" + product.targetName + ".ipa"
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.targetName + ".ipa")
fileTags: ["ipa"]
}
prepare: {
- var signingIdentity = product.moduleProperty("cpp", "signingIdentity");
+ var signingIdentity = product.moduleProperty("xcode", "actualSigningIdentity");
+ var signingIdentityDisplay = product.moduleProperty("xcode",
+ "actualSigningIdentityDisplayName");
if (!signingIdentity)
throw "The name of a valid signing identity must be set using " +
- "cpp.signingIdentity in order to build an IPA package.";
+ "xcode.signingIdentity in order to build an IPA package.";
- var provisioningProfile = product.moduleProperty("cpp", "provisioningProfile");
- if (!provisioningProfile)
+ var provisioningProfilePath = product.moduleProperty("xcode",
+ "provisioningProfilePath");
+ if (!provisioningProfilePath)
throw "The path to a provisioning profile must be set using " +
- "cpp.provisioningProfile in order to build an IPA package.";
+ "xcode.provisioningProfilePath in order to build an IPA package.";
- var args = ["-sdk", product.moduleProperty("cpp", "xcodeSdkName"), "PackageApplication",
- "-v", product.buildDirectory + "/" + product.moduleProperty("bundle", "bundleName"),
- "-o", outputs.ipa[0].filePath, "--sign", signingIdentity,
- "--embed", provisioningProfile];
+ var args = [input.filePath,
+ "-o", output.filePath,
+ "--sign", signingIdentity,
+ "--embed", provisioningProfilePath];
- var command = "/usr/bin/xcrun";
- var cmd = new Command(command, args)
- cmd.description = "creating ipa, signing with " + signingIdentity;
+ var cmd = new Command("PackageApplication", args);
+ cmd.description = "creating ipa, signing with " + signingIdentityDisplay;
cmd.highlight = "codegen";
- cmd.workingDirectory = product.buildDirectory;
return cmd;
}
}
diff --git a/share/qbs/modules/xcode/xcode.js b/share/qbs/modules/xcode/xcode.js
index 6b93b9985..75c0ec580 100644
--- a/share/qbs/modules/xcode/xcode.js
+++ b/share/qbs/modules/xcode/xcode.js
@@ -30,6 +30,7 @@
var File = loadExtension("qbs.File");
var FileInfo = loadExtension("qbs.FileInfo");
+var Process = loadExtension("qbs.Process");
var PropertyList = loadExtension("qbs.PropertyList");
function applePlatformDirectoryName(targetOSList, version, throwOnError) {
@@ -97,3 +98,53 @@ function sdkInfoList(sdksPath) {
return sdkInfo;
}
+
+function findSigningIdentities(security, searchString) {
+ var process;
+ var identities;
+ if (searchString) {
+ try {
+ process = new Process();
+ if (process.exec(security, ["find-identity", "-p", "codesigning", "-v"], true) !== 0)
+ print(process.readStdErr());
+
+ var lines = process.readStdOut().split("\n");
+ for (var i in lines) {
+ // e.g. 1) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "Mac Developer: John Doe (XXXXXXXXXX) john.doe@example.org"
+ var match = lines[i].match(/^\s*[0-9]+\)\s+([A-Fa-f0-9]{40})\s+"([^"]+)"$/);
+ if (match !== null) {
+ var hexId = match[1];
+ var displayName = match[2];
+ if (hexId === searchString || displayName.startsWith(searchString)) {
+ if (!identities)
+ identities = [];
+ identities.push([hexId, displayName]);
+ break;
+ }
+ }
+ }
+ } finally {
+ process.close();
+ }
+ }
+ return identities;
+}
+
+function readProvisioningProfileData(path) {
+ var process;
+ try {
+ process = new Process();
+ if (process.exec("openssl", ["smime", "-verify", "-noverify", "-inform", "DER", "-in", path], true) !== 0)
+ print(process.readStdErr());
+
+ var propertyList = new PropertyList();
+ try {
+ propertyList.readFromString(process.readStdOut());
+ return propertyList.toObject();
+ } finally {
+ propertyList.clear();
+ }
+ } finally {
+ process.close();
+ }
+}
diff --git a/share/qbs/modules/xcode/xcode.qbs b/share/qbs/modules/xcode/xcode.qbs
index a8464b330..998520417 100644
--- a/share/qbs/modules/xcode/xcode.qbs
+++ b/share/qbs/modules/xcode/xcode.qbs
@@ -54,6 +54,36 @@ Module {
}
}
+ property string signingIdentity
+ readonly property string actualSigningIdentity: {
+ if (_actualSigningIdentity && _actualSigningIdentity.length === 1)
+ return _actualSigningIdentity[0][0];
+ }
+
+ readonly property string actualSigningIdentityDisplayName: {
+ if (_actualSigningIdentity && _actualSigningIdentity.length === 1)
+ return _actualSigningIdentity[0][1];
+ }
+
+ property string provisioningProfile
+ property path provisioningProfilePath: {
+ var files = _availableProvisioningProfiles;
+ for (var i in files) {
+ var data = Utils.readProvisioningProfileData(files[i]);
+ if (data["UUID"] === provisioningProfile ||
+ data["Name"] === provisioningProfile) {
+ return files[i];
+ }
+ }
+ }
+
+ property string securityName: "security"
+ property string securityPath: securityName
+
+ property string codesignName: "codesign"
+ property string codesignPath: codesignName
+ property stringList codesignFlags
+
readonly property path toolchainPath: FileInfo.joinPaths(toolchainsPath,
"XcodeDefault" + ".xctoolchain")
readonly property path platformPath: FileInfo.joinPaths(platformsPath,
@@ -75,6 +105,23 @@ Module {
readonly property path toolchainInfoPlist: FileInfo.joinPaths(toolchainPath,
"ToolchainInfo.plist")
+ readonly property stringList _actualSigningIdentity: {
+ if (/^[A-Fa-f0-9]{40}$/.test(signingIdentity)) {
+ return signingIdentity;
+ }
+
+ var identities = Utils.findSigningIdentities(securityPath, signingIdentity);
+ if (identities && identities.length > 1) {
+ throw "Signing identity '" + signingIdentity + "' is ambiguous";
+ }
+
+ return identities;
+ }
+
+ property path provisioningProfilesPath: {
+ return FileInfo.joinPaths(qbs.getEnv("HOME"), "Library/MobileDevice/Provisioning Profiles");
+ }
+
readonly property var _availableSdks: Utils.sdkInfoList(sdksPath)
readonly property var _sdkSettings: {
@@ -135,6 +182,7 @@ Module {
property var buildEnv: {
return {
+ "CODESIGN_ALLOCATE": platformPath + "/Developer/usr/bin/codesign_allocate",
"DEVELOPER_DIR": developerPath
};
}