comment for performance issues.
[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         m_vars->insertMulti("CMAKE_CURRENT_LIST_DIR", QStringList(KUrl(path).directory()));
534         CMakeFileContent include = CMakeListsParser::readCMakeFile(path);
535         if ( !include.isEmpty() )
536         {
537             kDebug(9042) << "including:" << path;
538             walk(include, 0, true);
539         }
540         else
541         {
542             //FIXME: Put here the error.
543             kDebug(9042) << "Include. Parsing error.";
544         }
545         m_vars->remove("CMAKE_CURRENT_LIST_FILE");
546         m_vars->remove("CMAKE_CURRENT_LIST_DIR");
547     }
548     else
549     {
550         if(!inc->optional())
551         {
552             kDebug(9032) << "error!! Could not find" << inc->includeFile() << "=" << possib << "into" << modulePath;
553         }
554     }
555
556     if(!inc->resultVariable().isEmpty())
557     {
558         QString result="NOTFOUND";
559         if(!path.isEmpty())
560             result=path;
561         m_vars->insert(inc->resultVariable(), QStringList(result));
562     }
563     kDebug(9042) << "include of" << inc->includeFile() << "done.";
564     return 1;
565 }
566
567 int CMakeProjectVisitor::visit(const FindPackageAst *pack)
568 {
569     if(!haveToFind(pack->name()))
570         return 1;
571     kDebug(9042) << "Find:" << pack->name() << "package." << m_modulePath << "No module: " << pack->noModule();
572
573     QStringList possibleModuleNames;
574     if(!pack->noModule()) //TODO Also implied by a whole slew of additional options.
575     {
576         // Look for a Find{package}.cmake
577         QString possib=pack->name();
578         if(!possib.endsWith(".cmake"))
579             possib += ".cmake";
580         possib.prepend("Find");
581         possibleModuleNames += possib;
582     }
583
584     QString var="CMAKE_INSTALL_PREFIX";
585     QString instPath;
586     if(m_vars->contains(var))
587         instPath = m_vars->value(var).join(QString());
588     else if(m_cache->contains(var))
589         instPath = m_cache->value(var).value;
590
591     kDebug(9042) << "config mode" << m_vars->value(var).join(QString()) << m_cache->value(var).value << instPath;
592
593     #if defined(Q_OS_WIN)
594     const QStringList modulePath = QStringList() << instPath + "/cmake" << instPath << m_vars->value("CMAKE_MODULE_PATH") + m_modulePath;
595     #else
596     const QStringList modulePath = m_vars->value("CMAKE_MODULE_PATH") + m_modulePath;
597     #endif
598     QString name=pack->name();
599     QStringList postfix=QStringList() << QString() << "/cmake" << "/CMake";
600     QStringList configPath;
601     foreach(const QString& post, postfix)
602     {
603         configPath.prepend(instPath+"/share/"+name.toLower()+post);
604         configPath.prepend(instPath+"/lib/"+name.toLower()+post);
605         configPath.prepend(instPath+"/share/"+name+post);
606         configPath.prepend(instPath+"/lib/"+name+post);
607     }
608
609     QString varName=pack->name()+"_DIR";
610     if(m_cache->contains(varName))
611         configPath.prepend(m_cache->value(varName).value);
612
613     QStringList possibleConfigNames;
614     possibleConfigNames+=QString("%1Config.cmake").arg(pack->name());
615     possibleConfigNames+=QString("%1-config.cmake").arg(pack->name().toLower());
616
617     QString path;
618     foreach(const QString& possib, possibleModuleNames)
619     {
620         path=findFile(possib, modulePath);
621         if(!path.isEmpty()) {
622             break;
623         }
624     }
625     if (path.isEmpty()) {
626         foreach(const QString& possib, possibleConfigNames) {
627             path = findFile(possib, configPath);
628             if (!path.isEmpty()) {
629                 m_vars->insert(pack->name()+"_DIR", QStringList(KUrl(path).directory()));
630                 break;
631             }
632         }
633     }
634
635     if(!path.isEmpty())
636     {
637         m_vars->insertMulti("CMAKE_CURRENT_LIST_FILE", QStringList(path));
638         m_vars->insertMulti("CMAKE_CURRENT_LIST_DIR", QStringList(KUrl(path).directory()));
639         if(pack->isRequired())
640             m_vars->insert(pack->name()+"_FIND_REQUIRED", QStringList("TRUE"));
641         if(pack->isQuiet())
642             m_vars->insert(pack->name()+"_FIND_QUIET", QStringList("TRUE"));
643         CMakeFileContent package=CMakeListsParser::readCMakeFile( path );
644         if ( !package.isEmpty() )
645         {
646             path=KUrl(path).pathOrUrl();
647             kDebug(9042) << "================== Found" << path << "===============";
648             walk(package, 0, true);
649         }
650         else
651         {
652             kDebug(9032) << "error: find_package. Parsing error." << path;
653         }
654
655         if(pack->noModule())
656         {
657             m_vars->insert(QString("%1_CONFIG").arg(pack->name()), QStringList(path));
658         }
659         m_vars->remove("CMAKE_CURRENT_LIST_FILE");
660         m_vars->remove("CMAKE_CURRENT_LIST_DIR");
661     }
662     else
663     {
664         if(pack->isRequired()) {
665             //FIXME: Put here the error.
666             kDebug(9032) << "error: Could not find" << pack->name() << "into" << modulePath;
667         }
668         m_vars->insert(QString("%1_DIR").arg(pack->name()), QStringList(QString("%1_DIR-NOTFOUND").arg(pack->name())));
669     }
670     kDebug(9042) << "Exit. Found:" << pack->name() << m_vars->value(pack->name()+"_FOUND");
671
672     return 1;
673 }
674
675 KDevelop::ReferencedTopDUContext CMakeProjectVisitor::createContext(const KUrl& path, ReferencedTopDUContext aux,
676                                                                     int endl ,int endc, bool isClean)
677 {
678     DUChainWriteLocker lock(DUChain::lock());
679     KDevelop::ReferencedTopDUContext topctx=DUChain::self()->chainForDocument(path);
680     
681     if(topctx)
682     {
683         EditorIntegrator editor;
684         editor.setCurrentUrl(topctx->url());
685         
686         SmartConverter converter(&editor);
687         converter.deconvertDUChain(topctx);
688         
689         if(isClean) {
690             topctx->deleteLocalDeclarations();
691             topctx->deleteChildContextsRecursively();
692             topctx->deleteUses();
693         }
694     }
695     else
696     {
697         IndexedString idxpath(path);
698         topctx=new TopDUContext(idxpath, SimpleRange(0,0, endl, endc),
699                                 new ParsingEnvironmentFile(idxpath));
700         DUChain::self()->addDocumentChain(topctx);
701
702         Q_ASSERT(DUChain::self()->chainForDocument(path));
703     }
704     
705     //Clean the re-used top-context. This is problematic since it may affect independent projects, but it's better then letting things accumulate.
706     ///@todo This is problematic when the same file is used from within multiple CMakeLists.txts,
707     ///      for example a standard import like FindKDE4.cmake, because it creates a cross-dependency
708     ///      between the topducontext's of independent projects, like for example kdebase and kdevplatform
709     ///@todo Solve that by creating unique versions of all used top-context on a per-project basis using ParsingEnvironmentFile for disambiguation.
710     
711     foreach(DUContext* importer, topctx->importers())
712         importer->removeImportedParentContext(topctx);
713     topctx->clearImportedParentContexts();
714     
715     topctx->addImportedParentContext(aux);
716
717     /// @todo should we check for NULL or assert?
718     if (aux)
719       aux->addImportedParentContext(topctx);
720     
721     return topctx;
722 }
723
724 bool CMakeProjectVisitor::haveToFind(const QString &varName)
725 {
726     if(m_vars->contains(varName+"_FOUND"))
727         return false;
728     else if(m_vars->contains(varName+"-NOTFOUND"))
729         m_vars->remove(varName+"-NOTFOUND");
730     return true;
731 }
732
733 int CMakeProjectVisitor::visit(const FindProgramAst *fprog)
734 {
735     if(!haveToFind(fprog->variableName()))
736         return 1;
737     if(m_cache->contains(fprog->variableName()))
738     {
739         kDebug(9042) << "FindProgram: cache" << fprog->variableName() << m_cache->value(fprog->variableName()).value;
740         m_vars->insert(fprog->variableName(), m_cache->value(fprog->variableName()).value.split(';'));
741         return 1;
742     }
743
744     QStringList modulePath = fprog->path();
745 #ifdef Q_OS_WIN
746     if(!fprog->noSystemEnvironmentPath() && !fprog->noDefaultPath())
747         modulePath += envVarDirectories("Path");
748     kDebug() << "added Path env for program finding" << envVarDirectories("Path");
749 #else
750     if(!fprog->noSystemEnvironmentPath() && !fprog->noDefaultPath())
751         modulePath += envVarDirectories("PATH");
752 #endif
753     kDebug(9042) << "Find:" << fprog->variableName() << fprog->filenames() << "program into" << modulePath<<":"<< fprog->path();
754     QString path;
755     foreach(const QString& filename, fprog->filenames())
756     {
757         path=findExecutable(filename, modulePath, fprog->pathSuffixes());
758         if(!path.isEmpty())
759             break;
760     }
761
762     if(!path.isEmpty())
763         m_vars->insert(fprog->variableName(), QStringList(path));
764     else
765         m_vars->insert(fprog->variableName()+"-NOTFOUND", QStringList());
766
767     kDebug(9042) << "FindProgram:" << fprog->variableName() << "=" << m_vars->value(fprog->variableName()) << modulePath;
768     return 1;
769 }
770
771 QString CMakeProjectVisitor::findExecutable(const QString& file,
772                 const QStringList& directories, const QStringList& pathSuffixes) const
773 {
774     QString path;
775     QStringList suffixes=m_vars->value("CMAKE_EXECUTABLE_SUFFIX");
776     suffixes.prepend(QString());
777     kDebug() << "finding executable, using suffixes" << suffixes;
778
779     foreach(const QString& suffix, suffixes)
780     {
781         path=findFile(file+suffix, directories, pathSuffixes);
782         if(!path.isEmpty())
783             break;
784     }
785     return path;
786 }
787
788 int CMakeProjectVisitor::visit(const FindPathAst *fpath)
789 {
790     if(!haveToFind(fpath->variableName()))
791         return 1;
792     if(m_cache->contains(fpath->variableName()))
793     {
794         kDebug() << "FindPath: cache" << fpath->variableName();
795         m_vars->insert(fpath->variableName(), m_cache->value(fpath->variableName()).value.split(';'));
796         return 1;
797     }
798
799     bool error=false;
800     QStringList locationOptions = fpath->path()+fpath->hints();
801     QStringList path, files=fpath->filenames();
802     QStringList suffixes=fpath->pathSuffixes();
803
804     if(!fpath->noDefaultPath())
805     {
806         QStringList pp=m_vars->value("CMAKE_PREFIX_PATH");
807         foreach(const QString& path, pp) {
808             locationOptions += path+"/include";
809         }
810         locationOptions += pp;
811         locationOptions += m_vars->value("CMAKE_INCLUDE_PATH");
812         locationOptions += m_vars->value("CMAKE_FRAMEWORK_PATH");
813         
814         pp=m_vars->value("CMAKE_SYSTEM_PREFIX_PATH");
815         foreach(const QString& path, pp) {
816             locationOptions += path+"/include";
817         }
818         locationOptions += m_vars->value("CMAKE_SYSTEM_INCLUDE_PATH");
819         locationOptions += m_vars->value("CMAKE_SYSTEM_FRAMEWORK_PATH");
820     }
821
822     kDebug(9042) << "Find:" << /*locationOptions << "@" <<*/ fpath->variableName() << /*"=" << files <<*/ " path.";
823     foreach(const QString& p, files)
824     {
825         QString p1=findFile(p, locationOptions, suffixes, true);
826         if(p1.isEmpty())
827         {
828             kDebug(9042) << p << "not found";
829             error=true;
830         }
831         else
832         {
833             path += p1;
834         }
835     }
836
837     if(!path.isEmpty())
838     {
839         m_vars->insert(fpath->variableName(), QStringList(path));
840     }
841     else
842     {
843         kDebug(9042) << "Path not found";
844     }
845     kDebug(9042) << "Find path: " << fpath->variableName() << m_vars->value(fpath->variableName());
846 //     m_vars->insert(fpath->variableName()+"-NOTFOUND", QStringList());
847     return 1;
848 }
849
850 int CMakeProjectVisitor::visit(const FindLibraryAst *flib)
851 {
852     if(!haveToFind(flib->variableName()))
853         return 1;
854     if(m_cache->contains(flib->variableName()))
855     {
856         kDebug(9042) << "FindLibrary: cache" << flib->variableName();
857         m_vars->insert(flib->variableName(), m_cache->value(flib->variableName()).value.split(';'));
858         return 1;
859     }
860
861     bool error=false;
862     QStringList locationOptions = flib->path()+flib->hints();
863     QStringList files=flib->filenames();
864     QString path;
865
866     if(!flib->noDefaultPath())
867     {
868
869         QStringList opt=m_vars->value("CMAKE_PREFIX_PATH");
870         foreach(const QString& s, opt)
871             locationOptions.append(s+"/lib");
872
873         locationOptions += m_vars->value("CMAKE_LIBRARY_PATH");
874         locationOptions += m_vars->value("CMAKE_FRAMEWORK_PATH");
875         
876         locationOptions += m_vars->value("CMAKE_SYSTEM_LIBRARY_PATH");
877         
878         opt=m_vars->value("CMAKE_SYSTEM_PREFIX_PATH");
879         foreach(const QString& s, opt)
880             locationOptions.append(s+"/lib");
881     }
882     
883     foreach(const QString& p, files)
884     {
885         foreach(const QString& prefix, m_vars->value("CMAKE_FIND_LIBRARY_PREFIXES"))
886         {
887             foreach(const QString& suffix, m_vars->value("CMAKE_FIND_LIBRARY_SUFFIXES"))
888             {
889                 QString p1=findFile(prefix+p+suffix, locationOptions, flib->pathSuffixes());
890                 if(p1.isEmpty())
891                 {
892                     kDebug(9042) << p << "not found";
893                     error=true;
894                 }
895                 else
896                 {
897                     path = p1;
898                     break;
899                 }
900             }
901             if(!path.isEmpty())
902                 break;
903         }
904         if(!path.isEmpty())
905             break;
906     }
907
908     if(!path.isEmpty())
909     {
910         m_vars->insert(flib->variableName(), QStringList(path));
911     }
912     else
913         kDebug(9032) << "error. Library" << flib->filenames() << "not found";
914 //     m_vars->insert(fpath->variableName()+"-NOTFOUND", QStringList());
915     kDebug(9042) << "Find Library:" << flib->filenames() << m_vars->value(flib->variableName());
916     return 1;
917 }
918
919 int CMakeProjectVisitor::visit(const FindFileAst *ffile)
920 {
921     if(!haveToFind(ffile->variableName()))
922         return 1;
923     if(m_cache->contains(ffile->variableName()))
924     {
925         kDebug(9042) << "FindFile: cache" << ffile->variableName();
926         m_vars->insert(ffile->variableName(), m_cache->value(ffile->variableName()).value.split(';'));
927         return 1;
928     }
929
930     bool error=false;
931     QStringList locationOptions = ffile->path()+ffile->hints();
932     if(!ffile->noDefaultPath())
933     {
934         QStringList pp=m_vars->value("CMAKE_PREFIX_PATH");
935         foreach(const QString& path, pp) {
936             locationOptions += path+"/include";
937         }
938         locationOptions += pp;
939         locationOptions += m_vars->value("CMAKE_INCLUDE_PATH");
940         locationOptions += m_vars->value("CMAKE_FRAMEWORK_PATH");
941         
942         pp=m_vars->value("CMAKE_SYSTEM_PREFIX_PATH");
943         foreach(const QString& path, pp) {
944             locationOptions += path+"/include";
945         }
946         locationOptions += m_vars->value("CMAKE_SYSTEM_INCLUDE_PATH");
947         locationOptions += m_vars->value("CMAKE_SYSTEM_FRAMEWORK_PATH");
948     }
949     QStringList path, files=ffile->filenames();
950
951     kDebug(9042) << "Find File:" << ffile->filenames();
952     foreach(const QString& p, files)
953     {
954         QString p1=findFile(p, locationOptions, ffile->pathSuffixes());
955         if(p1.isEmpty())
956         {
957             kDebug(9042) << p << "not found";
958             error=true;
959         }
960         else
961         {
962             path += p1;
963         }
964     }
965
966     if(!path.isEmpty())
967     {
968         m_vars->insert(ffile->variableName(), QStringList(path));
969     }
970     else
971         kDebug(9032) << "error. File" << ffile->filenames() << "not found";
972 //     m_vars->insert(fpath->variableName()+"-NOTFOUND", QStringList());
973     return 1;
974 }
975
976
977 int CMakeProjectVisitor::visit(const TryCompileAst *tca)
978 {
979     kDebug(9042) << "try_compile" << tca->resultName() << tca->binDir() << tca->source()
980             << "cmakeflags" << tca->cmakeFlags() << "outputvar" << tca->outputName();
981     if(m_projectName.isEmpty())
982     {
983         kDebug(9042) << "file compile" << tca->compileDefinitions() << tca->copyFile();
984     }
985     else
986     {
987         kDebug(9042) << "project compile" << tca->projectName() << tca->targetName();
988     }
989     
990     QString value;
991     if(m_cache->contains(tca->resultName()))
992         value=m_cache->value(tca->resultName()).value;
993     else
994         value="TRUE";
995     
996     m_vars->insert(tca->resultName(), QStringList(value));
997     return 1;
998 }
999
1000
1001 int CMakeProjectVisitor::visit(const TargetLinkLibrariesAst *)
1002 {
1003     kDebug(9042) << "target_link_libraries";
1004     return 1;
1005 }
1006
1007 void CMakeProjectVisitor::macroDeclaration(const CMakeFunctionDesc& def, const CMakeFunctionDesc& end, const QStringList& args)
1008 {
1009     if(def.arguments.isEmpty() || end.arguments.isEmpty())
1010         return;
1011     QString id=def.arguments.first().value.toLower();
1012     
1013     DUChainWriteLocker lock(DUChain::lock());
1014     QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(id));
1015     SimpleRange sr=def.arguments.first().range();
1016     SimpleRange endsr=end.arguments.first().range();
1017     int idx;
1018     
1019     if(!decls.isEmpty())
1020     {
1021         idx=m_topctx->indexForUsedDeclaration(decls.first());
1022         m_topctx->createUse(idx, sr, 0);
1023     }
1024     else
1025     {
1026         Declaration *d = new Declaration(sr, m_topctx);
1027         d->setIdentifier( Identifier(id) );
1028
1029         FunctionType* func=new FunctionType();
1030         foreach(const QString& arg, args)
1031         {
1032             DelayedType *delayed=new DelayedType;
1033             delayed->setIdentifier( IndexedTypeIdentifier(arg) );
1034             func->addArgument(AbstractType::Ptr(delayed));
1035         }
1036         d->setAbstractType( AbstractType::Ptr(func) );
1037         idx=m_topctx->indexForUsedDeclaration(d);
1038     }
1039     m_topctx->createUse(idx, endsr, 0);
1040 }
1041
1042 int CMakeProjectVisitor::visit(const MacroAst *macro)
1043 {
1044     kDebug(9042) << "Adding macro:" << macro->macroName();
1045     Macro m;
1046     m.name = macro->macroName();
1047     m.knownArgs=macro->knownArgs();
1048     m.isFunction=false;
1049     
1050     return declareFunction(m, macro->content(), macro->line(), "endmacro");
1051 }
1052
1053 int CMakeProjectVisitor::visit(const FunctionAst *func)
1054 {
1055     kDebug(9042) << "Adding function:" << func->name();
1056     Macro m;
1057     m.name = func->name();
1058     m.knownArgs=func->knownArgs();
1059     m.isFunction=true;
1060     
1061     return declareFunction(m, func->content(), func->line(), "endfunction");
1062 }
1063
1064 int CMakeProjectVisitor::declareFunction(Macro m, const CMakeFileContent& content,
1065                                          int initial, const QString& end)
1066 {
1067     CMakeFileContent::const_iterator it=content.constBegin()+initial;
1068     CMakeFileContent::const_iterator itEnd=content.constEnd();
1069
1070     int lines=0;
1071     for(; it!=itEnd; ++it)
1072     {
1073         if(it->name.toLower()==end)
1074             break;
1075         m.code += *it;
1076         ++lines;
1077     }
1078     ++lines; //We do not want to return to endmacro
1079
1080     if(it!=itEnd)
1081     {
1082         m_macros->insert(m.name, m);
1083
1084         macroDeclaration(content[initial], content[initial+lines-1], m.knownArgs);
1085     }
1086     return lines;
1087 }
1088
1089 int CMakeProjectVisitor::visit(const MacroCallAst *call)
1090 {
1091     if(m_macros->contains(call->name()))
1092     {
1093         const Macro code=m_macros->value(call->name());
1094         kDebug(9042) << "Running macro:" << call->name() << "params:" << call->arguments() << "=" << code.knownArgs << "for" << code.code.count() << "lines";
1095         
1096         if(code.knownArgs.count() > call->arguments().count())
1097         {
1098             kDebug(9032) << "error: more parameters needed when calling" << call->name();
1099         }
1100         else
1101         {
1102             {
1103                 DUChainWriteLocker lock(DUChain::lock());
1104                 QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(call->name().toLower()));
1105
1106                 if(!decls.isEmpty())
1107                 {
1108                     int idx=m_topctx->indexForUsedDeclaration(decls.first());
1109                     m_topctx->createUse(idx, call->content()[call->line()].nameRange(), 0);
1110                 }
1111             }
1112
1113             //Giving value to parameters
1114             QStringList::const_iterator mit = code.knownArgs.constBegin();
1115             QStringList::const_iterator cit = call->arguments().constBegin();
1116             QStringList argn;
1117             bool haveArgn=false;
1118             int i=0;
1119             while(cit != call->arguments().constEnd())
1120             {
1121                 m_vars->insertMulti(QString("ARGV%1").arg(i), QStringList(*cit));
1122                 if(mit!=code.knownArgs.constEnd())
1123                 {
1124                     kDebug(9042) << "param:" << *mit << "=" << *cit;
1125                     m_vars->insertMulti(*mit, QStringList(*cit));
1126                     mit++;
1127                 }
1128                 else
1129                 {
1130                     haveArgn=true;
1131                     argn += *cit;
1132                 }
1133                 cit++;
1134                 i++;
1135             }
1136             m_vars->insertMulti("ARGN", argn);
1137             m_vars->insertMulti("ARGV", call->arguments());
1138             m_vars->insertMulti("ARGC", QStringList(QString::number(call->arguments().count())));
1139             kDebug(9042) << "argn=" << m_vars->value("ARGN");
1140
1141             //Executing
1142             int len = walk(code.code, 1);
1143             kDebug(9042) << "visited!" << call->name()  <<
1144                 m_vars->value("ARGV") << "_" << m_vars->value("ARGN") << "..." << len;
1145
1146             //Restoring
1147             i=1;
1148             foreach(const QString& name, code.knownArgs)
1149             {
1150                 m_vars->take(QString("ARGV%1").arg(i));
1151                 m_vars->take(name);
1152                 i++;
1153             }
1154
1155             m_vars->take("ARGV");
1156             m_vars->take("ARGC");
1157             m_vars->take("ARGN");
1158
1159         }
1160     }
1161     else
1162     {
1163         kDebug(9032) << "error: Did not find the macro:" << call->name() << call->content()[call->line()].writeBack();
1164     }
1165     
1166     return 1;
1167 }
1168
1169 void usesForArguments(const QStringList& names, const QList<int>& args, const ReferencedTopDUContext& topctx, const CMakeFunctionDesc& func)
1170 {
1171     //TODO: Should not return here
1172     if(args.size()!=names.size())
1173         return;
1174
1175     //We define the uses for the used variable without ${}
1176     foreach(int use, args)
1177     {
1178         DUChainWriteLocker lock(DUChain::lock());
1179         QString var=names[use];
1180         QList<Declaration*> decls=topctx->findDeclarations(Identifier(var));
1181
1182         if(!decls.isEmpty() && func.arguments.count() > use)
1183         {
1184             CMakeFunctionArgument arg=func.arguments[use];
1185             int idx=topctx->indexForUsedDeclaration(decls.first());
1186             topctx->createUse(idx, SimpleRange(arg.line-1, arg.column-1, arg.line-1, arg.column-1+var.size()), 0);
1187         }
1188     }
1189 }
1190
1191 int CMakeProjectVisitor::visit(const IfAst *ifast)  //Highly crappy code
1192 {
1193     int lines=ifast->line();
1194     if( ifast->condition().isEmpty() )
1195     {
1196         const CMakeFunctionDesc d = ifast->content().at( ifast->line() );
1197         kWarning() << "Parser couldn't parse condition of an IF in file:" << ifast->condition() << d.filePath << d.line;
1198     }
1199
1200     int inside=0;
1201 //     kDebug(9042) << "if() was false, looking for an else/elseif @" << lines;
1202     CMakeFileContent::const_iterator it=ifast->content().constBegin()+lines;
1203     CMakeFileContent::const_iterator itEnd=ifast->content().constEnd();
1204
1205     bool visited=false;
1206     QList<int> ini;
1207     for(; it!=itEnd; ++it, lines++)
1208     {
1209         QString funcName=it->name.toLower();
1210 //         kDebug(9032) << "looking @" << lines << it->writeBack() << ">>" << inside << visited;
1211         if(funcName=="if")
1212         {
1213             inside++;
1214         }
1215         else if(funcName=="endif")
1216         {
1217             inside--; 
1218             if(inside<=0) {
1219 //                 Q_ASSERT(!ini.isEmpty());
1220                 if(!it->arguments.isEmpty())
1221                     usesForArguments(ifast->condition(), ini, m_topctx, *it);
1222                 break;
1223             }
1224 //                 kDebug(9042) << "found an endif at:" << lines << "but" << inside;
1225         }
1226
1227         if(inside==1)
1228         {
1229             bool result = false;
1230
1231             if(funcName=="if" || funcName=="elseif")
1232             {
1233                 CMakeCondition cond(this);
1234                 IfAst myIf;
1235                 QStringList condition;
1236
1237                 if(funcName=="if")
1238                 {
1239                     condition=ifast->condition();
1240                 }
1241                 else
1242                 {
1243                     if(!myIf.parseFunctionInfo(resolveVariables(*it)))
1244                         kDebug(9042) << "uncorrect condition correct" << it->writeBack();
1245                     condition=myIf.condition();
1246                 }
1247                 result=cond.condition(condition);
1248                 if(funcName=="if")
1249                     ini=cond.variableArguments();
1250
1251                 usesForArguments(condition, cond.variableArguments(), m_topctx, *it);
1252                 kDebug(9042) << ">> " << funcName << condition << result;
1253             }
1254             else if(funcName=="else")
1255             {
1256                 kDebug(9042) << ">> else";
1257                 result=true;
1258                 usesForArguments(ifast->condition(), ini, m_topctx, *it);
1259             }
1260
1261             if(!visited && result)
1262             {
1263                 kDebug(9042) << "About to visit " << funcName << "?" << result;
1264
1265                 int oldpos=lines;
1266                 lines = walk(ifast->content(), lines+1)-1;
1267
1268                 it+=lines-oldpos;
1269
1270                 visited=true;
1271 //                 kDebug(9042) << "Visited. now in" << it->name;
1272             }
1273         }
1274     }
1275
1276     if(it==itEnd)
1277     {
1278         kDebug() << "error. found an unfinished endif";
1279         return ifast->content().size()-ifast->line();
1280     }
1281     else
1282     {
1283 //     kDebug() << "finish" << "<>" << ifast->condition() << '|' << lines-ifast->line() << " to " << lines << '<' << ifast->content().size();
1284 //     kDebug(9042) << "endif==" << ifast->content()[lines].writeBack();
1285         return lines-ifast->line()+1;
1286     }
1287 }
1288
1289 int CMakeProjectVisitor::visit(const ExecProgramAst *exec)
1290 {
1291     QString execName = exec->executableName();
1292     QStringList argsTemp = exec->arguments();
1293     QStringList args;
1294
1295     foreach(const QString& arg, argsTemp)
1296     {
1297         args += arg.split(' ');
1298     }
1299     kDebug(9042) << "Executing:" << execName << "::" << args << "in" << exec->workingDirectory();
1300
1301     KProcess p;
1302     if(!exec->workingDirectory().isEmpty())
1303         p.setWorkingDirectory(exec->workingDirectory());
1304     p.setOutputChannelMode(KProcess::MergedChannels);
1305     p.setProgram(execName, args);
1306     p.start();
1307
1308     if(!p.waitForFinished())
1309     {
1310         kDebug(9032) << "error: failed to execute:" << execName << "error:" << p.error() << p.exitCode();
1311     }
1312
1313     if(!exec->returnValue().isEmpty())
1314     {
1315         kDebug(9042) << "execution returned: " << exec->returnValue() << " = " << p.exitCode();
1316         m_vars->insert(exec->returnValue(), QStringList(QString::number(p.exitCode())));
1317     }
1318
1319     if(!exec->outputVariable().isEmpty())
1320     {
1321         QByteArray b = p.readAllStandardOutput();
1322         QString t;
1323         t.prepend(b.trimmed());
1324         m_vars->insert(exec->outputVariable(), QStringList(t.trimmed()));
1325         kDebug(9042) << "executed" << execName << "<" << t;
1326     }
1327     return 1;
1328 }
1329
1330 int CMakeProjectVisitor::visit(const ExecuteProcessAst *exec)
1331 {
1332     kDebug(9042) << "executing... " << exec->commands();
1333     QList<KProcess*> procs;
1334     foreach(const QStringList& _args, exec->commands())
1335     {
1336         if (_args.isEmpty())
1337         {
1338             kDebug(9032) << "Error: trying to execute empty command";
1339             break;
1340         }
1341         QStringList args(_args);
1342         KProcess *p=new KProcess(), *prev=0;
1343         if(!procs.isEmpty())
1344         {
1345             prev=procs.last();
1346         }
1347         p->setWorkingDirectory(exec->workingDirectory());
1348         p->setOutputChannelMode(KProcess::MergedChannels);
1349         QString execName=args.takeFirst();
1350         p->setProgram(execName, args);
1351         p->start();
1352         procs.append(p);
1353         kDebug(9042) << "Executing:" << execName << "::" << args /*<< "into" << *m_vars*/;
1354
1355         if(prev)
1356         {
1357             prev->setStandardOutputProcess(p);
1358         }
1359     }
1360
1361     foreach(KProcess* p, procs)
1362     {
1363         if(!p->waitForFinished())
1364         {
1365             kDebug(9042) << "error: failed to execute:" << p;
1366         }
1367     }
1368     
1369     if(!procs.isEmpty() && !exec->resultVariable().isEmpty())
1370     {
1371         kDebug(9042) << "execution returned: " << exec->resultVariable() << " = " << procs.last()->exitCode();
1372         m_vars->insert(exec->resultVariable(), QStringList(QString::number(procs.last()->exitCode())));
1373     }
1374
1375     //FIXME: remove condition when filtering bad output
1376     if(!procs.isEmpty() && !exec->outputVariable().isEmpty())
1377     {
1378         QByteArray b = procs.last()->readAllStandardOutput();
1379         QString t;
1380         t.prepend(b.trimmed());
1381         m_vars->insert(exec->outputVariable(), QStringList(t.trimmed().replace("\\", "\\\\")));
1382         kDebug(9042) << "executed " << exec->outputVariable() << "=" << t;
1383     }
1384     qDeleteAll(procs);
1385     return 1;
1386 }
1387
1388
1389 int CMakeProjectVisitor::visit(const FileAst *file)
1390 {
1391     Q_ASSERT(m_vars->contains("CMAKE_CURRENT_SOURCE_DIR"));
1392     switch(file->type()) //TODO
1393     {
1394         case FileAst::Write:
1395             kDebug(9042) << "(ni) File write: " << file->path() << file->message();
1396             break;
1397         case FileAst::Append:
1398             kDebug(9042) << "(ni) File append: " << file->path() << file->message();
1399             break;
1400         case FileAst::Read:
1401         {
1402             KUrl filename=file->path();
1403             QFileInfo ifile(filename.toLocalFile());
1404             kDebug(9042) << "FileAst: reading " << file->path() << ifile.isFile();
1405             if(!ifile.isFile())
1406                 return 1;
1407             QFile f(filename.toLocalFile());
1408             if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
1409                 return 1;
1410             QString output;
1411             while (!f.atEnd()) {
1412                 QByteArray line = f.readLine();
1413                 output += line;
1414             }
1415             m_vars->insert(file->variable(), QStringList(output));
1416             kDebug(9042) << "FileAst: read ";
1417         }
1418             break;
1419         case FileAst::Glob: {
1420             QStringList matches;
1421             QString relative=file->path();
1422             foreach(const QString& glob, file->globbingExpressions())
1423             {
1424                 QStringList globs;
1425                 QString current;
1426                 if(KUrl::isRelativeUrl(glob)) {
1427                     KUrl urlGlob(glob);
1428                     current=urlGlob.upUrl().path();
1429                     
1430                     globs.append(urlGlob.fileName());
1431                 } else if(!relative.isEmpty()) {
1432                     current=relative;
1433                     globs.append(glob);
1434                 } else {
1435                     current=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
1436                     globs.append(glob);
1437                 }
1438                 
1439                 QDir d(current);
1440                 matches+=d.entryList(globs, QDir::NoDotAndDotDot | QDir::AllEntries);
1441             }
1442             m_vars->insert(file->variable(), matches);
1443             kDebug(9042) << "file glob" << file->path() << file->globbingExpressions() << matches;
1444         } break;
1445         case FileAst::GlobRecurse: {
1446             QString current;
1447             if(file->path().isEmpty())
1448                 current=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
1449             else
1450                 current=file->path();
1451             QQueue<QString> candidates;
1452             candidates.enqueue(current);
1453             QStringList directories;
1454             while(!candidates.isEmpty())
1455             {
1456                 QString dir=candidates.dequeue();
1457                 directories.append(dir);
1458                 QDir direc(dir);
1459                 candidates += direc.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
1460             }
1461
1462             QDir d(current);
1463             QStringList matches=d.entryList(file->globbingExpressions(), QDir::NoDotAndDotDot | QDir::AllEntries);
1464             m_vars->insert(file->variable(), matches);
1465             kDebug(9042) << "file glob_recurse" << file->path() << file->globbingExpressions() << matches;
1466         }   break;
1467         case FileAst::Remove:
1468         case FileAst::RemoveRecurse:
1469             kDebug(9042) << "warning. file-remove or remove_recurse. KDevelop won't remove anything.";
1470             break;
1471         case FileAst::MakeDirectory:
1472             kDebug(9042) << "warning. file-make_directory. KDevelop won't create anything.";
1473             break;
1474         case FileAst::RelativePath:
1475             m_vars->insert(file->variable(), QStringList(KUrl::relativePath(file->directory(), file->path())));
1476             kDebug(9042) << "file relative_path" << file->directory() << file->path();
1477             break;
1478         case FileAst::ToCmakePath:
1479 #ifdef Q_OS_WIN
1480             m_vars->insert(file->variable(), file->path().replace("\\", "/").split(';'));
1481 #else
1482             m_vars->insert(file->variable(), file->path().split(':'));
1483 #endif
1484             kDebug(9042) << "file TO_CMAKE_PATH variable:" << file->variable() << "="
1485                     << m_vars->value(file->variable()) << "path:" << file->path();
1486             break;
1487         case FileAst::ToNativePath:
1488             m_vars->insert(file->variable(), QStringList(file->path().replace('/', QDir::separator())));
1489             kDebug(9042) << "file TO_NATIVE_PATH variable:" << file->variable() << "="
1490                     << m_vars->value(file->variable()) << "path:" << file->path();
1491             break;
1492         default:
1493             kDebug(9032) << "error: not implemented. file:" << file->type() <<
1494                 "variable:" << file->variable() << "file:" << file->path() << file->content()[file->line()].arguments[0].value;
1495             break;
1496     }
1497     return 1;
1498 }
1499
1500 int CMakeProjectVisitor::visit(const MessageAst *msg)
1501 {
1502     s_msgcallback(msg->message().join(QString()));
1503     return 1;
1504 }
1505
1506 int CMakeProjectVisitor::visit(const MathAst *math)
1507 {
1508     QScriptEngine eng;
1509     QScriptValue result = eng.evaluate(math->expression());
1510
1511     if (result.isError())
1512     {
1513         kDebug(9032) << "error: found an error while calculating" << math->expression();
1514     }
1515     kDebug(9042) << "math. " << math->expression() << "=" << result.toString();
1516     m_vars->insert(math->outputVariable(), QStringList(result.toString()));
1517     return 1;
1518 }
1519
1520 int CMakeProjectVisitor::visit(const GetFilenameComponentAst *filecomp)
1521 {
1522     QString dir;
1523     Q_ASSERT(m_vars->contains("CMAKE_CURRENT_SOURCE_DIR"));
1524
1525     dir=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").first();
1526     QFileInfo fi(dir, filecomp->fileName());
1527
1528     QString val;
1529     switch(filecomp->type())
1530     {
1531         case GetFilenameComponentAst::Path:
1532             val=fi.canonicalPath();
1533             break;
1534         case GetFilenameComponentAst::Absolute:
1535             val=fi.absoluteFilePath();
1536             break;
1537         case GetFilenameComponentAst::Name:
1538             val=fi.fileName();
1539             break;
1540         case GetFilenameComponentAst::Ext:
1541             val=fi.suffix();
1542             break;
1543         case GetFilenameComponentAst::NameWe:
1544             val=fi.fileName().left(fi.fileName().length()-fi.suffix().length()-1);
1545             break;
1546         case GetFilenameComponentAst::Program:
1547             kDebug(9042) << "error: filenamecopmonent PROGRAM not implemented"; //TODO: <<
1548             break;
1549     }
1550     m_vars->insert(filecomp->variableName(), QStringList(val));
1551     kDebug(9042) << "filename component" << filecomp->variableName() << "= "
1552             << filecomp->fileName() << "=" << val << endl;
1553     return 1;
1554 }
1555
1556 int CMakeProjectVisitor::visit(const GetSourceFilePropAst* prop)
1557 {
1558     kDebug(9042) << "not supported yet :::" << prop->variableName();
1559     m_vars->insert(prop->variableName(), QStringList());
1560     return 1;
1561 }
1562
1563 int CMakeProjectVisitor::visit(const OptionAst *opt)
1564 {
1565     kDebug(9042) << "option" << opt->variableName() << "-" << opt->description();
1566     if(!m_vars->contains(opt->variableName()) && !m_cache->contains(opt->variableName()))
1567     {
1568         m_vars->insert(opt->variableName(), QStringList(opt->defaultValue()));
1569     }
1570     return 1;
1571 }
1572
1573 int CMakeProjectVisitor::visit(const ListAst *list)
1574 {
1575     QString output = list->output();
1576     
1577     QStringList theList = m_vars->value(list->list());
1578     switch(list->type())
1579     {
1580         case ListAst::Length:
1581             m_vars->insert(output, QStringList(QString::number(theList.count())));
1582             kDebug(9042) << "List length" << m_vars->value(output);
1583             break;
1584         case ListAst::Get: {
1585             bool contains = m_vars->contains(list->list());
1586             QStringList indices;
1587             if(contains) {
1588                 foreach(int idx, list->index())
1589                 {
1590                     if(idx>=theList.count())
1591                         kDebug(9032) << "error! trying to GET an element that doesn't exist!" << idx;
1592                     else if(idx>=0)
1593                         indices += theList[idx];
1594                     else
1595                         indices += theList[theList.size()+idx];
1596                 }
1597             } else
1598                 indices += "NOTFOUND";
1599             m_vars->insert(output, indices);
1600             kDebug(9042) << "List: Get" << list->list() << theList << list->output() << list->index();
1601         }   break;
1602         case ListAst::Append:
1603             theList += list->elements();
1604             m_vars->insert(list->list(), theList);
1605             break;
1606         case ListAst::Find: {
1607             int idx=theList.indexOf(list->elements().first());
1608             
1609             m_vars->insert(list->output(), QStringList(QString::number(idx)));
1610             kDebug(9042) << "List: Find" << theList << list->output() << list->elements() << idx;
1611         }   break;
1612         case ListAst::Insert: {
1613             int p=list->index().first();
1614             foreach(const QString& elem, list->elements())
1615             {
1616                 theList.insert(p >=0 ? p : (theList.size()+p), elem);
1617                 p += p>=0? 1 : 0;
1618             }
1619             m_vars->insert(list->list(), theList);
1620         }   break;
1621         case ListAst::RemoveItem:
1622             kDebug(9042) << "list remove item: " << theList << list->elements();
1623             foreach(const QString& elem, list->elements())
1624             {
1625                 theList.removeAll(elem);
1626             }
1627
1628             m_vars->insert(list->list(), theList);
1629             break;
1630         case ListAst::RemoveAt: {
1631             QList<int> indices=list->index();
1632             qSort(indices);
1633             QList<int>::const_iterator it=indices.constEnd();
1634             kDebug(9042) << "list remove: " << theList << indices;
1635             do
1636             {
1637                 --it;
1638                 theList.removeAt(*it >= 0 ? *it : theList.size()+*it);
1639             } while(it!=indices.constBegin());
1640             m_vars->insert(list->list(), theList);
1641         }   break;
1642         case ListAst::Sort:
1643             qSort(theList);
1644             m_vars->insert(list->list(), theList);
1645             break;
1646         case ListAst::Reverse: {
1647             QStringList reversed;
1648             foreach(const QString& elem, theList)
1649                 reversed.prepend(elem);
1650             m_vars->insert(list->list(), reversed);
1651             }
1652             break;
1653         case ListAst::RemoveDuplicates: {
1654             QStringList noduplicates;
1655             foreach(const QString& elem, theList) {
1656                 if(!noduplicates.contains(elem))
1657                     noduplicates.append(elem);
1658             }
1659             m_vars->insert(list->list(), noduplicates);
1660         }   break;
1661     }
1662     kDebug(9042) << "List!!" << list->output() << '='<< m_vars->value(list->output()) << " -> " << m_vars->value(list->list());
1663     return 1;
1664 }
1665
1666 int CMakeProjectVisitor::visit(const ForeachAst *fea)
1667 {
1668     kDebug(9042) << "foreach>" << fea->loopVar() << "=" << fea->arguments() << "range=" << fea->range();
1669     int end = 1;
1670     if(fea->range())
1671     {
1672         if (fea->ranges().start < fea->ranges().stop)
1673         {
1674             for( int i = fea->ranges().start; i < fea->ranges().stop; i += fea->ranges().step )
1675             {
1676                 m_vars->insertMulti(fea->loopVar(), QStringList(QString::number(i)));
1677                 end=walk(fea->content(), fea->line()+1);
1678                 m_vars->take(fea->loopVar());
1679             }
1680         }
1681         else
1682         {
1683             // loop never runs, skip over to matching endforeach
1684
1685             // FIXME this code is duplicated from the non-range case.
1686             // It should be probably factored into a separate helper function.
1687
1688             int lines=fea->line()+1, depth=1;
1689             CMakeFileContent::const_iterator it=fea->content().constBegin()+lines;
1690             CMakeFileContent::const_iterator itEnd=fea->content().constEnd();
1691             for(; depth>0 && it!=itEnd; ++it, lines++)
1692             {
1693                 if(it->name.toLower()=="foreach")
1694                 {
1695                     depth++;
1696                 }
1697                 else if(it->name.toLower()=="endforeach")
1698                 {
1699                     depth--;
1700                 }
1701             }
1702             end=lines-1;
1703         }
1704     }
1705     else
1706     {
1707         //Looping in a list of values
1708         QStringList args=fea->arguments();
1709         if(args.count()==1 && args.first().isEmpty()) { //if the args are empty
1710             int lines=fea->line()+1, depth=1;
1711             CMakeFileContent::const_iterator it=fea->content().constBegin()+lines;
1712             CMakeFileContent::const_iterator itEnd=fea->content().constEnd();
1713             for(; depth>0 && it!=itEnd; ++it, lines++)
1714             {
1715                 if(it->name.toLower()=="foreach")
1716                 {
1717                     depth++;
1718                 }
1719                 else if(it->name.toLower()=="endforeach")
1720                 {
1721                     depth--;
1722                 }
1723             }
1724             end=lines-1;
1725         }
1726         else
1727         {
1728             foreach(const QString& s, args)
1729             {
1730                 m_vars->insert(fea->loopVar(), QStringList(s));
1731                 kDebug(9042) << "looping" << fea->loopVar() << "=" << m_vars->value(fea->loopVar());
1732                 end=walk(fea->content(), fea->line()+1);
1733             }
1734         }
1735     }
1736     kDebug(9042) << "EndForeach" << fea->loopVar();
1737     return end-fea->line()+1;
1738 }
1739
1740 int CMakeProjectVisitor::visit(const StringAst *sast)
1741 {
1742     kDebug(9042) << "String to" /*<< sast->input()*/ << sast->outputVariable();
1743     switch(sast->type())
1744     {
1745         case StringAst::Regex:
1746         {
1747             QStringList res;
1748             QRegExp rx(sast->regex());
1749             rx.setPatternSyntax(QRegExp::RegExp2);
1750             QString totalInput = sast->input().join(QString());
1751             switch(sast->cmdType())
1752             {
1753                 case StringAst::Match:
1754                 {
1755                     int match=rx.indexIn(totalInput);
1756                     if(match>=0) {
1757                         res = QStringList(totalInput.mid(match, rx.matchedLength()));
1758                         break;
1759                     }
1760                     break;
1761                 }
1762                 case StringAst::MatchAll:
1763                 {
1764                     int pos = 0;
1765                     while( (pos = rx.indexIn( totalInput, pos ) ) != -1 )
1766                     {
1767                         res << rx.cap();
1768                         pos += rx.matchedLength();
1769                     }
1770                     break;
1771                 }
1772                 case StringAst::RegexReplace:
1773                 {
1774                     foreach(const QString& in, sast->input())
1775                     {
1776                         // QString() is required to get rid of the const
1777                         res.append(QString(in).replace(rx, sast->replace()));
1778                     }
1779                 }
1780                     break;
1781                 default:
1782                     kDebug(9032) << "ERROR String: Not a regex. " << sast->cmdType();
1783                     break;
1784             }
1785             kDebug() << "regex" << sast->outputVariable() << "=" << sast->regex() << res;
1786             m_vars->insert(sast->outputVariable(), res);
1787         }
1788             break;
1789         case StringAst::Replace: {
1790             QStringList out;
1791             foreach(const QString& _in, sast->input())
1792             {
1793                 QString in(_in);
1794                 QString aux=in.replace(sast->regex(), sast->replace());
1795                 out += aux.split(';'); //FIXME: HUGE ugly hack
1796             }
1797             kDebug(9042) << "string REPLACE" << sast->input() << "=>" << out;
1798             m_vars->insert(sast->outputVariable(), out);
1799         }   break;
1800         case StringAst::Compare:
1801         {
1802             QString res;
1803             switch(sast->cmdType()){
1804                 case StringAst::Equal:
1805                 case StringAst::NotEqual:
1806                     if(sast->input()[0]==sast->input()[1] && sast->cmdType()==StringAst::Equal)
1807                         res = "TRUE";
1808                     else
1809                         res = "FALSE";
1810                     break;
1811                 case StringAst::Less:
1812                 case StringAst::Greater:
1813                     if(sast->input()[0]<sast->input()[1] && sast->cmdType()==StringAst::Less)
1814                         res = "TRUE";
1815                     else
1816                         res = "FALSE";
1817                     break;
1818                 default:
1819                     kDebug(9042) << "String: Not a compare. " << sast->cmdType();
1820             }
1821             m_vars->insert(sast->outputVariable(), QStringList(res));
1822         }
1823             break;
1824         case StringAst::Ascii: {
1825             QString res;
1826             foreach(const QString& ascii, sast->input())
1827             {
1828                 bool ok;
1829                 res += QChar(ascii.toInt(&ok, 10));
1830             }
1831
1832             m_vars->insert(sast->outputVariable(), QStringList(res));
1833         }   break;
1834         case StringAst::Configure:
1835             //This is not up to the cmake support
1836             kDebug(9032) << "warning! String configure is not supported!" << sast->content()[sast->line()].writeBack();
1837             break;
1838         case StringAst::ToUpper:
1839             m_vars->insert(sast->outputVariable(), QStringList(sast->input()[0].toUpper()));
1840             break;
1841         case StringAst::ToLower:
1842             m_vars->insert(sast->outputVariable(), QStringList(sast->input()[0].toLower()));
1843             break;
1844         case StringAst::Length:
1845             m_vars->insert(sast->outputVariable(), QStringList(QString::number(sast->input()[0].count())));
1846             break;
1847         case StringAst::Substring:
1848         {
1849             QString res=sast->input()[0];
1850             res=res.mid(sast->begin(), sast->length());
1851             m_vars->insert(sast->outputVariable(), QStringList(res));
1852         }   break;
1853         case StringAst::Strip:
1854             m_vars->insert(sast->outputVariable(), QStringList(CMakeFunctionArgument::unescapeValue( sast->string() )));
1855             break;
1856         case StringAst::Random: {
1857             QString alphabet=sast->string(), result;
1858             for(int i=0; i<sast->length(); i++)
1859             {
1860                 int randv=qrand() % alphabet.size();
1861                 result += alphabet[randv];
1862             }
1863             m_vars->insert(sast->outputVariable(), QStringList(result));
1864         }   break;
1865     }
1866     kDebug(9042) << "String " << m_vars->value(sast->outputVariable());
1867     return 1;
1868 }
1869
1870
1871 int CMakeProjectVisitor::visit(const GetCMakePropertyAst *past)
1872 {
1873     QStringList output;
1874     switch(past->type())
1875     {
1876         case GetCMakePropertyAst::Variables:
1877             kDebug(9042) << "get cmake prop: variables:" << m_vars->size();
1878             output = m_vars->keys();
1879             break;
1880         case GetCMakePropertyAst::CacheVariables:
1881             output = m_cache->keys();
1882             break;
1883         case GetCMakePropertyAst::Commands:      //FIXME: We do not have commands yet
1884             output = QStringList();
1885             break;
1886         case GetCMakePropertyAst::Macros:
1887             output = m_macros->keys();
1888             break;
1889     }
1890     m_vars->insert(past->variableName(), output);
1891     return 1;
1892 }
1893
1894 int CMakeProjectVisitor::visit(const CustomCommandAst *ccast)
1895 {
1896     kDebug(9042) << "CustomCommand" << ccast->outputs();
1897     if(ccast->isForTarget())
1898     {
1899         //TODO: implement me
1900     }
1901     else
1902     {
1903         foreach(const QString& out, ccast->outputs())
1904         {
1905             m_generatedFiles[out] = QStringList(ccast->mainDependency())/*+ccast->otherDependencies()*/;
1906             kDebug(9042) << "Have to generate:" << out << "with" << m_generatedFiles[out];
1907         }
1908     }
1909     return 1;
1910 }
1911
1912 int CMakeProjectVisitor::visit(const CustomTargetAst *ctar)
1913 {
1914     kDebug(9042) << "custom_target " << ctar->target() << ctar->dependencies() << ", " << ctar->commandArgs();
1915     kDebug(9042) << ctar->content()[ctar->line()].writeBack();
1916
1917     defineTarget(ctar->target(), ctar->dependencies(), Target::Custom);
1918     return 1;
1919 }
1920
1921 QPair<QString, QString> definition(const QString& param)
1922 {
1923     QPair<QString, QString> ret;
1924     if(!param.startsWith("-D"))
1925         return ret;
1926     int eq=param.indexOf('=', 2);
1927     ret.first=param.mid(2, eq-2);
1928     if(eq>0)
1929         ret.second=param.mid(eq+1);
1930     return ret;
1931 }
1932
1933 int CMakeProjectVisitor::visit(const AddDefinitionsAst *addDef)
1934 {
1935 //     kDebug(9042) << "Adding defs: " << addDef->definitions();
1936     foreach(const QString& def, addDef->definitions())
1937     {
1938         if(def.isEmpty())
1939             continue;
1940         QPair<QString, QString> definePair=definition(def);
1941         if(definePair.first.isEmpty())
1942             kDebug(9042) << "error: definition not matched" << def;
1943
1944         m_defs[definePair.first]=definePair.second;
1945         kDebug(9042) << "added definition" << definePair.first << "=" << definePair.second << " from " << def;
1946     }
1947     return 1;
1948 }
1949
1950 int CMakeProjectVisitor::visit(const RemoveDefinitionsAst *remDef)
1951 {
1952     foreach(const QString& def, remDef->definitions())
1953     {
1954         if(def.isEmpty())
1955             continue;
1956         QPair<QString, QString> definePair=definition(def);
1957         if(definePair.first.isEmpty())
1958             kDebug(9042) << "error: definition not matched" << def;
1959
1960         m_defs.remove(definePair.first);
1961         kDebug(9042) << "removed definition" << definePair.first << " from " << def;
1962     }
1963     return 1;
1964 }
1965
1966 int CMakeProjectVisitor::visit(const MarkAsAdvancedAst *maa)
1967 {
1968     kDebug(9042) << "Mark As Advanced" << maa->advancedVars();
1969     return 1;
1970 }
1971
1972 /// @todo Add support for platform-specific argument splitting
1973 /// (UNIX_COMMAND and WINDOWS_COMMAND) introduced in CMake 2.8.
1974 int CMakeProjectVisitor::visit( const SeparateArgumentsAst * separgs )
1975 {
1976     QString varName=separgs->variableName();
1977     QStringList res;
1978     foreach(const QString& value, m_vars->value(varName))
1979     {
1980         res += value.split(' ');
1981     }
1982     m_vars->insert(separgs->variableName(), res);
1983     return 1;
1984 }
1985
1986 int CMakeProjectVisitor::visit(const SetPropertyAst* setp)
1987 {
1988     kDebug() << "setprops" << setp->type() << setp->name() << setp->values();
1989     if(setp->type()==GlobalProperty)
1990         m_props[GlobalProperty][QString()][setp->name()]=setp->values();
1991     else
1992     {
1993         CategoryType& cm=m_props[setp->type()];
1994         foreach(const QString &it, setp->args())
1995             cm[it].insert(setp->name(), setp->values());
1996     }
1997     return 1;
1998 }
1999
2000 int CMakeProjectVisitor::visit(const GetPropertyAst* getp)
2001 {
2002     kDebug() << "getprops";
2003     QStringList retv;
2004     QString catn;
2005     if(getp->type()!=GlobalProperty)
2006     {
2007         catn=getp->typeName();
2008     }
2009     retv=m_props[getp->type()][catn][getp->name()];
2010     m_vars->insert(getp->outputVariable(), retv);
2011     return 1;
2012 }
2013
2014 int CMakeProjectVisitor::visit(const GetDirPropertyAst* getdp)
2015 {
2016     kDebug() << "getprops";
2017     QStringList retv;
2018     QString dir=getdp->directory();
2019     if(dir.isEmpty()) {
2020         dir=m_vars->value("CMAKE_CURRENT_SOURCE_DIR").join(QString());
2021     } else if(KUrl::isRelativeUrl(dir)) {
2022         KUrl u(m_vars->value("CMAKE_CURRENT_SOURCE_DIR").join(QString()));
2023         u.addPath(dir);
2024         dir=u.path();
2025     }
2026     
2027     retv=m_props[DirectoryProperty][dir][getdp->propName()];
2028     m_vars->insert(getdp->outputVariable(), retv);
2029     
2030     return 1;
2031 }
2032
2033 int CMakeProjectVisitor::visit( const WhileAst * whileast)
2034 {
2035     CMakeCondition cond(this);
2036     bool result=cond.condition(whileast->condition());
2037     usesForArguments(whileast->condition(), cond.variableArguments(), m_topctx, whileast->content()[whileast->line()]);
2038
2039     kDebug(9042) << "Visiting While" << whileast->condition() << "?" << result;
2040     int end=whileast->line()+1;
2041     if(result)
2042     {
2043         walk(whileast->content(), whileast->line()+1);
2044         
2045         if(m_hitBreak) {
2046             kDebug() << "break found. leaving loop";
2047             m_hitBreak=false;
2048         } else
2049             walk(whileast->content(), whileast->line());
2050     }
2051     CMakeFileContent::const_iterator it=whileast->content().constBegin()+end;
2052     CMakeFileContent::const_iterator itEnd=whileast->content().constEnd();
2053     int lines=0, inside=1;
2054     for(; inside>0 && it!=itEnd; ++it, lines++)
2055     {
2056         QString funcName=it->name.toLower();
2057         if(funcName=="while")
2058             inside++;
2059         else if(funcName=="endwhile")
2060             inside--;
2061     }
2062
2063     if(it!=itEnd) {
2064         usesForArguments(whileast->condition(), cond.variableArguments(), m_topctx, *(it-1));
2065     }
2066     return lines;
2067 }
2068
2069 CMakeFunctionDesc CMakeProjectVisitor::resolveVariables(const CMakeFunctionDesc & exp)
2070 {
2071     CMakeFunctionDesc ret=exp;
2072     ret.arguments.clear();
2073
2074     foreach(const CMakeFunctionArgument &arg, exp.arguments)
2075     {
2076         if(arg.value.contains('$'))
2077             ret.addArguments(resolveVariable(arg));
2078         else
2079             ret.arguments.append(arg);
2080     }
2081
2082     return ret;
2083 }
2084
2085 enum RecursivityType { No, Yes, End, Break };
2086
2087 RecursivityType recursivity(const QString& functionName)
2088 {
2089     QString upperFunctioName=functionName.toUpper();
2090     if(upperFunctioName=="IF" || upperFunctioName=="WHILE" ||
2091        upperFunctioName=="FOREACH" || upperFunctioName=="MACRO")
2092         return Yes;
2093     else if(upperFunctioName=="ELSE" || upperFunctioName=="ELSEIF" || upperFunctioName.startsWith("END"))
2094         return End;
2095     else if(upperFunctioName=="BREAK")
2096         return Break;
2097     return No;
2098 }
2099
2100 int CMakeProjectVisitor::walk(const CMakeFileContent & fc, int line, bool isClean)
2101 {
2102     ReferencedTopDUContext aux=m_topctx;
2103     KUrl url(fc[0].filePath);
2104     
2105     if(!m_topctx || m_topctx->url().toUrl()!=url)
2106     {
2107         kDebug(9042) << "Creating a context for" << url;
2108         m_topctx=createContext(url, aux ? aux : m_parentCtx, fc.last().endLine-1, fc.last().endColumn-1, isClean);
2109         if(!aux)
2110             aux=m_topctx;
2111     }
2112     VisitorState p;
2113     p.code = &fc;
2114     p.context = m_topctx;
2115     p.line = line;
2116
2117     m_backtrace.push(p);
2118
2119     CMakeFileContent::const_iterator it=fc.constBegin()+line, itEnd=fc.constEnd();
2120     for(; it!=itEnd; )
2121     {
2122         Q_ASSERT( line<fc.count() );
2123         Q_ASSERT( line>=0 );
2124 //         kDebug(9042) << "@" << line;
2125
2126         Q_ASSERT( *it == fc[line] );
2127 //         kDebug(9042) << "At line" << line << "/" << fc.count();
2128
2129         RecursivityType r = recursivity(it->name);
2130         if(r==End || r==Break || m_hitBreak)
2131         {
2132 //             kDebug(9042) << "Found an end." << func.writeBack();
2133             m_backtrace.pop();
2134             m_topctx=aux;
2135             
2136             if(r==Break)
2137                 m_hitBreak=true;
2138             return line;
2139         }
2140         
2141         CMakeAst* element = AstFactory::self()->createAst(it->name);
2142
2143         if(!element)
2144             element = new MacroCallAst;
2145
2146         createUses(*it);
2147 //         kDebug(9042) << "resolving:" << it->writeBack();
2148             
2149         CMakeFunctionDesc func = resolveVariables(*it); //FIXME not correct in while case
2150         bool correct = element->parseFunctionInfo(func);
2151 //         kDebug(9042) << "resolved:" << func.writeBack() << correct;
2152         if(!correct)
2153         {
2154             kDebug(9042) << "error! found an error while processing" << func.writeBack() << "was" << it->writeBack() << endl
2155                 << " at" << func.filePath << ":" << func.line << endl;
2156             //FIXME: Should avoid to run?
2157         }
2158         
2159         if(element->isDeprecated()) {
2160             kDebug(9032) << "Warning: Using the function: " << func.name << " which is deprecated by cmake.";
2161             DUChainWriteLocker lock(DUChain::lock());
2162             KSharedPtr<Problem> p(new Problem);
2163             p->setDescription(i18n("%1 is a deprecated command and should not be used", func.name));
2164             p->setRange(it->nameRange());
2165             p->setFinalLocation(DocumentRange(url.prettyUrl(), KTextEditor::Range(it->nameRange().start.textCursor(), it->nameRange().end.textCursor())));
2166             m_topctx->addProblem(p);
2167         }
2168         element->setContent(fc, line);
2169
2170         createDefinitions(element);
2171
2172         m_vars->insert("CMAKE_CURRENT_LIST_LINE", QStringList(QString::number(it->line)));
2173         int lines=element->accept(this);
2174         line+=lines;
2175         m_backtrace.top().line = line;
2176         m_backtrace.top().context = m_topctx;
2177         delete element;
2178         
2179         if(line>fc.count()) {
2180             DUChainWriteLocker lock(DUChain::lock());
2181             KSharedPtr<Problem> p(new Problem);
2182             p->setDescription(i18n("Unfinished function. "));
2183             p->setRange(it->nameRange());
2184             p->setFinalLocation(DocumentRange(url.prettyUrl(), KTextEditor::Range(fc.first().range().start.textCursor(), fc.last().range().end.textCursor())));
2185             m_topctx->addProblem(p);
2186             
2187             break;
2188         }
2189         
2190         it+=lines;
2191     }
2192     m_backtrace.pop();
2193     m_topctx=aux;
2194     kDebug(9042) << "Walk stopped @" << line;
2195     return line;
2196 }
2197
2198 void CMakeProjectVisitor::createDefinitions(const CMakeAst *ast)
2199 {
2200     if(!m_topctx)
2201         return;
2202     DUChainWriteLocker lock(DUChain::lock());
2203     foreach(const CMakeFunctionArgument &arg, ast->outputArguments())
2204     {
2205         if(!arg.isCorrect())
2206             continue;
2207         QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(arg.value));
2208         if(decls.isEmpty())
2209         {
2210             Declaration *d = new Declaration(arg.range(), m_topctx);
2211             d->setIdentifier( Identifier(arg.value) );
2212         }
2213         else
2214         {
2215             int idx=m_topctx->indexForUsedDeclaration(decls.first());
2216             m_topctx->createUse(idx, arg.range(), 0);
2217         }
2218     }
2219 }
2220
2221 void CMakeProjectVisitor::createUses(const CMakeFunctionDesc& desc)
2222 {
2223     if(!m_topctx)
2224         return;
2225     DUChainWriteLocker lock(DUChain::lock());
2226     foreach(const CMakeFunctionArgument &arg, desc.arguments)
2227     {
2228         if(!arg.isCorrect() || !arg.value.contains('$'))
2229             continue;
2230
2231         QList<IntPair> var = parseArgument(arg.value);
2232         QList<IntPair>::const_iterator it, itEnd=var.constEnd();
2233         for(it=var.constBegin(); it!=itEnd; ++it)
2234         {
2235             QString var=arg.value.mid(it->first+1, it->second-it->first-1);
2236             QList<Declaration*> decls=m_topctx->findDeclarations(Identifier(var));
2237             
2238             if(!decls.isEmpty())
2239             {
2240                 int idx=m_topctx->indexForUsedDeclaration(decls.first());
2241                 m_topctx->createUse(idx, SimpleRange(arg.line-1, arg.column+it->first, arg.line-1, arg.column+it->second-1), 0);
2242             }
2243         }
2244     }
2245 }
2246
2247 void CMakeProjectVisitor::setCacheValues( CacheValues* cache)
2248 {
2249     m_cache=cache;
2250 }
2251
2252 void CMakeProjectVisitor::setVariableMap(VariableMap * vars)
2253 {
2254     m_vars=vars;
2255 }
2256
2257 bool isGenerated(const QString& name)
2258 {
2259     return name.indexOf("#[")>=0;
2260 }
2261
2262 QStringList CMakeProjectVisitor::dependees(const QString& s) const
2263 {
2264     QStringList ret;
2265     if(isGenerated(s))
2266     {
2267         foreach(const QString& f, m_generatedFiles[s])
2268             ret += dependees(f);
2269     }
2270     else
2271     {
2272         ret += s;
2273     }
2274     return ret;
2275 }
2276
2277 QStringList CMakeProjectVisitor::resolveDependencies(const QStringList & files) const
2278 {
2279     QStringList ret;
2280     foreach(const QString& s, files)
2281     {
2282         if(isGenerated(s))
2283         {
2284             kDebug(9042) << "Generated:" << s;
2285             QStringList gen = dependees(s);
2286             
2287             foreach(const QString& file, gen)
2288             {
2289                 if(!ret.contains(file))
2290                     ret.append(file);
2291             }
2292         }
2293         else
2294         {
2295             ret.append(s);
2296         }
2297     }
2298     return ret;
2299 }
2300