aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJake Petroules <jake.petroules@petroules.com>2015-05-09 00:49:08 -0700
committerJake Petroules <jake.petroules@petroules.com>2015-05-28 15:46:43 +0000
commitc9d511989698159acff1432186bafd82bdb6bacc (patch)
tree6fbe8ca0145509295a2013399e4ae6302d2fecb1
parent2a13a5ec3843ef06a1a41f7220d50b7b63883abb (diff)
Add support for tracking class, h files produced by Java inner classes.
This also resolves the "perfect parsing" issue and so determining output file paths for Java class files won't be fooled by comments, etc. Feel free to use NULLs, unpaired surrogates, emoji, etc! Task-number: QBS-228 Change-Id: I3576a9a8e51a717ab8159db35f1a01e3ad91df88 Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
-rw-r--r--doc/qbs.qdoc1
-rw-r--r--qbs.qbs1
-rw-r--r--share/qbs/modules/java/JavaModule.qbs16
-rw-r--r--share/qbs/modules/java/utils.js54
-rw-r--r--src/libexec/libexec.pro5
-rw-r--r--src/libexec/libexec.qbs1
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/Artifact.java75
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListJsonWriter.java153
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListTextWriter.java60
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListWriter.java40
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListXmlWriter.java98
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/tools/JavaCompilerScannerTool.java66
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerOptions.java100
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerScanner.java136
-rw-r--r--src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/NullFileObject.java166
-rw-r--r--src/libexec/qbs-javac-scan/qbs-javac-scan.pro62
-rw-r--r--src/libexec/qbs-javac-scan/qbs-javac-scan.qbs19
-rw-r--r--tests/auto/blackbox/testdata/java/Car.java5
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp2
19 files changed, 1031 insertions, 29 deletions
diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc
index 460833ec6..4f88b8e5c 100644
--- a/doc/qbs.qdoc
+++ b/doc/qbs.qdoc
@@ -149,6 +149,7 @@
\table
\header \li Option \li Notes
+ \row \li qbs_enable_java \li Enable support for compiling Java source code.
\row \li qbs_enable_unit_tests \li Enable additional autotests.
\row \li qbs_disable_rpath \li Disable the use of rpath. This can be used when packaging
\QBS for distributions which do not permit the use of rpath,
diff --git a/qbs.qbs b/qbs.qbs
index 979d6adef..2fc90776d 100644
--- a/qbs.qbs
+++ b/qbs.qbs
@@ -3,6 +3,7 @@ import qbs 1.0
Project {
minimumQbsVersion: "1.4"
qbsSearchPaths: ["qbs-resources"]
+ property bool enableJava: false
property bool enableUnitTests: false
property bool enableProjectFileUpdates: false
property bool enableRPath: true
diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs
index b41fbe4d9..c1b1b18af 100644
--- a/share/qbs/modules/java/JavaModule.qbs
+++ b/share/qbs/modules/java/JavaModule.qbs
@@ -111,21 +111,9 @@ Module {
multiplex: true
inputs: ["java.java"]
inputsFromDependencies: ["java.jar"]
- outputFileTags: ["java.class"] // Annotations can produce additional java source files. Ignored for now.
+ outputFileTags: ["java.class", "hpp"] // Annotations can produce additional java source files. Ignored for now.
outputArtifacts: {
- // Note: We'd have to duplicate some javac functionality to catch all outputs
- // (e.g. private classes), so ignore these for now.
- var oFilePaths = [];
- // Extract package name to find out where the class name will be
- for (var i = 0; i < inputs["java.java"].length; ++i) {
- var inp = inputs["java.java"][i];
- var packageName = JavaUtils.extractPackageName(inp.filePath);
- var oFileName = inp.completeBaseName + ".class";
- var oFilePath = FileInfo.joinPaths(ModUtils.moduleProperty(product, "classFilesDir"),
- packageName.split('.').join('/'), oFileName);
- oFilePaths.push({ filePath: oFilePath, fileTags: ["java.class"] });
- }
- return oFilePaths;
+ return JavaUtils.outputArtifacts(product, inputs);
}
prepare: {
var cmd = new Command(ModUtils.moduleProperty(product, "compilerFilePath"),
diff --git a/share/qbs/modules/java/utils.js b/share/qbs/modules/java/utils.js
index 8b469a013..49264027a 100644
--- a/share/qbs/modules/java/utils.js
+++ b/share/qbs/modules/java/utils.js
@@ -28,19 +28,9 @@
**
****************************************************************************/
-var TextFile = loadExtension("qbs.TextFile");
-
-// TODO: Parse exactly, so we won't get fooled by e.g. comments.
-function extractPackageName(filePath)
-{
- var file = new TextFile(filePath);
- var contents = file.readAll();
- file.close();
- var packageName = contents.replace(/[\s\S]*package\s+([^\s;]+)[\s;][\s\S]*/m, "$1");
- if (packageName === contents) // no package statement
- return "";
- return packageName;
-}
+var File = loadExtension("qbs.File");
+var FileInfo = loadExtension("qbs.FileInfo");
+var Process = loadExtension("qbs.Process");
function supportsGeneratedNativeHeaderFiles(product) {
var compilerVersionMajor = ModUtils.moduleProperty(product, "compilerVersionMajor");
@@ -91,3 +81,41 @@ function javacArguments(product, inputs) {
args.push(inputs["java.java"][i].filePath);
return args;
}
+
+function outputArtifacts(product, inputs) {
+ var process;
+
+ // 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
+ try {
+ process = new Process();
+ if (product.moduleProperty("qbs", "hostOS").contains("windows"))
+ process.exec("cmd", ["/c", "mkdir",
+ FileInfo.toWindowsSeparators(
+ ModUtils.moduleProperty(product, "classFilesDir"))],
+ true);
+ else
+ process.exec("mkdir", ["-p", ModUtils.moduleProperty(product, "classFilesDir")],
+ true);
+ } finally {
+ process.close();
+ }
+
+ if (!File.exists(FileInfo.joinPaths(product.moduleProperty("qbs", "libexecPath"),
+ "qbs-javac-scan.jar"))) {
+ throw "Qbs was built without Java support. Rebuild Qbs with CONFIG+=qbs_enable_java " +
+ "(qmake) or project.enableJava:true (self hosted build)";
+ }
+
+ try {
+ process = new Process();
+ process.exec(product.moduleProperty("java", "interpreterFilePath"), ["-jar",
+ FileInfo.joinPaths(product.moduleProperty("qbs", "libexecPath"),
+ "qbs-javac-scan.jar"),
+ "--output-format", "json"]
+ .concat(javacArguments(product, inputs)), true);
+ return JSON.parse(process.readStdOut());
+ } finally {
+ process.close();
+ }
+}
diff --git a/src/libexec/libexec.pro b/src/libexec/libexec.pro
index 967108504..f1044a609 100644
--- a/src/libexec/libexec.pro
+++ b/src/libexec/libexec.pro
@@ -1 +1,6 @@
TEMPLATE = subdirs
+
+qbs_enable_java {
+ SUBDIRS += \
+ qbs-javac-scan
+}
diff --git a/src/libexec/libexec.qbs b/src/libexec/libexec.qbs
index 489864a26..f132925e2 100644
--- a/src/libexec/libexec.qbs
+++ b/src/libexec/libexec.qbs
@@ -2,5 +2,6 @@ import qbs
Project {
references: [
+ "qbs-javac-scan/qbs-javac-scan.qbs"
]
}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/Artifact.java b/src/libexec/qbs-javac-scan/io/qt/qbs/Artifact.java
new file mode 100644
index 000000000..6d250043e
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/Artifact.java
@@ -0,0 +1,75 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Artifact {
+ private String filePath;
+ private List<String> fileTags;
+
+ public Artifact(String filePath) {
+ if (filePath == null)
+ throw new IllegalArgumentException("filePath");
+
+ this.filePath = filePath;
+ this.fileTags = new ArrayList<String>();
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public List<String> getFileTags() {
+ return fileTags;
+ }
+
+ public void setFileTags(List<String> fileTags) {
+ this.fileTags = fileTags;
+ }
+
+ public void addFileTag(String fileTag) {
+ this.fileTags.add(fileTag);
+ }
+
+ public void removeFileTag(String fileTag) {
+ this.fileTags.remove(fileTag);
+ }
+
+ public void clearFileTags() {
+ this.fileTags.clear();
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListJsonWriter.java b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListJsonWriter.java
new file mode 100644
index 000000000..02198d0f4
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListJsonWriter.java
@@ -0,0 +1,153 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+/**
+ * This uses a custom JSON implementation because the Java Standard Library does
+ * not yet have native support for JSON, and only minimal support is required
+ * here.
+ */
+public class ArtifactListJsonWriter implements ArtifactListWriter {
+ private static final int TAB_WIDTH = 4;
+
+ // based on escapeString from qtbase/qjsonwriter.cpp
+ private static String escapeString(String s) {
+ String out = "";
+ for (int i = 0; i < s.length();) {
+ int u = s.codePointAt(i);
+
+ // unpaired surrogate
+ if (u >= Character.MIN_SURROGATE && u <= Character.MAX_SURROGATE) {
+ out += "\ufffd";
+ i += Character.charCount(u);
+ continue;
+ }
+
+ if (u < 0x80) {
+ if (u < 0x20 || u == 0x22 || u == 0x5c) {
+ out += "\\";
+ switch (u) {
+ case 0x22:
+ out += "\"";
+ break;
+ case 0x5c:
+ out += "\\";
+ break;
+ case 0x8:
+ out += "b";
+ break;
+ case 0xc:
+ out += "f";
+ break;
+ case 0xa:
+ out += "n";
+ break;
+ case 0xd:
+ out += "r";
+ break;
+ case 0x9:
+ out += "t";
+ break;
+ default:
+ out += "u";
+ out += "0";
+ out += "0";
+ String hex = Integer.toHexString(u);
+ if (hex.length() == 1)
+ out += "0";
+ out += hex;
+ break;
+ }
+ } else {
+ out += s.substring(i, i + Character.charCount(u));
+ }
+ } else {
+ out += s.substring(i, i + Character.charCount(u));
+ }
+
+ i += Character.charCount(u);
+ }
+
+ return out;
+ }
+
+ private static void writeString(PrintStream printWriter, String s) {
+ printWriter.print("\"");
+ printWriter.print(escapeString(s));
+ printWriter.print("\"");
+ }
+
+ private static void writeIndent(PrintStream printWriter, int level) {
+ for (int i = 0; i < level * TAB_WIDTH; ++i) {
+ printWriter.print(" ");
+ }
+ }
+
+ private static void writeArtifact(Artifact artifact,
+ PrintStream printWriter, int indentLevel, Boolean comma) {
+ writeIndent(printWriter, indentLevel++);
+ printWriter.print("{\n");
+ writeIndent(printWriter, indentLevel);
+ writeString(printWriter, "filePath");
+ printWriter.print(": ");
+ writeString(printWriter, artifact.getFilePath());
+ printWriter.println(",");
+ writeIndent(printWriter, indentLevel);
+ writeString(printWriter, "fileTags");
+ printWriter.print(": [");
+ for (int i = 0; i < artifact.getFileTags().size(); ++i) {
+ writeString(printWriter, artifact.getFileTags().get(i));
+ if (i != artifact.getFileTags().size() - 1)
+ printWriter.print(", ");
+ }
+ printWriter.println("]");
+ writeIndent(printWriter, --indentLevel);
+ printWriter.println("}" + (comma ? "," : ""));
+ }
+
+ @Override
+ public void write(List<Artifact> artifacts, OutputStream outputStream)
+ throws IOException {
+ PrintStream printWriter = new PrintStream(outputStream);
+ printWriter.print("[\n");
+ for (int i = 0; i < artifacts.size(); ++i) {
+ writeArtifact(artifacts.get(i), printWriter, 1,
+ i != artifacts.size() - 1);
+ }
+
+ printWriter.println("]");
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListTextWriter.java b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListTextWriter.java
new file mode 100644
index 000000000..2b32bdd2a
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListTextWriter.java
@@ -0,0 +1,60 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+public class ArtifactListTextWriter implements ArtifactListWriter {
+ private String join(List<String> list, String separator) {
+ String string = "";
+ if (!list.isEmpty()) {
+ string += list.get(0);
+ for (int i = 1; i < list.size(); ++i) {
+ string += separator + list;
+ }
+ }
+
+ return string;
+ }
+
+ @Override
+ public void write(List<Artifact> artifacts, OutputStream outputStream)
+ throws IOException {
+ PrintStream stream = new PrintStream(outputStream);
+ for (Artifact artifact : artifacts) {
+ stream.format("%s [%s]\n", artifact.getFilePath(),
+ join(artifact.getFileTags(), ", "));
+ }
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListWriter.java b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListWriter.java
new file mode 100644
index 000000000..30a5b816f
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListWriter.java
@@ -0,0 +1,40 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+public interface ArtifactListWriter {
+ public abstract void write(List<Artifact> artifacts,
+ OutputStream outputStream) throws IOException;
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListXmlWriter.java b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListXmlWriter.java
new file mode 100644
index 000000000..9fbf09ed1
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/ArtifactListXmlWriter.java
@@ -0,0 +1,98 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class ArtifactListXmlWriter implements ArtifactListWriter {
+ @Override
+ public void write(List<Artifact> artifacts, OutputStream outputStream)
+ throws IOException {
+ try {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
+ .newInstance();
+ DocumentBuilder documentBuilder = documentBuilderFactory
+ .newDocumentBuilder();
+
+ Document document = documentBuilder.newDocument();
+ Element rootElement = document.createElement("artifacts");
+ document.appendChild(rootElement);
+
+ for (Artifact artifact : artifacts) {
+ Element artifactElement = document.createElement("artifact");
+ rootElement.appendChild(artifactElement);
+
+ Element filePathElement = document.createElement("filePath");
+ artifactElement.appendChild(filePathElement);
+ filePathElement.appendChild(document.createTextNode(artifact
+ .getFilePath()));
+
+ Element fileTagsElement = document.createElement("fileTags");
+ artifactElement.appendChild(fileTagsElement);
+
+ for (String fileTag : artifact.getFileTags()) {
+ Element fileTagElement = document.createElement("fileTag");
+ fileTagsElement.appendChild(fileTagElement);
+ fileTagElement
+ .appendChild(document.createTextNode(fileTag));
+ }
+ }
+
+ TransformerFactory transformerFactory = TransformerFactory
+ .newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ DOMSource source = new DOMSource(document);
+ StreamResult result = new StreamResult(outputStream);
+
+ transformer.transform(source, result);
+ new PrintStream(outputStream).println();
+ } catch (ParserConfigurationException pce) {
+ throw new IOException(pce);
+ } catch (TransformerException tfe) {
+ throw new IOException(tfe);
+ }
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/tools/JavaCompilerScannerTool.java b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/JavaCompilerScannerTool.java
new file mode 100644
index 000000000..5bef0c90f
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/JavaCompilerScannerTool.java
@@ -0,0 +1,66 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs.tools;
+
+import io.qt.qbs.tools.utils.JavaCompilerScanner;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class JavaCompilerScannerTool {
+ public static void main(String[] args) {
+ JavaCompilerScanner scanner = new JavaCompilerScanner();
+
+ List<String> compilerArguments = new ArrayList<String>(
+ Arrays.asList(args));
+ if (args.length >= 1 && args[0].equals("--output-format")) {
+ compilerArguments.remove(0);
+ if (args.length < 2) {
+ throw new IllegalArgumentException(
+ "--output-format requires an argument");
+ }
+
+ scanner.setOutputFormat(args[1]);
+ compilerArguments.remove(0);
+ }
+
+ try {
+ int result = scanner.run(compilerArguments);
+ scanner.write(System.out);
+ System.exit(result);
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerOptions.java b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerOptions.java
new file mode 100644
index 000000000..53cf71033
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerOptions.java
@@ -0,0 +1,100 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs.tools.utils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.SourceVersion;
+import javax.tools.JavaCompiler;
+import javax.tools.StandardJavaFileManager;
+
+public class JavaCompilerOptions {
+ private final List<String> recognizedOptions;
+ private final List<String> classNames;
+ private final List<File> files;
+
+ private JavaCompilerOptions(List<String> recognizedOptions,
+ List<String> classNames, List<File> files) {
+ this.recognizedOptions = recognizedOptions;
+ this.classNames = classNames;
+ this.files = files;
+ }
+
+ public static JavaCompilerOptions parse(JavaCompiler compiler,
+ StandardJavaFileManager fileManager, String... arguments) {
+ List<String> recognizedOptions = new ArrayList<String>();
+ List<String> classNames = new ArrayList<String>();
+ List<File> files = new ArrayList<File>();
+
+ for (int i = 0; i < arguments.length; ++i) {
+ int argumentCount = compiler.isSupportedOption(arguments[i]);
+ if (argumentCount < 0)
+ argumentCount = fileManager.isSupportedOption(arguments[i]);
+
+ if (argumentCount >= 0) {
+ for (int j = 0; j < argumentCount + 1; ++j) {
+ if (i + j >= arguments.length) {
+ throw new IllegalArgumentException(arguments[i]);
+ }
+
+ recognizedOptions.add(arguments[i + j]);
+ }
+
+ i += argumentCount;
+ } else {
+ File file = new File(arguments[i]);
+ if (file.exists())
+ files.add(file);
+ else if (SourceVersion.isName(arguments[i]))
+ classNames.add(arguments[i]);
+ else
+ throw new IllegalArgumentException(arguments[i]);
+ }
+ }
+
+ return new JavaCompilerOptions(recognizedOptions, classNames, files);
+ }
+
+ public List<String> getRecognizedOptions() {
+ return Collections.unmodifiableList(recognizedOptions);
+ }
+
+ public List<File> getFiles() {
+ return Collections.unmodifiableList(files);
+ }
+
+ public List<String> getClassNames() {
+ return Collections.unmodifiableList(classNames);
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerScanner.java b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerScanner.java
new file mode 100644
index 000000000..5a80916a7
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/JavaCompilerScanner.java
@@ -0,0 +1,136 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs.tools.utils;
+
+import io.qt.qbs.Artifact;
+import io.qt.qbs.ArtifactListJsonWriter;
+import io.qt.qbs.ArtifactListTextWriter;
+import io.qt.qbs.ArtifactListWriter;
+import io.qt.qbs.ArtifactListXmlWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class JavaCompilerScanner {
+ private static final String humanReadableTextFormat = "human-readable-text";
+ private static final String jsonFormat = "json";
+ private static final String xmlFormat = "xml1";
+
+ private List<Artifact> artifacts = new ArrayList<Artifact>();
+ private String outputFormat = humanReadableTextFormat;
+
+ private static Map<String, ArtifactListWriter> getOutputFormatters() {
+ Map<String, ArtifactListWriter> outputFormatters = new HashMap<String, ArtifactListWriter>();
+ outputFormatters.put(humanReadableTextFormat,
+ new ArtifactListTextWriter());
+ outputFormatters.put(jsonFormat, new ArtifactListJsonWriter());
+ outputFormatters.put(xmlFormat, new ArtifactListXmlWriter());
+ return outputFormatters;
+ }
+
+ public String getOutputFormat() {
+ return this.outputFormat;
+ }
+
+ public void setOutputFormat(String outputFormat) {
+ this.outputFormat = outputFormat;
+ }
+
+ public List<Artifact> getArtifacts() {
+ return this.artifacts;
+ }
+
+ public int run(List<String> compilerArguments) {
+ artifacts.clear();
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager standardFileManager = compiler
+ .getStandardFileManager(null, null, null);
+ JavaFileManager fileManager = new ForwardingJavaFileManager<StandardJavaFileManager>(
+ standardFileManager) {
+ public JavaFileObject getJavaFileForOutput(
+ JavaFileManager.Location location, String className,
+ JavaFileObject.Kind kind, FileObject sibling)
+ throws IOException {
+ JavaFileObject o = super.getJavaFileForOutput(location,
+ className, kind, sibling);
+ Artifact artifact = new Artifact(o.toUri().getPath());
+ if (kind.equals(JavaFileObject.Kind.CLASS)) {
+ artifact.addFileTag("java.class");
+ } else if (kind.equals(JavaFileObject.Kind.SOURCE)) {
+ artifact.addFileTag("java.java");
+ }
+ artifacts.add(artifact);
+ return new NullFileObject(o);
+ }
+
+ public FileObject getFileForOutput(
+ JavaFileManager.Location location, String packageName,
+ String relativeName, FileObject sibling) throws IOException {
+ FileObject o = super.getFileForOutput(location, packageName,
+ relativeName, sibling);
+ Artifact artifact = new Artifact(o.toUri().getPath());
+ if (o.getName().endsWith(".h")) {
+ artifact.addFileTag("hpp");
+ }
+ artifacts.add(artifact);
+ return new NullFileObject(o);
+ }
+ };
+
+ final JavaCompilerOptions options = JavaCompilerOptions
+ .parse(compiler, standardFileManager, compilerArguments
+ .toArray(new String[compilerArguments.size()]));
+ return compiler.getTask(
+ null,
+ fileManager,
+ null,
+ options.getRecognizedOptions(),
+ options.getClassNames(),
+ standardFileManager.getJavaFileObjectsFromFiles(options
+ .getFiles())).call() ? 0 : 1;
+ }
+
+ public void write(OutputStream outputStream) throws IOException {
+ getOutputFormatters().get(outputFormat).write(artifacts, outputStream);
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/NullFileObject.java b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/NullFileObject.java
new file mode 100644
index 000000000..e5a4fc4a3
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/io/qt/qbs/tools/utils/NullFileObject.java
@@ -0,0 +1,166 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 Jake Petroules.
+ ** Contact: http://www.qt.io/licensing
+ **
+ ** This file is part of the Qt Build Suite.
+ **
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms and
+ ** conditions see http://www.qt.io/terms-conditions. For further information
+ ** use the contact form at http://www.qt.io/contact-us.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 or version 3 as published by the Free
+ ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+ ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+ ** following information to ensure the GNU Lesser General Public License
+ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+ ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, The Qt Company gives you certain additional
+ ** rights. These rights are described in The Qt Company LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ****************************************************************************/
+
+package io.qt.qbs.tools.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+
+/**
+ * Represents a FileObject that discards its output when written.
+ */
+public class NullFileObject implements FileObject, JavaFileObject {
+ FileObject obj;
+
+ public NullFileObject(FileObject obj) {
+ this.obj = obj;
+ }
+
+ @Override
+ public URI toUri() {
+ return obj.toUri();
+ }
+
+ @Override
+ public String getName() {
+ return obj.getName();
+ }
+
+ @Override
+ public InputStream openInputStream() throws IOException {
+ return new InputStream() {
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+ };
+ }
+
+ @Override
+ public OutputStream openOutputStream() throws IOException {
+ return new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ }
+ };
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+ return new Reader() {
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ return -1;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ };
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors)
+ throws IOException {
+ return obj.getCharContent(ignoreEncodingErrors);
+ }
+
+ @Override
+ public Writer openWriter() throws IOException {
+ return new Writer() {
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ }
+
+ @Override
+ public void flush() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ };
+ }
+
+ @Override
+ public long getLastModified() {
+ return obj.getLastModified();
+ }
+
+ @Override
+ public boolean delete() {
+ return obj.delete();
+ }
+
+ @Override
+ public Kind getKind() {
+ if (obj instanceof JavaFileObject) {
+ return ((JavaFileObject) obj).getKind();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isNameCompatible(String simpleName, Kind kind) {
+ if (obj instanceof JavaFileObject) {
+ return ((JavaFileObject) obj).isNameCompatible(simpleName, kind);
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ if (obj instanceof JavaFileObject) {
+ return ((JavaFileObject) obj).getNestingKind();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Modifier getAccessLevel() {
+ if (obj instanceof JavaFileObject) {
+ return ((JavaFileObject) obj).getAccessLevel();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/libexec/qbs-javac-scan/qbs-javac-scan.pro b/src/libexec/qbs-javac-scan/qbs-javac-scan.pro
new file mode 100644
index 000000000..0d9921512
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/qbs-javac-scan.pro
@@ -0,0 +1,62 @@
+TARGET = qbs-javac-scan
+
+DESTDIR = ../../../libexec
+
+PATHPREFIX = $$PWD/io/qt/qbs/
+
+JAVACLASSPATH += $$PWD
+JAVASOURCES += \
+ $$PATHPREFIX/Artifact.java \
+ $$PATHPREFIX/ArtifactListJsonWriter.java \
+ $$PATHPREFIX/ArtifactListTextWriter.java \
+ $$PATHPREFIX/ArtifactListWriter.java \
+ $$PATHPREFIX/ArtifactListXmlWriter.java \
+ $$PATHPREFIX/tools/JavaCompilerScannerTool.java \
+ $$PATHPREFIX/tools/utils/JavaCompilerOptions.java \
+ $$PATHPREFIX/tools/utils/JavaCompilerScanner.java \
+ $$PATHPREFIX/tools/utils/NullFileObject.java
+
+JAVAMAINCLASS = io.qt.qbs.tools.JavaCompilerScannerTool
+
+# install
+target.path = $${QBS_INSTALL_PREFIX}/libexec
+INSTALLS += target
+
+# from mkspecs/features/java.prf
+TEMPLATE = lib
+
+CLASS_DIR = .classes
+
+CONFIG -= qt
+
+# Without these, qmake adds a name prefix and versioning postfixes (as well as file
+# links) to the target. This is hardcoded in the qmake code, so for now we use
+# the plugin configs to get what we want.
+CONFIG += plugin no_plugin_name_prefix
+
+javac.input = JAVASOURCES
+javac.output = $$CLASS_DIR
+javac.CONFIG += combine
+javac.commands = javac -source 1.6 -target 1.6 -Xlint:unchecked -cp $$shell_quote($$system_path($$join(JAVACLASSPATH, $$DIRLIST_SEPARATOR))) -d $$shell_quote($$CLASS_DIR) ${QMAKE_FILE_IN}
+# Force rebuild every time, because we don't know the paths of the destination files
+# as they depend on the code.
+javac.depends = FORCE
+QMAKE_EXTRA_COMPILERS += javac
+
+mkpath($$absolute_path($$CLASS_DIR, $$OUT_PWD)) | error("Aborting.")
+
+# Disable all linker flags since we are overriding the regular linker
+QMAKE_LFLAGS =
+QMAKE_CFLAGS =
+QMAKE_LFLAGS_RPATH =
+QMAKE_LFLAGS_PLUGIN =
+QMAKE_LIBS =
+QMAKE_LIBS_OPENGL_ES2 =
+QMAKE_LIBDIR =
+QMAKE_EXTENSION_SHLIB = jar
+
+QMAKE_LINK_SHLIB_CMD = jar cfe $(TARGET) $$JAVAMAINCLASS -C $$CLASS_DIR .
+
+# Force link step to always happen, since we are always updating the
+# .class files
+PRE_TARGETDEPS += FORCE
diff --git a/src/libexec/qbs-javac-scan/qbs-javac-scan.qbs b/src/libexec/qbs-javac-scan/qbs-javac-scan.qbs
new file mode 100644
index 000000000..05fbe3c18
--- /dev/null
+++ b/src/libexec/qbs-javac-scan/qbs-javac-scan.qbs
@@ -0,0 +1,19 @@
+import qbs
+
+JavaJarFile {
+ condition: project.enableJava
+
+ property string entryPoint: "io.qt.qbs.tools.JavaCompilerScannerTool"
+
+ destinationDirectory: "libexec"
+
+ Group {
+ fileTagsFilter: ["java.jar"]
+ qbs.install: true
+ qbs.installDir: "libexec"
+ }
+
+ files: [
+ "io/qt/qbs/**/*.java"
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/java/Car.java b/tests/auto/blackbox/testdata/java/Car.java
index 3dfe73be3..7cded0377 100644
--- a/tests/auto/blackbox/testdata/java/Car.java
+++ b/tests/auto/blackbox/testdata/java/Car.java
@@ -4,4 +4,9 @@ class Car implements Vehicle
{
System.out.println("Driving!");
}
+
+ public class InternalCombustionEngine
+ {
+ public native void run();
+ }
}
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index 7781112e2..cb1336da4 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -1518,8 +1518,6 @@ void TestBlackbox::java()
// Now check whether we correctly predicted the class file output paths.
QCOMPARE(runQbs(QbsRunParameters("clean", QStringList() << "--all-artifacts")), 0);
foreach (const QString &classFile, classFiles1) {
- if (classFile.contains("NoPackage"))
- QEXPECT_FAIL(0, "Fix parser", Continue);
QVERIFY2(!regularFileExists(classFile), qPrintable(classFile));
}
}