diff options
author | Jake Petroules <jake.petroules@petroules.com> | 2014-07-11 16:45:34 -0400 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@digia.com> | 2014-07-22 14:41:07 +0200 |
commit | 496707ce5ae88e124617553e8e24d887a750777c (patch) | |
tree | 9b4c9b43d6d7e226e9aeb77e873c81edf285af41 | |
parent | 8c0a975afacc10f0f2c9defffc95b8ae22e8da63 (diff) |
Add support for compiling Xcode asset catalogs.
Task-number: QBS-389
Change-Id: I2d93a55228c6ada801ed889404fac753182b82f6
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r-- | share/qbs/imports/qbs/DarwinTools/darwin-tools.js | 14 | ||||
-rw-r--r-- | share/qbs/modules/cpp/DarwinGCC.qbs | 20 | ||||
-rw-r--r-- | share/qbs/modules/ib/IBModule.qbs | 74 | ||||
-rw-r--r-- | share/qbs/modules/ib/ib.js | 29 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs | 15 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png | bin | 0 -> 3303 bytes | |||
-rw-r--r-- | tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16@2x.png | bin | 0 -> 3336 bytes | |||
-rw-r--r-- | tests/auto/blackbox/testdata/ib/assetcatalog/main.c | 4 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 37 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.h | 1 |
10 files changed, 190 insertions, 4 deletions
diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index 3c30aba50..56a727399 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -1,6 +1,20 @@ var FileInfo = loadExtension("qbs.FileInfo"); /** + * Returns the most appropriate Apple platform name given a targetOS list. + * Possible platform names include macosx, iphoneos, and iphonesimulator. + */ +function applePlatformName(targetOSList) { + if (targetOSList.contains("ios-simulator")) + return "iphonesimulator"; + else if (targetOSList.contains("ios")) + return "iphoneos"; + else if (targetOSList.contains("osx")) + return "macosx"; + throw("No Apple platform corresponds to target OS list: " + targetOSList); +} + +/** * Replace characters unsafe for use in a domain name with a '-' character (RFC 1034). */ function rfc1034(inStr) { diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs index 1081d3dc5..0de1041ce 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -152,7 +152,7 @@ UnixGCC { Rule { multiplex: true - inputs: ["qbs"] + inputs: ["qbs", "partial_infoplist"] Artifact { filePath: product.destinationDirectory + "/" + BundleTools.infoPlistPath(product) @@ -163,6 +163,7 @@ UnixGCC { var cmd = new JavaScriptCommand(); cmd.description = "generating Info.plist for " + product.name; cmd.highlight = "codegen"; + cmd.partialInfoPlistFiles = inputs.partial_infoplist; cmd.infoPlistFile = ModUtils.moduleProperty(product, "infoPlistFile"); cmd.infoPlist = ModUtils.moduleProperty(product, "infoPlist") || {}; cmd.processInfoPlist = ModUtils.moduleProperty(product, "processInfoPlist"); @@ -287,6 +288,15 @@ UnixGCC { env[key] = qmakeEnv[key]; DarwinTools.expandPlistEnvironmentVariables(aggregatePlist, env, true); + + // Add keys from partial Info.plists from asset catalogs, XIBs, and storyboards + for (i in partialInfoPlistFiles) { + var partialInfoPlist = BundleTools.infoPlistContents(partialInfoPlistFiles[i].filePath) || {}; + for (key in partialInfoPlist) { + if (partialInfoPlist.hasOwnProperty(key)) + aggregatePlist[key] = partialInfoPlist[key]; + } + } } if (infoPlistFormat === "same-as-input" && infoPlistFile) @@ -332,8 +342,9 @@ UnixGCC { Rule { multiplex: true - inputs: ["application", "infoplist", "pkginfo", "icns", "application_dsym", "compiled_nib", - "resourcerules", "ipa"] + inputs: ["application", "infoplist", "pkginfo", "icns", "application_dsym", + "compiled_nib", "compiled_assetcatalog", + "resourcerules", "ipa"] Artifact { filePath: product.destinationDirectory + "/" + BundleTools.wrapperName(product) @@ -351,7 +362,8 @@ UnixGCC { Rule { multiplex: true - inputs: ["dynamiclibrary", "infoplist", "pkginfo", "icns", "dynamiclibrary_dsym", "compiled_nib"] + inputs: ["dynamiclibrary", "infoplist", "pkginfo", "icns", "dynamiclibrary_dsym", + "compiled_nib", "compiled_assetcatalog"] Artifact { filePath: product.destinationDirectory + "/" + BundleTools.wrapperName(product) diff --git a/share/qbs/modules/ib/IBModule.qbs b/share/qbs/modules/ib/IBModule.qbs index bee1dd9e4..ebcf8fc2e 100644 --- a/share/qbs/modules/ib/IBModule.qbs +++ b/share/qbs/modules/ib/IBModule.qbs @@ -6,6 +6,8 @@ import qbs.ModUtils import 'ib.js' as Ib Module { + Depends { name: "cpp" } // to put toolchainInstallPath in the PATH for actool + condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin") property bool warnings: true @@ -23,9 +25,17 @@ Module { property string ibtoolPath: ibtoolName property bool flatten: true + // Asset catalog specific + property string actoolName: "actool" + property string actoolPath: actoolName + property string appIconName + property string launchImageName + property bool compressPngs: true + // private properties property string outputFormat: "human-readable-text" property string appleIconSuffix: ".icns" + property string compiledAssetCatalogSuffix: ".car" property string compiledNibSuffix: ".nib" property string ibtoolVersion: { return Ib.ibtoolVersion(ibtoolPath); } @@ -57,6 +67,11 @@ Module { fileTags: ["nib"] } + FileTagger { + patterns: ["*.xcassets"] // bundle + fileTags: ["assetcatalog"] + } + Rule { inputs: ["iconset"] @@ -126,4 +141,63 @@ Module { return cmd; } } + + Rule { + inputs: ["assetcatalog"] + + // We only return one artifact, as this is a little complicated... + // actool takes an output *directory*, and in this directory it will + // potentially output "Assets.car" and/or one or more additional files. + // We can discover which files were written in an easily parseable manner + // through use of --output-format xml1, but we have a chicken and egg problem + // in that we only gain that information *after* running the compilation, so + // if we want to know in advance which artifacts are generated we have to run + // the compilation twice which probably isn't worth it. + outputArtifacts: { + var outputDirectory = BundleTools.isBundleProduct(product) + ? BundleTools.unlocalizedResourcesFolderPath(product) + : product.destinationDirectory; + return [{ + filePath: FileInfo.joinPaths(outputDirectory, "Assets" + ModUtils.moduleProperty(product, "compiledAssetCatalogSuffix")), + fileTags: ["compiled_assetcatalog"] + }, + { + filePath: FileInfo.joinPaths(product.destinationDirectory, "assetcatalog_generated_info.plist"), + fileTags: ["partial_infoplist"] + }]; + } + + outputFileTags: ["compiled_assetcatalog", "partial_infoplist"] + + // Just a note, the man page for actool is somewhat outdated (probably forgotten to be updated late in the development cycle). + // It mentions the file extension .assetcatalog (which isn't used by Xcode), the --write option does not exist, and the example + // invocation near the bottom of the man page doesn't work at all. + // There's also the undocumented --export-dependency-info <output.txt> which is used by Xcode and generated a \0x00\0x02-delimited + // file (yes, really) that contains the output file names, identical to the output of actool itself (what's the point?). + prepare: { + var args = Ib.prepareIbtoold(product, input, outputs); + + var flags = ModUtils.moduleProperty(input, "flags"); + if (flags) + args = args.concat(flags); + + var outputPath = FileInfo.path(outputs.compiled_assetcatalog[0].filePath); + + args.push("--compile"); + args.push(outputPath); + args.push(input.filePath); + + var cmd = new Command(ModUtils.moduleProperty(input, "actoolPath"), args); + cmd.description = ModUtils.moduleProperty(input, "actoolName") + ' ' + input.fileName; + cmd.highlight = "compiler"; + cmd.stdoutFilterFunction = function(stdout) { + stdout = stdout.replace("/* com.apple.actool.compilation-results */\n", ""); + return stdout.split("\n").filter(function(line) { + return line.length > 0 /*&& line.indexOf(outputPath) !== 0*/; + }).join("\n"); + } + + return cmd; + } + } } diff --git a/share/qbs/modules/ib/ib.js b/share/qbs/modules/ib/ib.js index ac5d14462..3254cdb06 100644 --- a/share/qbs/modules/ib/ib.js +++ b/share/qbs/modules/ib/ib.js @@ -1,3 +1,4 @@ +var DarwinTools = loadExtension("qbs.DarwinTools"); var ModUtils = loadExtension("qbs.ModUtils"); var Process = loadExtension("qbs.Process"); var PropertyList = loadExtension("qbs.PropertyList"); @@ -22,6 +23,19 @@ function prepareIbtoold(product, input, outputs) { args.push("--notices"); if (input.fileTags.contains("assetcatalog")) { + args.push("--platform", DarwinTools.applePlatformName(product.moduleProperty("qbs", "targetOS"))); + + var appIconName = ModUtils.moduleProperty(input, "appIcon"); + if (appIconName) + args.push("--app-icon", appIconName); + + var launchImageName = ModUtils.moduleProperty(input, "launchImage"); + if (launchImageName) + args.push("--launch-image", launchImageName); + + // Undocumented but used by Xcode (only for iOS?), probably runs pngcrush or equivalent + if (ModUtils.moduleProperty(input, "compressPngs")) + args.push("--compress-pngs"); } else { var sysroot = product.moduleProperty("qbs", "sysroot"); if (sysroot) @@ -43,6 +57,21 @@ function prepareIbtoold(product, input, outputs) { } } + // --target-device and -output-partial-info-plist were introduced in Xcode 6.0 for ibtool + if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 6 || input.fileTags.contains("assetcatalog")) { + args.push("--output-partial-info-plist", outputs.partial_infoplist[0].filePath); + + if (product.moduleProperty("qbs", "targetOS").contains("osx")) + args.push("--target-device", "mac"); + + if (product.moduleProperty("qbs", "targetOS").contains("ios")) { + // TODO: Only output the devices specified in TARGET_DEVICE_FAMILY + // We can't get this info from Info.plist keys due to dependency order + args.push("--target-device", "iphone"); + args.push("--target-device", "ipad"); + } + } + return args; } diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs b/tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs new file mode 100644 index 000000000..bdd45fede --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs @@ -0,0 +1,15 @@ +import qbs + +Project { + property bool includeIconset + + CppApplication { + Depends { name: "ib" } + files: { + var filez = ["main.c", "empty.xcassets"]; + if (project.includeIconset) + filez.push("empty.xcassets/empty.iconset"); + return filez; + } + } +} diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png b/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png Binary files differnew file mode 100644 index 000000000..65ff1ff57 --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16@2x.png b/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16@2x.png Binary files differnew file mode 100644 index 000000000..a5c69bccd --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16@2x.png diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/main.c b/tests/auto/blackbox/testdata/ib/assetcatalog/main.c new file mode 100644 index 000000000..905869dfa --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index c791a6993..8e0edef9e 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -2295,6 +2295,43 @@ void TestBlackbox::testIconsetApp() QVERIFY(regularFileExists(productBuildDir("iconsetapp") + "/iconsetapp.app/Contents/Resources/white.icns")); } +void TestBlackbox::testAssetCatalog() +{ + if (!HostOsInfo::isOsxHost()) + SKIP_TEST("only applies on OS X"); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/assetcatalog")); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs"; + QCOMPARE(runQbs(params), 0); + + // empty asset catalogs must still produce output + QVERIFY((bool)m_qbsStdout.contains("actool")); + + // should not produce a CAR since minimumOsxVersion will be < 10.9 + QVERIFY(!regularFileExists(productBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Assets.car")); + + rmDirR(buildDir); + params.arguments.append("cpp.minimumOsxVersion:10.9"); // force CAR generation + QCOMPARE(runQbs(params), 0); + + // empty asset catalogs must still produce output + QVERIFY((bool)m_qbsStdout.contains("actool")); + QVERIFY(regularFileExists(productBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Assets.car")); + + // this asset catalog happens to have an embedded icon set, + // but this should NOT be built since it is not in the files list + QVERIFY(!(bool)m_qbsStdout.contains("iconutil")); + + // now we'll add the iconset + rmDirR(buildDir); + params.arguments.append("project.includeIconset:true"); + QCOMPARE(runQbs(params), 0); + QVERIFY((bool)m_qbsStdout.contains("actool")); + QVERIFY((bool)m_qbsStdout.contains("iconutil")); +} + QString TestBlackbox::uniqueProductName(const QString &productName) const { return productName + '.' + buildProfileName; diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 0ac522d76..320039713 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -187,6 +187,7 @@ private slots: void testTypeScript(); void testIconset(); void testIconsetApp(); + void testAssetCatalog(); private: QString uniqueProductName(const QString &productName) const; |