[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#609674: kdevelop: does not correct parse CMake (GLOB and GLOB_RECURSE) variables



Package: kdevelop
Version: 4:4.0.1-1.1
Severity: normal
Tags: squeeze

Incorrect parse CMake FILE command if there is GLOB or GLOB_RECURSE, like 
this:
FILE(GLOB_RECURSE SOURCES "../../../src/*.cpp" )

This issue is already fixed in 4.1.2 and 4.2 branches of kdevelop
(http://git.reviewboard.kde.org/r/100283/)
In attach patch witch fix this issue.
It is little bit adapted to 4.0 branch.
I test and use it :) Please commit it(or rework original patch) to testing :)



-- System Information:
Debian Release: 6.0
  APT prefers testing
  APT policy: (500, 'testing'), (111, 'unstable'), (110, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.32-5-amd64 (SMP w/2 CPU cores)
Locale: LANG=uk_UA.UTF-8, LC_CTYPE=uk_UA.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages kdevelop depends on:
ii  kdebase-runtime              4:4.4.5-1   runtime components from the offici
ii  kdevelop-data                4:4.0.1-1.1 data files for the KDevelop IDE
ii  kdevplatform1-libs           1.0.1-1     shared libraries for the KDevelop 
ii  lcov                         1.8-2       Summarise Code coverage 
informatio
ii  libc6                        2.11.2-7    Embedded GNU C Library: Shared 
lib
ii  libgcc1                      1:4.4.5-8   GCC support library
ii  libkdecore5                  4:4.4.5-2   the KDE Platform Core Library
ii  libkdeui5                    4:4.4.5-2   the KDE Platform User Interface 
Li
ii  libkio5                      4:4.4.5-2   the Network-enabled File 
Managemen
ii  libkparts4                   4:4.4.5-2   the Framework for the KDE 
Platform
ii  libktexteditor4              4:4.4.5-2   the KTextEditor interfaces for 
the
ii  libprocessui4a               4:4.4.5-6   library for ksysguard process 
user
ii  libqt4-dbus                  4:4.6.3-4   Qt 4 D-Bus module
ii  libqt4-help                  4:4.6.3-4   Qt 4 help module
ii  libqt4-network               4:4.6.3-4   Qt 4 network module
ii  libqt4-script                4:4.6.3-4   Qt 4 script module
ii  libqt4-webkit                4:4.6.3-4   Qt 4 WebKit module
ii  libqtcore4                   4:4.6.3-4   Qt 4 core module
ii  libqtgui4                    4:4.6.3-4   Qt 4 GUI module
ii  libstdc++6                   4.4.5-8     The GNU Standard C++ Library v3
ii  libsublime1                  1.0.1-1     an user interface library
ii  libthreadweaver4             4:4.4.5-2   the ThreadWeaver Library for the 
K

Versions of packages kdevelop recommends:
ii  gdb                7.0.1-2+b1            The GNU Debugger
ii  valgrind           1:3.6.0~svn11254+nmu1 A memory debugger and profiler

Versions of packages kdevelop suggests:
ii  cmake                        2.8.2-2     a cross-platform, open-source 
make
ii  kdevelop-l10n-uk [kdevelop-l 4:4.0.1-1.1 Ukrainian (uk) localization files 

-- no debconf information
diff --git a/projectmanagers/cmake/parser/cmakeast.cpp b/projectmanagers/cmake/parser/cmakeast.cpp
index 7e40013..2b6bc5d 100644
--- a/projectmanagers/cmake/parser/cmakeast.cpp
+++ b/projectmanagers/cmake/parser/cmakeast.cpp
@@ -1080,9 +1080,26 @@ bool FileAst::parseFunctionInfo( const CMakeFunctionDesc& func )
             addOutputArgument(func.arguments[2]);
             break;
         case Glob:
+            addOutputArgument(func.arguments[1]);
+            m_variable = func.arguments[1].value;
+            it=func.arguments.constBegin()+2;
+            itEnd=func.arguments.constEnd();
+
+            for(; it!=itEnd; ++it) {
+                if(it->value=="RELATIVE") {
+                    it++;
+                    if(it==itEnd)
+                        return false;
+                    else
+                        m_path = it->value;
+                } else
+                    m_globbingExpressions << it->value;
+            }
+            break;
         case GlobRecurse:
             addOutputArgument(func.arguments[1]);
             m_variable = func.arguments[1].value;
+            m_isFollowingSymlinks = false;
             it=func.arguments.constBegin()+2;
             itEnd=func.arguments.constEnd();
             
@@ -1093,6 +1110,8 @@ bool FileAst::parseFunctionInfo( const CMakeFunctionDesc& func )
                         return false;
                     else
                         m_path = it->value;
+                } else if(it->value=="FOLLOW_SYMLINKS") {
+                    m_isFollowingSymlinks = true;
                 } else
                     m_globbingExpressions << it->value;
             }
