《CMake 构建实战-项目开发卷》阅读分享2 -- CMake基础语法
<div class='showpostmsg'><div> </div><div><strong><span style="font-size:18px;">CMake 程序分类</span></strong></div>
<div>根据用途可以分为三类:</div>
<ul>
<li><strong>目录程序 CMakeLists.txt</strong><br />
我们开发者绝大部分情况下接触的是这里文件,即 CMake 被用于构建时,需要处理项目的源程序。处理的入口,就是项目顶层目录下的 CMakeLists.txt。CMakeLists.txt 构成了源程序逻辑上的目录结构。</li>
<li><strong>脚本 script.cmake</strong><br />
这里程序我们很少接触,是指在行以 -P 参数运行 CMake 命令行工具执行脚本类型的 CMake 程序。这种 CMake 程序不会配置生成任何构建系统,因此一些与构建相关的 CMake 命令是不允许出现在脚本中的。<br />
【注:绝大部分情况下,可以忽略这种用法】</li>
<li><strong>模块 module.cmake</strong></li>
</ul>
<div>CMake 的目录程序和脚本均可以通过 include 等命令引用 CMake 模块程序。CMake 提供了很多预制模块供用户使用,多数与环境检测、搜索使用第三方库有关。</div>
<div>【注:这种模块使用的不多,但是有必要了解】</div>
<h1>命令调用</h1>
<div>CMake 程序几乎完全由命令调用构成。是因为除此之外,只剩下注释和空白符。CMake 程序中的 if 条件分支、for 循环等程序结构统一采用命令调用形式。</div>
<div>命令调用的形式即先写命令名称,其后跟括号括起来的命令参数。注意,CMake 的命令一般不区分大小写,但是一般使用小写。</div>
<div>最简单的命令调用示例如下:</div>
<div></div>
<h2>命令参数</h2>
<div>它出现在命令的括号中,可以由以下三种类型:</div>
<ol>
<li><strong>引号参数</strong><br />
即用引号括起来的参数,而且 CMake 规定它必须使用双引号。引号参数作为一个整体传递给命令,引号中间的空白符也会作为这个整体的一部分。那么引号参数中不仅可以包含空格,也可以包含换行符。<br />
引号参数示例<br />
</li>
<li><strong>非引号参数</strong><br />
非引号参数即没有被引号包裹的参数。这种参数不能包含任何空白符,也不能包括圆括号、#符号、双引号和反斜杠,除非经过转移。非引号参数也支持变量引用和转义字符。<br />
非引号参数不总是作为一个整体传递给命令,它有可能被拆分成若干参数传递。实际上,非引号参数在被传递前,会被当做CMake列表来处理,而列表中的每一个元素都会作为一个单独的参数传递个命令。<br />
以下是一个非引号参数的示例:<br />
</li>
<li><strong>括号参数</strong></li>
</ol>
<div>与引号参数一样,CMake 的括号参数也会作为一个整体传递给命令。通过自定义的特殊括号将原始文本包括在其中,它不处理文本中的任何特殊字符(包括转义字符)或变量引号语法,直接保留原始文本。</div>
<div>一个简单的括号参数示例</div>
<div></div>
<h1>变量</h1>
<div>CMake 变量与其他编程语言的不同之处在于:其数据类型总是文本类型的,只不过在使用时,文本型的变量可能被一些命令解释成数值、列表等。</div>
<h2>变量分类</h2>
<div>CMake 变量有三种:</div>
<ol>
<li><strong>普通变量</strong><br />
大多数变量都是普通变量,它们具有特定的作用于。<br />
</li>
<li><strong>缓存变量</strong></li>
</ol>
<div>被缓存起来的变量,被持久化到缓存文件 CMakeCache.txt 中。 CMake 程序每次执行时都会从缓存文件中读取缓存变量的值。这样可以避免每次都执行一些耗时的过程来获取数据。</div>
<div>缓存变量具有全局作用域。</div>
<div>【题外话】说到缓存变量,都是泪啊。第一次迁移公司项目到 CMake + GCC 编译环境,把SDK 拆分成了各个模块module1/module2/module3等等,应用文件夹下不同的应用 app1/app2/app3 依赖不同的模块,所以不是所有的模块都需要参与编译,而且某些模块有互斥关系。我第一次写 CMake 缺少经验,发现 appX 中设置的变量 MODULE_<x>_CFG怎么在模块文件夹下总是初始值,又或者在某个地方修改了 MODULE_<x>_CFG 变量的值但是别的地方没生效,这个问题把我带坑里好长时间。后来查资料解决办法是用缓存变量,SDK 需要配置两次,第一次配置会修改 MODULE_<x>_CFG 变量,第二次才把修改后的 MODULE_<x>_CFG 作用域整个工程,所有的模块都可以正确识别这个变量。虽说这种用法可以解决问题,但是不够优雅。</div>
<div>定义缓存变量的方法</div>
<div>set(<变量名> <值> … CACHE <变量类型> <变量描述> )</div>
<div>变量类型有以下几种:</div>
<ul>
<li>BOOL 布尔类型;</li>
<li>FILEPATH 文件路径类型;</li>
<li>PATH 目录路径类型;</li>
<li>STRING 文本类型</li>
<li>INTERNAL 内部使用(隐含设置 FORCE 参数)</li>
</ul>
<ol>
<li><strong>环境变量</strong></li>
</ol>
<div>即操作系统中的变量,因此它对于 CMake 进程来说具有全局的作用域。</div>
<div>定义环境变量的语法如下:</div>
<div>set (ENV${<变量名>} <值>)</div>
<div>通过 CMake 的 set 命令定义的环境变量只会影响当前的 CMake 进程,不会影响到父进程或者系统的环境变量设置。</div>
<h1>列表(变量)</h1>
<div>列表即分号隔开的字符串。</div>
<div>定义列表变量没有什么特别的,可以用 set 命令定义列表变量。首先可以利用引号参数直接定义一个包含分号的字符串,这就是一个列表。其次 set 命令还支持指定多个作为变量值的参数,这样引号参数和非引号参数都可以使用。</div>
<div></div>
<h1>条件语法</h1>
<div>在 CMake 中几乎一切都是命令,没有表达式语句,即条件语法也是命令中特定的参数形式。</div>
<h2>常量、变量和字符串条件</h2>
<div>常量、变量和字符串条件这3种条件在形式上完全一致,需要根据上下文及量的值来判断具体是哪一种条件。</div>
<h3>常量条件</h3>
<div>常量条件仅有一个常量组成,分为真值常量和假值常量。</div>
<div>if 命令中使用的常量条件形式如下:</div>
<table border="1">
<tbody>
<tr>
<td>常量类型</td>
<td>常量值</td>
<td>条件结果</td>
</tr>
<tr>
<td>真值常量</td>
<td>1/ON/YES/TRUE/Y/非零数值(不区分大小写)</td>
<td>真</td>
</tr>
<tr>
<td>假值常量</td>
<td>0/OFF/NO/FALSE/N/IGNORE/空字符串/NOTFOUND/或以-NOTFOUND结尾的字符串(不区分大小写)</td>
<td>假</td>
</tr>
</tbody>
</table>
<h3>变量和字符串</h3>
<div>如果条件中仅包含一个字符串,且这个字符串不是真值常量或者假值常量,那么它还有可能是一个变量的名称。如果以这个字符串为名的变量确实存在,则它是一个变量条件,否则是一个字符串条件。它们的形式如下:</div>
<div>if (<字符串|变量>)</div>
<div>endif()</div>
<ul>
<li>如果条件中的字符串是一个变量的名称,且这个变量的值不是一个假值常量,那么条件为真。</li>
<li>在其他情况下(如果指定的变量的值为假或者变量为定义),条件为假。</li>
</ul>
<div></div>
<div>作者在此处举了一个奇怪的变量的示例,如下所示:</div>
<div></div>
<div>代码中(1)处 if(on) 遇到了 on ,它是一个字符串常量,为真值,所以调用了第6行代码打印 ON。</div>
<div>代码中(2)处 if(${on}) 先取出变量 on 的值,即 OFF 字符串常量,而在 if() 条件中 OFF 是假值,故调用了第14行代码。</div>
<div>有点绕,注意 if() 条件先判断字符串字面量是否是常量,然后再判断是否是变量。</div>
<h2>逻辑运算</h2>
<div>条件语法中可以包含 AND/OR/NOT 三种逻辑运算,参与运算的也是符合条件语法的参数:</div>
<div>if (条件1 AND 条件2)</div>
<div>endif()</div>
<div>if (条件2 OR 条件2)</div>
<div>endif()</div>
<div>if (NOT 条件)</div>
<div>endif()</div>
<div>有编程经验的人一看就懂,无需多言。</div>
<h2>单参数条件</h2>
<div>即条件中调用命令,命令接收一个参数,以命令的结果作为条件的判断参数。一般用于存在性判断和类型判断。</div>
<table border="1">
<tbody>
<tr>
<td>条件语法</td>
<td>条件判断类型</td>
<td>描述</td>
</tr>
<tr>
<td>If(COMMAND <命令名称>)</td>
<td>命令判断</td>
<td>当<命令名称>指代一个可被调用的命令、宏、或函数时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(POLICY <策略名称>)</td>
<td>策略判断</td>
<td>当<策略名称>指代一个已定义的策略时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(TARGET <目标名称>)</td>
<td>目标判断</td>
<td>当<目标名称>指代一个在任意目录用 add_executable/add_library或者add_custom_target 命令创建的目标时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(TEST <测试名称>)</td>
<td>测试判断</td>
<td>当<测试名称>指代一个用add_test命令创建的测试时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(DEFINED <变量名称>)</td>
<td>变量定义判断</td>
<td>当<变量名称>指代一个变量时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(CACHE{<缓存变量名称>})</td>
<td>缓存变量定义判断</td>
<td>当<缓存变量名称>指代一个缓存变量时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(ENV{环境变量名称})</td>
<td>环境变量定义判断</td>
<td>当<环境变量名称>指代一个环境变量时,条件为真,否则为假</td>
</tr>
<tr>
<td>If(EXISTS <文件或目录路径>)</td>
<td>文件或目录存在判断</td>
<td>当指定的<文件或目录路径>确实存在时,条件为真,否则为假。该条件要求路径为绝对路径。另外,如果路径指向一个符号链接,那么仅当符号链接对应的文件或目录存在时,条件为真。</td>
</tr>
<tr>
<td>If(IS_DIRECTORY <目录路径>)</td>
<td>目录判断</td>
<td>当指定的<目录路径>确实存在且是一个目录时,条件为真,否则为假。该条件要求路径为绝对路径。</td>
</tr>
<tr>
<td>If(IS_SYMLINK <文件路径>)</td>
<td>符号链接判断</td>
<td>当指定的<文件路径>确实存在且是一个符号链接时,条件为真,否则为假。该条件要求路径为绝对路径。</td>
</tr>
<tr>
<td>If(IS_ABSOLUTE <路径>)</td>
<td>绝对路径判断</td>
<td>当指定的<路径>是一个绝对路径时,条件为真,否则为假。</td>
</tr>
</tbody>
</table>
<h2>双参数条件</h2>
<div>双参数条件通过两个参数的取值来决定条件是否为真,一般用于比较关系的判断。</div>
<h3>数值比较</h3>
<div>数值比较有 “小于”,“小于等于”,“等于”,“大于等于”,“大于” 这5中比较关系。当关系成立时,条件为真,否则为假。</div>
<div>if (<字符串 | 变量> LESS <字符串 | 变量>) # 小于</div>
<div>if (<字符串 | 变量> LESS_EQUAL <字符串 | 变量>) # 小于等于</div>
<div>if (<字符串 | 变量> EQUAL <字符串 | 变量>) # 等于</div>
<div>if (<字符串 | 变量> GREATER_EQUAL <字符串 | 变量>) # 大于等于</div>
<div>if (<字符串 | 变量> GREATER <字符串 | 变量>) # 大于</div>
<div>对于 <字符串> 或 <变量> 的取值规则:如果它是一个存在的变量名,则变量的值,否则取字符串本身。由于这一组条件仅用于数值比较,取值会被转换为数值类型后再进行比较。</div>
<h3>字符串比较</h3>
<div>字符串同样也有5种关系,比较时会根据字典序决定两个字符串取值的大小:</div>
<div>if (<字符串 | 变量> STRLESS <字符串 | 变量>) # 小于</div>
<div>if (<字符串 | 变量> STRLESS_EQUAL <字符串 | 变量>) # 小于等于</div>
<div>if (<字符串 | 变量> STREQUAL <字符串 | 变量>) # 等于</div>
<div>if (<字符串 | 变量> STRGREATER_EQUAL <字符串 | 变量>) # 大于等于</div>
<div>if (<字符串 | 变量> STRGREATER <字符串 | 变量>) # 大于</div>
<h3>字符串匹配</h3>
<div>用于判断字符串是否匹配特定的正则表达式,仅当匹配成功时,条件为真,否则为假。</div>
<div>if (<字符串 | 变量> MATCHES <正则表达式>)</div>
<h3>版本号比较</h3>
<div>版本号的格式为: 主版本号[.此版本号[.补丁版本号[.修订版本号]]]</div>
<div>下面的双参数条件用于比较版本号:</div>
<div>if (<字符串 | 变量> VERSION_LESS <字符串 | 变量>) # 小于</div>
<div>if (<字符串 | 变量> VERSION_GREATER <字符串 | 变量>) # 大于</div>
<div>if (<字符串 | 变量> VERSION_EQUAL <字符串 | 变量>) # 等于</div>
<div>if (<字符串 | 变量> VERSION_LESS_EQUAL <字符串 | 变量>) # 小于或等于</div>
<div>if (<字符串 | 变量> VERSION_GREATER_EQUAL <字符串 | 变量>) # 大于或等于</div>
<h3>列表元素判断</h3>
<div>下面这个条件语法用于判断列表中的元素是否存在。当第二个参数<列表变量>的元素中存在第一个参数的取值时,条件为真,否则为假。</div>
<div>if (<字符串 | 变量> IN_LIST <列表变量>)</div>
<h1>命令定义</h1>
<ol>
<li><strong>宏定义</strong><br />
形式为:<br />
macro(<宏名> [<参数1>…])<br />
<命令>…<br />
endmacro()<br />
宏所包含的命令序列仅在宏被调用时执行,且执行时不会产生额外的作用域。<br />
在 CMake 中,命令的名称不区分大小写,宏作为一种命令,其名称也不例外。<br />
</li>
<li><strong>函数定义</strong></li>
</ol>
<div>函数定义的形式如下:</div>
<div>function(<函数名> [<参数1>…])</div>
<div><命令>…</div>
<div>endfunction()</div>
<div>函数会产生一个新的作用域,仅在函数内部生效。因此函数内部直接使用 set() 命令定义的变量是不能被外部所访问的。为了实现外部可访问的目的,必须为 set() 命令指定 PARENT_SCOPE 参数,是的变量定义到外部作用域。</div>
<div></div>
<div>在本示例中定义了一个函数 my_func 有两个形参 x1 和 x2. 函数内部的 loc_result 是局部变量,global_result 因为添加 PARENT_SCOPE 选项,所以它的作用域是调用者可见的。</div>
<div>截图中(1)处打印 loc_result 是一个空字符串,因为主代码中没有定义,在 my_func() 定义的局部变量 loc_result 不可见,所以是空字符串。而 global_result 是以对调用者可见的,所以结果不为空。</div>
<div>截图中(2)处因为现在主代码中设置了 loc_result 和 global_result,所以 loc_result 值不为空,只为 14 行设定的字符串。而 global_result 被 my_func() 修改了,所以15行设定的内容改变了。</div>
<div> </div>
<p><!--importdoc--></p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页:
[1]