CMake support: avoid infinite loop on foreach(RANGE) that never runs.
[kdevelop:kdevelop.git] / projectmanagers / cmake / parser / cmakeprojectvisitor.cpp
1 /* KDevelop CMake Support
2  *
3  * Copyright 2007-2008 Aleix Pol <aleixpol@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  */
20
21 #include "cmakeprojectvisitor.h"
22 #include "cmakeast.h"
23 #include "cmakecondition.h"
24 #include "astfactory.h"
25
26 #include <language/editor/simplerange.h>
27 #include <language/duchain/topducontext.h>
28 #include <language/duchain/duchain.h>
29 #include <language/duchain/duchainlock.h>
30 #include <language/duchain/parsingenvironment.h>
31 #include <language/duchain/declaration.h>
32 #include <language/duchain/types/functiontype.h>
33 #include <language/duchain/types/delayedtype.h>
34
35 #include <KProcess>
36 #include <KLocale>
37 #include <KDebug>
38 #include <QHash>
39 #include <QQueue>
40 #include <QFile>
41 #include <QDir>
42 #include <QtCore/qglobal.h>
43 #include <QByteArray>
44 #include <QFileInfo>
45 #include <QScriptEngine>
46 #include <QScriptValue>
47 #include <language/editor/editorintegrator.h>
48 #include <language/duchain/smartconverter.h>
49
50 using namespace KDevelop;
51
52 void debugMsgs(const QString& message) { kDebug(9032) << "message:" << message; }
53
54 CMakeProjectVisitor::message_callback CMakeProjectVisitor::s_msgcallback=debugMsgs;
55
56 CMakeProjectVisitor::CMakeProjectVisitor(const QString& root, ReferencedTopDUContext parent)
57     : m_root(root), m_vars(0), m_macros(0), m_topctx(0), m_parentCtx(parent), m_hitBreak(false)
58 {
59 }
60
61 QStringList CMakeProjectVisitor::envVarDirectories(const QString &varName)
62 {
63     QString env=QString::fromLatin1(qgetenv(varName.toLatin1()));
64 //     kDebug(9042) << ".......resolving env:" << varName << "=" << QProcess::systemEnvironment() << env;
65     if(!env.isEmpty())
66     {
67         QChar separator;
68 #ifdef Q_OS_WIN
69         separator = ';';
70 #else
71         separator = ':';
72 #endif
73         kDebug(9042) << "resolving env:" << varName << "=" << env;
74         return env.split(separator);
75     }
76     else
77     {
78         kDebug(9032) << "warning:" << varName << " not found";
79         return QStringList();
80     }
81 }
82
83 QList< CMakeProjectVisitor::IntPair > CMakeProjectVisitor::parseArgument(const QString &exp)
84 {
85     QString name;
86     QStack<int> opened;
87     QList< IntPair > pos;
88     bool gotDollar=false;
89     for(int i=exp.indexOf('$'); i<exp.size(); i++)
90     {
91         switch(exp[i].unicode())
92         {
93             case '$':
94                 gotDollar=true;
95                 break;
96             case '{':
97                 if(gotDollar)
98                 {
99                     opened.push(i);
100                 }
101                 gotDollar=false;
102                 break;
103             case '}':
104                 if(!opened.isEmpty())
105                     pos.append(IntPair(opened.pop(), i, opened.count()));
106                 break;
107         }
108     }
109
110     for(int i=pos.count()-1; i>=0 && !opened.isEmpty(); i--)
111     {
112         if(pos[i].first==opened.top())
113             opened.pop();
114         pos[i].level -= opened.size();
115     }
116     return pos;
117 }
118
119 QStringList CMakeProjectVisitor::theValue(const QString& exp, const IntPair& thecase) const
120 {
121     int dollar=exp.lastIndexOf('$', thecase.first);
122     QString type=exp.mid(dollar+1, thecase.first-dollar-1);
123     QString var=exp.mid(thecase.first+1, thecase.second-thecase.first-1);
124     QStringList vars;
125 //     kDebug() << "lalalallalala" << exp << thecase.print();
126     if(type.isEmpty())
127     {
128         if(m_vars->contains(var))
129             vars = m_vars->value(var);
130         else if(m_cache->contains(var))
131             vars = m_cache->value(var).value.split(';');
132     }
133     else if(type=="ENV")
134     {
135         vars=envVarDirectories(var);
136     }
137     else
138         kDebug() << "error: I do not understand the key: " << type;
139
140 //     kDebug() << "solving: " << var << vars << exp;
141     return vars;
142 }
143
144 QString replaceOne(const QString& var, const QString& id, const QString& value, int dollar)
145 {
146 //     kDebug() << "ooo" << var << value << id << var[dollar+id.size()-1] << (dollar+id.size());
147 //     kDebug() << "kkkk" << var.mid(0, dollar) << value << var.mid(dollar+id.size(), var.size()-(dollar+id.size()));
148     return var.mid(0, dollar)+value+var.mid(dollar+id.size(), var.size()-(dollar+id.size()));
149 }
150
151 QStringList CMakeProjectVisitor::value(const QString& exp, const QList<IntPair>& poss, int& desired) const
152 {
153     QString var=exp;
154     QList<IntPair> invars;
155     invars += poss[desired];
156     //kDebug() << ">>>>>" << exp << desired << poss.count();
157     for(; desired+1<poss.size() && poss[desired].level>1; desired++)
158     {
159         invars+=poss[desired+1];
160         //kDebug() << "poss@"<< desired+1 << "="<< poss[desired+1].print();
161     }
162
163     //kDebug() << ";;;;;" << invars.count();
164     if(invars.count()>1)
165     {
166         QList<IntPair>::const_iterator itConstEnd=invars.constEnd();
167         QList<IntPair>::iterator itEnd=invars.end();
168         QList<IntPair>::iterator itBegin=invars.begin();
169         for(QList<IntPair>::const_iterator it=invars.constBegin(); (it+1)!=itConstEnd; ++it)
170         {
171             const IntPair& subvar=*it;
172             int dollar=var.lastIndexOf('$', subvar.first);
173             QString id=var.mid(dollar, subvar.second-dollar+1), value=theValue(var, subvar).join(QChar(';'));
174
175             int diff=value.size()-id.size();
176             for(QList<IntPair>::iterator it=itBegin; it!=itEnd; ++it)
177             {
178                 if(it->first > subvar.first) it->first += diff;
179                 if(it->second> subvar.second) it->second+= diff;
180             }
181
182             var=replaceOne(var, id, value, dollar);
183         }
184     }
185     return theValue(var, invars.last());
186 }
187
188 QStringList CMakeProjectVisitor::resolveVariable(const CMakeFunctionArgument &exp)
189 {
190     QStringList ret;
191     ret += QString();
192     QList< IntPair > var = parseArgument(exp.value);
193
194     int i=0;
195     IntPair last(-1,-1, 0);
196
197     for(QList<IntPair>::const_iterator it=var.constBegin(); it!=var.constEnd(); ++it, ++i)
198     {
199         while(it!=var.constEnd() && it->level>1)
200             ++it;
201
202         const IntPair& p=*it;
203 //         kDebug () << "reeeeeet" << ret << exp.value << p.print();
204         int dollar=exp.value.lastIndexOf('$', p.first);
205         QString pre=exp.value.mid(last.second+1, dollar-last.second-1);
206
207         QStringList vars = value(exp.value, var, i);
208 //         kDebug() << "aaaaaaaaaA" << pre << vars;
209
210         if(!vars.isEmpty())
211         {
212             pre+=vars.takeFirst();
213         }
214         ret.last()+=pre;
215         ret += vars;
216         last=p;
217         
218 //         kDebug() << "yaaaaaaa" << ret;
219 //         i++;
220     }
221     ret.last().append(exp.value.mid(last.second+1, exp.value.count()-last.second));
222
223     if(exp.quoted)
224     {
225         ret=QStringList(ret.join(QChar(';')));
226     }
227     return ret;
228 }
229
230 int CMakeProjectVisitor::notImplemented(const QString &name) const
231 {
232     kDebug(9042) << "not implemented!" << name;
233     return 1;
234 }
235
236 bool CMakeProjectVisitor::hasMacro(const QString& name) const
237 {
238     Q_ASSERT(m_macros);
239     return m_macros->contains(name);
240 }
241
242 int CMakeProjectVisitor::visit(const CMakeAst *ast)
243 {
244     kDebug(9042) << "error! function not implemented" << ast->content()[ast->line()].name;
245     foreach(const CMakeFunctionArgument& arg, ast->outputArguments())
246     {
247         //NOTE: this is a workaround, but fixes some issues.
248         kDebug(9042) << "reseting: " << arg.value;
249         m_vars->insert(arg.value, QStringList());
250     }
251     return 1;
252 }
253
254 int CMakeProjectVisitor::visit( const AddTestAst * test)
255 {
256     Q_UNUSED(test);
257     return 1;
258 }
259
260 int CMakeProjectVisitor::visit(const ProjectAst *project)
261 {
262     m_projectName = project->projectName();
263     if(!m_vars->contains("CMAKE_PROJECT_NAME"))
264         m_vars->insert("CMAKE_PROJECT_NAME", QStringList(project->projectName()));
265     
266     m_vars->insert("PROJECT_NAME", QStringList(project->projectName()));
267     m_vars->insert("PROJECT_SOURCE_DIR", QStringList(m_root));
268     m_vars->insert("PROJECT_BINARY_DIR", m_vars->value("CMAKE_CURRENT_BINARY_DIR"));
269     m_vars->insert(QString("%1_SOURCE_DIR").arg(m_projectName), QStringList(m_root));
270     m_vars->insert(QString("%1_BINARY_DIR").arg(m_projectName), m_vars->value("CMAKE_CURRENT_BINARY_DIR"));
271     return 1;
272 }
273
274 int CMakeProjectVisitor::visit( const SetTargetPropsAst * targetProps)
275 {
276     kDebug(9042) << "setting target props for " << targetProps->targets() << targetProps->properties();
277     foreach(const QString& tname, targetProps->targets())
278     {
279         foreach(const SetTargetPropsAst::PropPair& t, targetProps->properties())
280         {
281             m_props[TargetProperty][tname][t.first] = t.second.split(';');
282         }
283     }
284     return 1;
285 }
286
287 int CMakeProjectVisitor::visit( const SetDirectoryPropsAst * dirProps)
288 {   
289     QString dir=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").join(QString());
290     kDebug(9042) << "setting directory props for " << dirProps->properties() << dir;
291     foreach(const SetDirectoryPropsAst::PropPair& t, dirProps->properties())
292     {
293         m_props[DirectoryProperty][dir][t.first] = t.second.split(';');
294     }
295     return 1;
296 }
297
298 int CMakeProjectVisitor::visit( const GetTargetPropAst * prop)
299 {
300     kDebug(9042) << "getting target " << prop->target() << " prop " << prop->property() << prop->variableName();
301     QStringList value;
302     
303     if(m_props[TargetProperty].contains(prop->target())) {
304         QMap<QString, QStringList>& targetProps = m_props[TargetProperty][prop->target()];
305         if(!targetProps.contains(prop->property())) {
306             if(prop->property().startsWith("LOCATION_") && targetProps.contains("IMPORTED_"+prop->property()))
307                 targetProps[prop->property()]=targetProps["IMPORTED_"+prop->property()];
308         }
309         value = targetProps.value(prop->property());
310     }
311     if(value.isEmpty())
312         value += QString(prop->variableName()+"-NOTFOUND");
313     
314     m_vars->insert(prop->variableName(), value);
315 //     kDebug(9042) << "goooooot" << m_vars->value(prop->variableName());
316     return 1;
317 }
318
319 int CMakeProjectVisitor::visit(const AddSubdirectoryAst *subd)
320 {
321     kDebug(9042) << "adding subdirectory" << subd->sourceDir();
322
323     VisitorState p=stackTop();
324
325     Subdirectory d;
326     d.name=subd->sourceDir();
327     d.build_dir=subd->binaryDir().isEmpty() ? d.name : subd->binaryDir();
328     d.desc=p.code->at(p.line);
329     
330     m_subdirectories += d;
331     return 1;
332 }
333
334 int CMakeProjectVisitor::visit(const SubdirsAst *sdirs)
335 {
336     kDebug(9042) << "adding subdirectories" << sdirs->directories() << sdirs->exluceFromAll();
337     VisitorState p=stackTop();
338     CMakeFunctionDesc desc=p.code->at(p.line);
339     
340     foreach(const QString& dir, sdirs->directories() + sdirs->exluceFromAll()) {
341         Subdirectory d;
342         d.name=dir;
343         d.build_dir=dir;
344         d.desc=desc;
345         
346         m_subdirectories += d;
347     }
348     return 1;
349 }
350
351 void CMakeProjectVisitor::printBacktrace(const QStack<VisitorState> &backtrace)
352 {
353     int i=0;
354     kDebug() << "backtrace" << backtrace.count();
355     foreach(const VisitorState& v, backtrace)
356     {
357         if(v.code->count()>v.line)
358           kDebug(9042) << i << ": ";//           << v.code->at(v.line).name;
359         else
360           kDebug(9042) << i << ": ------------------------";
361         i++;
362     }
363 }
364
365 CMakeProjectVisitor::VisitorState CMakeProjectVisitor::stackTop() const
366 {
367     VisitorState p;
368     QString filename=m_backtrace.front().code->at(m_backtrace.front().line).filePath;
369     QStack<VisitorState>::const_iterator it=m_backtrace.constBegin();
370
371     for(; it!=m_backtrace.constEnd(); ++it)
372     {
373         if(filename!=it->code->at(it->line).filePath)
374             break;
375
376         p=*it;
377     }
378     return p;
379 }
380
381 void CMakeProjectVisitor::defineTarget(const QString& id, const QStringList& sources, Target::Type t)
382 {
383     kDebug(9042) << "Defining target" << id;
384     if (m_targetForId.contains(id))
385         kDebug(9032) << "warning! there already was a target called" << id;
386
387     VisitorState p=stackTop();
388
389     Declaration *d=0;
390     if(!p.code->at(p.line).arguments.isEmpty()) {
391         DUChainWriteLocker lock(DUChain::lock());
392         d= new Declaration(p.code->at(p.line).arguments.first().range(), p.context);
393         d->setIdentifier( Identifier(id) );
394     }
395     
396     Target target;
397     target.name=id.isEmpty() ? "<wrong-target>" : id;
398     target.declaration=d;
399     target.files=sources;
400     target.type=t;
401     target.desc=p.code->at(p.line);
402     
403     m_targetForId[id]=target;
404
405     QString exe=id;
406     switch(t) {
407         case Target::Executable:
408             exe += m_vars->value("CMAKE_EXECUTABLE_SUFFIX").join(QString());
409             break;
410         case Target::Library:
411             exe = QString("%1%2%3").arg(m_vars->value("CMAKE_LIBRARY_PREFIX").join(QString())).arg(id).arg(m_vars->value("CMAKE_LIBRARY_SUFFIX").join(QString()));
412             break;
413         case Target::Custom:
414             break;
415     }
416     
417     m_props[TargetProperty][id]["LOCATION"]=QStringList(m_vars->value("CMAKE_CURRENT_BINARY_DIR").join(QString())+'/'+exe);
418 }
419
420 int CMakeProjectVisitor::visit(const AddExecutableAst *exec)
421 {
422     if(!exec->isImported())
423         defineTarget(exec->executable(), exec->sourceLists(), Target::Executable);
424     else
425         kDebug(9042) << "imported executable" << exec->executable();
426     kDebug(9042) << "exec:" << exec->executable() << "->" << m_targetForId.contains(exec->executable())
427         << "imported" << exec->isImported();
428     return 1;
429 }
430
431 int CMakeProjectVisitor::visit(const AddLibraryAst *lib)
432 {
433     if(!lib->isImported())
434         defineTarget(lib->libraryName(), lib->sourceLists(), Target::Library);
435     kDebug(9042) << "lib:" << lib->libraryName();
436     return 1;
437 }
438
439 int CMakeProjectVisitor::visit(const SetAst *set)
440 {
441     //TODO: Must deal with ENV{something} case
442     QStringList values;
443     if(set->storeInCache() && m_cache->contains(set->variableName()))
444         values = m_cache->value(set->variableName()).value.split(';');
445     else
446         values = set->values();
447     kDebug(9042) << "setting variable:" << set->variableName()/* << "to" << values*/;
448     m_vars->insert(set->variableName(), values);
449     return 1;
450 }
451
452 int CMakeProjectVisitor::visit(const IncludeDirectoriesAst * dirs)
453 {
454     kDebug(9042) << "adding include directories" << dirs->includedDirectories();
455     IncludeDirectoriesAst::IncludeType t = dirs->includeType();
456
457     QStringList toInclude = dirs->includedDirectories();
458
459     if(t==IncludeDirectoriesAst::Default)
460     {
461         if(m_vars->contains("CMAKE_INCLUDE_DIRECTORIES_BEFORE") && m_vars->value("CMAKE_INCLUDE_DIRECTORIES_BEFORE")[0]=="ON")
462             t = IncludeDirectoriesAst::Before;
463         else
464             t = IncludeDirectoriesAst::After;
465     }
466
467     if(t==IncludeDirectoriesAst::After)
468         m_includeDirectories += toInclude;
469     else
470         m_includeDirectories = toInclude + m_includeDirectories;
471     kDebug(9042) << "done." << m_includeDirectories;
472     return 1;
473 }
474
475 QString CMakeProjectVisitor::findFile(const QString &file, const QStringList &folders,
476         const QStringList& suffixes, bool location)
477 {
478     if( file.isEmpty() || QFileInfo(file).isAbsolute() )
479          return file;
480
481     QStringList suffixFolders, useSuffixes(suffixes);
482     useSuffixes.prepend(QString());
483     foreach(const QString& apath, folders)
484     {
485         foreach(const QString& suffix, useSuffixes)
486         {
487             suffixFolders.append(apath+'/'+suffix);
488         }
489     }
490
491     KUrl path;
492     foreach(const QString& mpath, suffixFolders)
493     {
494         if(mpath.isEmpty())
495             continue;
496
497         KUrl afile(mpath);
498         afile.addPath(file);
499         kDebug(9042) << "Trying:" << mpath << '.' << file;
500         if(QFile::exists(afile.toLocalFile()))
501         {
502             if(location)
503                 path=mpath;
504             else
505                 path=afile;
506             break;
507         }
508     }
509     //kDebug(9042) << "find file" << file << "into:" << folders << "found at:" << path;
510     return path.toLocalFile(KUrl::RemoveTrailingSlash);
511 }
512
513 int CMakeProjectVisitor::visit(const IncludeAst *inc)
514 {
515     Q_ASSERT(m_vars->contains("CMAKE_CURRENT_SOURCE_DIR"));
516     const QStringList modulePath = m_vars->value("CMAKE_MODULE_PATH") + m_modulePath + m_vars->value("CMAKE_CURRENT_SOURCE_DIR");
517     kDebug(9042) << "Include:" << inc->includeFile() << "@" << modulePath << " into ";
518
519     QString possib=inc->includeFile();
520     QString path;
521     if(!KUrl(possib).isRelative() && QFile::exists(possib))
522         path=possib;
523     else
524     {
525         if(!possib.contains('.'))
526             possib += ".cmake";
527         path=findFile(possib, modulePath);
528     }
529
530     if(!path.isEmpty())
531     {
532         m_vars->insertMulti("CMAKE_CURRENT_LIST_FILE", QStringList(path));
533         CMakeFileContent include = CMakeListsParser::readCMakeFile(path);
534         if ( !include.isEmpty() )
535         {
536             kDebug(9042) << "including:" << path;
537             walk(include, 0, true);
538         }
539         else
540         {
541             //FIXME: Put here the error.
542             kDebug(9042) << "Include. Parsing error.";
543         }
544         m_vars->take("CMAKE_CURRENT_LIST_FILE");
545     }
546     else
547     {
548         if(!inc->optional())
549         {
550             kDebug(9032) << "error!! Could not find" << inc->includeFile() << "=" << possib << "into" << modulePath;
551         }
552     }
553
554     if(!inc->resultVariable().isEmpty())
555     {
556         QString result="NOTFOUND";
557         if(!path.isEmpty())
558             result=path;
559         m_vars->insert(inc->resultVariable(), QStringList(result));
560     }
561     kDebug(9042) << "include of" << inc->includeFile() << "done.";
562     return 1;
563 }
564
565 int CMakeProjectVisitor::visit(const FindPackageAst *pack)
566 {
567     if(!haveToFind(pack->name()))
568         return 1;
569     kDebug(9042) << "Find:" << pack->name() << "package." << m_modulePath << "No module: " << pack->noModule();
570
571     QStringList possibleModuleNames;
572     if(!pack->noModule()) //TODO Also implied by a whole slew of additional options.
573     {
574         // Look for a Find{package}.cmake
575         QString possib=pack->name();
576         if(!possib.endsWith(".cmake"))
577             possib += ".cmake";
578         possib.prepend("Find");
579         possibleModuleNames += possib;
580     }
581
582     QString var="CMAKE_INSTALL_PREFIX";
583     QString instPath;
584     if(m_vars->contains(var))
585         instPath = m_vars->value(var).join(QString());
586     else if(m_cache->contains(var))
587         instPath = m_cache->value(var).value;
588
589     kDebug(9042) << "config mode" << m_vars->value(var).join(QString()) << m_cache->value(var).value << instPath;
590
591     #if defined(Q_OS_WIN)
592     const QStringList modulePath = QStringList() << instPath + "/cmake" << instPath << m_vars->value("CMAKE_MODULE_PATH") + m_modulePath;
593     #else
594     const QStringList modulePath = m_vars->value("CMAKE_MODULE_PATH") + m_modulePath;
595     #endif
596     QString name=pack->name();
597     QStringList postfix=QStringList() << QString() << "/cmake" << "/CMake";
598     QStringList configPath;
599     foreach(const QString& post, postfix)
600     {
601         configPath.prepend(instPath+"/share/"+name.toLower()+post);
602         configPath.prepend(instPath+"/lib/"+name.toLower()+post);
603     }
604
605     QString varName=pack->name()+"_DIR";
606     if(m_cache->contains(varName))
607         configPath.prepend(m_cache->value(varName).value);
608
609     QStringList possibleConfigNames;
610     possibleConfigNames+=QString("%1Config.cmake").arg(pack->name());
611     possibleConfigNames+=QString("%1-config.cmake").arg(pack->name().toLower());
612
613     QString path;
614     foreach(const QString& possib, possibleModuleNames)
615     {
616         path=findFile(possib, modulePath);
617         if(!path.isEmpty()) {
618             break;
619         }
620     }
621     if (path.isEmpty()) {
622         foreach(const QString& possib, possibleConfigNames) {
623             path = findFile(possib, configPath);
624             if (!path.isEmpty()) {
625                 m_vars->insert(pack->name()+"_DIR", QStringList(KUrl(path).directory()));
626                 break;
627             }
628         }
629     }
630
631     if(!path.isEmpty())
632     {
633         m_vars->insertMulti("CMAKE_CURRENT_LIST_FILE", QStringList(path));
634         if(pack->isRequired())
635             m_vars->insert(pack->name()+"_FIND_REQUIRED", QStringList("TRUE"));
636         if(pack->isQuiet())
637             m_vars->insert(pack->name()+"_FIND_QUIET", QStringList("TRUE"));
638         CMakeFileContent package=CMakeListsParser::readCMakeFile( path );
639         if ( !package.isEmpty() )
640         {
641             path=KUrl(path).pathOrUrl();
642             kDebug(9042) << "================== Found" << path << "===============";
643             walk(package, 0, true);
644         }
645         else
646         {
647             kDebug(9032) << "error: find_package. Parsing error." << path;
648         }
649
650         if(pack->noModule())
651         {
652             m_vars->insert(QString("%1_CONFIG").arg(pack->name()), QStringList(path));
653         }
654         m_vars->take("CMAKE_CURRENT_LIST_FILE");
655     }
656     else
657     {
658         if(pack->isRequired()) {
659             //FIXME: Put here the error.
660             kDebug(9032) << "error: Could not find" << pack->name() << "into" << modulePath;
661         }
662         m_vars->insert(QString("%1_DIR").arg(pack->name()), QStringList(QString("%1_DIR-NOTFOUND").arg(pack->name())));
663     }
664     kDebug(9042) << "Exit. Found:" << pack->name() << m_vars->value(pack->name()+"_FOUND");
665
666     return 1;
667 }
668
669 KDevelop::ReferencedTopDUContext CMakeProjectVisitor::createContext(const KUrl& path, ReferencedTopDUContext aux,
670                                                                     int endl ,int endc, bool isClean)
671 {
672     DUChainWriteLocker lock(DUChain::lock());
673     KDevelop::ReferencedTopDUContext topctx=DUChain::self()->chainForDocument(path);
674     
675     if(topctx)
676     {
677         EditorIntegrator editor;
678         editor.setCurrentUrl(topctx->url());
679         
680         SmartConverter converter(&editor);
681         converter.deconvertDUChain(topctx);
682         
683         if(isClean) {
684             topctx->deleteLocalDeclarations();
685             topctx->deleteChildContextsRecursively();
686             topctx->deleteUses();
687         }
688     }
689     else
690     {
691         IndexedString idxpath(path);
692         topctx=new TopDUContext(idxpath, SimpleRange(0,0, endl, endc),
693                                 new ParsingEnvironmentFile(idxpath));
694         DUChain::self()->addDocumentChain(topctx);
695
696         Q_ASSERT(DUChain::self()->chainForDocument(path));
697     }
698     
699     //Clean the re-used top-context. This is problematic since it may affect independent projects, but it's better then letting things accumulate.
700     ///@todo This is problematic when the same file is used from within multiple CMakeLists.txts,
701     ///      for example a standard import like FindKDE4.cmake, because it creates a cross-dependency
702     ///      between the topducontext's of independent projects, like for example kdebase and kdevplatform
703     ///@todo Solve that by creating unique versions of all used top-context on a per-project basis using ParsingEnvironmentFile for disambiguation.
704     
705     foreach(DUContext* importer, topctx->importers())
706         importer->removeImportedParentContext(topctx);
707     topctx->clearImportedParentContexts();
708     
709     topctx->addImportedParentContext(aux);
710
711     /// @todo should we check for NULL or assert?
712     if (aux)
713       aux->addImportedParentContext(topctx);
714     
715     return topctx;
716 }
717
718 bool CMakeProjectVisitor::haveToFind(const QString &varName)
719 {
720     if(m_vars->contains(varName+"_FOUND"))
721         return false;
722     else if(m_vars->contains(varName+"-NOTFOUND"))
723         m_vars->remove(varName+"-NOTFOUND");
724     return true;
725 }
726
727 int CMakeProjectVisitor::visit(const FindProgramAst *fprog)
728 {
729     if(!haveToFind(fprog->variableName()))
730         return 1;
731     if(m_cache->contains(fprog->variableName()))
732     {
733         kDebug(9042) << "FindProgram: cache" << fprog->variableName() << m_cache->value(fprog->variableName()).value;
734         m_vars->insert(fprog->variableName(), m_cache->value(fprog->variableName()).value.split(';'));
735         return 1;
736     }
737
738     QStringList modulePath = fprog->path();
739 #ifdef Q_OS_WIN
740     if(!fprog->noSystemEnvironmentPath() && !fprog->noDefaultPath())
741         modulePath += envVarDirectories("Path");
742     kDebug() << "added Path env for program finding" << envVarDirectories("Path");
743 #else
744     if(!fprog->noSystemEnvironmentPath() && !fprog->noDefaultPath())
745         modulePath += envVarDirectories("PATH");
746 #endif
747     kDebug(9042) << "Find:" << fprog->variableName() << fprog->filenames() << "program into" << modulePath<<":"<< fprog->path();
748     QString path;
749     foreach(const QString& filename, fprog->filenames())
750     {
751         path=findExecutable(filename, modulePath, fprog->pathSuffixes());
752         if(!path.isEmpty())
753             break;
754     }
755
756     if(!path.isEmpty())
757         m_vars->insert(fprog->variableName(), QStringList(path));
758     else
759         m_vars->insert(fprog->variableName()+"-NOTFOUND", QStringList());
760
761     kDebug(9042) << "FindProgram:" << fprog->variableName() << "=" << m_vars->value(fprog->variableName()) << modulePath;
762     return 1;
763 }
764
765 QString CMakeProjectVisitor::findExecutable(const QString& file,
766                 const QStringList& directories, const QStringList& pathSuffixes) const
767 {
768     QString path;
769     QStringList suffixes=m_vars->value("CMAKE_EXECUTABLE_SUFFIX");
770     suffixes.prepend(QString());
771     kDebug() << "finding executable, using suffixes" << suffixes;
772
773     foreach(const QString& suffix, suffixes)
774     {
775         path=findFile(file+suffix, directories, pathSuffixes);
776         if(!path.isEmpty())
777             break;
778     }
779     return path;
780 }
781
782 int CMakeProjectVisitor::visit(const FindPathAst *fpath)
783 {
784     if(!haveToFind(fpath->variableName()))
785         return 1;
786     if(m_cache->contains(fpath->variableName()))
787     {
788         kDebug() << "FindPath: cache" << fpath->variableName();
789         m_vars->insert(fpath->variableName(), m_cache->value(fpath->variableName()).value.split(';'));
790         return 1;
791     }
792
793     bool error=false;
794     QStringList locationOptions = fpath->path()+fpath->hints();
795     QStringList path, files=fpath->filenames();
796     QStringList suffixes=fpath->pathSuffixes();
797
798     if(!fpath->noDefaultPath())
799     {
800         QStringList pp=m_vars->value("CMAKE_PREFIX_PATH");
801         foreach(const QString& path, pp) {
802             locationOptions += path+"/include";
803         }
804         locationOptions += pp;
805         locationOptions += m_vars->value("CMAKE_INCLUDE_PATH");
806         locationOptions += m_vars->value("CMAKE_FRAMEWORK_PATH");
807         
808         pp=m_vars->value("CMAKE_SYSTEM_PREFIX_PATH");
809         foreach(const QString& path, pp) {
810             locationOptions += path+"/include";
811         }
812         locationOptions += m_vars->value("CMAKE_SYSTEM_INCLUDE_PATH");
813         locationOptions += m_vars->value("CMAKE_SYSTEM_FRAMEWORK_PATH");
814     }
815
816     kDebug(9042) << "Find:" << /*locationOptions << "@" <<*/ fpath->variableName() << /*"=" << files <<*/ " path.";
817     foreach(const QString& p, files)
818     {
819         QString p1=findFile(p, locationOptions, suffixes, true);
820         if(p1.isEmpty())
821         {
822             kDebug(9042) << p << "not found";
823             error=true;
824         }
825         else
826         {
827             path += p1;
828         }
829     }
830
831     if(!path.isEmpty())
832     {
833         m_vars->insert(fpath->variableName(), QStringList(path));
834     }
835     else
836     {
837         kDebug(9042) << "Path not found";
838     }
839     kDebug(9042) << "Find path: " << fpath->variableName() << m_vars->value(fpath->variableName());
840 //     m_vars->insert(fpath->variableName()+"-NOTFOUND", QStringList());
841     return 1;
842 }
843
844 int CMakeProjectVisitor::visit(const FindLibraryAst *flib)
845 {
846     if(!haveToFind(flib->variableName()))
847         return 1;
848     if(m_cache->contains(flib->variableName()))
849     {
850         kDebug(9042) << "FindLibrary: cache" << flib->variableName();
851         m_vars->insert(flib->variableName(), m_cache->value(flib->variableName()).value.split(';'));
852         return 1;
853     }
854
855     bool error=false;
856     QStringList locationOptions = flib->path()+flib->hints();
857     QStringList files=flib->filenames();
858     QString path;
859
860     if(!flib->noDefaultPath())
861     {
862
863         QStringList opt=m_vars->value("CMAKE_PREFIX_PATH");
864         foreach(const QString& s, opt)
865             locationOptions.append(s+"/lib");
866
867         locationOptions += m_vars->value("CMAKE_LIBRARY_PATH");
868         locationOptions += m_vars->value("CMAKE_FRAMEWORK_PATH");
869         
870         locationOptions += m_vars->value("CMAKE_SYSTEM_LIBRARY_PATH");
871         
872         opt=m_vars->value("CMAKE_SYSTEM_PREFIX_PATH");
873         foreach(const QString& s, opt)
874             locationOptions.append(s+"/lib");
875     }
876     
877     foreach(const QString& p, files)
878     {
879         foreach(const QString& prefix, m_vars->value("CMAKE_FIND_LIBRARY_PREFIXES"))
880         {
881             foreach(const QString& suffix, m_vars->value("CMAKE_FIND_LIBRARY_SUFFIXES"))
882             {
883                 QString p1=findFile(prefix+p+suffix, locationOptions, flib->pathSuffixes());
884                 if(p1.isEmpty())
885                 {
886                     kDebug(9042) << p << "not found";
887                     error=true;
888                 }
889                 else
890                 {
891                     path = p1;
892                     break;
893                 }
894             }
895             if(!path.isEmpty())
896                 break;
897         }
898         if(!path.isEmpty())
899             break;
900     }
901
902     if(!path.isEmpty())
903     {
904         m_vars->insert(flib->variableName(), QStringList(path));
905     }
906     else
907         kDebug(9032) << "error. Library" << flib->filenames() << "not found";
908 //     m_vars->insert(fpath->variableName()+"-NOTFOUND", QStringList());
909     kDebug(9042) << "Find Library:" << flib->filenames() << m_vars->value(flib->variableName());
910     return 1;
911 }
912
913 int CMakeProjectVisitor::visit(const FindFileAst *ffile)
914 {
915     if(!haveToFind(ffile->variableName()))
916         return 1;
917     if(m_cache->contains(ffile->variableName()))
918     {
919         kDebug(9042) << "FindFile: cache" << ffile->variableName();
920         m_vars->insert(ffile->variableName(), m_cache->value(ffile->variableName()).value.split(';'));
921         return 1;
922     }
923
924     bool error=false;
925     QStringList locationOptions = ffile->path()+ffile->hints();
926     if(!ffile->noDefaultPath())
927     {
928         QStringList pp=m_vars->value("CMAKE_PREFIX_PATH");
929         foreach(const QString& path, pp) {
930             locationOptions += path+"/include";
931         }
932         locationOptions += pp;
933         locationOptions += m_vars->value("CMAKE_INCLUDE_PATH");
934         locationOptions += m_vars->value("CMAKE_FRAMEWORK_PATH");
935         
936         pp=m_vars->value("CMAKE_SYSTEM_PREFIX_PATH");
937         foreach(const QString& path, pp) {
938             locationOptions += path+"/include";
939         }
940         locationOptions += m_vars->value("CMAKE_SYSTEM_INCLUDE_PATH");
941         locationOptions += m_vars->value("CMAKE_SYSTEM_FRAMEWORK_PATH");
942     }
943     QStringList path, files=ffile->filenames();
944
945     kDebug(9042) << "Find File:" << ffile->filenames();
946     foreach(const QString& p, files)
947     {
948         QString p1=findFile(p, locationOptions, ffile->pathSuffixes());
949         if(p1.isEmpty())
950         {
951             kDebug(9042) << p << "not found";
952             error=true;
953         }
954         else
955         {
956             path += p1;
957         }
958     }
959
960     if(!path.isEmpty())
961     {
962         m_vars->insert(ffile->variableName(), QStringList(path));
963     }
964     else
965         kDebug(9032) << "error. File" << ffile->filenames() << "not found";
966 //     m_vars->insert(fpath->variableName()+"-NOTFOUND", QStringList());
967     return 1;
968 }
969
970
971 int CMakeProjectVisitor::visit(const TryCompileAst *tca)
972 {
973     kDebug(9042) << "try_compile" << tca->resultName() << tca->binDir() << tca->source()
974             << "cmakeflags" << tca->cmakeFlags() << "outputvar" << tca->outputName();
975     if(m_projectName.isEmpty())
976     {
977         kDebug(9042) << "file compile" << tca->compileDefinitions() << tca->copyFile();
978     }
979     else
980     {
981         kDebug(9042) << "project compile" << tca->projectName() << tca->targetName();
982     }
983     
984     QString value;
985     if(m_cache->contains(tca->resultName()))
986         value=m_cache->value(tca->resultName()).value;
987     else
988         value="TRUE";
989     
990     m_vars->insert(tca->resultName(), QStringList(value));
991     return 1;
992 }
993
994
995 int CMakeProjectVisitor::visit(const TargetLinkLibrariesAst *)
996 {
997     kDebug(9042) << "target_link_libraries";
998     return 1;
999 }
1000
1001 void CMakeProjectVisitor::macroDeclaration(const CMakeFunctionDesc& def, const CMakeFunctionDesc& end, const QStringList& args)
1002 {
1003     if(def.arguments.isEmpty() || end.arguments.isEmpty())
1004         return;
1005     QString id=def.arguments.first().value.toLower();
1006     
1007     DUChainWriteLocker lock(DUChain::lock());
1008     QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(id));
1009     SimpleRange sr=def.arguments.first().range();
1010     SimpleRange endsr=end.arguments.first().range();
1011     int idx;
1012     
1013     if(!decls.isEmpty())
1014     {
1015         idx=m_topctx->indexForUsedDeclaration(decls.first());
1016         m_topctx->createUse(idx, sr, 0);
1017     }
1018     else
1019     {
1020         Declaration *d = new Declaration(sr, m_topctx);
1021         d->setIdentifier( Identifier(id) );
1022
1023         FunctionType* func=new FunctionType();
1024         foreach(const QString& arg, args)
1025         {
1026             DelayedType *delayed=new DelayedType;
1027             delayed->setIdentifier( IndexedTypeIdentifier(arg) );
1028             func->addArgument(AbstractType::Ptr(delayed));
1029         }
1030         d->setAbstractType( AbstractType::Ptr(func) );
1031         idx=m_topctx->indexForUsedDeclaration(d);
1032     }
1033     m_topctx->createUse(idx, endsr, 0);
1034 }
1035
1036 int CMakeProjectVisitor::visit(const MacroAst *macro)
1037 {
1038     kDebug(9042) << "Adding macro:" << macro->macroName();
1039     Macro m;
1040     m.name = macro->macroName();
1041     m.knownArgs=macro->knownArgs();
1042     m.isFunction=false;
1043     
1044     return declareFunction(m, macro->content(), macro->line(), "endmacro");
1045 }
1046
1047 int CMakeProjectVisitor::visit(const FunctionAst *func)
1048 {
1049     kDebug(9042) << "Adding function:" << func->name();
1050     Macro m;
1051     m.name = func->name();
1052     m.knownArgs=func->knownArgs();
1053     m.isFunction=true;
1054     
1055     return declareFunction(m, func->content(), func->line(), "endfunction");
1056 }
1057
1058 int CMakeProjectVisitor::declareFunction(Macro m, const CMakeFileContent& content,
1059                                          int initial, const QString& end)
1060 {
1061     CMakeFileContent::const_iterator it=content.constBegin()+initial;
1062     CMakeFileContent::const_iterator itEnd=content.constEnd();
1063
1064     int lines=0;
1065     for(; it!=itEnd; ++it)
1066     {
1067         if(it->name.toLower()==end)
1068             break;
1069         m.code += *it;
1070         ++lines;
1071     }
1072     ++lines; //We do not want to return to endmacro
1073
1074     if(it!=itEnd)
1075     {
1076         m_macros->insert(m.name, m);
1077
1078         macroDeclaration(content[initial], content[initial+lines-1], m.knownArgs);
1079     }
1080     return lines;
1081 }
1082
1083 int CMakeProjectVisitor::visit(const MacroCallAst *call)
1084 {
1085     if(m_macros->contains(call->name()))
1086     {
1087         const Macro code=m_macros->value(call->name());
1088         kDebug(9042) << "Running macro:" << call->name() << "params:" << call->arguments() << "=" << code.knownArgs << "for" << code.code.count() << "lines";
1089         
1090         if(code.knownArgs.count() > call->arguments().count())
1091         {
1092             kDebug(9032) << "error: more parameters needed when calling" << call->name();
1093         }
1094         else
1095         {
1096             {
1097                 DUChainWriteLocker lock(DUChain::lock());
1098                 QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(call->name().toLower()));
1099
1100                 if(!decls.isEmpty())
1101                 {
1102                     int idx=m_topctx->indexForUsedDeclaration(decls.first());
1103                     m_topctx->createUse(idx, call->content()[call->line()].nameRange(), 0);
1104                 }
1105             }
1106
1107             //Giving value to parameters
1108             QStringList::const_iterator mit = code.knownArgs.constBegin();
1109             QStringList::const_iterator cit = call->arguments().constBegin();
1110             QStringList argn;
1111             bool haveArgn=false;
1112             int i=0;
1113             while(cit != call->arguments().constEnd())
1114             {
1115                 m_vars->insertMulti(QString("ARGV%1").arg(i), QStringList(*cit));
1116                 if(mit!=code.knownArgs.constEnd())
1117                 {
1118                     kDebug(9042) << "param:" << *mit << "=" << *cit;
1119                     m_vars->insertMulti(*mit, QStringList(*cit));
1120                     mit++;
1121                 }
1122                 else
1123                 {
1124                     haveArgn=true;
1125                     argn += *cit;
1126                 }
1127                 cit++;
1128                 i++;
1129             }
1130             m_vars->insertMulti("ARGN", argn);
1131             m_vars->insertMulti("ARGV", call->arguments());
1132             m_vars->insertMulti("ARGC", QStringList(QString::number(call->arguments().count())));
1133             kDebug(9042) << "argn=" << m_vars->value("ARGN");
1134
1135             //Executing
1136             int len = walk(code.code, 1);
1137             kDebug(9042) << "visited!" << call->name()  <<
1138                 m_vars->value("ARGV") << "_" << m_vars->value("ARGN") << "..." << len;
1139
1140             //Restoring
1141             i=1;
1142             foreach(const QString& name, code.knownArgs)
1143             {
1144                 m_vars->take(QString("ARGV%1").arg(i));
1145                 m_vars->take(name);
1146                 i++;
1147             }
1148
1149             m_vars->take("ARGV");
1150             m_vars->take("ARGC");
1151             m_vars->take("ARGN");
1152
1153         }
1154     }
1155     else
1156     {
1157         kDebug(9032) << "error: Did not find the macro:" << call->name() << call->content()[call->line()].writeBack();
1158     }
1159     
1160     return 1;
1161 }
1162
1163 void usesForArguments(const QStringList& names, const QList<int>& args, const ReferencedTopDUContext& topctx, const CMakeFunctionDesc& func)
1164 {
1165     //TODO: Should not return here
1166     if(args.size()!=names.size())
1167         return;
1168
1169     //We define the uses for the used variable without ${}
1170     foreach(int use, args)
1171     {
1172         DUChainWriteLocker lock(DUChain::lock());
1173         QString var=names[use];
1174         QList<Declaration*> decls=topctx->findDeclarations(Identifier(var));
1175
1176         if(!decls.isEmpty() && func.arguments.count() > use)
1177         {
1178             CMakeFunctionArgument arg=func.arguments[use];
1179             int idx=topctx->indexForUsedDeclaration(decls.first());
1180             topctx->createUse(idx, SimpleRange(arg.line-1, arg.column-1, arg.line-1, arg.column-1+var.size()), 0);
1181         }
1182     }
1183 }
1184
1185 int CMakeProjectVisitor::visit(const IfAst *ifast)  //Highly crappy code
1186 {
1187     int lines=ifast->line();
1188     if( ifast->condition().isEmpty() )
1189     {
1190         const CMakeFunctionDesc d = ifast->content().at( ifast->line() );
1191         kWarning() << "Parser couldn't parse condition of an IF in file:" << ifast->condition() << d.filePath << d.line;
1192     }
1193
1194     int inside=0;
1195 //     kDebug(9042) << "if() was false, looking for an else/elseif @" << lines;
1196     CMakeFileContent::const_iterator it=ifast->content().constBegin()+lines;
1197     CMakeFileContent::const_iterator itEnd=ifast->content().constEnd();
1198
1199     bool visited=false;
1200     QList<int> ini;
1201     for(; it!=itEnd; ++it, lines++)
1202     {
1203         QString funcName=it->name.toLower();
1204 //         kDebug(9032) << "looking @" << lines << it->writeBack() << ">>" << inside << visited;
1205         if(funcName=="if")
1206         {
1207             inside++;
1208         }
1209         else if(funcName=="endif")
1210         {
1211             inside--; 
1212             if(inside<=0) {
1213 //                 Q_ASSERT(!ini.isEmpty());
1214                 if(!it->arguments.isEmpty())
1215                     usesForArguments(ifast->condition(), ini, m_topctx, *it);
1216                 break;
1217             }
1218 //                 kDebug(9042) << "found an endif at:" << lines << "but" << inside;
1219         }
1220
1221         if(inside==1)
1222         {
1223             bool result = false;
1224
1225             if(funcName=="if" || funcName=="elseif")
1226             {
1227                 CMakeCondition cond(this);
1228                 IfAst myIf;
1229                 QStringList condition;
1230
1231                 if(funcName=="if")
1232                 {
1233                     condition=ifast->condition();
1234                 }
1235                 else
1236                 {
1237                     if(!myIf.parseFunctionInfo(resolveVariables(*it)))
1238                         kDebug(9042) << "uncorrect condition correct" << it->writeBack();
1239                     condition=myIf.condition();
1240                 }
1241                 result=cond.condition(condition);
1242                 if(funcName=="if")
1243                     ini=cond.variableArguments();
1244
1245                 usesForArguments(condition, cond.variableArguments(), m_topctx, *it);
1246                 kDebug(9042) << ">> " << funcName << condition << result;
1247             }
1248             else if(funcName=="else")
1249             {
1250                 kDebug(9042) << ">> else";
1251                 result=true;
1252                 usesForArguments(ifast->condition(), ini, m_topctx, *it);
1253             }
1254
1255             if(!visited && result)
1256             {
1257                 kDebug(9042) << "About to visit " << funcName << "?" << result;
1258
1259                 int oldpos=lines;
1260                 lines = walk(ifast->content(), lines+1)-1;
1261
1262                 it+=lines-oldpos;
1263
1264                 visited=true;
1265 //                 kDebug(9042) << "Visited. now in" << it->name;
1266             }
1267         }
1268     }
1269
1270     if(it==itEnd)
1271     {
1272         kDebug() << "error. found an unfinished endif";
1273         return ifast->content().size()-ifast->line();
1274     }
1275     else
1276     {
1277 //     kDebug() << "finish" << "<>" << ifast->condition() << '|' << lines-ifast->line() << " to " << lines << '<' << ifast->content().size();
1278 //     kDebug(9042) << "endif==" << ifast->content()[lines].writeBack();
1279         return lines-ifast->line()+1;
1280     }
1281 }
1282
1283 int CMakeProjectVisitor::visit(const ExecProgramAst *exec)
1284 {
1285     QString execName = exec->executableName();
1286     QStringList argsTemp = exec->arguments();
1287     QStringList args;
1288
1289     foreach(const QString& arg, argsTemp)
1290     {
1291         args += arg.split(' ');
1292     }
1293     kDebug(9042) << "Executing:" << execName << "::" << args << "in" << exec->workingDirectory();
1294
1295     KProcess p;
1296     if(!exec->workingDirectory().isEmpty())
1297         p.setWorkingDirectory(exec->workingDirectory());
1298     p.setOutputChannelMode(KProcess::MergedChannels);
1299     p.setProgram(execName, args);
1300     p.start();
1301
1302     if(!p.waitForFinished())
1303     {
1304         kDebug(9032) << "error: failed to execute:" << execName << "error:" << p.error() << p.exitCode();
1305     }
1306
1307     if(!exec->returnValue().isEmpty())
1308     {
1309         kDebug(9042) << "execution returned: " << exec->returnValue() << " = " << p.exitCode();
1310         m_vars->insert(exec->returnValue(), QStringList(QString::number(p.exitCode())));
1311     }
1312
1313     if(!exec->outputVariable().isEmpty())
1314     {
1315         QByteArray b = p.readAllStandardOutput();
1316         QString t;
1317         t.prepend(b.trimmed());
1318         m_vars->insert(exec->outputVariable(), QStringList(t.trimmed()));
1319         kDebug(9042) << "executed" << execName << "<" << t;
1320     }
1321     return 1;
1322 }
1323
1324 int CMakeProjectVisitor::visit(const ExecuteProcessAst *exec)
1325 {
1326     kDebug(9042) << "executing... " << exec->commands();
1327     QList<KProcess*> procs;
1328     foreach(const QStringList& _args, exec->commands())
1329     {
1330         if (_args.isEmpty())
1331         {
1332             kDebug(9032) << "Error: trying to execute empty command";
1333             break;
1334         }
1335         QStringList args(_args);
1336         KProcess *p=new KProcess(), *prev=0;
1337         if(!procs.isEmpty())
1338         {
1339             prev=procs.last();
1340         }
1341         p->setWorkingDirectory(exec->workingDirectory());
1342         p->setOutputChannelMode(KProcess::MergedChannels);
1343         QString execName=args.takeFirst();
1344         p->setProgram(execName, args);
1345         p->start();
1346         procs.append(p);
1347         kDebug(9042) << "Executing:" << execName << "::" << args /*<< "into" << *m_vars*/;
1348
1349         if(prev)
1350         {
1351             prev->setStandardOutputProcess(p);
1352         }
1353     }
1354
1355     foreach(KProcess* p, procs)
1356     {
1357         if(!p->waitForFinished())
1358         {
1359             kDebug(9042) << "error: failed to execute:" << p;
1360         }
1361     }
1362     
1363     if(!procs.isEmpty() && !exec->resultVariable().isEmpty())
1364     {
1365         kDebug(9042) << "execution returned: " << exec->resultVariable() << " = " << procs.last()->exitCode();
1366         m_vars->insert(exec->resultVariable(), QStringList(QString::number(procs.last()->exitCode())));
1367     }
1368
1369     //FIXME: remove condition when filtering bad output
1370     if(!procs.isEmpty() && !exec->outputVariable().isEmpty())
1371     {
1372         QByteArray b = procs.last()->readAllStandardOutput();
1373         QString t;
1374         t.prepend(b.trimmed());
1375         m_vars->insert(exec->outputVariable(), QStringList(t.trimmed().replace("\\", "\\\\")));
1376         kDebug(9042) << "executed " << exec->outputVariable() << "=" << t;
1377     }
1378     qDeleteAll(procs);
1379     return 1;
1380 }
1381
1382
1383 int CMakeProjectVisitor::visit(const FileAst *file)
1384 {
1385     Q_ASSERT(m_vars->contains("CMAKE_CURRENT_SOURCE_DIR"));
1386     switch(file->type()) //TODO
1387     {
1388         case FileAst::Write:
1389             kDebug(9042) << "(ni) File write: " << file->path() << file->message();
1390             break;
1391         case FileAst::Append:
1392             kDebug(9042) << "(ni) File append: " << file->path() << file->message();
1393             break;
1394         case FileAst::Read:
1395         {
1396             KUrl filename=file->path();
1397             QFileInfo ifile(filename.toLocalFile());
1398             kDebug(9042) << "FileAst: reading " << file->path() << ifile.isFile();
1399             if(!ifile.isFile())
1400                 return 1;
1401             QFile f(filename.toLocalFile());
1402             if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
1403                 return 1;
1404             QString output;
1405             while (!f.atEnd()) {
1406                 QByteArray line = f.readLine();
1407                 output += line;
1408             }
1409             m_vars->insert(file->variable(), QStringList(output));
1410             kDebug(9042) << "FileAst: read ";
1411         }
1412             break;
1413         case FileAst::Glob: {
1414             QStringList matches;
1415             QString relative=file->path();
1416             foreach(const QString& glob, file->globbingExpressions())
1417             {
1418                 QStringList globs;
1419                 QString current;
1420                 if(KUrl::isRelativeUrl(glob)) {
1421                     KUrl urlGlob(glob);
1422                     current=urlGlob.upUrl().path();
1423                     
1424                     globs.append(urlGlob.fileName());
1425                 } else if(!relative.isEmpty()) {
1426                     current=relative;
1427                     globs.append(glob);
1428                 } else {
1429                     current=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
1430                     globs.append(glob);
1431                 }
1432                 
1433                 QDir d(current);
1434                 matches+=d.entryList(globs, QDir::NoDotAndDotDot | QDir::AllEntries);
1435             }
1436             m_vars->insert(file->variable(), matches);
1437             kDebug(9042) << "file glob" << file->path() << file->globbingExpressions() << matches;
1438         } break;
1439         case FileAst::GlobRecurse: {
1440             QString current;
1441             if(file->path().isEmpty())
1442                 current=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
1443             else
1444                 current=file->path();
1445             QQueue<QString> candidates;
1446             candidates.enqueue(current);
1447             QStringList directories;
1448             while(!candidates.isEmpty())
1449             {
1450                 QString dir=candidates.dequeue();
1451                 directories.append(dir);
1452                 QDir direc(dir);
1453                 candidates += direc.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
1454             }
1455
1456             QDir d(current);
1457             QStringList matches=d.entryList(file->globbingExpressions(), QDir::NoDotAndDotDot | QDir::AllEntries);
1458             m_vars->insert(file->variable(), matches);
1459             kDebug(9042) << "file glob_recurse" << file->path() << file->globbingExpressions() << matches;
1460         }   break;
1461         case FileAst::Remove:
1462         case FileAst::RemoveRecurse:
1463             kDebug(9042) << "warning. file-remove or remove_recurse. KDevelop won't remove anything.";
1464             break;
1465         case FileAst::MakeDirectory:
1466             kDebug(9042) << "warning. file-make_directory. KDevelop won't create anything.";
1467             break;
1468         case FileAst::RelativePath:
1469             m_vars->insert(file->variable(), QStringList(KUrl::relativePath(file->directory(), file->path())));
1470             kDebug(9042) << "file relative_path" << file->directory() << file->path();
1471             break;
1472         case FileAst::ToCmakePath:
1473 #ifdef Q_OS_WIN
1474             m_vars->insert(file->variable(), file->path().replace("\\", "/").split(';'));
1475 #else
1476             m_vars->insert(file->variable(), file->path().split(':'));
1477 #endif
1478             kDebug(9042) << "file TO_CMAKE_PATH variable:" << file->variable() << "="
1479                     << m_vars->value(file->variable()) << "path:" << file->path();
1480             break;
1481         case FileAst::ToNativePath:
1482             m_vars->insert(file->variable(), QStringList(file->path().replace('/', QDir::separator())));
1483             kDebug(9042) << "file TO_NATIVE_PATH variable:" << file->variable() << "="
1484                     << m_vars->value(file->variable()) << "path:" << file->path();
1485             break;
1486         default:
1487             kDebug(9032) << "error: not implemented. file:" << file->type() <<
1488                 "variable:" << file->variable() << "file:" << file->path() << file->content()[file->line()].arguments[0].value;
1489             break;
1490     }
1491     return 1;
1492 }
1493
1494 int CMakeProjectVisitor::visit(const MessageAst *msg)
1495 {
1496     s_msgcallback(msg->message().join(QString()));
1497     return 1;
1498 }
1499
1500 int CMakeProjectVisitor::visit(const MathAst *math)
1501 {
1502     QScriptEngine eng;
1503     QScriptValue result = eng.evaluate(math->expression());
1504
1505     if (result.isError())
1506     {
1507         kDebug(9032) << "error: found an error while calculating" << math->expression();
1508     }
1509     kDebug(9042) << "math. " << math->expression() << "=" << result.toString();
1510     m_vars->insert(math->outputVariable(), QStringList(result.toString()));
1511     return 1;
1512 }
1513
1514 int CMakeProjectVisitor::visit(const GetFilenameComponentAst *filecomp)
1515 {
1516     QString dir;
1517     Q_ASSERT(m_vars->contains("CMAKE_CURRENT_SOURCE_DIR"));
1518
1519     dir=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
1520     QFileInfo fi(dir, filecomp->fileName());
1521
1522     QString val;
1523     switch(filecomp->type())
1524     {
1525         case GetFilenameComponentAst::Path:
1526             val=fi.canonicalPath();
1527             break;
1528         case GetFilenameComponentAst::Absolute:
1529             val=fi.absoluteFilePath();
1530             break;
1531         case GetFilenameComponentAst::Name:
1532             val=fi.fileName();
1533             break;
1534         case GetFilenameComponentAst::Ext:
1535             val=fi.suffix();
1536             break;
1537         case GetFilenameComponentAst::NameWe:
1538             val=fi.fileName().left(fi.fileName().length()-fi.suffix().length()-1);
1539             break;
1540         case GetFilenameComponentAst::Program:
1541             kDebug(9042) << "error: filenamecopmonent PROGRAM not implemented"; //TODO: <<
1542             break;
1543     }
1544     m_vars->insert(filecomp->variableName(), QStringList(val));
1545     kDebug(9042) << "filename component" << filecomp->variableName() << "= "
1546             << filecomp->fileName() << "=" << val << endl;
1547     return 1;
1548 }
1549
1550 int CMakeProjectVisitor::visit(const GetSourceFilePropAst* prop)
1551 {
1552     kDebug(9042) << "not supported yet :::" << prop->variableName();
1553     m_vars->insert(prop->variableName(), QStringList());
1554     return 1;
1555 }
1556
1557 int CMakeProjectVisitor::visit(const OptionAst *opt)
1558 {
1559     kDebug(9042) << "option" << opt->variableName() << "-" << opt->description();
1560     if(!m_vars->contains(opt->variableName()))
1561     {
1562         m_vars->insert(opt->variableName(), QStringList(opt->defaultValue()));
1563     }
1564     return 1;
1565 }
1566
1567 int CMakeProjectVisitor::visit(const ListAst *list)
1568 {
1569     QString output = list->output();
1570     
1571     QStringList theList = m_vars->value(list->list());
1572     switch(list->type())
1573     {
1574         case ListAst::Length:
1575             m_vars->insert(output, QStringList(QString::number(theList.count())));
1576             kDebug(9042) << "List length" << m_vars->value(output);
1577             break;
1578         case ListAst::Get: {
1579             bool contains = m_vars->contains(list->list());
1580             QStringList indices;
1581             if(contains) {
1582                 foreach(int idx, list->index())
1583                 {
1584                     if(idx>=theList.count())
1585                         kDebug(9032) << "error! trying to GET an element that doesn't exist!" << idx;
1586                     else if(idx>=0)
1587                         indices += theList[idx];
1588                     else
1589                         indices += theList[theList.size()+idx];
1590                 }
1591             } else
1592                 indices += "NOTFOUND";
1593             m_vars->insert(output, indices);
1594             kDebug(9042) << "List: Get" << list->list() << theList << list->output() << list->index();
1595         }   break;
1596         case ListAst::Append:
1597             theList += list->elements();
1598             m_vars->insert(list->list(), theList);
1599             break;
1600         case ListAst::Find: {
1601             int idx=theList.indexOf(list->elements().first());
1602             
1603             m_vars->insert(list->output(), QStringList(QString::number(idx)));
1604             kDebug(9042) << "List: Find" << theList << list->output() << list->elements() << idx;
1605         }   break;
1606         case ListAst::Insert: {
1607             int p=list->index().first();
1608             foreach(const QString& elem, list->elements())
1609             {
1610                 theList.insert(p >=0 ? p : (theList.size()+p), elem);
1611                 p += p>=0? 1 : 0;
1612             }
1613             m_vars->insert(list->list(), theList);
1614         }   break;
1615         case ListAst::RemoveItem:
1616             kDebug(9042) << "list remove item: " << theList << list->elements();
1617             foreach(const QString& elem, list->elements())
1618             {
1619                 theList.removeAll(elem);
1620             }
1621
1622             m_vars->insert(list->list(), theList);
1623             break;
1624         case ListAst::RemoveAt: {
1625             QList<int> indices=list->index();
1626             qSort(indices);
1627             QList<int>::const_iterator it=indices.constEnd();
1628             kDebug(9042) << "list remove: " << theList << indices;
1629             do
1630             {
1631                 --it;
1632                 theList.removeAt(*it >= 0 ? *it : theList.size()+*it);
1633             } while(it!=indices.constBegin());
1634             m_vars->insert(list->list(), theList);
1635         }   break;
1636         case ListAst::Sort:
1637             qSort(theList);
1638             m_vars->insert(list->list(), theList);
1639             break;
1640         case ListAst::Reverse: {
1641             QStringList reversed;
1642             foreach(const QString& elem, theList)
1643                 reversed.prepend(elem);
1644             m_vars->insert(list->list(), reversed);
1645             }
1646             break;
1647         case ListAst::RemoveDuplicates: {
1648             QStringList noduplicates;
1649             foreach(const QString& elem, theList) {
1650                 if(!noduplicates.contains(elem))
1651                     noduplicates.append(elem);
1652             }
1653             m_vars->insert(list->list(), noduplicates);
1654         }   break;
1655     }
1656     kDebug(9042) << "List!!" << list->output() << '='<< m_vars->value(list->output()) << " -> " << m_vars->value(list->list());
1657     return 1;
1658 }
1659
1660 int CMakeProjectVisitor::visit(const ForeachAst *fea)
1661 {
1662     kDebug(9042) << "foreach>" << fea->loopVar() << "=" << fea->arguments() << "range=" << fea->range();
1663     int end = 1;
1664     if(fea->range())
1665     {
1666         if (fea->ranges().start < fea->ranges().stop)
1667         {
1668             for( int i = fea->ranges().start; i < fea->ranges().stop; i += fea->ranges().step )
1669             {
1670                 m_vars->insertMulti(fea->loopVar(), QStringList(QString::number(i)));
1671                 end=walk(fea->content(), fea->line()+1);
1672                 m_vars->take(fea->loopVar());
1673             }
1674         }
1675         else
1676         {
1677             // loop never runs, skip over to matching endforeach
1678
1679             // FIXME this code is duplicated from the non-range case.
1680             // It should be probably factored into a separate helper function.
1681
1682             int lines=fea->line()+1, depth=1;
1683             CMakeFileContent::const_iterator it=fea->content().constBegin()+lines;
1684             CMakeFileContent::const_iterator itEnd=fea->content().constEnd();
1685             for(; depth>0 && it!=itEnd; ++it, lines++)
1686             {
1687                 if(it->name.toLower()=="foreach")
1688                 {
1689                     depth++;
1690                 }
1691                 else if(it->name.toLower()=="endforeach")
1692                 {
1693                     depth--;
1694                 }
1695             }
1696             end=lines-1;
1697         }
1698     }
1699     else
1700     {
1701         //Looping in a list of values
1702         QStringList args=fea->arguments();
1703         if(args.count()==1 && args.first().isEmpty()) { //if the args are empty
1704             int lines=fea->line()+1, depth=1;
1705             CMakeFileContent::const_iterator it=fea->content().constBegin()+lines;
1706             CMakeFileContent::const_iterator itEnd=fea->content().constEnd();
1707             for(; depth>0 && it!=itEnd; ++it, lines++)
1708             {
1709                 if(it->name.toLower()=="foreach")
1710                 {
1711                     depth++;
1712                 }
1713                 else if(it->name.toLower()=="endforeach")
1714                 {
1715                     depth--;
1716                 }
1717             }
1718             end=lines-1;
1719         }
1720         else
1721         {
1722             foreach(const QString& s, args)
1723             {
1724                 m_vars->insert(fea->loopVar(), QStringList(s));
1725                 kDebug(9042) << "looping" << fea->loopVar() << "=" << m_vars->value(fea->loopVar());
1726                 end=walk(fea->content(), fea->line()+1);
1727             }
1728         }
1729     }
1730     kDebug(9042) << "EndForeach" << fea->loopVar();
1731     return end-fea->line()+1;
1732 }
1733
1734 int CMakeProjectVisitor::visit(const StringAst *sast)
1735 {
1736     kDebug(9042) << "String to" /*<< sast->input()*/ << sast->outputVariable();
1737     switch(sast->type())
1738     {
1739         case StringAst::Regex:
1740         {
1741             QStringList res;
1742             QRegExp rx(sast->regex());
1743             rx.setPatternSyntax(QRegExp::RegExp2);
1744             QString totalInput = sast->input().join(QString());
1745             switch(sast->cmdType())
1746             {
1747                 case StringAst::Match:
1748                 {
1749                     int match=rx.indexIn(totalInput);
1750                     if(match>=0) {
1751                         res = QStringList(totalInput.mid(match, rx.matchedLength()));
1752                         break;
1753                     }
1754                     break;
1755                 }
1756                 case StringAst::MatchAll:
1757                 {
1758                     int pos = 0;
1759                     while( (pos = rx.indexIn( totalInput, pos ) ) != -1 )
1760                     {
1761                         res << rx.cap();
1762                         pos += rx.matchedLength();
1763                     }
1764                     break;
1765                 }
1766                 case StringAst::RegexReplace:
1767                 {
1768                     foreach(const QString& in, sast->input())
1769                     {
1770                         // QString() is required to get rid of the const
1771                         res.append(QString(in).replace(rx, sast->replace()));
1772                     }
1773                 }
1774                     break;
1775                 default:
1776                     kDebug(9032) << "ERROR String: Not a regex. " << sast->cmdType();
1777                     break;
1778             }
1779             kDebug() << "regex" << sast->outputVariable() << "=" << sast->regex() << res;
1780             m_vars->insert(sast->outputVariable(), res);
1781         }
1782             break;
1783         case StringAst::Replace: {
1784             QStringList out;
1785             foreach(const QString& _in, sast->input())
1786             {
1787                 QString in(_in);
1788                 QString aux=in.replace(sast->regex(), sast->replace());
1789                 out += aux.split(';'); //FIXME: HUGE ugly hack
1790             }
1791             kDebug(9042) << "string REPLACE" << sast->input() << "=>" << out;
1792             m_vars->insert(sast->outputVariable(), out);
1793         }   break;
1794         case StringAst::Compare:
1795         {
1796             QString res;
1797             switch(sast->cmdType()){
1798                 case StringAst::Equal:
1799                 case StringAst::NotEqual:
1800                     if(sast->input()[0]==sast->input()[1] && sast->cmdType()==StringAst::Equal)
1801                         res = "TRUE";
1802                     else
1803                         res = "FALSE";
1804                     break;
1805                 case StringAst::Less:
1806                 case StringAst::Greater:
1807                     if(sast->input()[0]<sast->input()[1] && sast->cmdType()==StringAst::Less)
1808                         res = "TRUE";
1809                     else
1810                         res = "FALSE";
1811                     break;
1812                 default:
1813                     kDebug(9042) << "String: Not a compare. " << sast->cmdType();
1814             }
1815             m_vars->insert(sast->outputVariable(), QStringList(res));
1816         }
1817             break;
1818         case StringAst::Ascii: {
1819             QString res;
1820             foreach(const QString& ascii, sast->input())
1821             {
1822                 bool ok;
1823                 res += QChar(ascii.toInt(&ok, 10));
1824             }
1825
1826             m_vars->insert(sast->outputVariable(), QStringList(res));
1827         }   break;
1828         case StringAst::Configure:
1829             //This is not up to the cmake support
1830             kDebug(9032) << "warning! String configure is not supported!" << sast->content()[sast->line()].writeBack();
1831             break;
1832         case StringAst::ToUpper:
1833             m_vars->insert(sast->outputVariable(), QStringList(sast->input()[0].toUpper()));
1834             break;
1835         case StringAst::ToLower:
1836             m_vars->insert(sast->outputVariable(), QStringList(sast->input()[0].toLower()));
1837             break;
1838         case StringAst::Length:
1839             m_vars->insert(sast->outputVariable(), QStringList(QString::number(sast->input()[0].count())));
1840             break;
1841         case StringAst::Substring:
1842         {
1843             QString res=sast->input()[0];
1844             res=res.mid(sast->begin(), sast->length());
1845             m_vars->insert(sast->outputVariable(), QStringList(res));
1846         }   break;
1847         case StringAst::Strip:
1848             m_vars->insert(sast->outputVariable(), QStringList(CMakeFunctionArgument::unescapeValue( sast->string() )));
1849             break;
1850         case StringAst::Random: {
1851             QString alphabet=sast->string(), result;
1852             for(int i=0; i<sast->length(); i++)
1853             {
1854                 int randv=qrand() % alphabet.size();
1855                 result += alphabet[randv];
1856             }
1857             m_vars->insert(sast->outputVariable(), QStringList(result));
1858         }   break;
1859     }
1860     kDebug(9042) << "String " << m_vars->value(sast->outputVariable());
1861     return 1;
1862 }
1863
1864
1865 int CMakeProjectVisitor::visit(const GetCMakePropertyAst *past)
1866 {
1867     QStringList output;
1868     switch(past->type())
1869     {
1870         case GetCMakePropertyAst::Variables:
1871             kDebug(9042) << "get cmake prop: variables:" << m_vars->size();
1872             output = m_vars->keys();
1873             break;
1874         case GetCMakePropertyAst::CacheVariables:
1875             output = m_cache->keys();
1876             break;
1877         case GetCMakePropertyAst::Commands:      //FIXME: We do not have commands yet
1878             output = QStringList();
1879             break;
1880         case GetCMakePropertyAst::Macros:
1881             output = m_macros->keys();
1882             break;
1883     }
1884     m_vars->insert(past->variableName(), output);
1885     return 1;
1886 }
1887
1888 int CMakeProjectVisitor::visit(const CustomCommandAst *ccast)
1889 {
1890     kDebug(9042) << "CustomCommand" << ccast->outputs();
1891     if(ccast->isForTarget())
1892     {
1893         //TODO: implement me
1894     }
1895     else
1896     {
1897         foreach(const QString& out, ccast->outputs())
1898         {
1899             m_generatedFiles[out] = QStringList(ccast->mainDependency())/*+ccast->otherDependencies()*/;
1900             kDebug(9042) << "Have to generate:" << out << "with" << m_generatedFiles[out];
1901         }
1902     }
1903     return 1;
1904 }
1905
1906 int CMakeProjectVisitor::visit(const CustomTargetAst *ctar)
1907 {
1908     kDebug(9042) << "custom_target " << ctar->target() << ctar->dependencies() << ", " << ctar->commandArgs();
1909     kDebug(9042) << ctar->content()[ctar->line()].writeBack();
1910
1911     defineTarget(ctar->target(), ctar->dependencies(), Target::Custom);
1912     return 1;
1913 }
1914
1915 QPair<QString, QString> definition(const QString& param)
1916 {
1917     QPair<QString, QString> ret;
1918     if(!param.startsWith("-D"))
1919         return ret;
1920     int eq=param.indexOf('=', 2);
1921     ret.first=param.mid(2, eq-2);
1922     if(eq>0)
1923         ret.second=param.mid(eq+1);
1924     return ret;
1925 }
1926
1927 int CMakeProjectVisitor::visit(const AddDefinitionsAst *addDef)
1928 {
1929 //     kDebug(9042) << "Adding defs: " << addDef->definitions();
1930     foreach(const QString& def, addDef->definitions())
1931     {
1932         if(def.isEmpty())
1933             continue;
1934         QPair<QString, QString> definePair=definition(def);
1935         if(definePair.first.isEmpty())
1936             kDebug(9042) << "error: definition not matched" << def;
1937
1938         m_defs[definePair.first]=definePair.second;
1939         kDebug(9042) << "added definition" << definePair.first << "=" << definePair.second << " from " << def;
1940     }
1941     return 1;
1942 }
1943
1944 int CMakeProjectVisitor::visit(const RemoveDefinitionsAst *remDef)
1945 {
1946     foreach(const QString& def, remDef->definitions())
1947     {
1948         if(def.isEmpty())
1949             continue;
1950         QPair<QString, QString> definePair=definition(def);
1951         if(definePair.first.isEmpty())
1952             kDebug(9042) << "error: definition not matched" << def;
1953
1954         m_defs.remove(definePair.first);
1955         kDebug(9042) << "removed definition" << definePair.first << " from " << def;
1956     }
1957     return 1;
1958 }
1959
1960 int CMakeProjectVisitor::visit(const MarkAsAdvancedAst *maa)
1961 {
1962     kDebug(9042) << "Mark As Advanced" << maa->advancedVars();
1963     return 1;
1964 }
1965
1966 /// @todo Add support for platform-specific argument splitting
1967 /// (UNIX_COMMAND and WINDOWS_COMMAND) introduced in CMake 2.8.
1968 int CMakeProjectVisitor::visit( const SeparateArgumentsAst * separgs )
1969 {
1970     QString varName=separgs->variableName();
1971     QStringList res;
1972     foreach(const QString& value, m_vars->value(varName))
1973     {
1974         res += value.split(' ');
1975     }
1976     m_vars->insert(separgs->variableName(), res);
1977     return 1;
1978 }
1979
1980 int CMakeProjectVisitor::visit(const SetPropertyAst* setp)
1981 {
1982     kDebug() << "setprops" << setp->type() << setp->name() << setp->values();
1983     if(setp->type()==GlobalProperty)
1984         m_props[GlobalProperty][QString()][setp->name()]=setp->values();
1985     else
1986     {
1987         CategoryType& cm=m_props[setp->type()];
1988         foreach(const QString &it, setp->args())
1989             cm[it].insert(setp->name(), setp->values());
1990     }
1991     return 1;
1992 }
1993
1994 int CMakeProjectVisitor::visit(const GetPropertyAst* getp)
1995 {
1996     kDebug() << "getprops";
1997     QStringList retv;
1998     QString catn;
1999     if(getp->type()!=GlobalProperty)
2000     {
2001         catn=getp->typeName();
2002     }
2003     retv=m_props[getp->type()][catn][getp->name()];
2004     m_vars->insert(getp->outputVariable(), retv);
2005     return 1;
2006 }
2007
2008 int CMakeProjectVisitor::visit(const GetDirPropertyAst* getdp)
2009 {
2010     kDebug() << "getprops";
2011     QStringList retv;
2012     QString dir=getdp->directory();
2013     if(dir.isEmpty()) {
2014         dir=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").join(QString());
2015     } else if(KUrl::isRelativeUrl(dir)) {
2016         KUrl u(m_vars->value("CMAKE_CURRENT_SOURCE_DIR").join(QString()));
2017         u.addPath(dir);
2018         dir=u.path();
2019     }
2020     
2021     retv=m_props[DirectoryProperty][dir][getdp->propName()];
2022     m_vars->insert(getdp->outputVariable(), retv);
2023     
2024     return 1;
2025 }
2026
2027 int CMakeProjectVisitor::visit( const WhileAst * whileast)
2028 {
2029     CMakeCondition cond(this);
2030     bool result=cond.condition(whileast->condition());
2031     usesForArguments(whileast->condition(), cond.variableArguments(), m_topctx, whileast->content()[whileast->line()]);
2032
2033     kDebug(9042) << "Visiting While" << whileast->condition() << "?" << result;
2034     int end=whileast->line()+1;
2035     if(result)
2036     {
2037         walk(whileast->content(), whileast->line()+1);
2038         
2039         if(m_hitBreak) {
2040             kDebug() << "break found. leaving loop";
2041             m_hitBreak=false;
2042         } else
2043             walk(whileast->content(), whileast->line());
2044     }
2045     CMakeFileContent::const_iterator it=whileast->content().constBegin()+end;
2046     CMakeFileContent::const_iterator itEnd=whileast->content().constEnd();
2047     int lines=0, inside=1;
2048     for(; inside>0 && it!=itEnd; ++it, lines++)
2049     {
2050         QString funcName=it->name.toLower();
2051         if(funcName=="while")
2052             inside++;
2053         else if(funcName=="endwhile")
2054             inside--;
2055     }
2056
2057     if(it!=itEnd) {
2058         usesForArguments(whileast->condition(), cond.variableArguments(), m_topctx, *(it-1));
2059     }
2060     return lines;
2061 }
2062
2063 CMakeFunctionDesc CMakeProjectVisitor::resolveVariables(const CMakeFunctionDesc & exp)
2064 {
2065     CMakeFunctionDesc ret=exp;
2066     ret.arguments.clear();
2067
2068     foreach(const CMakeFunctionArgument &arg, exp.arguments)
2069     {
2070         if(arg.value.contains('$'))
2071             ret.addArguments(resolveVariable(arg));
2072         else
2073             ret.arguments.append(arg);
2074     }
2075
2076     return ret;
2077 }
2078
2079 enum RecursivityType { No, Yes, End, Break };
2080
2081 RecursivityType recursivity(const QString& functionName)
2082 {
2083     QString upperFunctioName=functionName.toUpper();
2084     if(upperFunctioName=="IF" || upperFunctioName=="WHILE" ||
2085        upperFunctioName=="FOREACH" || upperFunctioName=="MACRO")
2086         return Yes;
2087     else if(upperFunctioName=="ELSE" || upperFunctioName=="ELSEIF" || upperFunctioName.startsWith("END"))
2088         return End;
2089     else if(upperFunctioName=="BREAK")
2090         return Break;
2091     return No;
2092 }
2093
2094 int CMakeProjectVisitor::walk(const CMakeFileContent & fc, int line, bool isClean)
2095 {
2096     ReferencedTopDUContext aux=m_topctx;
2097     KUrl url(fc[0].filePath);
2098     
2099     if(!m_topctx || m_topctx->url().toUrl()!=url)
2100     {
2101         kDebug(9042) << "Creating a context for" << url;
2102         m_topctx=createContext(url, aux ? aux : m_parentCtx, fc.last().endLine-1, fc.last().endColumn-1, isClean);
2103         if(!aux)
2104             aux=m_topctx;
2105     }
2106     VisitorState p;
2107     p.code = &fc;
2108     p.context = m_topctx;
2109     p.line = line;
2110
2111     m_backtrace.push(p);
2112
2113     CMakeFileContent::const_iterator it=fc.constBegin()+line, itEnd=fc.constEnd();
2114     for(; it!=itEnd; )
2115     {
2116         Q_ASSERT( line<fc.count() );
2117         Q_ASSERT( line>=0 );
2118 //         kDebug(9042) << "@" << line;
2119
2120         Q_ASSERT( *it == fc[line] );
2121 //         kDebug(9042) << "At line" << line << "/" << fc.count();
2122
2123         RecursivityType r = recursivity(it->name);
2124         if(r==End || r==Break || m_hitBreak)
2125         {
2126 //             kDebug(9042) << "Found an end." << func.writeBack();
2127             m_backtrace.pop();
2128             m_topctx=aux;
2129             
2130             if(r==Break)
2131                 m_hitBreak=true;
2132             return line;
2133         }
2134         
2135         CMakeAst* element = AstFactory::self()->createAst(it->name);
2136
2137         if(!element)
2138             element = new MacroCallAst;
2139
2140         createUses(*it);
2141 //         kDebug(9042) << "resolving:" << it->writeBack();
2142             
2143         CMakeFunctionDesc func = resolveVariables(*it); //FIXME not correct in while case
2144         bool correct = element->parseFunctionInfo(func);
2145 //         kDebug(9042) << "resolved:" << func.writeBack() << correct;
2146         if(!correct)
2147         {
2148             kDebug(9042) << "error! found an error while processing" << func.writeBack() << "was" << it->writeBack() << endl
2149                 << " at" << func.filePath << ":" << func.line << endl;
2150             //FIXME: Should avoid to run?
2151         }
2152         
2153         if(element->isDeprecated()) {
2154             kDebug(9032) << "Warning: Using the function: " << func.name << " which is deprecated by cmake.";
2155             DUChainWriteLocker lock(DUChain::lock());
2156             KSharedPtr<Problem> p(new Problem);
2157             p->setDescription(i18n("%1 is a deprecated command and should not be used", func.name));
2158             p->setRange(it->nameRange());
2159             p->setFinalLocation(DocumentRange(url.prettyUrl(), KTextEditor::Range(it->nameRange().start.textCursor(), it->nameRange().end.textCursor())));
2160             m_topctx->addProblem(p);
2161         }
2162         element->setContent(fc, line);
2163
2164         createDefinitions(element);
2165
2166         m_vars->insert("CMAKE_CURRENT_LIST_LINE", QStringList(QString::number(it->line)));
2167         int lines=element->accept(this);
2168         line+=lines;
2169         m_backtrace.top().line = line;
2170         m_backtrace.top().context = m_topctx;
2171         delete element;
2172         
2173         if(line>fc.count()) {
2174             DUChainWriteLocker lock(DUChain::lock());
2175             KSharedPtr<Problem> p(new Problem);
2176             p->setDescription(i18n("Unfinished function. "));
2177             p->setRange(it->nameRange());
2178             p->setFinalLocation(DocumentRange(url.prettyUrl(), KTextEditor::Range(fc.first().range().start.textCursor(), fc.last().range().end.textCursor())));
2179             m_topctx->addProblem(p);
2180             
2181             break;
2182         }
2183         
2184         it+=lines;
2185     }
2186     m_backtrace.pop();
2187     m_topctx=aux;
2188     kDebug(9042) << "Walk stopped @" << line;
2189     return line;
2190 }
2191
2192 void CMakeProjectVisitor::createDefinitions(const CMakeAst *ast)
2193 {
2194     if(!m_topctx)
2195         return;
2196     DUChainWriteLocker lock(DUChain::lock());
2197     foreach(const CMakeFunctionArgument &arg, ast->outputArguments())
2198     {
2199         if(!arg.isCorrect())
2200             continue;
2201         QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(arg.value));
2202         if(decls.isEmpty())
2203         {
2204             Declaration *d = new Declaration(arg.range(), m_topctx);
2205             d->setIdentifier( Identifier(arg.value) );
2206         }
2207         else
2208         {
2209             int idx=m_topctx->indexForUsedDeclaration(decls.first());
2210             m_topctx->createUse(idx, arg.range(), 0);
2211         }
2212     }
2213 }
2214
2215 void CMakeProjectVisitor::createUses(const CMakeFunctionDesc& desc)
2216 {
2217     if(!m_topctx)
2218         return;
2219     DUChainWriteLocker lock(DUChain::lock());
2220     foreach(const CMakeFunctionArgument &arg, desc.arguments)
2221     {
2222         if(!arg.isCorrect() || !arg.value.contains('$'))
2223             continue;
2224
2225         QList<IntPair> var = parseArgument(arg.value);
2226         QList<IntPair>::const_iterator it, itEnd=var.constEnd();
2227         for(it=var.constBegin(); it!=itEnd; ++it)
2228         {
2229             QString var=arg.value.mid(it->first+1, it->second-it->first-1);
2230             QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(var));
2231             
2232             if(!decls.isEmpty())
2233             {
2234                 int idx=m_topctx->indexForUsedDeclaration(decls.first());
2235                 m_topctx->createUse(idx, SimpleRange(arg.line-1, arg.column+it->first, arg.line-1, arg.column+it->second-1), 0);
2236             }
2237         }
2238     }
2239 }
2240
2241 void CMakeProjectVisitor::setCacheValues( CacheValues* cache)
2242 {
2243     m_cache=cache;
2244 }
2245
2246 void CMakeProjectVisitor::setVariableMap(VariableMap * vars)
2247 {
2248     m_vars=vars;
2249 }
2250
2251 bool isGenerated(const QString& name)
2252 {
2253     return name.indexOf("#[")>=0;
2254 }
2255
2256 QStringList CMakeProjectVisitor::dependees(const QString& s) const
2257 {
2258     QStringList ret;
2259     if(isGenerated(s))
2260     {
2261         foreach(const QString& f, m_generatedFiles[s])
2262             ret += dependees(f);
2263     }
2264     else
2265     {
2266         ret += s;
2267     }
2268     return ret;
2269 }
2270
2271 QStringList CMakeProjectVisitor::resolveDependencies(const QStringList & files) const
2272 {
2273     QStringList ret;
2274     foreach(const QString& s, files)
2275     {
2276         if(isGenerated(s))
2277         {
2278             kDebug(9042) << "Generated:" << s;
2279             QStringList gen = dependees(s);
2280             
2281             foreach(const QString& file, gen)
2282             {
2283                 if(!ret.contains(file))
2284                     ret.append(file);
2285             }
2286         }
2287         else
2288         {
2289             ret.append(s);
2290         }
2291     }
2292     return ret;
2293 }
2294