diff --git a/projectmanagers/cmake/parser/cmakeast.h b/projectmanagers/cmake/parser/cmakeast.h
index 54a52d0..a305f7d 100644
--- a/projectmanagers/cmake/parser/cmakeast.h
+++ b/projectmanagers/cmake/parser/cmakeast.h
@@ -252,6 +252,7 @@ CMAKE_ADD_AST_MEMBER( QString, variable )
 CMAKE_ADD_AST_MEMBER( QString, directory )
 CMAKE_ADD_AST_MEMBER( QString, message )
 CMAKE_ADD_AST_MEMBER( QStringList, globbingExpressions )
+CMAKE_ADD_AST_MEMBER( bool, isFollowingSymlinks )
 CMAKE_ADD_AST_MEMBER( QStringList, directories )
 
 CMAKE_ADD_AST_MEMBER( KUrl, url )
diff --git a/projectmanagers/cmake/parser/cmakedebugvisitor.cpp b/projectmanagers/cmake/parser/cmakedebugvisitor.cpp
index 03a0d8c..09a6854 100644
--- a/projectmanagers/cmake/parser/cmakedebugvisitor.cpp
+++ b/projectmanagers/cmake/parser/cmakedebugvisitor.cpp
@@ -96,7 +96,7 @@ int CMakeAstDebugVisitor::visit( const MessageAst * ast )
 
 int CMakeAstDebugVisitor::visit( const FileAst * ast )
 {
-    WRITEOUT << "FILE: " << "(type,variable,directory,path,globbingExpressions,message,directories) = (" << ast->type() << "," << ast->variable() << "," << ast->directory() << "," << ast->path() << "," << ast->globbingExpressions() << "," << ast->message() << "," << ast->directories() << ")";
+    WRITEOUT << "FILE: " << "(type,variable,directory,path,globbingExpressions,message,directories,followSymlinks) = (" << ast->type() << "," << ast->variable() << "," << ast->directory() << "," << ast->path() << "," << ast->globbingExpressions() << "," << ast->message() << "," << ast->directories() << "," << ast->isFollowingSymlinks() << ")";
     return 1;
 }
 
diff --git a/projectmanagers/cmake/parser/cmakeprojectvisitor.cpp b/projectmanagers/cmake/parser/cmakeprojectvisitor.cpp
index e2f16d1..96bc00c 100644
--- a/projectmanagers/cmake/parser/cmakeprojectvisitor.cpp
+++ b/projectmanagers/cmake/parser/cmakeprojectvisitor.cpp
@@ -1396,53 +1396,52 @@ int CMakeProjectVisitor::visit(const FileAst *file)
             kDebug(9042) << "FileAst: read ";
         }
             break;
