Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtIncludePaths.java')
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtIncludePaths.java282
1 files changed, 282 insertions, 0 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtIncludePaths.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtIncludePaths.java
new file mode 100644
index 00000000000..20a75126d36
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtIncludePaths.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2013 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.qt.internal.core;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializableProvider;
+import org.eclipse.cdt.core.settings.model.CIncludePathEntry;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
+import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
+import org.eclipse.cdt.core.settings.model.ICSettingEntry;
+import org.eclipse.cdt.qt.core.QtPlugin;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
+import org.eclipse.core.resources.IResource;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Discovers and persists the list of Qt include paths for a particular installation of
+ * Qt. The Qt installation is described by the path to qmake.
+ * <p>
+ * Qt uses a tool called qmake to generate makefiles for Qt projects. The tool has a
+ * query mode that can be used to discover information about the Qt installation. Here
+ * qmake is used to build a list of all installed Qt include paths.
+ * <p>
+ * These paths are persisted into a file called language-settings.xml in the workspace
+ * metadata area.
+ *
+ * @see QtIncludePathsProvider
+ */
+public class QtIncludePaths extends LanguageSettingsSerializableProvider {
+
+ /**
+ * The path to the qmake executable uniquely identifies this installation.
+ */
+ private final String qmakePath;
+
+ /**
+ * The cached data is reloaded when the qmake executable is modified.
+ */
+ private long qmakeModTime;
+
+ /**
+ * The cached data is reloaded when the folder holding the include paths
+ * is removed.
+ */
+ private String qtInstallHeadersPath;
+
+ /**
+ * The cached data is reloaded when the folder containing the include folders is
+ * modified.
+ */
+ private long qtInstallHeadersModTime;
+
+ private static final String ATTR_QMAKE = "qmake";
+ private static final String ATTR_QMAKE_MOD = "qmakeModification";
+ private static final String ATTR_QT_INSTALL_HEADERS = "QT_INSTALL_HEADERS";
+ private static final String ATTR_QT_INSTALL_HEADERS_MOD = "qtInstallHeadersModification";
+
+ /**
+ * Create a new instance of the include path wrapper for the Qt installation for
+ * the given qmake binary.
+ */
+ public QtIncludePaths(String qmakePath) {
+ this.qmakePath = qmakePath;
+ }
+
+ /**
+ * Create and load an instance of QtIncludePaths from data that was serialized into the
+ * given XML element. Return null if an instance cannot be loaded or if the installation
+ * is no longer valid.
+ */
+ public static QtIncludePaths loadFrom(Node node) {
+ if (node.getNodeType() != Node.ELEMENT_NODE)
+ return null;
+
+ Element element = (Element) node;
+ String qmakePath = element.getAttribute(ATTR_QMAKE);
+ if (qmakePath == null
+ || qmakePath.isEmpty())
+ return null;
+
+ QtIncludePaths qtIncludePaths = new QtIncludePaths(qmakePath);
+ qtIncludePaths.load(element);
+ return qtIncludePaths;
+ }
+
+ public String getQMakePath() {
+ return qmakePath;
+ }
+
+ /**
+ * Return true if the receiver points to a valid Qt installation and false otherwise.
+ * The installation is considered valid if an executable qmake binary exists at the
+ * expected location.
+ */
+ public boolean isValid() {
+ if (qmakePath == null
+ || qmakePath.isEmpty())
+ return false;
+
+ File qmake = new File(qmakePath);
+ return qmake.exists()
+ && qmake.canExecute();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof QtIncludePaths))
+ return super.equals(obj);
+
+ // Include paths are equivalent when they point to the same qmake binary. All other
+ // values are reloaded from that binary and do not need to be directly compared.
+ QtIncludePaths other = (QtIncludePaths) obj;
+ return qmakePath == null ? other.qmakePath == null : qmakePath.equals(other.qmakePath);
+ }
+
+ @Override
+ public int hashCode() {
+ return qmakePath == null ? 0 : qmakePath.hashCode();
+ }
+
+ /**
+ * Return a current list of the include paths for this Qt installation. Return null if
+ * no such paths can be found.
+ * <p>
+ * Updates the cached results if needed. If the settings are updated then the new list
+ * will be serialized into the workspace metadata area.
+ */
+ @Override
+ public List<ICLanguageSettingEntry> getSettingEntries(ICConfigurationDescription configDesc, IResource rc, String languageId) {
+ List<ICLanguageSettingEntry> entries = null;
+
+ File qmake = new File(qmakePath);
+ if (!qmake.exists()
+ || qmakeModTime != qmake.lastModified())
+ entries = reload();
+ else {
+ File qtInstallHeadersDir = new File(qtInstallHeadersPath);
+ if (!qtInstallHeadersDir.exists()
+ || qtInstallHeadersModTime != qtInstallHeadersDir.lastModified())
+ entries = reload();
+ }
+
+ // If the cache was not reloaded, then return the previously discovered entries.
+ if (entries == null)
+ return super.getSettingEntries(configDesc, rc, languageId);
+
+ // Otherwise store, persist, and return the newly discovered values.
+ setSettingEntries(configDesc, rc, languageId, entries);
+ serializeLanguageSettingsInBackground(null);
+ return entries;
+ }
+
+ @Override
+ public Element serializeAttributes(Element parentElement) {
+ parentElement.setAttribute(ATTR_QMAKE, qmakePath);
+ parentElement.setAttribute(ATTR_QMAKE_MOD, Long.toString(qmakeModTime));
+ parentElement.setAttribute(ATTR_QT_INSTALL_HEADERS, qtInstallHeadersPath);
+ parentElement.setAttribute(ATTR_QT_INSTALL_HEADERS_MOD, Long.toString(qtInstallHeadersModTime));
+
+ // The parent implementation tries to create a new child node (provider) that is used
+ // as the part for later entries. This isn't needed in this case, we just want to
+ // use the part that serializes the languages.
+ return parentElement;
+ }
+
+ @Override
+ public void loadAttributes(Element element) {
+ qmakeModTime = getLongAttribute(element, ATTR_QMAKE_MOD);
+ qtInstallHeadersPath = element.getAttribute(ATTR_QT_INSTALL_HEADERS);
+ qtInstallHeadersModTime = getLongAttribute(element, ATTR_QT_INSTALL_HEADERS_MOD);
+
+ // The parent implementation tries to create a new child node (provider) that is used
+ // as the part for later entries. This isn't needed in this case, we just want to
+ // use the part that serializes the languages.
+ }
+
+ /**
+ * Parse and return the given attribute as a long. Return 0 if the attribute does
+ * not have a valid value.
+ */
+ private static long getLongAttribute(Element element, String attr) {
+ String value = element.getAttribute(attr);
+ if (value == null
+ || value.isEmpty())
+ return 0;
+
+ try {
+ return Long.parseLong(value);
+ } catch(NumberFormatException e) {
+ QtPlugin.log("attribute name:" + attr + " value:" + value, e);
+ return 0;
+ }
+ }
+
+ /**
+ * Reload and return the entries if possible, return null otherwise.
+ */
+ private List<ICLanguageSettingEntry> reload() {
+ // All keys are reset and then updated as their values are discovered. This allows partial
+ // success to skip over previously calculated values.
+ qmakeModTime = 0;
+ qtInstallHeadersPath = null;
+ qtInstallHeadersModTime = 0;
+
+ File qmake = new File(qmakePath);
+ if (!qmake.exists()
+ || !qmake.canExecute())
+ return Collections.emptyList();
+
+ qmakeModTime = qmake.lastModified();
+
+ // Run `qmake -query QT_INSTALL_HEADERS` to get output like "/opt/qt-5.0.0/include".
+ BufferedReader reader = null;
+ Process process = null;
+ try {
+ process = ProcessFactory.getFactory().exec(new String[]{ qmakePath, "-query", "QT_INSTALL_HEADERS" });
+ reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ qtInstallHeadersPath = reader.readLine();
+ } catch(IOException e) {
+ QtPlugin.log(e);
+ } finally {
+ try {
+ if (reader != null)
+ reader.close();
+ } catch(IOException e) {
+ /* ignore */
+ } finally {
+ if (process != null)
+ process.destroy();
+ }
+ }
+
+ if (qtInstallHeadersPath == null)
+ return Collections.emptyList();
+
+ File qtInstallHeadersDir = new File(qtInstallHeadersPath);
+
+ qtInstallHeadersModTime = qtInstallHeadersDir.lastModified();
+ if (!qtInstallHeadersDir.exists()
+ || !qtInstallHeadersDir.canRead()
+ || !qtInstallHeadersDir.isDirectory())
+ return Collections.emptyList();
+
+ // Create an include path entry for all sub-folders in the QT_INSTALL_HEADERS location, including
+ // the QT_INSTALL_HEADERS folder itself.
+ File[] files = qtInstallHeadersDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.exists() && pathname.isDirectory();
+ }
+ });
+
+ List<ICLanguageSettingEntry> entries = new ArrayList<ICLanguageSettingEntry>(files.length + 1);
+ safeAdd(entries, qtInstallHeadersDir);
+ for(File file : files)
+ safeAdd(entries, file);
+
+ return entries;
+ }
+
+ private static void safeAdd(List<ICLanguageSettingEntry> entries, File file) {
+ try {
+ entries.add(new CIncludePathEntry(file.getCanonicalPath(), ICSettingEntry.READONLY | ICSettingEntry.RESOLVED));
+ } catch(IOException e) {
+ QtPlugin.log(e);
+ }
+ }
+}

Back to the top