qmake language 内建函数 内置函数 自定义函数 defineTest(testfunctionname) defineReplace(repacefunctionname)

目录

说明

1、内建函数(下面的函数中var表示变量的名称)

1.1、特殊函数

1.2、test函数

1.3、replace函数

2、自定义函数

2.1、要点

2.2、案例

2.3、test函数返回值特例

3、打印信息

4、常见语法语义报错

4.1 说明

4.2 常见语法语义报错

5、杂谈


使用qt版本windows qt5.12.0+vs编译器

说明

qmake中函数分为test function  和 replace function , test function返回true 或者false, replace function 返回变量内容。replace函数调用时需要在前面加$$ ,表示取函数的返回值,不加会有违背语法;而test函数加了会违背语法。qmake提供很多内置(build-in )函数,其部分函数的逻辑实现是在qmake的c++代码中(具体相关C++代码逻辑所在位置为:QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\qmake\library\qmakebuiltins.cpp),部分函数的逻辑实现是在配置文件中,可以在Makefile中找到目标为Makefile的规则查看具体的配置文件依赖项。

有关build-in函数的使用参考帮助文档目录:
Qt 5.12->qmake Manual->Test Functions 和 Qt 5.12->qmake Manual->Replace Functions

1、内建函数(下面的函数中var表示变量的名称)

qmake在源码内部用C++实现了一些内建函数的逻辑,在配置文件中定义了一些用qmake language定义的内置函数,都可以在pro文件中调用。对于用C++的内建函数,qt的帮助手册中有对这些函数进行描述,可以参考上面提及的帮助手册,如果手册看完还不懂,可以看qmake源码,在杂谈中提及如何快速定位C++端对函数的处理逻辑。对于配置文件中库函数查找可以参考:快速查找qt pro文件中的用qmake language写的库函数

1.1、特殊函数

特殊函数是指既不是test函数也不是replace函数,只是qmake在源码中特意用C++实现的函数。

option():option(host_build)必须要一起使用,option中的参数不能为其他参数,否则会报错。qmake源码中显示的作用就是强制设置host_build=true。host_build默认值为false。host_build=true表示创建 编译本地运行的程序 的工程,如果要创建 编译其他平台运行的程序的 工程,应该让host_build=false,并目标平台设置到属性QMAKE_XSPEC中。(可选的目标平台都在这里面QtIntallDir\Qt5.12.0\5.12.0\Src\qtbase\mkspecs中)。host_build的作用是指定创建QMAKE_SPEC的配置的工程 还是QMAKE_XSPEC的配置的工程,默认为false,指定后者。
 

1.2、test函数

(1)defined(string[,type]):判断名字为string的函数是否被定义,可以判断脚本代码中的自定义的变量名、自定义repace函数名、自定义test函数名是否已经被定义。实际操作大致与文档介绍相同,只是对build-in function进行测试时是无法检测出来的。 
qmake language defined(name[, type])函数_丘上人的博客-CSDN博客 
(2)touch(targetfilename, referencefilename):将targetfilename文件的写入时间改成referencefilename文件的写入时间。
(3)log(message)/warning(message)/message(message)/error(string):打印日志,如果调用error会终止脚本解析。
(4)eval(string):传入的string是脚本代码,执行string中的代码逻辑。

!build_pass:{
    code = $$escape_expand("\nvar = tt \nmessage(\$\$var)")
    eval($$code)
    message(out:$$var)
}

需要注意的是eval还是个replace函数,使用请参考: qmake language eval(string) 函数  
(5)unset(var):传入变量名,将定义的变量取消掉。
(6)contains(var, pattern, [mutuals]) :var一般是一个存放字符串组(list,空格为间隔符)的变量名称,查询数组中的字符串是否含有匹配pattern(正则表达式)的字符串,如果没有正则表达式中的特殊字符,表示查找var中是否含有字符串pattern。
如果有mutuals参数,匹配的字符串还要保证在mutuals中(对于mutuals的参数的处理,qmake源码中有bug,处理逻辑的for循环中有个多余的return,无法达到这个目的。如果非要解释成一种用法的话,那就是:查询mutuals中从右往左数的第一个与var中元素相同的字符串,如果找到,匹配是否符合pattern,符合返回true,不符合返回false,不会再继续mutuals中第二个的匹配。如果未找到返回false)。

QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
        const QMakeInternal::QMakeBuiltin &adef, const ProKey &function, const ProStringList &args){
.....
    case T_CONTAINS: {
.........
        {//处理三个参数的逻辑。
            const auto mutuals = args.at(2).toQStringRef().split(QLatin1Char('|'),
                                                                 QString::SkipEmptyParts);
            for (int i = l.size() - 1; i >= 0; i--) {
                const ProString &val = l[i];  //l表示存放第一个参数的字符串 list。
                for (int mut = 0; mut < mutuals.count(); mut++) {
                    auto stttt = val.toQStringRef();
                    if (val.toQStringRef() == mutuals[mut].trimmed()) {
                        if (val == qry)
                            return ReturnTrue;
                        if (!regx.isEmpty()) {
                            ProStringRoUser u2(val, m_tmp[m_toggle ^= 1]);
                            if (regx.exactMatch(u2.str()))
                                return ReturnTrue;
                        }
                        return ReturnFalse;   //多余的return,导致bug。 
                    }
                }
            }
        }
.....

}
var=ttr1 ttr2 ttr3
contains(var,ttr1):message(1) #输出1
contains(var,.*1.*):message(2) #输出2
contains(var,.*1,ttr1|ttr2):message(3) #输出空。 

(7)include(file, [into, [silent]]) :包含脚本文件,into表示对脚本文件中的变量添加前缀。silent值为true 或false,为true时表示解析file时不报错,比如变量缺失等问题不显示报错,并且即使解析错误还会让include返回true。

#in file a.prl
var1 = eee
-------------------------------------
#in file *.pro
include(a.prl,tt,true)
message($${var1})  #输出空
message($${tt.var1}) #输出eee

(8)write_file(filename, [var, [append] [exe]]):如果没有filename,则创建文件,filename可以带路径,默认相对于工程所在路径,如果需要转到目标路径可以使用shadowed(path)对路径进行转换。将var中的内容写入到文件中(注意var是变量名)。当有三个参数,第三个参数可选为append或exe。var为存放字符串组的变量名称,字符串组以空格为间隔,写入文件时一个字符串占一行。默认对文件重新写入,添加append参数时,表示对文件追加写入。输入exe表示为文件设置exe文件权限(可能在linux中用到)。在qt creator中需要明确选择右键 执行qmake  才会创建和写入文件,ctrl+s虽然保存和执行了脚本代码,但是不会创建或写入文件

var = "tt 111" 222 333 aa
write_file(a.txt,var)

-----------
in file a.txt:
tt 111
222
333
aa


(9)count(var,count,[ op=operator]):判断var中元素个数,var是一个存放字符串组的变量名称,字符串组以空格为间隔。count为要判断的数字,如果相同返回ture。当有第三个参数时,第三个参数可选为:>、greaterThan、>=、<、lessThan、<=、equals、isEqual、=、== (后面四个都表示等于的意思),表示var元素个数与count用第三个参数的符号比较,成功返回ture。

!build_pass:{
var = tt
    count(var,1,=):message(go1)  #输出go1
    count(var,1,equals):message(go2)  #输出go2
    count(var,11,<=):message(go3)  #输出go3
}

(10)isEmpty(var):判断变量是否为空。
(11)infile(filename,var,[pattern]):filename是指脚本文件,filename可以带路径。判断变量是否在脚本文件filename中,解析过程中会将脚本文件filename代码执行一遍。pattern表示正则表达式,如果有pattern参数,就进一步判断变量的值是否匹配pattern。成功返回true。

#in file a.prl
var1 = eee vvv
var2
message(here1)

-----------------------

#in file *.pro
build_pass:infile(a.prl,var1):message(suc)      #输出here1  suc  
build_pass:infile(a.prl,var1,vvv):message(suc)  #输出here1  suc
build_pass:infile(a.prl,var1,ttt):message(suc)  #输出here1
build_pass:infile(a.prl,var2):message(suc)  #输出here1
build_pass:infile(a.prl,var3):message(suc)  #输出here1

(12)parseJson(var,into):var存放json的内容,json内容中的字符串键或值需要对双引号添加转义字符。解析json文件中的内容到前缀为into的变量中。注意下面案例的脚本中变量与json中键值的对应关系

var = {\
    \"FirstName\": \"John\", \
    \"Phone\": [ \
        \"+44 1234567\", \
        \"+44 2345678\" \
    ] \
}

build_pass:parseJson(var,tt):{
    message($$tt.FirstName)  #输出John
    message($${tt.Phone.0})  #输出+44 1234567
    message($$size(tt.Phone.0))#输出1,也就是将"+44 1234567"解析成一个字符串,而不是一个字符串组。
    message($${tt.Phone.1})  #输出+44 2345678
    message($$tt._KEYS_)     #输出FirstName Phone
    message($$tt.Phone._KEYS_) #输出0 1
}

(13)mkpath(path):相对于当前路径创建文件夹,可传入多层文件夹参数,如果输入全路径,则在对应的路径下创建文件夹,如果需要转到目标路径可以使用shadowed(path)对路径进行转换。与write_file一样,都需要显示右键 执行qmake 才会创建文件夹。 
(14)requires(condition,condition.....):要求条件列表,条件不满足时,停止解析脚本并退出。满足时会返回false。condition参考:qmake language 条件语句 
(15)greaterThan(var,value),lessThan(var,value),equals(var,value),isEqual(var,value):var是变量名称,value是要比较的数值。var只能存放一个数字字符串,不能存放字符串组,对于字符串组解析时会直接拼接成一个字符串,用空格隔开,然后再拿去解析,对于数字串中有非法字符(比如空格)会将字符串解析成0。数字转换最终会调用下面的函数。

//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\tools\qlocale.cpp
bool QLocaleData::numberToCLocale(QStringView s, QLocale::NumberOptions number_options,CharBuff *result)
var = 11
lessThan(var,12):message(go) #输出go 

 (16)cache([var], [set|add|sub] [transient] [super|stash], [var]):向QMakeBaseEnv的QMakeEvaluate(存放了缓存文件和配置文件的上下文)中操作变量,并且向缓存文件中以追加的方式写入和修改变量。第一个和第三个参数都表示变量名称,第一个指要写入缓存文件中的变量名,第三个参数指本地变量名。第二个参数可以是一个字符串组,第一个表示写入的方式,第二个表示是否存入文件,第三组表示写入的文件,默认写入.qmake.cache中,当含有super参数时写入.qmake.super中,当含有stash参数时写入.qmake.stash中。需要显示执行qmake才会生效。

var  = 111
var1 = tt1
vars = gggg
!build_pass:cache(var,set)  #向目标路径下的.qmake.cache中以append的方式写入var = 111
!build_pass:cache(var,set)  #var与.qmake.cache中的var值一直,不写入
var=222
!build_pass:cache(var,set)  #向目标路径下的.qmake.cache中以append的方式写入var = 222
!build_pass:cache(var,add,var1)  #向目标路径下的.qmake.cache中以append的方式写入var += tt1
var2=111 zzz
!build_pass:cache(var,add,var2)  #向目标路径下的.qmake.cache中以append的方式写入
#var += \
#    111 \
#    zzz
var3=111 xxx
!build_pass:cache(var,sub,var3)  #向目标路径下的.qmake.cache中以append的方式写入
#var -= \
#    111 \
#    zzz

!build_pass:cache(var1,set transient) #为.qmake.cache所在的上下文(QMakeEvaluator)添加变量,但是不写入文件。
!build_pass:cache(vars,set stash) #向目标路径下的.qmake.stash中以append的方式写入var = 111

需要注意的是,修改缓存中的QMAKEPATH和QMAKEFETURES两个变量时,需要使用super参数才会有效。 所有相关处理逻辑都在下面的函数中:

E:\workspace\QtWork\qmake\library\qmakebuiltins.cpp
QMakeEvaluator::VisitReturn QMakeEvaluator::testFunc_cache(const ProStringList &args)

(17)CONFIG(config,[mutuals])/isActiveConfig(config,[mutuals]):两者的作用一致,判断config是否在全局变量CONFIG中存在。如果有mutuals参数,判断config是否既在CONFIG中又在mutuals中。 
(18)system(exec):执行shell/dos的脚本命令。
(19)discard_from(filename):filename是输入的脚本文件路径,如果脚本文件filename在之前通过include或load加载过,其内部的变量和函数会一直影响下文。这个函数可以将filename指定的文件的所有变量和函数记录全部消除掉,从而下文无法访问或使用相关内容。
(20)debug(level,message):输出到qmake的输出日志中。level表示日志的级别,从0开始的数字,qmake 参数中通过-d来设置日志级别一个-d表示升一级默认为0级,“-d”参数在qtcreator中不要使用,可在dos窗口运行qmake时使用。
(21)reload_properties():重新加载属性(properties)。返回true。参考:qmake属性

1.3、replace函数

(1)export(var) :传入变量名称,使函数内部的局部变量变成全局变量(变量的生命周期在调用函数之后)。

defineTest(ttfun){
    myvar.aa.cc=12345
    myvar.aa.bb=$${LITERAL_HASH}define TT 12345  #$${LITERAL_HASH}表示qmake language中输出#号
    export(myvar.aa.bb)
}

message($$myvar.aa.bb)  #输出为空
ttfun()
message($$myvar.aa.cc)  #输出空,全局环境中没有名字为myvar.aa.cc的变量
message($$myvar.aa.bb)  #输出#define TT 12345,全局环境中有名字为myvar.aa.bb的变量且有值

(2)escape_expand(string1 [, string2 ..., stringn])  :qmake language在对string的转义处理是在词法处理阶段,转义字符包括: ​​,而\n,\t,\r不会做转义。如果要在string中嵌入('\n','\t','\r'),需要用escape_expand(string)进行操作。

message(\[ \] \{ \} \( \) \$ \\ \' \" aa\nbb\rcc\tdd)
message($$escape_expand(\[ \] \{ \} \( \) \$ \\ \' \" aa\nbb\rcc\tdd))

 输出: 

​​

qmake language 内建函数 escape_expand(arg1 [, arg2 ..., argn])_丘上人的博客-CSDN博客

(3)val_escape(var)  :将qmake language string中无法单独显示的字符用合法显示的方式进行替换。对字符串组var进行这一操作。

var = \\ \" \' $ $${LITERAL_HASH} $$escape_expand(aa\nbb\rcc\tdd)
message($$val_escape(var))
#输出:
#Project MESSAGE: \\ \" \' \$ $${LITERAL_HASH} aa$$escape_expand(\\n)bb$$escape_expand(\\r)cc$$escape_expand(\\t)dd

var = message(\\ \" \' $ $${LITERAL_HASH} $$escape_expand(aa\nbb\rcc\tdd))
message($$val_escape(var))
eval($$val_escape(var))

输出:

​​

qmake language 内建函数 val_escape(variablename)_丘上人的博客-CSDN博客

(4)eval(string)/eval(string[,string[,string....]]):这个函数比较特殊,即作为test函数,又作为replace函数,作为test函数,传入一个参数,表示将string作为qmake language的语法进行执行,作为replace函数,表示将variable中的值取出赋予外部变量。

tttt=the value
var = "var = tttt$$escape_expand(\n)message(gogogo)"
message($$var)
eval($$var)   #将eval作为test函数使用
message(out:$$var)  #作为test函数时eval中执行的代码会影响后面的语义。
var=$$eval($$var)  #将eval作为replace函数使用。replace函数使用时必须在前面加$$,同样的,在$$会强制将eval解释为replace函数
message($$var)
#输出:
#Project MESSAGE: var = tttt
#message(gogogo)
#Project MESSAGE: gogogo
#Project MESSAGE: out:tttt
#Project MESSAGE: the value

qmake language eval(string) 函数_丘上人的博客-CSDN博客 

(5)shadowed(path):传入字符串,其作用是将源码路径path 映射到构建路径中相应位置。输入的path不能是工程文件夹之外,否则会输出空字符串。
qmake language shadowed(path)_丘上人的博客-CSDN博客 
(6)member(var, [start, [end]]):输出字符串组的从start下标到end下标的成员,下标左闭右闭,var是传入的字符串组,start、end描述字符串开始和结束下标。start小于end时正序,start大于end时反序。start 或end的值大于var的元素个数时返回空。传入两个参数时,第二个参数可以是start..end(中间两个点)的形式。

!build_pass:{
    var = "1 2" 3 4 5 6
    message($$member(var,1,4))  #输出3 4 5 6
    message($$member(var,4,1))  #输出6 5 4 3
    message($$member(var,1))    #输出3 4 5 6
    message($$member(var,7))    #输出空
    message($$member(var,1,7))  #输出空
    message($$member(var,1..4)) #输出3 4 5 6
    message($$member(var))      #输出1 2
}

(7)str_member(string,[start, [end]]):与member类同,专门处理单个字符串的。
(8)first(var):获取字符串组var中的第一个成员。var为空时返回空
(9)last(var):获取字符串组var中的最后一个成员。var为空时返回空
(10)take_first(var):从字符串组var中取走第一个成员。var中会删掉该成员。
(11)take_last(var):从字符串组var中取走最后一个成员。var中会删掉该成员。

!build_pass:{                               
    var = 1 2 3 4 5 6
    message($$first(var))        #输出1
    message($$last(var))         #输出6
    message($$var)               #输出1 2 3 4 5 6
    message($$take_first(var))   #输出1
    message($$take_last(var))    #输出6
    message($$var)               #输出2 3 4 5
}

(12)size(var):输出字符串组var的成员个数。
(13)str_size(string):输出字符串的长度,输入的string是个字符串。 
(14)cat(file, [mode=true|blob|lines]):读取文件。mode表示读取的方式,默认为true。
当mode为true或false时,表示按行读取,每一行都按qmake language的值的解析方式解析成多个字符串,为false时会多加一个字符串"\n"
当mode为blob时,表示将整个文件读取成一个字符串。
当mode为lines是,表示对文件一行一行的读取,每一行当做一个字符串

in file a.txt
aaa bbb ccc
dd

-----

var1 = cat(a.txt)
var2 = cat(a.txt,false)
var3 = cat(a.txt,blob)
var4 = cat(a.txt,lines)
message($$size(var1):\"$$var1\")
message($$size(var2):\"$$var2\")
message($$size(var3):\"$$var3\")
message($$size(var4):\"$$var4\")

输出:
Project MESSAGE: 4:"aaa bbb ccc dd"
Project MESSAGE: 6:"aaa bbb ccc 
 dd 
"
Project MESSAGE: 1:"aaa bbb ccc
dd"
Project MESSAGE: 2:"aaa bbb ccc dd"

(15)fromfile(file,var):输入的file是一个qmake language的代码文件,从file中读取名字为var的变量的值。运行该函数时,qmake会为其创建一个临时的QMakeEvaluator,存放解析的变量值等内容。

in file a.prl
var1 = aa bb
var2 =
var3

------

message($$fromfile(a.prl,var1)) #输出aa bb
message($$fromfile(a.prl,var2)) #报错
message($$fromfile(a.prl,var3)) #报错
message($$fromfile(a.prl,varn)) #报错

(16)list(args):args是多个字符串参数的意思,qmake内部产生一个临时的变量,存放这些值方便处理,一般结合for循环使用

for(var,list(one,two,three)){
    message($$var)
}

(17)sprintf(format, ...):格式化输出字符串
(18)format_number(number, [options...]):格式化数字。number为输入的数字,options为多选项。
ibase=n,表示输入的数字的进制,默认十进制
obase=n,表示输出的数字的进制,默认十进制
width=n,表示输出的数字的长度,不足处空格补齐,默认右对齐
zeropad,表示不足处用0补齐
padsign,表示为正数预留一个符号位不填充
alwayssign,表示为正数添加"+"
leftalign,表示左对齐。
暂时不支持浮点数

 message($$format_number(BAD, ibase=16 width=6 zeropad))  #输出002989

(19)num_add(num, ...):数字相加,可以有多个参数

first=10
second=11
second_neg = -$$second
second_neg ~= s/^--//               #将开头的连续的两个“-”替换成空。           
sum = $$num_add($$first, $$second_neg)
message($$sum)                        #输出-1

(20)join(var, [glue, [before, [after]]]):将字符数组var中的字符串拼接成一个字符串,glue表示拼接字符,默认glue为空字符串,before、after表示为拼接出的字符串添加前后缀,默认before、after为空字符串。

var = 1 2 3 4
message($$size(var):$$var) #输出4:1 2 3 4
tt=$$join(var,_,s,e)
message($$size(tt):$$tt)  #输出1: s1_2_3_4e
tt = $$join(var,,,e)
message($$size(tt):$$tt) #输出1: 1234e

(21)split(var, sep):将字符串组中的所有字符串进行拆分,sep作为拆分标识串,默认为空格

var = s1_2_3_4e 1_2_3_4
tt=$$split(var,_)
message($$size(tt):$$tt)   #输出8:s1 2 3 4e 1 2 3 4
var = "a  a" bb cc
tt= $$split(var)
message($$size(tt):$$tt)  #输出4:a a bb cc

(22)basename(var)/dirname(var):提取字符串组中的文件名和路径名

var = E:/aaa/a.prl  E:/aaa/a/
message($$basename(var))   #输出a.prl
message($$dirname(var))     #输出E:/aaa E:/aaa/a

(24)section(var, sep, begin, [end]):对字符串组var中的每个成员提取其中的部分你内容,sep默认为空格,begin,end下标满足左闭右闭

CONTACT = firstname:middlename:surname:phone  aa:bb:vv:cc
message($$section(CONTACT, :, 2, 2))  #输出surname vv

(25)find(var,pattern):从字符串组var中查找匹配的字符串,组合成新的字符串组输出

var = aa ab ff fa cc
message($$find(var,.*a))  #输出aa ab fa

(26)system(command, [mode], [stsvar]):运行命令行命令,返回执行命令时的输出内容,mode与cat中的mode类似,默认为true。stsvar存放状态值,如果有的话。
(27)unique(var):将字符串组var中相同的字符串剔除,只留一个
(28)sorted(var):将字符串组var中的字符串按字母表的顺序排列
(29)reverse(var):将字符串组var中的字符串逆序排列。

var = tt1 tt2 tt3
message($$reverse(var)) #输出tt3 tt2 tt1

(30)quote(args):args表示多个字符串参数,将多个字符串拼接成一个字符串,中间用空格隔开
(31)upper(args)\lower(args)\title(args):args表示多个字符串参数,upper将字符串全部转换成大写,存放到字符串组中。lower转换成小写,title首字母大写。
(34)re_escape(args):转换成正则表达式的字符串。参考qmake language 正则表达式
(35)files(pattern, [true|false]):在当前路径下查找符合pattern规则的文件,并输出文件名字(含子文件夹路径),recursive为true时会进入到子文件夹中查找,默认为false。
(36)prompt(question, [decorate]):question就是一个输出的字符串。该函数目的从输入窗口向脚本中输入值,qt creator无法进行输入,可能是qt creator的bug,只能通过命令行窗口通过命令的方式运行qmake才能进行输入,否则会被卡住。decorate可以为true或者false,默认为true,就是在questions后加一个“?”

message($$prompt(tt))

输入tt1 tt2 tt3 

(37)replace(var, pattern, string):将字符串组var中的字符串符合pattern的部分替换成string,string不指定表示删除字符串中符合pattern的部分。

var = afea ab ff fea cc
message($$replace(var,fe,vvv))  #输出avvva ab ff vvva cc

(38)sort_depends(var, [prefix, [suffixes, [prio-suffix]]]):内部函数,不需要
(39)resolve_depends(var, [prefix, [suffixes, [prio-suffix]]]):内部函数
(40)enumerate_vars():将当前上下文中所有变量名都列举出来。
(41)absolute_path(path, [base]):返回绝对路径,base默认值为当前路径
(42)relative_path(path, [base]):返回相对路径
(43)clean_path(path):将路径的分隔符全部换成"/",去除路径中的"." 和".."
(44)system_path(path):转换成操作系统风格的路径。
(45)shell_path(path):转成成shell风格的路径
(46)shell_quote(string):将string用双引号引用起来,并对shell中的特殊字符做转义处理
(47)getenv(arg):与$$(variablename)功能一样,只是这个函数可以支持名字中带有()的环境变量。

2、自定义函数

 qmake language支持自定义函数,自定义函数有关内容在qt帮助文档该目录下:Qt 5.12->qmake Manual->qmake Language

2.1、要点

1、函数定义的函数体或代码块必须用{}括起来,左括号“{”必须与函数名或者条件语句同行。如果不在同一行,必须要在后面加转义符号。

#defineReplace(repaceFunctionName){#defineReplace是关键字,表示定义replace函数。定义函数名字为repaceFunctionName的replace函数,函数的参数个数是不限制的,不需要列举参数名
defineReplace(repaceFunctionName)\
{
#.......#(函数体)函数体中语句结束不需要“;”!!!
}

defineTest(testFunctionName){#defineTest是关键字,表示定义test函数。定义函数名字为testFunctionName的test函数。
#.......
}

2、函数返回值必须要用()括起来。test函数如果需要返回值,最好显示的"return (true)"或"return (false)"。replace函数没有返回值则默认返回为空。

3、代码中区分test函数和replace函数的两种方法:
(1)看函数定义
(2)replace函数使用时必须在前面加$$ 否则会被识别为test函数,如果不存在这样的test函数则会报错。 
4、test函数的返回值无法赋予给变量,也无法打印出来,只能直接将test函数当做条件来使用。对于复杂的判断语句也只能直接将函数名进行拼接。
5、内置的replace 和test函数,传入函数参数时需要注意,有的函数需要传入变量名,有的函数需要传入变量的内容,需要视情况而定。比如greaterThan($$ARGC,1)和count(ARGS,2,>=)这两个test函数的传入参数。常用的打印函数message(), 如果想获取变量中的内容,须传入变量中的内容。
6、函数参数的个数在定义时无法通过模式进行限制。但是可以在函数体种通过加入判断语句进行筛查。因为qmake language只有字符串类型数据,所以传入的参数只有字符串类型。如果函数体没有用逻辑进行筛查,理论是可以传入无数个参数的。函数参数用逗号隔开。如果直接传入字符串其字符串中含有逗号,需要用双引号引起来,否则字符串会被当成多个参数。
7、函数内部的ARGC表示参数个数,ARGS表示参数列表,用空格隔开。size(ARGS)不一定等于ARGC,因为传入的参数很可能有含有空格的字符串。第一个参数存放在变量名为1的变量中,第二个存放在2中,依次类推。

2.2、案例

#-------自定义replace函数案例-----------------------------

defineReplace(myre){      #定义函数名为myre的replace函数。
    message($$1)
    message($$eval($$1))  #将第一个参数的值作为变量,将该变量的值取出来打印
    return ($$eval($$1))
}

var1=123
var2=var1

#replace函数调用
var3=$$myre(var2)       #将函数myre的返回值赋予var3
message($$var3)         #取出变量var3的值后打印
message($$myre(var2))   #将var2这个名称作为参数传入  注意看内置函数调用时在前面加$$,表示取函数的返回值
message($$myre($$var2)) #将var2的值作为参数传入
#输出
#Project MESSAGE: var2  #var3=$$myre(var2),调用了myre函数,myre中有message函数,导致了这里两条的输出
#Project MESSAGE: var1  
#Project MESSAGE: var1  #message($$var3) 导致的这一条输出
#Project MESSAGE: var2  #message($$myre(var2))导致这里三条输出
#Project MESSAGE: var1
#Project MESSAGE: var1
#Project MESSAGE: var1  #message($$myre($$var2))导致这里三条输出
#Project MESSAGE: 123
#Project MESSAGE: 123

#----------end-----------------


#---------自定义test函数案例------------------

#(函数体)函数体中语句结束不需要“;”!!!
defineTest(mytt){                    #定义函数名字为mytt的test函数。 “{”需要与函数名同行。
    isEmpty(ARGS){message(no input)}  #ARGS表示参数列表,词句等价于count(ARGS,0),如果没有参数,返回true而执行message(no input),否则执行else  。
    else{                            #“{”必须与else在同一行!!! 该行语句的完整写法为:else:{
        message($$ARGS)              #输出函数
        message($$ARGC)              #输出ARGS的个数  #$$size(ARGS)
        !isEmpty(1){
            message($$1)             #输出第一个参数的值,$$1与$${1}等价
            var = $$eval(1)          #等价于var=$$1
            var = $$eval($$1)        #将第一个变量的值为变量名作为参数传入eval,取出该变量中的值放到var中(变量的嵌套访问)
            !isEmpty(var){          #判断变量var值不为空 此处等价于!isEmpty($$1){
                message($$var)       #输出变量var的值
                var=$$eval($$var)    #将var的值为变量名作为参数传入eval,取出该变量的值放到var中
                !isEmpty(var){      #此处等价于!isEmpty($$eval($$1)){
                    message($$var)   #判断变量var值不为空,并输出var的值,此处等价于message($$eval($$eval($$1)))
                }
            }
        }
        !isEmpty(2){                #与  !isEmpty($$2){ 等价
            message($$2)
            !isEmpty($$eval($$2)){message($$eval($$2))}
        }
        !isEmpty(3){
            message($$3)
        }
        isEmpty(4){message(no 4th input argument)}
        return (true)
    }
    message(end)
    return(false)
}

#test函数调用
mytt(){message(return true)}         #mytt()作为条件,返回true则打印return true的信息,返回false则打印return false信息
else{message("return false")}  
#输出
#Project MESSAGE: no input
#Project MESSAGE: end
#Project MESSAGE: return false

mytt(var2){message(return true)}
else{message(return false)}
#输出
#Project MESSAGE: one argument
#Project MESSAGE: var2
#Project MESSAGE: 1
#Project MESSAGE: var2
#Project MESSAGE: var1
#Project MESSAGE: 123
#Project MESSAGE: return true


mytt(var2,var1,1){message(return true)}
else{message(return false)}
#输出
#Project MESSAGE: var2 var1 1
#Project MESSAGE: 3
#Project MESSAGE: var2
#Project MESSAGE: var1
#Project MESSAGE: 123
#Project MESSAGE: var1
#Project MESSAGE: 123
#Project MESSAGE: return true

#------------end--------------------

函数参数需要用逗号隔开,size(ARGS)计算ARGS的个数时可能不准确,需要用ARGC变量,考虑情况如下:

defineTest(mytt1){
    message(ARGC=$$ARGC : ARGS=$$ARGS : size= $$size(ARGS) : 1=$$1)
}

tt=true de df
tt1=1
tt2=2
tt3=3
mytest9(tt) #输出ARGC=1 : ARGS=true de df : size = 3 : 1=true de df
mytest9(tt1,tt2,tt3) #输出ARGC=3 : ARGS=1 2 3 : size= 3 : 1=1 2 3) 

2.3、test函数返回值特例

自定义的test 函数(假设名字为xxxfun)有的时候没有写return。
1、没有在定义中写return 的情况:
对于空内容的xxxfun定义,返回值为true,参考mytest5;对于内部无test函数的xxxfun函数定义,返回值为true,参考mytest5和mytest8;对于内部有test函数的xxxfun函数定义,返回值为最后一个test函数的返回值。参考mytest4和mytest6

2、明确有写return() 等价于return true,参考mytest1

3、明确有写return (后面什么都没有),行为未定义,参看mytest9、mytest10。

3、带有test函数的xxxfun函数定义的分支中没有return,则该分支会返回内部test函数的返回值,参考mytest2和mytest3

4、对于带有test函数有分支的xxxfun函数定义,各个分支中都有return ,则返回return的返回值,参考mytest和mytest1

tt=true
ttv=123
tt1=  #定义空变量

message($$tt)  #输出true
message($$ttv) #输出123

isEmpty(tt){message(isEmpty_tt_true)      #输出: isEmpty_tt_false
}else{message(isEmpty_tt_false)}
isEmpty(ttv){message(isEmpty_ttv_true)    #输出: isEmpty_ttv_false
}else{message(isEmpty_ttv_false)}
isEmpty(tt1){message(isEmpty_tt1_true)    #输出: isEmpty_tt1_true
}else{message(isEmpty_tt1_false)}

defineTest(mytest){
    isEmpty($${1}):return (false)
    return()
}
mytest(tt){message(mytest_tt_true)}        #输出: mytest_tt_true
else{message(mytest_tt_false)}
mytest(ttv){message(mytest_ttv_true)}      #输出: mytest_ttv_true
else{message(mytest_ttv_false)}
mytest(tt1){message(mytest_tt1_true)}      #输出: mytest_tt1_false
else{message(mytest_tt1_false)}

defineTest(mytest1){
    isEmpty($${1}):return (false)
    return (true)
}
mytest1(tt){message(mytest1_tt_true)      #输出: mytest1_tt_true
}else{message(mytest1_tt_false)}
mytest1(ttv){message(mytest1_ttv_true)    #输出: mytest1_ttv_true
}else{message(mytest1_ttv_false)}
mytest1(tt1){message(mytest1_tt1_true)    #输出: mytest1_tt1_false
}else{message(mytest1_tt1_false)}

defineTest(mytest2){
    isEmpty($${1}):return (false)
}
mytest2(tt){message(mytest2_tt_true)      #输出: mytest2_tt_false
}else{message(mytest2_tt_false)}
mytest2(ttv){message(mytest2_ttv_true)    #输出: mytest2_ttv_false
}else{message(mytest2_ttv_false)}
mytest2(tt1){message(mytest2_tt1_true)    #输出: mytest2_tt1_false
}else{message(mytest2_tt1_false)}

defineTest(mytest3){
    isEmpty($${1}):return (true)
}
mytest3(tt){message(mytest3_tt_true)      #输出: mytest3_tt_false
}else{message(mytest3_tt_false)}
mytest3(ttv){message(mytest3_ttv_true)    #输出: mytest3_ttv_false
}else{message(mytest3_ttv_false)}
mytest3(tt1){message(mytest3_tt1_true)    #输出: mytest3_tt1_true
}else{message(mytest3_tt1_false)}

defineTest(mytest4){
    isEmpty($${1})
}
mytest4(tt){message(mytest4_tt_true)      #输出: mytest4_tt_false
}else{message(mytest4_tt_false)}
mytest4(ttv){message(mytest4_ttv_true)    #输出: mytest4_ttv_false
}else{message(mytest4_ttv_false)}
mytest4(tt1){message(mytest4_tt1_true)    #输出: mytest4_tt1_true
}else{message(mytest4_tt1_false)}

defineTest(mytest5){
}
mytest5(tt){message(mytest5_tt_true)      #输出: mytest5_tt_true
}else{message(mytest5_tt_false)}
mytest5(ttv){message(mytest5_ttv_true)    #输出: mytest5_ttv_true
}else{message(mytest5_ttv_false)}
mytest5(tt1){message(mytest5_tt1_true)    #输出: mytest5_tt1_true
}else{message(mytest5_tt1_false)}

defineTest(mytest6){
    isEmpty($${1})
    isEmpty($${2})
}
mytest6(ttv,tt1){message(mytest6_true)    #输出: mytest6_true
}else{message(mytest6_false)}
mytest6(tt1,ttv){message(mytest6_true)    #输出: mytest6_false
}else{message(mytest6_false)}

defineTest(mytest7){
    isEmpty($${1}):return (false)
    return
}
mytest7(tt){message(mytest7_tt_true)      #输出: mytest7_tt_false
}else{message(mytest7_tt_false)}
mytest7(ttv){message(mytest7_ttv_true)    #输出: mytest7_ttv_false
}else{message(mytest7_ttv_false)}
mytest7(tt1){message(mytest7_tt1_true)    #输出: mytest7_tt1_false
}else{message(mytest7_tt1_false)}

defineTest(mytest8){
    var1=$${1}
}
mytest8(tt){message(mytest8_tt_true)      #输出: mytest8_tt_true
}else{message(mytest8_tt_false)}
mytest8(ttv){message(mytest8_ttv_true)    #输出: mytest8_ttv_true
}else{message(mytest8_ttv_false)}
mytest8(tt1){message(mytest8_tt1_true)    #输出: mytest8_tt1_true
}else{message(mytest8_tt1_false)}

defineTest(mytest9){
    isEmpty($${1}):return (false)
    return
    message(here)     message是test函数
}

mytest9(tt){message(mytest9_tt_true) 
}else{message(mytest9_tt_false)}
mytest9(ttv){message(mytest9_ttv_true) 
}else{message(mytest9_ttv_false)}
mytest9(tt1){message(mytest9_tt1_true)
}else{message(mytest9_tt1_false)}
#输出:
#Project MESSAGE: here
#Project MESSAGE: mytest9_tt_false
#Project MESSAGE: here
#Project MESSAGE: mytest9_ttv_false
#Project MESSAGE: mytest9_tt1_false


defineTest(mytest10){
    isEmpty($${1}):return (true)
    return
    message(here)     message是test函数
}

mytest10(tt){message(mytest10_tt_true)
}else{message(mytest10_tt_false)}
mytest10(ttv){message(mytest10_ttv_true)
}else{message(mytest10_ttv_false)}
mytest10(tt1){message(mytest10_tt1_true)
}else{message(mytest10_tt1_false)}
#输出:
#Project MESSAGE: here
#Project MESSAGE: mytest10_tt_true
#Project MESSAGE: here
#Project MESSAGE: mytest10_ttv_true
#Project MESSAGE: mytest10_tt1_true

3、打印信息

对于打印信息的函数,qmake中提供了message(message)  warning(message) error(message)三个函数用于输出到qtcreator的概要信息窗口,另外还有一个log(message)用于输出到标准输出stderr的函数(这意味着默认情况下在qtcreator的概要信息窗口无法看到log输出来的信息,如果在DOS窗口中用命令行运行qmake则可以可以看到log输出的信息)。

如果.pro中的CONFIG有release和debug参数,或者有debug_and_release参数,就会生成Makefile.Debug 、Makefile.Release,以及debug、release文件夹。否则Makefile.Debug会与Makefile合并,只会生成Makefile。(这是qmake的工作,可以在.pro中用message($$CONFIG)来查看CONFIG变量的内容,qt生成一个Makefile就会输出一遍message内容,默认情况下CONFIG中是有debug_and_release这个参数的或者还会附加debug release参数,所以经常会看到打印函数message、waring、error输出量遍信息。可以通过在打印函数前加“!build_pass:”让打印函数一次qmake只输出一遍)
​​

4、常见语法语义报错

4.1 说明

1、qmake language中用message输出的信息的样式一般为(有类似格式的信息可能出自.conf会.pri等配置文件中):
​​

2、qmake langueage的一些配置提示信息会在加载配置文件的时候发出,其样与打印信息函数的样式一样。
3、常见的qmake language语法或语义错误的信息一般由qmake内部输出,其样式如下:
​​

4.2 常见语法语义报错

1、Extra characters after test expression.  test函数之后的逻辑中有无法识别的字符,一般是return 后的返回值没有用()括起来。
2、Conditional must expand to exactly one word.  $$后的表达式空。等同于在代码中加了一行“$$”,一般的$$replaceFunctionName(variable),如果replaceFunctionName(variable)返回空就会出现这个报错
3、'dirname' is not a recognized test function.  replace函数调用时前面没有加$$,调用dirname函数代码格式:$$dirname(file)

4、Top-level return() requires zero arguments.  test或者replace函数定义没有将最上面的“{”与函数名放在同一行

#正确写法
defineTest(mytt){  #不使用换行转义符,完整的写法,在函数名和条件语句else之后都加":",后接的逻辑代码的“{”必须与他们同行
    isEmpty(ARGS){message(no input) }
    else{
        message(here)
    }
    return (false)
}
defineTest(mytt){  #不使用换行转义符,简略写法
    isEmpty(ARGS){message(no input) }
    else{message(here)}
    return (false)
}
defineTest(mytt)\  #使用换行转义符
{
    isEmpty(ARGS){message(no input) }
    return (false)
}

#错误写法,
#defineTest(mytt)  #将{与函数名分开写了
#{
#    isEmpty(ARGS){message(no input) }
#    return (false)
#}

#defineTest(mytt)  #错误写法,将{与函数名分开写了
#{
#    isEmpty(ARGS){message(no input) }
#     else #错误写法,将else与{分开行写了
#     {
#        message(here) 
#     }
#    return (false)
#}

5、'count' is not a recognized replace function,count是test函数,对test函数使用$$ 就会报这个错误。

6、Unexpected return value from test .....  ,test函数的返回值只能为return (true)或return(false),不能返回test函数或变量或其他东西

5、杂谈

对于内建函数,可以通过在qmakebuiltins.cpp中进行关键字(T_*)快速查找,定位到对应内建函数的处理逻辑。

qmake 源码中处理replace函数的代码入口为:
E:\workspace\QtWork\qmake\library\qmakeevaluator.cpp
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateExpandFunction(const ProKey &func, const ushort *&tokPtr, ProStringList *ret)

qmake 源码中处理test函数的代码入口为:
E:\workspace\QtWork\qmake\library\qmakeevaluator.cpp
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( const ProKey &func, const ushort *&tokPtr)

//E:\workspace\QtWork\qmake\library\qmakebuiltins.cpp
void QMakeEvaluator::initFunctionStatics()
{
    static const QMakeBuiltinInit expandInits[] = {
        { "member", E_MEMBER, 1, 3, "var, [start, [end]]" },
        { "str_member", E_STR_MEMBER, -1, 3, "str, [start, [end]]" },
        { "first", E_FIRST, 1, 1, "var" },
        { "take_first", E_TAKE_FIRST, 1, 1, "var" },
        { "last", E_LAST, 1, 1, "var" },
        { "take_last", E_TAKE_LAST, 1, 1, "var" },
        { "size", E_SIZE, 1, 1, "var" },
        { "str_size", E_STR_SIZE, -1, 1, "str" },
        { "cat", E_CAT, 1, 2, "file, [mode=true|blob|lines]" },
        { "fromfile", E_FROMFILE, 2, 2, "file, var" },
        { "eval", E_EVAL, 1, 1, "var" },
        { "list", E_LIST, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "sprintf", E_SPRINTF, 1, QMakeBuiltinInit::VarArgs, "format, ..." },
        { "format_number", E_FORMAT_NUMBER, 1, 2, "number, [options...]" },
        { "num_add", E_NUM_ADD, 1, QMakeBuiltinInit::VarArgs, "num, ..." },
        { "join", E_JOIN, 1, 4, "var, [glue, [before, [after]]]" },
        { "split", E_SPLIT, 1, 2, "var, sep" },
        { "basename", E_BASENAME, 1, 1, "var" },
        { "dirname", E_DIRNAME, 1, 1, "var" },
        { "section", E_SECTION, 3, 4, "var, sep, begin, [end]" },
        { "find", E_FIND, 2, 2, "var, str" },
        { "system", E_SYSTEM, 1, 3, "command, [mode], [stsvar]" },
        { "unique", E_UNIQUE, 1, 1, "var" },
        { "sorted", E_SORTED, 1, 1, "var" },
        { "reverse", E_REVERSE, 1, 1, "var" },
        { "quote", E_QUOTE, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "escape_expand", E_ESCAPE_EXPAND, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "upper", E_UPPER, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "lower", E_LOWER, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "title", E_TITLE, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "re_escape", E_RE_ESCAPE, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "val_escape", E_VAL_ESCAPE, 1, 1, "var" },
        { "files", E_FILES, 1, 2, "pattern, [recursive=false]" },
        { "prompt", E_PROMPT, 1, 2, "question, [decorate=true]" },
        { "replace", E_REPLACE, 3, 3, "var, before, after" },
        { "sort_depends", E_SORT_DEPENDS, 1, 4, "var, [prefix, [suffixes, [prio-suffix]]]" },
        { "resolve_depends", E_RESOLVE_DEPENDS, 1, 4, "var, [prefix, [suffixes, [prio-suffix]]]" },
        { "enumerate_vars", E_ENUMERATE_VARS, 0, 0, "" },
        { "shadowed", E_SHADOWED, 1, 1, "path" },
        { "absolute_path", E_ABSOLUTE_PATH, -1, 2, "path, [base]" },
        { "relative_path", E_RELATIVE_PATH, -1, 2, "path, [base]" },
        { "clean_path", E_CLEAN_PATH, -1, 1, "path" },
        { "system_path", E_SYSTEM_PATH, -1, 1, "path" },
        { "shell_path", E_SHELL_PATH, -1, 1, "path" },
        { "system_quote", E_SYSTEM_QUOTE, -1, 1, "arg" },
        { "shell_quote", E_SHELL_QUOTE, -1, 1, "arg" },
        { "getenv", E_GETENV, 1, 1, "arg" },
    };
    statics.expands.reserve((int)(sizeof(expandInits)/sizeof(expandInits[0])));
    for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
        statics.expands.insert(ProKey(expandInits[i].name), QMakeBuiltin(expandInits[i]));

    static const QMakeBuiltinInit testInits[] = {
        { "requires", T_REQUIRES, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "greaterThan", T_GREATERTHAN, 2, 2, "var, val" },
        { "lessThan", T_LESSTHAN, 2, 2, "var, val" },
        { "equals", T_EQUALS, 2, 2, "var, val" },
        { "isEqual", T_EQUALS, 2, 2, "var, val" },
        { "versionAtLeast", T_VERSION_AT_LEAST, 2, 2, "var, version" },
        { "versionAtMost", T_VERSION_AT_MOST, 2, 2, "var, version" },
        { "exists", T_EXISTS, 1, 1, "file" },
        { "export", T_EXPORT, 1, 1, "var" },
        { "clear", T_CLEAR, 1, 1, "var" },
        { "unset", T_UNSET, 1, 1, "var" },
        { "eval", T_EVAL, 0, QMakeBuiltinInit::VarArgs, nullptr },
        { "CONFIG", T_CONFIG, 1, 2, "config, [mutuals]" },
        { "if", T_IF, 1, 1, "condition" },
        { "isActiveConfig", T_CONFIG, 1, 2, "config, [mutuals]" },
        { "system", T_SYSTEM, 1, 1, "exec" },
        { "discard_from", T_DISCARD_FROM, 1, 1, "file" },
        { "defined", T_DEFINED, 1, 2, "object, [\"test\"|\"replace\"|\"var\"]" },
        { "contains", T_CONTAINS, 2, 3, "var, val, [mutuals]" },
        { "infile", T_INFILE, 2, 3, "file, var, [values]" },
        { "count", T_COUNT, 2, 3, "var, count, [op=operator]" },
        { "isEmpty", T_ISEMPTY, 1, 1, "var" },
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        { "parseJson", T_PARSE_JSON, 2, 2, "var, into" },
#endif
        { "load", T_LOAD, 1, 2, "feature, [ignore_errors=false]" },
        { "include", T_INCLUDE, 1, 3, "file, [into, [silent]]" },
        { "debug", T_DEBUG, 2, 2, "level, message" },
        { "log", T_LOG, 1, 1, "message" },
        { "message", T_MESSAGE, 1, 1, "message" },
        { "warning", T_WARNING, 1, 1, "message" },
        { "error", T_ERROR, 0, 1, "message" },
        { "mkpath", T_MKPATH, 1, 1, "path" },
        { "write_file", T_WRITE_FILE, 1, 3, "name, [content var, [append] [exe]]" },
        { "touch", T_TOUCH, 2, 2, "file, reffile" },
        { "cache", T_CACHE, 0, 3, "[var], [set|add|sub] [transient] [super|stash], [srcvar]" },
        { "reload_properties", T_RELOAD_PROPERTIES, 0, 0, "" },
    };

qmake language variable true false test function host_build_丘上人的博客-CSDN博客 
qmake language eval(string) 函数_丘上人的博客-CSDN博客 
qmake language defined(name[, type])函数_丘上人的博客-CSDN博客 
qmake language shadowed(path)_丘上人的博客-CSDN博客
qmake(属性、变量、环境变量)_丘上人的博客-CSDN博客 
qt 工程构建过程 默认构建路径设置 通过Dos窗口运行命令编译qt工程_-CSDN博客_qt路径配置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值