配色: 字号:
编写 CMakeLists 文件掌握 CMake
2023-08-10 | 阅:  转:  |  分享 
  
编辑CMakeList文件CMakeLists文件几乎可以在任何文本编辑器中进行编辑。一些编辑器,如记事本++,带有CMake语法突出显示和
内置缩进支持。对于像Emacs或Vim这样的编辑器,CMake包括缩进和语法突出显示模式。这些可以找到在源发行版的目录中,或从CM
ake下载页面。Auxiliary在任何受支持的生成器(Makefiles、VisualStudio等)中,如果您编辑CMakeL
ists文件并重建,有些规则会自动调用CMake以更新生成的文件(例如生成文件或项目文件),根据需要。这有助于确保您生成的文件是始
终与您的CMakeList文件同步。清宗语言CMake语言由注释、命令和变量组成。评论注释从行尾开始并一直运行到行尾。有关更多详细
信息,请参阅手册。#变量CMakeLists文件使用变量与任何编程语言非常相似。清明变量名称区分大小写,只能包含字母数字字符和下划
线。许多有用的变量由CMake自动定义,它们是手册中讨论过。这些变量以开头。避免此命名约定(并且,理想情况下,建立自己的)用于特定
于项目的变量。CMAKE_所有CMake变量在内部存储为字符串,尽管它们可能有时被解释为其他类型的。使用该命令设置变量值。在最简单
的形式中,第一个参数是变量的名称和其余参数是值。打包了多个值参数到以分号分隔的列表中并存储在变量作为字符串。例如:set(Foo"
")#1quotedarg->valueis""set(Fooa)#1unquotedarg->valueis"a"set(Foo
"abc")#1quotedarg->valueis"abc"set(Fooabc)#3unquotedargs->valueis
"a;b;c"可以使用语法在命令参数中引用变量,其中是变量名称。如果命名变量未定义,引用将替换为空字符串;否则,它将替换为变量的值
。更换是在扩展未带引号的参数之前执行,因此可变包含分号的值被拆分为零个或多个参数原始未引用论点的位置。例如:${VAR}VARse
t(Fooabc)#3unquotedargs->valueis"a;b;c"command(${Foo})#unquotedar
greplacedbya;b;c#andexpandstothreeargumentscommand("${Foo}")#quot
edargvalueis"a;b;c"set(Foo"")#1quotedarg->valueisemptystringcomma
nd(${Foo})#unquotedargreplacedbyemptystring#andexpandstozeroargum
entscommand("${Foo}")#quotedargvalueisemptystring系统环境变量和Windows注册
表值可以是直接在CMake中访问。要访问系统环境变量,使用语法。CMake还可以引用注册表许多命令中的条目使用形式的语法,其中路径
从注册表树和注册表项生成。$ENV{VAR}[HKEY_CURRENT_USER\Software\path1\path2;key
]可变范围CMake中的变量的作用域与大多数变量略有不同语言。设置变量时,该变量对当前可见CMakeLists文件或函数以及任何子
目录的CMakeLists文件,调用的任何函数或宏,以及包含使用命令。当新的子目录被处理(或调用函数),创建一个新的变量范围,并且
使用调用中所有变量的当前值初始化范围。在子作用域中创建的任何新变量或所做的更改对现有变量,不会影响父范围。考虑以下示例:funct
ion(foo)message(${test})#testis1hereset(test2)message(${test})#te
stis2here,butonlyinthisscopeendfunction()set(test1)foo()message($
{test})#testwillstillbe1here在某些情况下,您可能希望函数或子目录设置变量在其父级的作用域中。有一种方法
可以让CMake返回一个值,可以通过使用带有命令的选项来完成。我们可以修改前面的示例,以便函数更改测试的值在其父级的范围内,如下所
示:PARENT_SCOPEfoofunction(foo)message(${test})#testis1hereset(tes
t2PARENT_SCOPE)message(${test})#teststill1inthisscopeendfunction(
)set(test1)foo()message(${test})#testwillnowbe2hereCMake中的变量按命令执行
的顺序定义。请考虑以下示例:#FOOisundefinedset(FOO1)#FOOisnowsetto1set(FOO0)#FO
Oisnowsetto0要了解变量的范围,请考虑以下示例:set(foo1)#processthedir1subdirectory
add_subdirectory(dir1)#includeandprocessthecommandsinfile1.cmakei
nclude(file1.cmake)set(bar2)#processthedir2subdirectoryadd_subdir
ectory(dir2)#includeandprocessthecommandsinfile2.cmakeinclude(fil
e2.cmake)在此示例中,由于变量是在首先,它将在处理DIR1和DIR2时定义。在相反,仅在处理DIR2时定义。同样,将在处理
file1.cmake和file2.cmake,而只会在处理时定义文件2.cmake.foobarfoobar命令命令由命令名称、
左括号、空格组成分隔的参数和右括号。每个命令在它在CMakeLists文件中的显示顺序。有关完整列表,请参阅手册CMake命令。C
Make不再区分大小写,因此在你看到的地方,你可以使用or代替。它被认为是使用小写命令的最佳做法。所有空格(空格、换行符、制表符)
被忽略,但分隔参数除外。因此,命令可能跨越多行,只要命令名称和左括号在同一行。commandCOMMANDCommandCMake
命令参数以空格分隔且区分大小写。命令参数可以是引用的,也可以是未引用的。引用的参数开始和结束在双引号(“)中,并且始终只表示一个参
数。任意双倍值中包含的引号必须使用反斜杠进行转义。考虑对需要转义的参数使用括号参数,请参阅手册。一个没有引用的论点以双引号以外的任
何字符开头(后面的双引号是文字),并通过以下方式自动扩展为零个或多个参数在值内的分号上分隔。例如:command("")#1quo
tedargumentcommand("abc")#1quotedargumentcommand("a;b;c")#1quoted
argumentcommand("a""b""c")#3quotedargumentscommand(abc)#3unquoted
argumentscommand(a;b;c)#1unquotedargumentexpandsto3基本命令正如我们之前看到的,
和命令显式设置或取消设置变量。、和命令提供字符串和列表的基本操作。和命令是主要的用于定义要构建的可执行文件和库的命令,以及哪些源文
件组成它们。对于VisualStudio项目,源文件将照常显示在IDE中,但任何头文件都显示在项目使用不会。要显示头文件,只需将它
们添加到可执行文件或库的源文件列表中;这可以为所有发电机完成。任何不使用的生成器头文件直接(例如基于Makefile的生成器)将干
脆忽略它们。流控制CMake语言提供了三种流控制结构来帮助组织您的CMakeList文件并保持它们可维护。条件语句(例如)循环构造
(例如和)程序定义(例如)条件语句首先,我们将考虑该命令。在许多方面,CMake中的命令就像任何其他语言。它计算其表达式并使用它来
执行代码在其正文中或子句中的代码(可选)。为例:if(FOO)#dosomethinghereelse()#dosomething
elseendif()CMake还支持帮助顺序测试多个条件。例如:if(MSVC80)#dosomethinghereelseif
(MSVC90)#dosomethingelseelseif(APPLE)#dosomethingelseendif()该命令记录
了它可以测试的许多条件。循环构造和命令允许您处理按顺序发生的重复性任务。命令中断在正常情况下脱离OR循环结束。该命令使您能够执行组
的CMake命令在列表成员上重复执行。考虑以下示例改编自VTKforeach(tfileTestAnisotropicDiffus
ion2DTestButterworthLowPassTestButterworthHighPassTestCityBlockDi
stanceTestConvolve)add_test(${tfile}-image${VTK_EXECUTABLE}${VTK_
SOURCE_DIR}/Tests/rtImageTest.tcl${VTK_SOURCE_DIR}/Tests/${tfile}
.tcl-D${VTK_DATA_ROOT}-VBaseline/Imaging/${tfile}.png-A${VTK_SOUR
CE_DIR}/Wrapping/Tcl)endforeach()命令的第一个参数是变量,每次迭代时将采用不同的值循环;其余参数是
要在其上执行的值列表圈。在此示例中,循环的主体只是一个CMake命令,.在的正文中,每个引用循环变量(在本例中)的时间将替换为列表
中的当前值。在第一个迭代,出现的将替换为。在下一次迭代中,将替换为。循环将继续循环,直到处理完所有参数。tfile${tfile}
TestAnisotropicDiffusion2D${tfile}TestButterworthLowPass值得一提的是,循环
可以嵌套,并且循环变量在任何其他变量之前被替换扩张。这意味着在循环的主体中,您可以使用循环变量构造变量名称。在下面的代码中,循环变
量展开,然后与连接。然后扩展并测试新变量名称看看它是否匹配.tfile_TEST_RESULTFAILEDif(${${tfile
}_TEST_RESULT}MATCHESFAILED)message("Test${tfile}failed.")endif()
该命令根据测试条件提供循环。这命令中测试表达式的格式与它适用于命令,如前所述。考虑以下示例,由CTest使用。请注意,CTest在
内部更新的值。CTEST_ELAPSED_TIME########################################
##############runparaviewandctesttestdashboardsfor6hours#while(${
CTEST_ELAPSED_TIME}LESS36000)set(START_TIME${CTEST_ELAPSED_TIME})
ctest_run_script("dash1_ParaView_vs71continuous.cmake")ctest_run_
script("dash1_cmake_vs71continuous.cmake")endwhile()过程定义和命令支持重复性任
务可能分散在您的CMakeLists文件中。一旦宏或函数被定义,它可以被任何CMakeList文件使用后处理它的定义。CMake中
的函数非常类似于C或C++中的函数。您可以将参数传递到其中,它们成为功能。同样,一些标准变量,如、、、和、等。是定义。函数调用具有
动态作用域。在一个函数中,你在新的变量范围内;这就像你如何掉进一个使用该命令的子目录,并且位于新的变量范围。函数时定义的所有变量被
称为保持定义,但对变量的任何更改或新的变量仅存在于函数中。当函数返回时,这些变量将消失。更简单地说:当你调用函数,推送一个新的变量
范围;当它返回时,弹出变量范围。ARGCARGVARGNARGV0ARGV1该命令定义一个新函数。第一个参数是要定义的函数的名称;
所有其他参数均为函数的形式参数。function(DetermineTime_time)#passtheresultuptowha
teverinvokedthisset(${_time}"1:23:45"PARENT_SCOPE)endfunction()#n
owusethefunctionwejustdefinedDetermineTime(current_time)if(DEFINE
Dcurrent_time)message(STATUS"Thetimeisnow:${current_time}")endif(
)请注意,在此示例中,用于传递返回变量。调用该命令时,其值为,该值为。最后,该命令使用该选项在调用方的作用域,而不是本地作用域。_
time_timecurrent_timePARENT_SCOPE宏的定义和调用方式与函数相同。这主要区别在于宏不会推送和弹出新变
量范围,并且宏的参数不被视为变量而是在执行之前替换字符串。这很像宏与C或C++中的函数之间的差异。第一个参数是要创建的宏的名称;所
有其他参数是宏的形式参数。#defineasimplemacromacro(assertTESTCOMMENT)if(NOT${T
EST})message("Assertionfailed:${COMMENT}")endif()endmacro()#useth
emacrofind_library(FOO_LIBfoo/usr/local/lib)assert(${FOO_LIB}"Una
bletofindlibraryfoo")上面的简单示例创建了一个名为的宏。宏定义为两个参数;第一个是要测试的值和第二个是如果测试
失败,要打印出的注释。身体的宏是带有命令的简单命令里面。当命令为发现。只需使用宏的名称即可调用宏,就好像它是命令。在上面的例子中,
如果未找到,则将显示消息,指示错误条件。assertFOO_LIB该命令还支持定义采用变量的宏参数列表。如果要定义一个宏,这会很有
用具有可选参数或多个签名。变量参数可以改为使用and、、等进行引用的形式参数。表示第一个参数宏;表示下一个,依此类推。你也可以混合
使用正式参数和变量参数,如下面的示例。ARGCARGV0ARGV1ARGV0ARGV1#defineamacrothattakes
atleasttwoarguments#(theformalarguments)plusanoptionalthirdargume
ntmacro(assertTESTCOMMENT)if(NOT${TEST})message("Assertionfailed:
${COMMENT}")#ifcalledwiththreeargumentsthenalsowritethe#messageto
afilespecifiedasthethirdargumentif(${ARGC}MATCHES3)file(APPEND${A
RGV2}"Assertionfailed:${COMMENT}")endif()endif()endmacro()#usethe
macrofind_library(FOO_LIBfoo/usr/local/lib)assert(${FOO_LIB}"Unab
letofindlibraryfoo")在此示例中,两个必需的参数是和。这些必需的参数可以按名称引用,如它们在本例中,或通过引用和
.如果要将参数作为列表进行处理,请使用和变量。(与,等相反)是宏的所有参数的列表,而是正式之后所有参数的列表参数。在宏中,您可以使
用以下命令迭代或根据需要迭代。TESTCOMMENTARGV0ARGV1ARGVARGNARGVARGV0ARGV1ARGNARG
VARGN该命令从函数、目录或文件返回。注意与函数不同,宏是就地扩展的,因此不能手柄.正则表达式一些CMake命令(如和)使用正则
表达式,也可以将正则表达式作为论点。在最简单的形式中,正则表达式是用于搜索完全匹配字符的字符。然而,许多乘以要找到的确切序列未知,
或者仅匹配字符串的开头或结尾是必需的。由于有几个指定正则表达式的不同约定,CMake的命令文档中描述了标准。这描述基于来自德克萨斯州的开源正则表达式类CMake用于解析正则表达式的仪器。高级命令有一些命令可能非常有用,但不是通常用于编写CMakeLists文件。本节将讨论其中一些命令以及它们何时有用。首先,考虑创建两个目标之间的依赖关系。CMake自动创建依赖项在目标之间,当它可以确定它们时。例如,CMake将自动为依赖于库目标。该命令通常是用于指定目标之间的目标间依赖关系,其中至少有一个是自定义目标(请参阅添加自定义命令部分)。该命令还涉及依赖。此命令控制正则表达式用于跟踪源代码依赖项。默认情况下,CMake将跟踪源文件(包括系统文件)的所有依赖项如。如果使用命令指定正则表达式,则该正则表达式将用于限制处理哪些包含文件。例如;如果软件项目的包含文件都以前缀foo开头(例如,等),您可以指定常规表达式,将依赖项检查限制为仅项目的文件。stdio.hfooMain.cfooStruct.h^foo.$
献花(0)
+1
(本文系云端筑梦师A...原创)