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: