qt文档中eval(string)只是一个内建(build-in)测试(test)函数,但是看到qmake源码,
发现eval既是build-in replace函数,形式为:eval(variablename);也是build-in test函数,形式为:eval(string[,string[,string...]]))
//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\qmake\library\qmakeevaluator_p.h
struct QMakeBuiltinInit
{
const char *name;
int func;
enum { VarArgs = 1000 };
int min_args, max_args;
const char *args;
};
//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\qmake\library\qmakebuiltins.cpp
void QMakeEvaluator::initFunctionStatics()
{
static const QMakeBuiltinInit expandInits[] = { //build-in replace函数列表
.....
{ "eval", E_EVAL, 1, 1, "var" },
.....
}
static const QMakeBuiltinInit testInits[] = { //build-in test函数列表
.....
{ "eval", T_EVAL, 0, QMakeBuiltinInit::VarArgs, nullptr },
.....
}
}
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
const QMakeBuiltin &adef, const ProKey &func, const ProStringList &args, ProStringList &ret)
{
.....
case E_EVAL: //eval 作为replace函数的具体执行步骤
ret += values(map(args.at(0)));
break;
}
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
const QMakeInternal::QMakeBuiltin &adef, const ProKey &function, const ProStringList &args)
{
.....
case T_EVAL: { //eval 作为test函数具体执行步骤。
VisitReturn ret = ReturnFalse;
QString contents = args.join(statics.field_sep);
ProFile *pro = m_parser->parsedProBlock(QStringRef(&contents),
0, m_current.pro->fileName(), m_current.line);
if (m_cumulative || pro->isOk()) {
m_locationStack.push(m_current);
visitProBlock(pro, pro->tokPtr());
ret = ReturnTrue; // This return value is not too useful, but that's qmake
m_current = m_locationStack.pop();
}
pro->deref();
return ret;
}
.....
}
qmake将eval解析成replace函数还是test函数,只看eval前面是否有$$,或者是否被$${}囊括。没有则是test函数!!用后者时右边大括号“}”必须紧贴着eval函数的右括号“)”。
eval(variablename)作为replace函数传入1个变量名参数,并将变量的值返回,如果变量是空变量(没有值),则会报错。主要用于以及嵌套取值和复杂变量名的拼接,并方便从复杂变量中取值。eval(ariablename)作为replace函数是无法用qmake language来实现的,其逻辑是在qmake的C++代码中实现的。qmake中无法让$${}进行直接的嵌套(比如不能 “$$ $$var”或者$${$$var},qmake language语法无法识别),当需要灵活访问复杂变量时以及嵌套取值时必须借助eval来实现。
defineReplace(myre){ #定义函数名为myre的函数。嵌套取值
message($$1)
message($$eval($$1))#将第一个参数的值作为变量,将该变量的值取出来打印
return ($$eval($$1))
}
#eval(string)作为 replace函数,主要用于复杂变量名的拼接,并方便从复杂变量中取值
TT = 3433
ZZ = TT
UU=rt
gg.rt.f=feg
message($$eval(gg.$${UU}.f)_rr) #输出feg_rr
message($${eval(gg.$${UU}.f)}_rr) #输出feg_rr
#message($${eval(gg.$${UU}.f)_rr}) #报错 Missing } terminator [found _] 右大括号必须紧贴eval函数的右括号
p=ZZ
message($$p)#输出ZZ
p=$$p
message($$p)#输出ZZ
p=$$p #通过这种递归方式是无法取出ZZ中的值
message($$p)#输出ZZ
message($$myre(ZZ))#将ZZ这个名称作为参数传入
message($$myre($$ZZ)#将ZZ的值作为参数传入
#输出
#Project MESSAGE: ZZ message($$myre(ZZ))的输出
#Project MESSAGE: TT
#Project MESSAGE: TT
#Project MESSAGE: TT message($$myre($$ZZ)的输出
#Project MESSAGE: 3433
#Project MESSAGE: 3433
YY = $$eval(TT UU) #eval前有$$符号,被解析成replace函数,此时“TT UU”被当做一个变量名称来解析
isEmpty(YY){message(Empty)}else{ #输出empty,因为没有"TT UU"的变量
message(2not empty:$$YY)}
YY = $$eval(TT)
isEmpty(YY){message(Empty)}else{
message(2not empty:$$YY)} #输出2not empty:3433
YY=$$eval(TT=yrr) #eval前有$$符号,被解析成replace函数,此时“TT=yrr”被当做一个变量名称来解析
#,实际不存在而名称为TT=yrr的变量。
isEmpty(YY){message(Empty)}else{ #输出Empty
message(3not empty:$$YY)}
message($$TT) #输出3433,注意eval做临时表达式使用时,所执行的代码段不会影响到下文!!
#YY=$${gg.$${UU}.f} //error $${}不能直接嵌套。
YY=$$eval(gg.$${UU}.f) #注意这里需要将UU用{}括起来,否则$$UU.f会将UU.f当做一个变量名
isEmpty(YY){message(Empty)}else{
message(4not empty:$$YY)} #输出4not empty:feg
YY=$${eval(TR.$${eval(UU)}.f)} 注意这里$$进行嵌套。qmake会先执行$${}中的eval函数。在执行$${}取值
isEmpty(YY){message(Empty)}else{
message(4not empty:$$YY)} #输出4not empty:feg
eval(string[,string[,string...]]) 作为test函数参数是不限的,返回ture或false,其作用是将string 作为qmake language语法的语句进行执行,官方文档是将这个用于对变量的定义或者赋值。
Evaluates the contents of the string using qmake syntax rules and returns true. Definitions and assignments can be used in the string to modify the values of existing variables or create new definitions.
For example:
eval(TARGET = myapp) { message($$TARGET) }Note: Quotation marks can be used to delimit the string, and the return value can be discarded if it is not needed.
eval函数传入的代码段(传入的string)只要没有语法错误都能返回ture。语句有语法错误eval函数才会返回false。eval返回ture或false 与语句本身逻辑是否退出、是否终止、是否返回错误没有关系,只与传入代码段是否语法错误有关系,其函数体实现是qmake在C++中实现的。
#eval作为测试函数使用,只要eval前面没有$$或者eval没有被$${}囊括,eval就会被解析成测试函数
eval(YY):message(here1) //返回ture 输出here1
eval(YY=$$TT):message(here1)//返回ture 输出here1
eval(isEmpty(TT)):message(here1)//返回ture 输出here1
TT = 3433
YY=isEmpty(TT) #isEmpty返回的是true 或者 false,此处YY将isEmpty(TT) 整个表达式作为内容存下来了,而不是存放true 或者false。
YY{message(Empty)}else{
message(1not empty:$$YY)} #输出 1not empty:isEmpty(TT)
#eval做测试函数使用
eval("TT=tet"){message($$TT)}else{ #输出tet
message(eval false)}
message($$TT) #输出tet 注意:eval做test函数使用时,所执行的代码段会影响到eval函数之外的下文!!
eval("error(ere) $$varEmpty"):message(ture) #eval执行的代码段的返回值以及是否逻辑错误并不影响eval的输出,只要代码段没有语法错误就能返回ture
else:message(false)
#输出:
#Project ERROR: ere
#Project MESSAGE: ture
#eval测试失败案例。会输出here2
YY= #定义变量YY,未赋值
eval("$$YY=$$TT"){ #赋值给与空变量 (此处YY是未定义的变量或者是空变量)
message(here1)
}else{
message(here2)
}
eval("defineReplace(cmakeModuleName) {\ #定义函数 语句不完整。
_module = $$1\
_name = $$eval(QT.$${_module}.name)\
cmake_module_name = $$replace(_name, ^Qt, )"){
message(here1)
}else{
message(here2)
}