Improve path search in configure.
authorFriedemann Kleint <Friedemann.Kleint@digia.com>
Thu, 1 Nov 2012 13:31:16 +0000 (14:31 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 5 Nov 2012 16:17:01 +0000 (17:17 +0100)
- Remove duplicated code locateFile/locateFileInPaths.
- Move basic path search functionality to Environment.
- Add functions for headerPaths/libraryPaths to  Environment.
- Use QStandardPaths::findExecutable().
- Replace Environment::detectExecutable by
  QStandardPaths::findExecutable().
- Introduce static path lists in findFile() to avoid
  repeated directory scans

Change-Id: I9b93066a3de65f40527780d6ddf7989bca35cd04
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
tools/configure/Makefile.mingw
tools/configure/Makefile.win32
tools/configure/configure.pro
tools/configure/configureapp.cpp
tools/configure/configureapp.h
tools/configure/environment.cpp
tools/configure/environment.h

index d6fe5b1..7212b0d 100644 (file)
@@ -52,6 +52,8 @@ OBJECTS = \
     qtextstream.o \
     qlogging.o \
     qtemporaryfile.o \
+    qstandardpaths.o \
+    qstandardpaths_win.o \
     qsystemlibrary.o \
     qbitarray.o \
     qdatetime.o \
index 5a917b1..5b0a21a 100644 (file)
@@ -50,6 +50,8 @@ OBJECTS = \
     qtextstream.obj \
     qlogging.obj \
     qtemporaryfile.obj \
+    qstandardpaths.obj \
+    qstandardpaths_win.obj \
     qsystemlibrary.obj \
     qbitarray.obj \
     qdatetime.obj \
@@ -119,6 +121,8 @@ qfsfileengine_iterator.obj: $(CORESRC)\io\qfsfileengine_iterator.cpp $(PCH)
 qiodevice.obj: $(CORESRC)\io\qiodevice.cpp $(PCH)
 qtextstream.obj: $(CORESRC)\io\qtextstream.cpp $(PCH)
 qtemporaryfile.obj: $(CORESRC)\io\qtemporaryfile.cpp $(PCH)
+qstandardpaths.obj: $(CORESRC)\io\qstandardpaths.cpp $(PCH)
+qstandardpaths_win.obj: $(CORESRC)\io\qstandardpaths_win.cpp $(PCH)
 qsystemlibrary.obj: $(CORESRC)\plugin\qsystemlibrary.cpp $(PCH)
 qbitarray.obj: $(CORESRC)\tools\qbitarray.cpp $(PCH)
 qdatetime.obj: $(CORESRC)\tools\qdatetime.cpp $(PCH)
index c970880..2163e37 100644 (file)
@@ -63,6 +63,7 @@ HEADERS  = configureapp.h environment.h tools.h\
            $$QT_SOURCE_TREE/src/corelib/io/qiodevice.h \
            $$QT_SOURCE_TREE/src/corelib/io/qtextstream.h \
            $$QT_SOURCE_TREE/src/corelib/io/qtemporaryfile.h \
+           $$QT_SOURCE_TREE/src/corelib/io/qstandardpaths.h \
            $$QT_SOURCE_TREE/src/corelib/tools/qbitarray.h \
            $$QT_SOURCE_TREE/src/corelib/tools/qdatetime.h \
            $$QT_SOURCE_TREE/src/corelib/tools/qmap.h \
@@ -110,6 +111,8 @@ SOURCES  = main.cpp configureapp.cpp environment.cpp tools.cpp \
            $$QT_SOURCE_TREE/src/corelib/io/qiodevice.cpp \
            $$QT_SOURCE_TREE/src/corelib/io/qtextstream.cpp \
            $$QT_SOURCE_TREE/src/corelib/io/qtemporaryfile.cpp \
+           $$QT_SOURCE_TREE/src/corelib/io/qstandardpaths.cpp \
+           $$QT_SOURCE_TREE/src/corelib/io/qstandardpaths_win.cpp \
            $$QT_SOURCE_TREE/src/corelib/plugin/qsystemlibrary.cpp \
            $$QT_SOURCE_TREE/src/corelib/tools/qbitarray.cpp \
            $$QT_SOURCE_TREE/src/corelib/tools/qdatetime.cpp \
index cb7a3f4..3a352f4 100644 (file)
@@ -49,6 +49,7 @@
 #include <qdir.h>
 #include <qdiriterator.h>
 #include <qtemporaryfile.h>
+#include <qstandardpaths.h>
 #include <qstack.h>
 #include <qdebug.h>
 #include <qfileinfo.h>
@@ -142,7 +143,7 @@ Configure::Configure(int& argc, char** argv)
     const QString installPath = buildPath;
 #endif
     if (sourceDir != buildDir) { //shadow builds!
-        if (!findFile("perl") && !findFile("perl.exe")) {
+        if (QStandardPaths::findExecutable(QStringLiteral("perl.exe")).isEmpty()) {
             cout << "Error: Creating a shadow build of Qt requires" << endl
                  << "perl to be in the PATH environment";
             exit(0); // Exit cleanly for Ctrl+C
@@ -1521,42 +1522,6 @@ void Configure::applySpecSpecifics()
     }
 }
 
-QString Configure::locateFileInPaths(const QString &fileName, const QStringList &paths)
-{
-    QDir d;
-    for (QStringList::ConstIterator it = paths.begin(); it != paths.end(); ++it) {
-        // Remove any leading or trailing ", this is commonly used in the environment
-        // variables
-        QString path = (*it);
-        if (path.startsWith("\""))
-            path = path.right(path.length() - 1);
-        if (path.endsWith("\""))
-            path = path.left(path.length() - 1);
-        if (d.exists(path + QDir::separator() + fileName)) {
-            return (path);
-        }
-    }
-    return QString();
-}
-
-QString Configure::locateFile(const QString &fileName)
-{
-    QString file = fileName.toLower();
-    QStringList paths;
-#if defined(Q_OS_WIN32)
-    QRegExp splitReg("[;,]");
-#else
-    QRegExp splitReg("[:]");
-#endif
-    if (file.endsWith(".h"))
-        paths = QString::fromLocal8Bit(getenv("INCLUDE")).split(splitReg, QString::SkipEmptyParts);
-    else if (file.endsWith(".lib"))
-        paths = QString::fromLocal8Bit(getenv("LIB")).split(splitReg, QString::SkipEmptyParts);
-    else
-        paths = QString::fromLocal8Bit(getenv("PATH")).split(splitReg, QString::SkipEmptyParts);
-    return locateFileInPaths(file, paths);
-}
-
 // Output helper functions ---------------------------------[ Stop ]-
 
 
@@ -1830,80 +1795,25 @@ bool Configure::displayHelp()
     return false;
 }
 
-QString Configure::findFileInPaths(const QString &fileName, const QString &paths)
-{
-#if defined(Q_OS_WIN32)
-    QRegExp splitReg("[;,]");
-#else
-    QRegExp splitReg("[:]");
-#endif
-    QStringList pathList = paths.split(splitReg, QString::SkipEmptyParts);
-    QDir d;
-    for (QStringList::ConstIterator it = pathList.begin(); it != pathList.end(); ++it) {
-        // Remove any leading or trailing ", this is commonly used in the environment
-        // variables
-        QString path = (*it);
-        if (path.startsWith('\"'))
-            path = path.right(path.length() - 1);
-        if (path.endsWith('\"'))
-            path = path.left(path.length() - 1);
-        if (d.exists(path + QDir::separator() + fileName))
-            return path;
-    }
-    return QString();
-}
-
-static QString mingwPaths(const QString &mingwPath, const QString &pathName)
-{
-    QString ret;
-    QDir mingwDir = QFileInfo(mingwPath).dir();
-    const QFileInfoList subdirs = mingwDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
-    for (int i = 0 ;i < subdirs.length(); ++i) {
-        const QFileInfo &fi = subdirs.at(i);
-        const QString name = fi.fileName();
-        if (name == pathName)
-            ret += fi.absoluteFilePath() + ';';
-        else if (name.contains("mingw"))
-            ret += fi.absoluteFilePath() + QDir::separator() + pathName + ';';
-    }
-    return ret;
-}
-
-bool Configure::findFile(const QString &fileName)
+// Locate a file and return its containing directory.
+QString Configure::locateFile(const QString &fileName) const
 {
     const QString file = fileName.toLower();
-    const QString pathEnvVar = QString::fromLocal8Bit(getenv("PATH"));
-    const QString mingwPath = dictionary["QMAKESPEC"].endsWith("-g++") ?
-        findFileInPaths("g++.exe", pathEnvVar) : QString();
-
-    QString paths;
+    QStringList pathList;
     if (file.endsWith(".h")) {
-        if (!mingwPath.isNull()) {
-            if (!findFileInPaths(file, mingwPaths(mingwPath, "include")).isNull())
-                return true;
-            //now let's try the additional compiler path
-
-            const QFileInfoList mingwConfigs = QDir(mingwPath + QLatin1String("/../lib/gcc")).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
-            for (int i = 0; i < mingwConfigs.length(); ++i) {
-                const QDir mingwLibDir = mingwConfigs.at(i).absoluteFilePath();
-                foreach(const QFileInfo &version, mingwLibDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
-                    if (!findFileInPaths(file, version.absoluteFilePath() + QLatin1String("/include")).isNull())
-                        return true;
-                }
-            }
-        }
-        paths = QString::fromLocal8Bit(getenv("INCLUDE"));
-        const QByteArray directXSdk = qgetenv("DXSDK_DIR");
-        if (!directXSdk.isEmpty()) // Add Direct X SDK for ANGLE
-            paths += QLatin1Char(';') + QString::fromLocal8Bit(directXSdk) + QLatin1String("/include");
+        static const QStringList headerPaths =
+            Environment::headerPaths(Environment::compilerFromQMakeSpec(dictionary[QStringLiteral("QMAKESPEC")]));
+        pathList = headerPaths;
     } else if (file.endsWith(".lib") ||  file.endsWith(".a")) {
-        if (!mingwPath.isNull() && !findFileInPaths(file, mingwPaths(mingwPath, "lib")).isNull())
-            return true;
-        paths = QString::fromLocal8Bit(getenv("LIB"));
+        static const QStringList libPaths =
+            Environment::libraryPaths(Environment::compilerFromQMakeSpec(dictionary[QStringLiteral("QMAKESPEC")]));
+        pathList = libPaths;
     } else {
-        paths = pathEnvVar;
+         // Fallback for .exe and .dll (latter are not covered by QStandardPaths).
+        static const QStringList exePaths = Environment::path();
+        pathList = exePaths;
     }
-    return !findFileInPaths(file, paths).isNull();
+    return Environment::findFileInPaths(file, pathList);
 }
 
 /*!
@@ -2058,7 +1968,8 @@ bool Configure::checkAvailability(const QString &part)
             dictionary[ "DONE" ] = "error";
         }
     } else if (part == "INCREDIBUILD_XGE") {
-        available = findFile("BuildConsole.exe") && findFile("xgConsole.exe");
+        available = !QStandardPaths::findExecutable(QStringLiteral("BuildConsole.exe")).isEmpty()
+                    && !QStandardPaths::findExecutable(QStringLiteral("xgConsole.exe")).isEmpty();
     } else if (part == "WMSDK") {
         available = findFile("wmsdk.h");
     } else if (part == "V8SNAPSHOT") {
@@ -2673,7 +2584,7 @@ void Configure::generateOutputVars()
 
     if (dictionary["QMAKESPEC"].endsWith("-g++")) {
         QString includepath = qgetenv("INCLUDE");
-        bool hasSh = Environment::detectExecutable("sh.exe");
+        const bool hasSh = !QStandardPaths::findExecutable(QStringLiteral("sh.exe")).isEmpty();
         QChar separator = (!includepath.contains(":\\") && hasSh ? QChar(':') : QChar(';'));
         qmakeVars += QString("TMPPATH            = $$quote($$(INCLUDE))");
         qmakeVars += QString("QMAKE_INCDIR_POST += $$split(TMPPATH,\"%1\")").arg(separator);
@@ -3461,7 +3372,7 @@ void Configure::generateHeaders()
         dictionary["SYNCQT"] = defaultTo("SYNCQT");
 
     if (dictionary["SYNCQT"] == "yes") {
-        if (findFile("perl.exe")) {
+        if (!QStandardPaths::findExecutable(QStringLiteral("perl.exe")).isEmpty()) {
             cout << "Running syncqt..." << endl;
             QStringList args;
             args += buildPath + "/bin/syncqt.bat";
index bfec520..24f4858 100644 (file)
@@ -167,8 +167,9 @@ private:
     QString formatPaths(const QStringList &paths);
     bool filesDiffer(const QString &file1, const QString &file2);
 
-    bool findFile(const QString &fileName);
-    static QString findFileInPaths(const QString &fileName, const QString &paths);
+    QString locateFile(const QString &fileName) const;
+    bool findFile(const QString &fileName) const { return !locateFile(fileName).isEmpty(); }
+    static QString findFileInPaths(const QString &fileName, const QStringList &paths);
 #if !defined(EVAL)
     void reloadCmdLine();
     void saveCmdLine();
@@ -181,8 +182,6 @@ private:
     void desc(const char *option, const char *description, bool skipIndent = false, char fillChar = '.');
     void desc(const char *mark_option, const char *mark, const char *option, const char *description, char fillChar = '.');
     void applySpecSpecifics();
-    static QString locateFile(const QString &fileName);
-    static QString locateFileInPaths(const QString &fileName, const QStringList &paths);
 };
 
 class MakeItem
index 50b121e..9d0194b 100644 (file)
@@ -47,6 +47,7 @@
 #include <qdir.h>
 #include <qfile.h>
 #include <qfileinfo.h>
+#include <qstandardpaths.h>
 
 #include <process.h>
 #include <errno.h>
@@ -141,6 +142,27 @@ QString Environment::detectQMakeSpec()
     return spec;
 }
 
+Compiler Environment::compilerFromQMakeSpec(const QString &qmakeSpec)
+{
+    if (qmakeSpec == QLatin1String("win32-msvc2012"))
+        return CC_NET2012;
+    if (qmakeSpec == QLatin1String("win32-msvc2012"))
+        return CC_NET2010;
+    if (qmakeSpec == QLatin1String("win32-msvc2008"))
+        return CC_NET2008;
+    if (qmakeSpec == QLatin1String("win32-msvc2005"))
+        return CC_NET2005;
+    if (qmakeSpec == QLatin1String("win32-msvc2003"))
+        return CC_NET2003;
+    if (qmakeSpec == QLatin1String("win32-icc"))
+        return CC_INTEL;
+    if (qmakeSpec == QLatin1String("win32-g++"))
+        return CC_MINGW;
+    if (qmakeSpec == QLatin1String("win32-borland"))
+        return CC_BORLAND;
+    return CC_UNKNOWN;
+}
+
 /*!
     Returns the enum of the compiler which was detected on the system.
     The compilers are detected in the order as entered into the
@@ -189,7 +211,7 @@ Compiler Environment::detectCompiler()
     if (!installed) {
         for(int i = 0; compiler_info[i].compiler; ++i) {
             QString executable = QString(compiler_info[i].executable).toLower();
-            if (executable.length() && Environment::detectExecutable(executable)) {
+            if (executable.length() && !QStandardPaths::findExecutable(executable).isEmpty()) {
                 if (detectedCompiler != compiler_info[i].compiler) {
                     ++installed;
                     detectedCompiler = compiler_info[i].compiler;
@@ -216,33 +238,6 @@ Compiler Environment::detectCompiler()
 };
 
 /*!
-    Returns true if the \a executable could be loaded, else false.
-    This means that the executable either is in the current directory
-    or in the PATH.
-*/
-bool Environment::detectExecutable(const QString &executable)
-{
-    PROCESS_INFORMATION procInfo;
-    memset(&procInfo, 0, sizeof(procInfo));
-
-    STARTUPINFO startInfo;
-    memset(&startInfo, 0, sizeof(startInfo));
-    startInfo.cb = sizeof(startInfo);
-
-    bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),
-                                      0, 0, false,
-                                      CREATE_NO_WINDOW | CREATE_SUSPENDED,
-                                      0, 0, &startInfo, &procInfo);
-
-    if (couldExecute) {
-        CloseHandle(procInfo.hThread);
-        TerminateProcess(procInfo.hProcess, 0);
-        CloseHandle(procInfo.hProcess);
-    }
-    return couldExecute;
-}
-
-/*!
     Creates a commandling from \a program and it \a arguments,
     escaping characters that needs it.
 */
