1
/* KDevelop CMake Support
3
* Copyright 2006 Matt Rogers <mattr@kde.org>
4
* Copyright 2008 Aleix Pol <aleixpol@gmail.com>
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
#include "cmakelistsparser.h"
24
// #include "cmakeprojectvisitor.h"
25
#include "astfactory.h"
30
QMap<QChar, QChar> whatToScape()
32
//Only add those where we're not scaping the next character
33
QMap<QChar, QChar> ret;
40
QMap<QChar, QChar> CMakeFunctionArgument::scapings=whatToScape();
42
static QChar scapingChar='\\';
43
QString CMakeFunctionArgument::unescapeValue(const QString& value)
45
int firstScape=value.indexOf(scapingChar);
53
for(int i=firstScape; i<value.size() && i>=0; i=value.indexOf(scapingChar, i+2))
55
newValue+=value.mid(last, i-last);
56
QChar current=value[i+1];
57
if(scapings.contains(current))
58
newValue += scapings[current];
64
newValue+=value.mid(last, value.size());
65
// qDebug() << "escapiiiiiiiiing" << value << newValue;
69
void CMakeFunctionDesc::addArguments( const QStringList& args )
73
CMakeFunctionArgument cmakeArg;
74
arguments.append( cmakeArg );
78
foreach( const QString& arg, args )
80
CMakeFunctionArgument cmakeArg( arg );
81
arguments.append( cmakeArg );
86
QString CMakeFunctionDesc::writeBack() const
88
QString output=name+"( ";
89
foreach(const CMakeFunctionArgument& arg, arguments)
91
QString o = arg.value;
101
enum RecursivityType { No, Yes, ElseIf, End };
103
RecursivityType recursivity(const QString& functionName)
105
if(functionName.toUpper()=="IF" || functionName.toUpper()=="WHILE" ||
106
functionName.toUpper()=="FOREACH" || functionName.toUpper()=="MACRO")
108
else if(functionName.toUpper()=="ELSE" || functionName.toUpper()=="ELSEIF")
110
else if(functionName.toUpper().startsWith("END"))
115
bool CMakeListsParser::parseCMakeFile( CMakeAst* root, const QString& fileName )
119
cmListFileLexer* lexer = cmListFileLexer_New();
122
if ( !cmListFileLexer_SetFileName( lexer, qPrintable( fileName ) ) )
125
bool parseError = false;
126
bool haveNewline = true;
127
cmListFileLexer_Token* token;
128
QStack<CMakeAst*> parent;
129
QStack<IfAst*> parentIf;
132
while(!parseError && (token = cmListFileLexer_Scan(lexer)))
135
if(token->type == cmListFileLexer_Token_Newline)
140
else if(token->type == cmListFileLexer_Token_Identifier)
144
CMakeAst* currentParent=parent.top();
146
CMakeFunctionDesc function;
147
function.name = token->text;
148
function.filePath = fileName;
149
function.line = token->line;
151
RecursivityType s=recursivity(function.name);
152
if(s==End) { //NOTE: We're not checking if ENDIF(<<this>>) is ok.
154
if(function.name.toUpper()=="ENDIF") {
159
QString ifWorkaround; //FIXME: Please check it!
160
if(function.name.toUpper().startsWith("ELSE")) {
161
ifWorkaround = function.name;
162
function.name = "IF";
164
parseError = !parseCMakeFunction( lexer, function, fileName, currentParent );
167
kDebug(9032) << "Error while parsing:" << function.name;
170
if(!ifWorkaround.isEmpty())
171
function.name = ifWorkaround;
173
if(!parseError && s!=No) {
174
CMakeAst* lastCreated = currentParent->children().last();
175
// kDebug(9032) << "Lol:" << function.name << s;
177
if(function.name.toUpper()=="IF") {
178
parentIf.append(dynamic_cast<IfAst*>(lastCreated));
179
} else if(function.name.toUpper()=="ELSEIF") {
180
IfAst* ifast = parentIf.top(), *elseif=dynamic_cast<IfAst*>(lastCreated);
181
ifast->conditions().append(elseif->conditions()[0]);
183
} else if(function.name.toUpper()=="ELSE") {
184
lastCreated = parentIf.top();
187
CMakeAst* child = new CMakeAst;
188
lastCreated->addChildAst(child);
193
case ElseIf: //For else and ifelse
204
// kDebug(9032) << "Parsing:" << function.name << "depth:" << parent.count();
212
bool CMakeListsParser::parseCMakeFunction( cmListFileLexer* lexer,
213
CMakeFunctionDesc& func,
214
const QString& fileName, CMakeAst *parent )
216
// Command name has already been parsed. Read the left paren.
217
cmListFileLexer_Token* token;
218
if(!(token = cmListFileLexer_Scan(lexer)))
222
if(token->type != cmListFileLexer_Token_ParenLeft)
228
unsigned long lastLine = cmListFileLexer_GetCurrentLine(lexer);
229
while((token = cmListFileLexer_Scan(lexer)))
231
if(token->type == cmListFileLexer_Token_ParenRight)
233
CMakeAst* newElement = AstFactory::self()->createAst(func.name);
239
newElement = new MacroCallAst;
241
bool err = newElement->parseFunctionInfo(func);
243
kDebug(9032) << "error! found an error while reading" << func.name << "at" << func.line;
244
parent->addChildAst(newElement);
245
// kDebug(9032) << "Adding:" << func.name << newElement << "as macro :" << asMacro;
248
//FIXME: should return the parseFunctionInfo boolean return value, err
250
else if(token->type == cmListFileLexer_Token_Identifier ||
251
token->type == cmListFileLexer_Token_ArgumentUnquoted)
253
CMakeFunctionArgument a( token->text, false, fileName, token->line );
256
else if(token->type == cmListFileLexer_Token_ArgumentQuoted)
258
CMakeFunctionArgument a( token->text, true, fileName, token->line );
261
else if(token->type != cmListFileLexer_Token_Newline)
265
lastLine = cmListFileLexer_GetCurrentLine(lexer);
273
CMakeFileContent CMakeListsParser::readCMakeFile(const QString & fileName)
275
cmListFileLexer* lexer = cmListFileLexer_New();
277
return CMakeFileContent();
278
if ( !cmListFileLexer_SetFileName( lexer, qPrintable( fileName ) ) ) {
279
kDebug(9042) << "cmake read error. could not read " << fileName;
280
return CMakeFileContent();
283
CMakeFileContent ret;
284
bool readError = false, haveNewline = true;
285
cmListFileLexer_Token* token;
287
while(!readError && (token = cmListFileLexer_Scan(lexer)))
290
if(token->type == cmListFileLexer_Token_Newline)
295
else if(token->type == cmListFileLexer_Token_Identifier)
300
CMakeFunctionDesc function;
301
function.name = token->text;
302
function.filePath = fileName;
303
function.line = token->line;
304
function.column = token->column;
306
readError = !readCMakeFunction( lexer, function, fileName );
307
ret.append(function);
311
kDebug(9032) << "Error while parsing:" << function.name << "at" << function.line;
320
bool CMakeListsParser::readCMakeFunction(cmListFileLexer *lexer, CMakeFunctionDesc &func, const QString & fileName)
322
// Command name has already been parsed. Read the left paren.
323
cmListFileLexer_Token* token;
324
if(!(token = cmListFileLexer_Scan(lexer)))
328
if(token->type != cmListFileLexer_Token_ParenLeft)
334
unsigned long lastLine = cmListFileLexer_GetCurrentLine(lexer);
336
while((token = cmListFileLexer_Scan(lexer)))
338
if(token->type == cmListFileLexer_Token_ParenRight)
342
func.endLine=token->line;
343
func.endColumn=token->column;
345
} else if(parenthesis<0)
349
CMakeFunctionArgument a( token->text, false, fileName, token->line, token->column );
353
else if(token->type == cmListFileLexer_Token_ParenLeft)
356
CMakeFunctionArgument a( token->text, false, fileName, token->line, token->column );
359
else if(token->type == cmListFileLexer_Token_Identifier ||
360
token->type == cmListFileLexer_Token_ArgumentUnquoted)
362
CMakeFunctionArgument a( token->text, false, fileName, token->line, token->column );
365
else if(token->type == cmListFileLexer_Token_ArgumentQuoted)
367
CMakeFunctionArgument a( token->text, true, fileName, token->line, token->column+1 );
370
else if(token->type != cmListFileLexer_Token_Newline)
374
lastLine = cmListFileLexer_GetCurrentLine(lexer);
381
bool CMakeFunctionDesc::operator==(const CMakeFunctionDesc & other) const
383
if(other.arguments.count()!=arguments.count() || name!=other.name)
386
QList<CMakeFunctionArgument>::const_iterator it=arguments.constBegin();
387
QList<CMakeFunctionArgument>::const_iterator itOther=other.arguments.constBegin();
388
for(;it!=arguments.constEnd(); ++it, ++itOther)
397
/*CMakeFunctionArgument::CMakeFunctionArgument(const CMakeFunctionArgument & r)
398
: value(r.value), quoted(r.quoted), filePath(r.filePath), line(r.line), column(r.column)
400
value=unescapeValue(value);
403
CMakeFunctionArgument::CMakeFunctionArgument(const QString & v, bool q, const QString &, quint32 l, quint32 c)
404
: value(v), quoted(q)/*, filePath(file)*/, line(l), column(c)
406
value=unescapeValue(value);