diff options
author | Jake Petroules <[email protected]> | 2015-02-02 22:38:55 -0800 |
---|---|---|
committer | Jake Petroules <[email protected]> | 2015-07-22 13:24:09 +0000 |
commit | d6e5db9d8f3771023465e13757e35fc752d7126c (patch) | |
tree | 86bae84d74ff004de9377bf50891f25edfaff9a3 | |
parent | a3cb59b277698ba92cdfde0ccc215e93b96ae92f (diff) |
Add manifest support in Java module.
The existing manifest property was never declared, so this never worked
in the first place. Additionally, add aggregation capabilities similar
to how Info.plist is handled in the DarwinGCC module.
Change-Id: I93e35936bdfd271adc2892bf2288d2f66d4f87a8
Reviewed-by: Christian Kandeler <[email protected]>
-rw-r--r-- | doc/reference/modules/java-module.qdoc | 24 | ||||
-rw-r--r-- | share/qbs/modules/java/JavaModule.qbs | 69 | ||||
-rw-r--r-- | share/qbs/modules/java/utils.js | 32 | ||||
-rw-r--r-- | src/lib/corelib/tools/hostosinfo.h | 9 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/java/vehicles.qbs | 13 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 32 |
6 files changed, 176 insertions, 3 deletions
diff --git a/doc/reference/modules/java-module.qdoc b/doc/reference/modules/java-module.qdoc index e7d5e2cdc..dd1647b83 100644 --- a/doc/reference/modules/java-module.qdoc +++ b/doc/reference/modules/java-module.qdoc @@ -150,6 +150,30 @@ \li The version of the Java runtime to generate compatible bytecode for. If undefined, the compiler will use its default. \row + \li manifest + \li object + \li 1.4.2 + \li undefined + \li The properties to add to the manifest file when building a JAR. + The contents of this property will be aggregated with the values from \c{manifestFile}. + If \c{manifest} and \c{manifestFile} contain the same key, the former will take + precedence. If undefined, will not be taken into account. + \row + \li manifestFile + \li path + \li 1.4.2 + \li undefined + \li The manifest file to embed when building a JAR. + The contents of this file will be aggregated with the values in \c{manifest}. + If \c{manifestFile} and \c{manifest} contain the same key, the latter will take + precedence. If undefined, will not be taken into account. + \row + \li manifestClassPath + \li stringList + \li 1.4.2 + \li undefined + \li The entries to add to the manifest's Class-Path when building a JAR. + \row \li warningsAsErrors \li bool \li 1.4 diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs index 70dd6477b..e39999c3c 100644 --- a/share/qbs/modules/java/JavaModule.qbs +++ b/share/qbs/modules/java/JavaModule.qbs @@ -33,6 +33,7 @@ import qbs.FileInfo import qbs.ModUtils import qbs.Probes import qbs.Process +import qbs.TextFile import "utils.js" as JavaUtils @@ -75,6 +76,30 @@ Module { description: "version of the Java runtime to generate compatible bytecode for" } + property var manifest: { + return { + "Manifest-Version": "1.0", + "Class-Path": manifestClassPath ? manifestClassPath.join(" ") : undefined + }; + } + + PropertyOptions { + name: "manifest" + description: "properties to add to the manifest file when building a JAR" + } + + property path manifestFile + PropertyOptions { + name: "manifestFile" + description: "manifest file to embed when building a JAR" + } + + property stringList manifestClassPath + PropertyOptions { + name: "manifestClassPath" + description: "entries to add to the manifest's Class-Path when building a JAR" + } + property bool warningsAsErrors: false property pathList jdkIncludePaths: { @@ -189,12 +214,50 @@ Module { } prepare: { - var i; + var i, key; var flags = "cf"; var args = [output.filePath]; - var manifestFile = ModUtils.moduleProperty(product, "manifest"); - if (manifestFile) { + var manifestFile = ModUtils.moduleProperty(product, "manifestFile"); + var manifest = ModUtils.moduleProperty(product, "manifest"); + var aggregateManifest = JavaUtils.manifestContents(manifestFile) || {}; + + // Add local key-value pairs (overrides equivalent keys specified in the file if + // one was given) + for (key in manifest) { + if (manifest.hasOwnProperty(key)) + aggregateManifest[key] = manifest[key]; + } + + for (key in aggregateManifest) { + if (aggregateManifest.hasOwnProperty(key) && aggregateManifest[key] === undefined) + delete aggregateManifest[key]; + } + + // Use default manifest unless we actually have properties to set + var needsManifestFile = manifestFile !== undefined || aggregateManifest !== {"Manifest-Version": "1.0"}; + + manifestFile = FileInfo.joinPaths(product.buildDirectory, "manifest.mf"); + + var mf; + try { + mf = new TextFile(manifestFile, TextFile.WriteOnly); + + // Ensure that manifest version comes first + mf.write("Manifest-Version: " + (aggregateManifest["Manifest-Version"] || "1.0") + "\n"); + delete aggregateManifest["Manifest-Version"]; + + for (key in aggregateManifest) + mf.write(key + ": " + aggregateManifest[key] + "\n"); + + mf.write("\n"); + } finally { + if (mf) { + mf.close(); + } + } + + if (needsManifestFile) { flags += "m"; args.push(manifestFile); } diff --git a/share/qbs/modules/java/utils.js b/share/qbs/modules/java/utils.js index c3ece67e2..7533e3dcc 100644 --- a/share/qbs/modules/java/utils.js +++ b/share/qbs/modules/java/utils.js @@ -237,6 +237,10 @@ function helperOverrideArgs(product, tool) { } function outputArtifacts(product, inputs) { + // Handle the case where a product depends on Java but has no Java sources + if (!inputs["java.java"] || inputs["java.java"].length === 0) + return []; + // We need to ensure that the output directory is created first, because the Java compiler // internally checks that it is present before performing any actions File.makePath(ModUtils.moduleProperty(product, "classFilesDir")); @@ -255,3 +259,31 @@ function outputArtifacts(product, inputs) { process.close(); } } + +function manifestContents(filePath) { + if (filePath === undefined) + return undefined; + + var contents, file; + try { + file = new TextFile(filePath); + contents = file.readAll(); + } finally { + if (file) { + file.close(); + } + } + + if (contents) { + var dict = {}; + var lines = contents.split(/[\r\n]/g); + for (var i in lines) { + var kv = lines[i].split(":"); + if (kv.length !== 2) + return undefined; + dict[kv[0]] = kv[1]; + } + + return dict; + } +} diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h index 8e8d6ae1c..fc27853c4 100644 --- a/src/lib/corelib/tools/hostosinfo.h +++ b/src/lib/corelib/tools/hostosinfo.h @@ -88,6 +88,15 @@ public: return isWindowsHost() ? Qt::CaseInsensitive: Qt::CaseSensitive; } + static QString libraryPathEnvironmentVariable() + { + if (isWindowsHost()) + return QStringLiteral("PATH"); + if (isOsxHost()) + return QStringLiteral("DYLD_LIBRARY_PATH"); + return QStringLiteral("LD_LIBRARY_PATH"); + } + static QChar pathListSeparator() { return isWindowsHost() ? QLatin1Char(';') : QLatin1Char(':'); diff --git a/tests/auto/blackbox/testdata/java/vehicles.qbs b/tests/auto/blackbox/testdata/java/vehicles.qbs index 4c2e7a5ed..5ac235172 100644 --- a/tests/auto/blackbox/testdata/java/vehicles.qbs +++ b/tests/auto/blackbox/testdata/java/vehicles.qbs @@ -24,6 +24,11 @@ Project { "Car.java", "HelloWorld.java", "Jet.java", "NoPackage.java", "Ship.java", "Vehicle.java", "Vehicles.java" ] + + Export { + Depends { name: "java" } + java.manifestClassPath: [product.targetName + ".jar"] + } } JavaJarFile { @@ -34,6 +39,11 @@ Project { fileTagsFilter: ["java.jar"] qbs.install: true } + + Export { + Depends { name: "java" } + java.manifestClassPath: [product.targetName + ".jar"] + } } JavaJarFile { @@ -50,6 +60,9 @@ Project { Export { Depends { name: "cpp" } cpp.systemIncludePaths: product.cppIncludePaths + + Depends { name: "java" } + java.manifestClassPath: [product.targetName + ".jar"] } Group { diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 9ffffff77..7bf59f487 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -1547,6 +1547,15 @@ void TestBlackbox::installTree() QVERIFY(QFile::exists(installRoot + "content/subdir2/baz.txt")); } +static QProcessEnvironment processEnvironmentWithCurrentDirectoryInLibraryPath() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(HostOsInfo::libraryPathEnvironmentVariable(), + QStringList({env.value(HostOsInfo::libraryPathEnvironmentVariable()), "."}) + .join(HostOsInfo::pathListSeparator())); + return env; +} + void TestBlackbox::java() { Settings settings((QString())); @@ -1584,6 +1593,29 @@ void TestBlackbox::java() foreach (const QString &classFile, classFiles1) { QVERIFY2(!regularFileExists(classFile), qPrintable(classFile)); } + + // This tests various things: java.manifestClassPath, JNI, etc. + QDir::setCurrent(relativeBuildDir() + "/install-root"); + QProcess process; + process.setProcessEnvironment(processEnvironmentWithCurrentDirectoryInLibraryPath()); + process.start("java", QStringList() << "-jar" << "jar_file.jar"); + QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + QVERIFY2(process.exitCode() == 0, process.readAllStandardError().constData()); + QByteArray stdout = process.readAllStandardOutput(); + QVERIFY2(stdout.contains("Driving!"), stdout.constData()); + QVERIFY2(stdout.contains("Flying!"), stdout.constData()); + QVERIFY2(stdout.contains("Flying (this is a space ship)!"), stdout.constData()); + QVERIFY2(stdout.contains("Sailing!"), stdout.constData()); + QVERIFY2(stdout.contains("Native code performing complex internal combustion process (0x"), + stdout.constData()); + + process.start("unzip", QStringList() << "-p" << "jar_file.jar"); + QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + stdout = process.readAllStandardOutput(); + QVERIFY2(stdout.contains("Class-Path: car_jar.jar random_stuff.jar"), stdout.constData()); + QVERIFY2(stdout.contains("Main-Class: Vehicles"), stdout.constData()); } void TestBlackbox::jsExtensionsFile() |