@@ -514,4 +509,124 @@ bool Environment::rmdir(const QString &name)
     return result;
 }
 
+static QStringList splitPathList(const QString &path)
+{
+#if defined(Q_OS_WIN)
+    QRegExp splitReg(QStringLiteral("[;,]"));
+#else
+    QRegExp splitReg(QStringLiteral("[:]"));
+#endif
+    QStringList result = path.split(splitReg, QString::SkipEmptyParts);
+    const QChar separator = QDir::separator();
+    const QStringList::iterator end = result.end();
+    for (QStringList::iterator it = result.begin(); it != end; ++it) {
+        // Remove any leading or trailing ", this is commonly used in the environment
+        // variables
+        if (it->startsWith('"'))
+            it->remove(0, 1);
+        if (it->endsWith('"'))
+            it->chop(1);
+        if (it->endsWith(separator))
+            it->chop(1);
+    }
+    return result;
+}
+
+QString Environment::findFileInPaths(const QString &fileName, const QStringList &paths)
+{
+    if (!paths.isEmpty()) {
+        QDir d;
+        const QChar separator = QDir::separator();
+        foreach (const QString &path, paths)
+            if (d.exists(path + separator + fileName))
+                    return path;
+    }
+    return QString();
+}
+
+QStringList Environment::path()
+{
+    return splitPathList(QString::fromLocal8Bit(qgetenv("PATH")));
+}
+
+static QStringList mingwPaths(const QString &mingwPath, const QString &pathName)
+{
+    QStringList ret;
+    QDir mingwDir(mingwPath);
+    const QFileInfoList subdirs = mingwDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+    for (int i = 0 ;i < subdirs.length(); ++i) {
+        const QFileInfo &fi = subdirs.at(i);
+        const QString name = fi.fileName();
+        if (name == pathName)
+            ret += fi.absoluteFilePath();
+        else if (name.contains(QLatin1String("mingw"))) {
+            ret += fi.absoluteFilePath() + QLatin1Char('/') + pathName;
+        }
+    }
+    return ret;
+}
+
+// Return MinGW location from "c:\mingw\bin" -> "c:\mingw"
+static inline QString detectMinGW()
+{
+    const QString gcc = QStandardPaths::findExecutable(QLatin1String("g++.exe"));
+    return gcc.isEmpty() ?
+           gcc : QFileInfo(QFileInfo(gcc).absolutePath()).absolutePath();
+}
+
+// Detect Direct X SDK up tp June 2010. Included in Windows Kit 8.
+QString Environment::detectDirectXSdk()
+{
+    const QByteArray directXSdkEnv = qgetenv("DXSDK_DIR");
+    if (directXSdkEnv.isEmpty())
+        return QString();
+    QString directXSdk = QDir::cleanPath(QString::fromLocal8Bit(directXSdkEnv));
+    if (directXSdk.endsWith(QLatin1Char('/')))
+        directXSdk.truncate(directXSdk.size() - 1);
+    return directXSdk;
+}
+
+QStringList Environment::headerPaths(Compiler compiler)
+{
+    QStringList headerPaths;
+    if (compiler == CC_MINGW) {
+        const QString mingwPath = detectMinGW();
+        headerPaths = mingwPaths(mingwPath, QLatin1String("include"));
+        // Additional compiler paths
+        const QFileInfoList mingwConfigs = QDir(mingwPath + QLatin1String("/lib/gcc")).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+        for (int i = 0; i < mingwConfigs.length(); ++i) {
+            const QDir mingwLibDir = mingwConfigs.at(i).absoluteFilePath();
+            foreach (const QFileInfo &version, mingwLibDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
+                headerPaths += version.absoluteFilePath() + QLatin1String("/include");
+        }
+    } else {
+        headerPaths = splitPathList(QString::fromLocal8Bit(getenv("INCLUDE")));
+    }
+    // Add Direct X SDK for ANGLE
+    const QString directXSdk = detectDirectXSdk();
+    if (!directXSdk.isEmpty()) // Add Direct X SDK for ANGLE
+        headerPaths += directXSdk + QLatin1String("/include");
+    return headerPaths;
+}
+
+QStringList Environment::libraryPaths(Compiler compiler)
+{
+    QStringList libraryPaths;
+    if (compiler == CC_MINGW) {
+        libraryPaths = mingwPaths(detectMinGW(), "lib");
+    } else {
+        libraryPaths = splitPathList(QString::fromLocal8Bit(qgetenv("LIB")));
+    }
+    // Add Direct X SDK for ANGLE
+    const QString directXSdk = detectDirectXSdk();
+    if (!directXSdk.isEmpty()) {
+#ifdef Q_OS_WIN64
+        libraryPaths += directXSdk + QLatin1String("/lib/x64");
+#else
+        libraryPaths += directXSdk + QLatin1String("/lib/x86");
+#endif
+    }
+    return libraryPaths;
+}
+
 QT_END_NAMESPACE
index af4bf4b..3818d51 100644 (file)
@@ -39,8 +39,7 @@
 **
 ****************************************************************************/
 
-#include <qstring.h>
-#include <qt_windows.h>
+#include <qstringlist.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -63,13 +62,20 @@ class Environment
 public:
     static Compiler detectCompiler();
     static QString detectQMakeSpec();
-    static bool detectExecutable(const QString &executable);
+    static Compiler compilerFromQMakeSpec(const QString &qmakeSpec);
 
     static int execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv);
     static QString execute(const QString &command, int *returnCode = 0);
     static bool cpdir(const QString &srcDir, const QString &destDir);
     static bool rmdir(const QString &name);
 
+    static QString findFileInPaths(const QString &fileName, const QStringList &paths);
+    static QStringList path();
+
+    static QString detectDirectXSdk();
+    static QStringList headerPaths(Compiler compiler);
+    static QStringList libraryPaths(Compiler compiler);
+
 private:
     static Compiler detectedCompiler;