From a5d8e84ceb1e2a5249c079072ccbdc48b9251d7c Mon Sep 17 00:00:00 2001
From: Thomas Gahr <thomas.gahr@physik.uni-muenchen.de>
Date: Sat, 6 Apr 2013 14:27:44 +0200
Subject: [PATCH] ModuleLoader: case-insensitive path-resolution for module

  Perform case-sensitive lookup for candidate subdirectories
  when looking for a module.
---
 src/lib/language/moduleloader.cpp | 116 ++++++++++++++++++++++++--------------
 src/lib/language/moduleloader.h   |   1 +
 2 files changed, 76 insertions(+), 41 deletions(-)

diff --git a/src/lib/language/moduleloader.cpp b/src/lib/language/moduleloader.cpp
index dfccdae..0e89713 100644
--- a/src/lib/language/moduleloader.cpp
+++ b/src/lib/language/moduleloader.cpp
@@ -423,52 +423,53 @@ ItemPtr ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext,
     QStringList searchPaths = extraSearchPaths;
     searchPaths.append(m_moduleSearchPaths);
 
-    const QString moduleSubDir = ModuleLoader::moduleSubDir(moduleName);
     bool triedToLoadModule = false;
     foreach (const QString &path, searchPaths) {
-        QString dirPath = FileInfo::resolvePath(path, moduleSubDir);
-        QStringList moduleFileNames = m_moduleDirListCache.value(dirPath);
-        if (moduleFileNames.isEmpty()) {
-            QString origDirPath = dirPath;
-            FileInfo dirInfo(dirPath);
-            if (!dirInfo.isDir()) {
-                bool found = false;
-                if (HostOsInfo::isWindowsHost()) {
-                    // On case sensitive file systems try to find the path.
-                    QStringList subPaths = moduleName;
-                    QDir dir(path);
-                    if (!dir.cd(moduleSearchSubDir))
+        const QStringList moduleSubDirs = ModuleLoader::moduleSubDirs(path, moduleName);
+        foreach(QString moduleSubDir, moduleSubDirs) {
+            QStringList moduleFileNames = m_moduleDirListCache.value(moduleSubDir);
+            if (moduleFileNames.isEmpty()) {
+                QString origDirPath = moduleSubDir;
+                FileInfo dirInfo(moduleSubDir);
+                if (!dirInfo.isDir()) {
+                    bool found = false;
+                    if (HostOsInfo::isWindowsHost()) {
+                        // On case sensitive file systems try to find the path.
+                        QStringList subPaths = moduleName;
+                        QDir dir(path);
+                        if (!dir.cd(moduleSearchSubDir))
+                            continue;
+                        do {
+                            QStringList lst = dir.entryList(QStringList(subPaths.takeFirst()),
+                                                            QDir::Dirs);
+                            if (lst.count() != 1)
+                                break;
+                            if (!dir.cd(lst.first()))
+                                break;
+                            if (subPaths.isEmpty()) {
+                                found = true;
+                                moduleSubDir = dir.absolutePath();
+                            }
+                        } while (!found);
+                    }
+                    if (!found)
                         continue;
-                    do {
-                        QStringList lst = dir.entryList(QStringList(subPaths.takeFirst()),
-                                                        QDir::Dirs);
-                        if (lst.count() != 1)
-                            break;
-                        if (!dir.cd(lst.first()))
-                            break;
-                        if (subPaths.isEmpty()) {
-                            found = true;
-                            dirPath = dir.absolutePath();
-                        }
-                    } while (!found);
                 }
-                if (!found)
-                    continue;
-            }
-            QDirIterator dirIter(dirPath, QStringList(QLatin1String("*.qbs")));
-            while (dirIter.hasNext())
-                moduleFileNames += dirIter.next();
+                QDirIterator dirIter(moduleSubDir, QStringList(QLatin1String("*.qbs")));
+                while (dirIter.hasNext())
+                    moduleFileNames += dirIter.next();
 
-            m_moduleDirListCache.insert(origDirPath, moduleFileNames);
-        }
-        foreach (const QString &filePath, moduleFileNames) {
-            triedToLoadModule = true;
-            ItemPtr module = loadModuleFile(productContext,
-                                            moduleName.count() == 1
-                                                && moduleName.first() == QLatin1String("qbs"),
-                                            filePath);
-            if (module)
-                return module;
+                m_moduleDirListCache.insert(origDirPath, moduleFileNames);
+            }
+            foreach (const QString &filePath, moduleFileNames) {
+                triedToLoadModule = true;
+                ItemPtr module = loadModuleFile(productContext,
+                                                moduleName.count() == 1
+                                                    && moduleName.first() == QLatin1String("qbs"),
+                                                filePath);
+                if (module)
+                    return module;
+            }
         }
     }
 
@@ -718,6 +719,39 @@ QString ModuleLoader::moduleSubDir(const QStringList &moduleName)
 #endif
 }
 
+/* Look inside path for candidate subdirectories where to find moduleName
+ *
+ * The lookup is case-insensitive, so the list returned for module "foo bar" contains:
+ *   path/Foo/Bar
+ *   path/foo/Bar
+ * if path contains both "foo" and "Foo"
+ */
+QStringList ModuleLoader::moduleSubDirs(const QString &path, const QStringList &moduleName)
+{
+    //theoretically, this allows for module names of arbitrary length as in
+    //  Depends{name: "One.Two.three"}, maybe an assertions to limit this
+    //to two elements is appropriate here
+    QBS_ASSERT(moduleName.count() <= 2, return QStringList());
+    QStringList ret;
+    ret << path;
+    foreach(QString subDir, moduleName) {
+        QStringList candidatePaths = ret;
+        ret.clear();
+        foreach(QString candidatePath, candidatePaths) {
+            QDirIterator subDirs(candidatePath);
+            while(subDirs.hasNext()) {
+                subDirs.next();
+                if(!subDirs.fileInfo().isDir())
+                    continue;
+                //case-insensitive -> use toLower
+                if(subDirs.fileName().toLower() == subDir)
+                    ret << subDirs.filePath();
+            }
+        }
+    }
+    return ret;
+}
+
 void ModuleLoader::copyProperty(const QString &propertyName, const ItemConstPtr &source,
                                 const ItemPtr &destination)
 {
diff --git a/src/lib/language/moduleloader.h b/src/lib/language/moduleloader.h
index 2bfe774..9487eb8 100644
--- a/src/lib/language/moduleloader.h
+++ b/src/lib/language/moduleloader.h
@@ -150,6 +150,7 @@ private:
     QStringList readExtraSearchPaths(const ItemPtr &item);
     static ItemPtr wrapWithProject(const ItemPtr &item);
     static QString moduleSubDir(const QStringList &moduleName);
+    static QStringList moduleSubDirs(const QString& path, const QStringList &moduleName);
     static void copyProperty(const QString &propertyName, const ItemConstPtr &source, const ItemPtr &destination);
 
     ScriptEngine *m_engine;
-- 
1.8.2

