From 5927d7ea309ea73c16ab953b298a67e44a50f4d2 Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Fri, 10 Jan 2014 22:20:17 +0100 Subject: [PATCH] Bug 422797 - API for retrieving QMake information from Qt project Adding a new IQMakeInfo getter: * getQMakeQueryMap() * getQtDocPath() * getResourceFiles() * getFormFiles() Fixing incorrect parsing of OTHER_FILES variable. Adding QMakeTests.testQMakeInfo() JUnit test for qmake output parser. Change-Id: Ic4e0180381967e2a96455d6a3411fe51ce1cef91 Signed-off-by: David Kaspar Reviewed-on: https://git.eclipse.org/r/20500 Reviewed-by: Doug Schaefer IP-Clean: Doug Schaefer Tested-by: Doug Schaefer --- .../META-INF/MANIFEST.MF | 3 +- .../cdt/internal/qt/core/index/QMakeInfo.java | 97 +++++++++++-------- .../internal/qt/core/index/QMakeParser.java | 18 ++-- .../qt/core/index/QMakeProjectInfo.java | 5 +- .../eclipse/cdt/qt/core/index/IQMakeInfo.java | 29 ++++++ .../org/eclipse/cdt/qt/tests/QMakeTests.java | 32 +++++- 6 files changed, 131 insertions(+), 53 deletions(-) diff --git a/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF index 38ced9002fe..959a2a7e900 100644 --- a/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF +++ b/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF @@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.expressions;bundle-version="[3.2.0,4.0.0)", org.eclipse.cdt.core, org.eclipse.cdt.codan.core, - org.eclipse.cdt.codan.core.cxx + org.eclipse.cdt.codan.core.cxx, + org.eclipse.core.filesystem Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Bundle-Localization: plugin diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeInfo.java index c4f769e7244..37ba713bc26 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeInfo.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeInfo.java @@ -35,37 +35,47 @@ public final class QMakeInfo implements IQMakeInfo { /** * Instance that is used to present an invalid IQMakeInfo. */ - public static final IQMakeInfo INVALID = new QMakeInfo( - false, null, Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + public static final IQMakeInfo INVALID = new QMakeInfo(false, Collections.emptyMap(), Collections.emptyMap()); private final boolean valid; + private final Map qmakeQueryMap; private final IQtVersion qtVersion; private final List involvedQMakeFiles; private final List qtImportPath; private final List qtQmlPath; + private final List qtDocPath; private final List includePath; private final List defines; private final List sourceFiles; private final List headerFiles; + private final List resourceFiles; + private final List formFiles; private final List otherFiles; - private QMakeInfo(boolean valid, IQtVersion qtVersion, List involvedQMakeFiles, List qtImportPath, List qtQmlPath, List includePath, List defines, List sourceFiles, List headerFiles, List otherFiles) { + public QMakeInfo(boolean valid, Map queryMap, Map proMap) { this.valid = valid; - this.qtVersion = qtVersion; - this.involvedQMakeFiles = Collections.unmodifiableList(involvedQMakeFiles); - this.qtImportPath = Collections.unmodifiableList(qtImportPath); - this.qtQmlPath = Collections.unmodifiableList(qtQmlPath); - this.includePath = Collections.unmodifiableList(includePath); - this.defines = Collections.unmodifiableList(defines); - this.sourceFiles = Collections.unmodifiableList(sourceFiles); - this.headerFiles = Collections.unmodifiableList(headerFiles); - this.otherFiles = Collections.unmodifiableList(otherFiles); - } + this.qmakeQueryMap = Collections.unmodifiableMap(queryMap); - public static IQMakeInfo create(File projectFile, File qmake, String[] extraEnv) { - return create(projectFile.getAbsolutePath(), qmake.getAbsolutePath(), extraEnv); + this.qtVersion = QMakeVersion.create(queryMap.get(QMakeParser.KEY_QT_VERSION)); + List tmpQtImportPaths = new ArrayList(QMakeParser.singleValue(queryMap, QMakeParser.KEY_QT_INSTALL_IMPORTS)); + List tmpQtQmlPaths = new ArrayList(QMakeParser.singleValue(queryMap, QMakeParser.KEY_QT_INSTALL_QML)); + this.qtDocPath = QMakeParser.singleValue(queryMap, QMakeParser.KEY_QT_INSTALL_DOCS); + + this.involvedQMakeFiles = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_QMAKE_INTERNAL_INCLUDED_FILES); + this.includePath = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_INCLUDEPATH); + this.defines = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_DEFINES); + this.sourceFiles = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_SOURCES); + this.headerFiles = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_HEADERS); + this.resourceFiles = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_RESOURCES); + this.formFiles = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_FORMS); + this.otherFiles = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_OTHER_FILES); + + // combine qtImportPath and qtQmlPath from both qmake runs + List qmlImportPath = QMakeParser.qmake3DecodeValueList(proMap, QMakeParser.KEY_QML_IMPORT_PATH); + tmpQtImportPaths.addAll(qmlImportPath); + tmpQtQmlPaths.addAll(qmlImportPath); + this.qtImportPath = Collections.unmodifiableList(tmpQtImportPaths); + this.qtQmlPath = Collections.unmodifiableList(tmpQtQmlPaths); } public static IQMakeInfo create(String proPath, String qmakePath, String[] extraEnv) { @@ -75,35 +85,17 @@ public final class QMakeInfo implements IQMakeInfo { // run "qmake -query" Map qmake1 = exec(PATTERN_QUERY_LINE, extraEnv, qmakePath, "-query"); - if (qmake1 == null) + if (qmake1 == null) { return INVALID; + } - // check the qmake version + // check the qmake version QMakeVersion version = QMakeVersion.create(qmake1.get(QMakeParser.KEY_QMAKE_VERSION)); // TODO - no support for pre-3.0 // for QMake version 3.0 or newer, run "qmake -E file.pro" Map qmake2 = version != null && version.getMajor() >= 3 ? exec(PATTERN_EVAL_LINE, extraEnv, qmakePath, "-E", proPath) : Collections.emptyMap(); - - IQtVersion qtVersion = QMakeVersion.create(qmake1.get(QMakeParser.KEY_QT_VERSION)); - List qtImportPaths = QMakeParser.singleValue(qmake1, QMakeParser.KEY_QT_INSTALL_IMPORTS); - List qtQmlPaths = QMakeParser.singleValue(qmake1, QMakeParser.KEY_QT_INSTALL_QML); - - List involvedQMakeFiles = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_QMAKE_INTERNAL_INCLUDED_FILES); - List includePath = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_INCLUDEPATH); - List defines = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_DEFINES); - List sourceFiles = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_SOURCES); - List headerFiles = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_HEADERS); - List otherFiles = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_HEADERS); - List qmlImportPath = QMakeParser.qmake3DecodeValueList(qmake2, QMakeParser.KEY_QML_IMPORT_PATH); - - // combine qtImportPath and qtQmlPath from both qmake runs - List realQtImportPaths = new ArrayList(qtImportPaths); - realQtImportPaths.addAll(qmlImportPath); - List realQtQmlPaths = new ArrayList(qtQmlPaths); - realQtQmlPaths.addAll(qmlImportPath); - - return new QMakeInfo(true, qtVersion, involvedQMakeFiles, realQtImportPaths, realQtQmlPaths, includePath, defines, sourceFiles, headerFiles, otherFiles); + return new QMakeInfo(true, qmake1, qmake2); } @Override @@ -111,9 +103,14 @@ public final class QMakeInfo implements IQMakeInfo { return valid; } + @Override + public Map getQMakeQueryMap() { + return qmakeQueryMap; + } + @Override public IQtVersion getQtVersion() { - return qtVersion; + return qtVersion; } @Override @@ -131,6 +128,11 @@ public final class QMakeInfo implements IQMakeInfo { return qtQmlPath; } + @Override + public List getQtDocPath() { + return qtDocPath; + } + @Override public List getIncludePath() { return includePath; @@ -152,9 +154,19 @@ public final class QMakeInfo implements IQMakeInfo { } @Override - public List getOtherFiles() { + public List getResourceFiles() { + return resourceFiles; + } + + @Override + public List getFormFiles() { + return formFiles; + } + + @Override + public List getOtherFiles() { return otherFiles; - } + } /** * Executes a command and parses its output into a map. @@ -164,7 +176,6 @@ public final class QMakeInfo implements IQMakeInfo { * @param cmd the command line * @return the map of resolved key-value pairs */ - @SuppressWarnings("resource") private static Map exec(Pattern regex, String[] extraEnv, String...command) { if (command.length < 1 || ! new File(command[0]).exists()) { QtPlugin.log("qmake: cannot run command: " + (command.length > 0 ? command[0] : "")); diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeParser.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeParser.java index c412ec976c6..601003f78b5 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeParser.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeParser.java @@ -28,11 +28,15 @@ public final class QMakeParser { public static final String KEY_QT_VERSION = "QT_VERSION"; public static final String KEY_QT_INSTALL_IMPORTS = "QT_INSTALL_IMPORTS"; public static final String KEY_QT_INSTALL_QML = "QT_INSTALL_QML"; + public static final String KEY_QT_INSTALL_DOCS = "QT_INSTALL_DOCS"; public static final String KEY_QMAKE_INTERNAL_INCLUDED_FILES = "QMAKE_INTERNAL_INCLUDED_FILES"; public static final String KEY_SOURCES = "SOURCES"; public static final String KEY_HEADERS = "HEADERS"; public static final String KEY_INCLUDEPATH = "INCLUDEPATH"; public static final String KEY_DEFINES = "DEFINES"; + public static final String KEY_RESOURCES = "RESOURCES"; + public static final String KEY_FORMS = "FORMS"; + public static final String KEY_OTHER_FILES = "OTHER_FILES"; public static final String KEY_QML_IMPORT_PATH = "QML_IMPORT_PATH"; /** @@ -40,7 +44,7 @@ public final class QMakeParser { * * @param regex the reg. exp. * @param reader the QMake output - * @return the map of parsed key-value pairs + * @return the modifiable map of parsed key-value pairs * @throws IOException when io error happens */ public static Map parse(Pattern regex, BufferedReader reader) throws IOException { @@ -64,11 +68,11 @@ public final class QMakeParser { } /** - * Returns a list with 0-1 values for a specific QMake variable. + * Returns an unmodifiable list with 0-1 values for a specific QMake variable. * * @param map the map * @param key the QMake variable - * @return the list of values + * @return the unmodifiable list of values */ public static List singleValue(Map map, String key) { String value = map.get(key); @@ -76,11 +80,11 @@ public final class QMakeParser { } /** - * Returns a list of values for a specific QMake variable that is decoded as a list of values. + * Returns an unmodifiable list of values for a specific QMake variable that is decoded as a list of values. * * @param map the map * @param key the QMake variable - * @return the list of values + * @return the unmodifiable list of values */ public static List qmake3DecodeValueList(Map map, String key) { String value = map.get(key); @@ -92,7 +96,7 @@ public final class QMakeParser { for (String item : qmake3SplitValueList(value)) { result.add(qmake3DecodeValue(item)); } - return result; + return Collections.unmodifiableList(result); } /** @@ -151,7 +155,7 @@ public final class QMakeParser { * Splits a specified QMake variable value into a list of values. * * @param value the value - * @return the list of values + * @return the modifiable list of values */ private static List qmake3SplitValueList(String value) { List result = new ArrayList(); diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java index 37b354c5639..753d16fd973 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java @@ -31,6 +31,7 @@ import org.eclipse.cdt.qt.core.index.IQMakeProjectInfo; import org.eclipse.cdt.qt.core.index.IQMakeProjectInfoListener; import org.eclipse.cdt.qt.core.index.QMakeEnvInfo; import org.eclipse.cdt.qt.core.index.IQMakeInfo; +import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -410,10 +411,12 @@ public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescr private static final class SensitiveSet extends HashSet { + private static final long serialVersionUID = 2684086006933209512L; + // adds a sensitive file in form of a specified absolute path private void addSensitiveFile(String sensitiveFile) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - IFile[] files = root.findFilesForLocation(Path.fromOSString(sensitiveFile)); + IFile[] files = root.findFilesForLocationURI(URIUtil.toURI(Path.fromOSString(sensitiveFile).makeAbsolute())); if (files != null && files.length > 0) { IFile file = files[0]; addSensitiveFile(file); diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeInfo.java index 2b8cd5d3c9e..6534f907673 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeInfo.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeInfo.java @@ -8,6 +8,7 @@ package org.eclipse.cdt.qt.core.index; import java.util.List; +import java.util.Map; /** * Represents a QMake information. @@ -25,6 +26,13 @@ public interface IQMakeInfo { */ boolean isValid(); + /** + * Returns a map of key-value pairs provided by "qmake -query" command. + * + * @return the map + */ + Map getQMakeQueryMap(); + /** * Returns a Qt version as provided by "qmake -query" command. * @@ -54,6 +62,13 @@ public interface IQMakeInfo { */ List getQtQmlPath(); + /** + * Returns a list of Qt Documentation paths. + * + * @return the list of Qt Documentation paths + */ + List getQtDocPath(); + /** * Returns a list of include paths that are used for compilation of a related project. * @@ -82,6 +97,20 @@ public interface IQMakeInfo { */ List getHeaderFiles(); + /** + * Returns a list of resource file paths i.e. specified via RESOURCES QMake variable. + * + * @return the list of other file paths + */ + List getResourceFiles(); + + /** + * Returns a list of other file paths i.e. specified via FORMS QMake variable. + * + * @return the list of other file paths + */ + List getFormFiles(); + /** * Returns a list of other file paths i.e. specified via OTHER_FILES QMake variable. * diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QMakeTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QMakeTests.java index 13aaf72aaef..0a4ee50ac1b 100644 --- a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QMakeTests.java +++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QMakeTests.java @@ -9,6 +9,7 @@ package org.eclipse.cdt.qt.tests; import java.io.BufferedReader; import java.io.StringReader; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -17,6 +18,7 @@ import junit.framework.TestCase; import org.eclipse.cdt.internal.qt.core.index.QMakeInfo; import org.eclipse.cdt.internal.qt.core.index.QMakeParser; import org.eclipse.cdt.internal.qt.core.index.QMakeVersion; +import org.eclipse.cdt.qt.core.index.IQMakeInfo; public class QMakeTests extends TestCase { @@ -40,7 +42,7 @@ public class QMakeTests extends TestCase { assertEquals(0, three_dot_zero.getMinor()); } - public void testQMakeInfo() throws Exception { + public void testQMake3Decoder() throws Exception { StringReader content = new StringReader("A = \\\\\\\"\nB = A\\n\\tB\nC = \"A \\\" B\" \"A \\\" B\""); BufferedReader reader = new BufferedReader(content); @@ -64,4 +66,32 @@ public class QMakeTests extends TestCase { assertEquals(0, D.size()); } + public void testQMakeInfo() throws Exception { + StringReader qmake1Content = new StringReader("QMAKE_VERSION:3.0\nQT_VERSION:5.2\nQT_INSTALL_IMPORTS:QtImports\nQT_INSTALL_QML:QtQmls\nQT_INSTALL_DOCS:QtDocs\nCustomKey:CustomValue\n"); + BufferedReader qmake1Reader = new BufferedReader(qmake1Content); + Map qmake1 = QMakeParser.parse(QMakeInfo.PATTERN_QUERY_LINE, qmake1Reader); + + StringReader qmake2Content = new StringReader("QMAKE_INTERNAL_INCLUDED_FILES=Internal1 Internal2\nSOURCES=Source1 Source2\nHEADERS=Header1 Header2\nINCLUDEPATH=Include1 Include2\nDEFINES=Def1 Def2\nRESOURCES=Resource1 Resource2\nFORMS=Form1 Form2\nOTHER_FILES=Other1 Other2\nQML_IMPORT_PATH=CustomImport\n"); + BufferedReader qmake2Reader = new BufferedReader(qmake2Content); + Map qmake2 = QMakeParser.parse(QMakeInfo.PATTERN_EVAL_LINE, qmake2Reader); + + IQMakeInfo info = new QMakeInfo(true, qmake1, qmake2); + + assertNotNull(info); + assertEquals(5, info.getQtVersion().getMajor()); + assertEquals(2, info.getQtVersion().getMinor()); + assertEquals(Arrays.asList("QtImports", "CustomImport"), info.getQtImportPath()); + assertEquals(Arrays.asList("QtQmls", "CustomImport"), info.getQtQmlPath()); + assertEquals(Arrays.asList("QtDocs"), info.getQtDocPath()); + assertEquals("CustomValue", info.getQMakeQueryMap().get("CustomKey")); + + assertEquals(Arrays.asList("Include1", "Include2"), info.getIncludePath()); + assertEquals(Arrays.asList("Def1", "Def2"), info.getDefines()); + assertEquals(Arrays.asList("Header1", "Header2"), info.getHeaderFiles()); + assertEquals(Arrays.asList("Source1", "Source2"), info.getSourceFiles()); + assertEquals(Arrays.asList("Resource1", "Resource2"), info.getResourceFiles()); + assertEquals(Arrays.asList("Form1", "Form2"), info.getFormFiles()); + assertEquals(Arrays.asList("Other1", "Other2"), info.getOtherFiles()); + } + }