aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJake Petroules <[email protected]>2015-02-02 22:38:55 -0800
committerJake Petroules <[email protected]>2015-07-22 13:24:09 +0000
commitd6e5db9d8f3771023465e13757e35fc752d7126c (patch)
tree86bae84d74ff004de9377bf50891f25edfaff9a3
parenta3cb59b277698ba92cdfde0ccc215e93b96ae92f (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.qdoc24
-rw-r--r--share/qbs/modules/java/JavaModule.qbs69
-rw-r--r--share/qbs/modules/java/utils.js32
-rw-r--r--src/lib/corelib/tools/hostosinfo.h9
-rw-r--r--tests/auto/blackbox/testdata/java/vehicles.qbs13
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp32
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()