/* * Copyright (c) 2013, 2015 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.cdt.internal.qt.core.index; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.eclipse.cdt.internal.qt.core.Activator; import org.eclipse.cdt.utils.spawner.ProcessFactory; /** * Holder for QMake information. */ public final class QMakeInfo implements IQMakeInfo { // reg. exp. for parsing output of "qmake -query" command public static final Pattern PATTERN_QUERY_LINE = Pattern.compile("^(\\w+):(.*)$"); // reg. exp. for parsing output of "qmake -E file.pro" command (for QMake 3.0 only) public static final Pattern PATTERN_EVAL_LINE = Pattern.compile("^([a-zA-Z0-9_\\.]+)\\s*=\\s*(.*)$"); /** * Instance that is used to present an invalid IQMakeInfo. */ public static final IQMakeInfo INVALID = new QMakeInfo(false, Collections.<String,String>emptyMap(), Collections.<String,String>emptyMap()); private final boolean valid; private final Map<String, String> qmakeQueryMap; private final IQtVersion qtVersion; private final List<String> involvedQMakeFiles; private final List<String> qtImportPath; private final List<String> qtQmlPath; private final List<String> qtDocPath; private final List<String> includePath; private final List<String> defines; private final List<String> sourceFiles; private final List<String> headerFiles; private final List<String> resourceFiles; private final List<String> formFiles; private final List<String> otherFiles; public QMakeInfo(boolean valid, Map<String,String> queryMap, Map<String,String> proMap) { this.valid = valid; this.qmakeQueryMap = Collections.unmodifiableMap(queryMap); this.qtVersion = QMakeVersion.create(queryMap.get(QMakeParser.KEY_QT_VERSION)); List<String> tmpQtImportPaths = new ArrayList<String>(QMakeParser.singleValue(queryMap, QMakeParser.KEY_QT_INSTALL_IMPORTS)); List<String> tmpQtQmlPaths = new ArrayList<String>(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<String> 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) { if (proPath == null || qmakePath == null) { return INVALID; } // run "qmake -query" Map<String, String> qmake1 = exec(PATTERN_QUERY_LINE, extraEnv, qmakePath, "-query"); if (qmake1 == null) { return INVALID; } // 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<String, String> qmake2 = version != null && version.getMajor() >= 3 ? exec(PATTERN_EVAL_LINE, extraEnv, qmakePath, "-E", proPath) : Collections.<String,String>emptyMap(); return new QMakeInfo(true, qmake1, qmake2); } @Override public boolean isValid() { return valid; } @Override public Map<String, String> getQMakeQueryMap() { return qmakeQueryMap; } @Override public IQtVersion getQtVersion() { return qtVersion; } @Override public List<String> getInvolvedQMakeFiles() { return involvedQMakeFiles; } @Override public List<String> getQtImportPath() { return qtImportPath; } @Override public List<String> getQtQmlPath() { return qtQmlPath; } @Override public List<String> getQtDocPath() { return qtDocPath; } @Override public List<String> getIncludePath() { return includePath; } @Override public List<String> getDefines() { return defines; } @Override public List<String> getSourceFiles() { return sourceFiles; } @Override public List<String> getHeaderFiles() { return headerFiles; } @Override public List<String> getResourceFiles() { return resourceFiles; } @Override public List<String> getFormFiles() { return formFiles; } @Override public List<String> getOtherFiles() { return otherFiles; } /** * Executes a command and parses its output into a map. * * @param regex the reg. exp. used for parsing the output * @param extraEnv the extra environment for command * @param cmd the command line * @return the map of resolved key-value pairs */ private static Map<String, String> exec(Pattern regex, String[] extraEnv, String...command) { if (command.length < 1 || ! new File(command[0]).exists()) { Activator.log("qmake: cannot run command: " + (command.length > 0 ? command[0] : "")); return null; } BufferedReader reader = null; Process process = null; try { if (extraEnv != null && extraEnv.length > 0) { process = ProcessFactory.getFactory().exec(command, extraEnv); } else { process = ProcessFactory.getFactory().exec(command); } reader = new BufferedReader(new InputStreamReader(process.getInputStream())); return QMakeParser.parse(regex, reader); } catch(IOException e) { Activator.log(e); return null; } finally { if (reader != null) try { reader.close(); } catch(IOException e) { /* ignore */ } if (process != null) { process.destroy(); } } } }