-        case FileAst::Glob: {
+        case FileAst::Glob:
+        case FileAst::GlobRecurse: {
             QStringList matches;
-            QString relative=file->path();
-            foreach(const QString& glob, file->globbingExpressions())
+            foreach(QString expr, file->globbingExpressions())
             {
-                QStringList globs;
-                QString current;
-                if(KUrl::isRelativeUrl(glob)) {
-                    KUrl urlGlob(glob);
-                    current=urlGlob.upUrl().path();
-                    
-                    globs.append(urlGlob.fileName());
-                } else if(!relative.isEmpty()) {
-                    current=relative;
-                    globs.append(glob);
-                } else {
-                    current=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
-                    globs.append(glob);
-                }
-                
-                QDir d(current);
-                matches+=d.entryList(globs, QDir::NoDotAndDotDot | QDir::AllEntries);
+                if (expr.isEmpty())
+                    continue;
+                QString startPath;
+                if (QDir::isRelativePath(expr))
+                    startPath = m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
+                // startPath must start from '/' if not empty when calling traverseRecursiveGlob()
+                if (expr[0] == '/')
+                {
+                    //moving slash to startPath (it should be empty before)
+                    expr = expr.mid(1);
+                    startPath += '/';
+                }
+                else
+                {
+                    if (!startPath.isEmpty())
+                        startPath += '/';
+                }
+                if (file->type() == FileAst::Glob)
+                {
+                    matches.append(traverseGlob(startPath, expr));
+                }
+                else
+                {
+                    matches.append(traverseGlob(startPath, expr, true, file->isFollowingSymlinks()));
+                }
             }
-            m_vars->insert(file->variable(), matches);
-            kDebug(9042) << "file glob" << file->path() << file->globbingExpressions() << matches;
-        } break;
-        case FileAst::GlobRecurse: {
-            QString current;
-            if(file->path().isEmpty())
-                current=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
-            else
-                current=file->path();
-            QQueue<QString> candidates;
-            candidates.enqueue(current);
-            QStringList directories;
-            while(!candidates.isEmpty())
-            {
-                QString dir=candidates.dequeue();
-                directories.append(dir);
-                QDir direc(dir);
-                candidates += direc.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
+            if (!file->path().isEmpty())
+            {
+                // RELATIVE was specified, so we need to make all paths relative to file->path()
+                QDir relative(file->path());
+                QStringList::iterator endIt = matches.end();
+                for(QStringList::iterator it = matches.begin(); it != endIt; ++it)
+                {
+                    *it = relative.relativeFilePath(*it);
+                }
             }
-
-            QDir d(current);
-            QStringList matches=d.entryList(file->globbingExpressions(), QDir::NoDotAndDotDot | QDir::AllEntries);
             m_vars->insert(file->variable(), matches);
-            kDebug(9042) << "file glob_recurse" << file->path() << file->globbingExpressions() << matches;
+            if (file->type() == FileAst::Glob)
+                kDebug(9042) << "file glob" << file->path() << file->globbingExpressions() << matches;
+            else
+                kDebug(9042) << "file glob_recurse" << file->path() << file->globbingExpressions() << matches;
         }   break;
         case FileAst::Remove:
         case FileAst::RemoveRecurse:
@@ -2237,3 +2236,90 @@ QStringList CMakeProjectVisitor::resolveDependencies(const QStringList & files)
     return ret;
 }
 
+QStringList CMakeProjectVisitor::traverseGlob(const QString& startPath,
+    const QString& expression, bool recursive, bool followSymlinks)
+{
+    kDebug(9042) << "Starting from (" << startPath << ", " << expression << ", " << followSymlinks << ")";
+    QString expr = expression;
+    int firstSlash = expr.indexOf('/');
+    int slashShift = 0;
+    while (firstSlash == slashShift)
+    {
+        slashShift++;
+        firstSlash = expr.indexOf('/', slashShift);
+    }
+    expr = expr.mid(slashShift);
+    if (firstSlash == -1)
+    {
+        kDebug(9042) << "Matching files in " << startPath << " with glob " << expr;
+        //We're here. Lets match files from startPath dir.
+        QStringList nameFilters;
+        nameFilters << expr;
+        QStringList dirsToSearch;
+        if (recursive)
+        {
+            QDir::Filters dirFilters = QDir::NoDotAndDotDot | QDir::Dirs;
+            if (!followSymlinks)
+                dirFilters |= QDir::NoSymLinks;
+            QQueue<QString> dirsToExpand;
+            dirsToExpand.enqueue(startPath);
+            while (!dirsToExpand.empty())
+            {
+                QString dir = dirsToExpand.dequeue();
+                kDebug(9042) << "Enqueueing " << dir;
+                dirsToSearch << dir;
+                QDir d(dir);
+                QStringList dirNames = d.entryList(dirFilters);
+                foreach(QString dirName, dirNames)
+                {
+                    dirsToExpand << d.filePath(dirName);
+                }
+            }
+        }
+        else
+            dirsToSearch << startPath;
+        QStringList filePaths;
+        foreach (QString dirToSearch, dirsToSearch)
+        {
+            QDir dir(dirToSearch);
+            QStringList fileNames;
+            fileNames = dir.entryList(nameFilters, QDir::Files);
+            foreach (QString fileName, fileNames)
+            {
+                filePaths << dir.filePath(fileName);
+            }
+        }
+        return filePaths;
+    }
+    firstSlash -= slashShift;
+    QString dirGlob = expr.left(firstSlash);
+    QString rightExpression = expr.mid(firstSlash + 1);
+    //Now we must find match for a directory specified in dirGlob
+    QStringList matchedDirs;
+    if (dirGlob.contains('*') || dirGlob.contains('?') || dirGlob.contains('['))
+    {
+        kDebug(9042) << "Got a dir glob " << dirGlob;
+        if (startPath.isEmpty())
+            return QStringList();
+        //it's really a glob, not just dir name
+        QStringList nameFilters;
+        nameFilters << dirGlob;
+        matchedDirs = QDir(startPath).entryList(nameFilters, QDir::NoDotAndDotDot | QDir::Dirs);
+    }
+    else
+    {
+        //just a directory name. Add it as a match.
+        kDebug(9042) << "Got a simple folder " << dirGlob;
+        matchedDirs << dirGlob;
+    }
+    QStringList matches;
+    QString path = startPath;
+    if (!path.isEmpty() && !path.endsWith('/'))
+        path += '/';
+    foreach(QString dirName, matchedDirs)
+    {
+        kDebug(9042) << "Going resursive into " << path + dirName << " and glob " << rightExpression;
+        matches.append(traverseGlob(path + dirName, rightExpression, recursive, followSymlinks));
+    }
+    return matches;
+}
diff --git a/projectmanagers/cmake/parser/cmakeprojectvisitor.h b/projectmanagers/cmake/parser/cmakeprojectvisitor.h
index a825d2b..63f82c9 100644
--- a/projectmanagers/cmake/parser/cmakeprojectvisitor.h
+++ b/projectmanagers/cmake/parser/cmakeprojectvisitor.h
@@ -175,6 +175,9 @@ class KDEVCMAKECOMMON_EXPORT CMakeProjectVisitor : CMakeAstVisitor
         QStringList dependees(const QString& s) const;
         int declareFunction(Macro m, const CMakeFileContent& content, int initial, const QString& end);
 
+        QStringList traverseGlob(const QString& startPath, const QString& expression,
+            bool recursive = false, bool followSymlinks = false);
+
         CMakeProperties m_props;
         QStringList m_modulePath;
         QString m_projectName;

Reply to: