专栏首页Frank909深入浅出CMake(二):基础语法及实现九九乘法表

深入浅出CMake(二):基础语法及实现九九乘法表

深入浅出CMake(一):基础篇文章中,我们已经知道了怎么依葫芦画瓢编写简单的 CMake 构建文件了,但如果应对复杂的工程的话,这还是远远不够的。

CMake 是一套编译构建体系,所以它有自己的一套语法概念,也有自己的 CMake Language,你可以讲它看做是一套脚本语言,所以它能做很多编程的事情。

这篇博文的目的是对 CMake 的基础语法进行概括,文章最后用一个九九乘法表的例子让读者加深印象。

文章开始前,让我们先回想 C/C++、JAVA、PYTHON 等等这些编程语言有什么特征

  • 能够进行条件判断 if/else
  • 能够循环处理 while/for/foreach
  • 能够操作字符串 string
  • 能够读写文件

这些特征足够我们在日常开发中编写常见的算法逻辑,处理很多问题了。

幸运的是 CMake 有类似的语法,能实现类似的功能。

CMake 有一个很重要的概念叫做命令.

COMMAND()

CMake 构建过程的操作基本上依靠各种各样的命令完成的,比如之前我们见过的

project(test)
add_executable(hello hello.cpp)

相信不难理解,官网中关于命令完整介绍在这里

下面开始,简单对 CMake 中一些语法过一眼

条件判断 if()/endif()

比如,它可以进行条件判断

if(<condition>)
  <commands>
elseif(<condition>) # optional block, can be repeated
  <commands>
else()              # optional block
  <commands>
endif()

if() 和 endif() 是配对使用的。

循环判断 foreach()/while()

foreach(<loop_var> <items>)
  <commands>
endforeach()

items 是一个 list,里面的元素用分号 ; 或者空格分割开来 比如

foreach(index A B C D)

也可以这样

foreach(index RANGE 9)

RANGE 是指定的迭代模型,index 取值从 0 到 9,包括 9.

while(<condition>)
  <commands>
endwhile()

while 循环和普通的开发无多大差别,这个不细说。

当然跳出当前判断也有

break()
continue()
return()

设置变量 set()/unset()

CMake 的变量可以分为 普通变量系统环境变量

set() 命令可以设置普通变量、系统环境变量、和缓存。

set(<variable> <value>... [PARENT_SCOPE])

设置普通的变量,将 value 的值赋值给 variable,后面的参数是可选的,代表有效域。

set(ENV{<variable>} [<value>])

设置系统环境变量

set(<variable> <value>... CACHE <type> <docstring> [FORCE])

设置缓存

访问变量的时候用 ${variable}

list

有自己的数据结构

Reading
  list(LENGTH <list> <out-var>)
  list(GET <list> <element index> [<index> ...] <out-var>)
  list(JOIN <list> <glue> <out-var>)
  list(SUBLIST <list> <begin> <length> <out-var>)

Search
  list(FIND <list> <value> <out-var>)

Modification
  list(APPEND <list> [<element>...])
  list(FILTER <list> {INCLUDE | EXCLUDE} REGEX <regex>)
  list(INSERT <list> <index> [<element>...])
  list(REMOVE_ITEM <list> <value>...)
  list(REMOVE_AT <list> <index>...)
  list(REMOVE_DUPLICATES <list>)
  list(TRANSFORM <list> <ACTION> [...])

Ordering
  list(REVERSE <list>)
  list(SORT <list> [...])

CMake 对于 list 操作手段比较丰富,涉及到读取 list 信息,查找元素,修改、添加、插入元素及排序。

篇幅限制,这里不一一展开。

读写文件 file()

可以读写文件

Reading
  file(READ <filename> <out-var> [...])
  file(STRINGS <filename> <out-var> [...])
  file(<HASH> <filename> <out-var>)
  file(TIMESTAMP <filename> <out-var> [...])

Writing
  file({WRITE | APPEND} <filename> <content>...)
  file({TOUCH | TOUCH_NOCREATE} [<file>...])
  file(GENERATE OUTPUT <output-file> [...])

Filesystem
  file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])
  file(RENAME <oldname> <newname>)
  file({REMOVE | REMOVE_RECURSE } [<files>...])
  file(MAKE_DIRECTORY [<dir>...])
  file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
  file(SIZE <filename> <out-var>)
  file(READ_SYMLINK <linkname> <out-var>)
  file(CREATE_LINK <original> <linkname> [...])

Path Conversion
  file(RELATIVE_PATH <out-var> <directory> <file>)
  file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)

Transfer
  file(DOWNLOAD <url> <file> [...])
  file(UPLOAD <file> <url> [...])

Locking
  file(LOCK <path> [...])

CMake 在构建过程中,避免不了要读写一些配置文件,这个时候 file() 命令就显得很有必要了。

操作字符串 string()

也可以对 string 文本进行操作

Search and Replace
  string(FIND <string> <substring> <out-var> [...])
  string(REPLACE <match-string> <replace-string> <out-var> <input>...)

Regular Expressions
  string(REGEX MATCH <match-regex> <out-var> <input>...)
  string(REGEX MATCHALL <match-regex> <out-var> <input>...)
  string(REGEX REPLACE <match-regex> <replace-expr> <out-var> <input>...)

Manipulation
  string(APPEND <string-var> [<input>...])
  string(PREPEND <string-var> [<input>...])
  string(CONCAT <out-var> [<input>...])
  string(JOIN <glue> <out-var> [<input>...])
  string(TOLOWER <string1> <out-var>)
  string(TOUPPER <string1> <out-var>)
  string(LENGTH <string> <out-var>)
  string(SUBSTRING <string> <begin> <length> <out-var>)
  string(STRIP <string> <out-var>)
  string(GENEX_STRIP <string> <out-var>)

Comparison
  string(COMPARE <op> <string1> <string2> <out-var>)

Hashing
  string(<HASH> <out-var> <input>)

Generation
  string(ASCII <number>... <out-var>)
  string(CONFIGURE <string1> <out-var> [...])
  string(MAKE_C_IDENTIFIER <string> <out-var>)
  string(RANDOM [<option>...] <out-var>)
  string(TIMESTAMP <out-var> [<format string>] [UTC])
  string(UUID <out-var> ...)

string() 命令包含的功能很多,文章篇幅限制,有机会我之后会单独写一篇博文详细介绍。

终端打印 message()

也可以向终端打印。

message([<mode>] "message to display" ...)

message() 命令可以向终端打印字符串 并且,它支持不同类型

  • none
  • STATUS
  • WARNING
  • AUTHOR_WARNING
  • SEND_ERROR
  • FATAL_ERROR
  • DEPRECATION

开发者可以根据实际情况定义不同的消息等级

数学运算 math()

还能做数学运算

math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>])

上面命令的用法是,计算数学表达是 experssion,然后将结果保存在 variable 中,后面是指定的格式为 10 进制还是 16 进制。

正因为有了这些灵活的命令,CMake 显得无所不能,带来的好处也显而易见,程序员编写 CMakeLists.txt 会更加得心应手。

下面,我们就把 CMake 当做是一种编码语言,写一个简单的例子。

九九乘法表

我们的目的是用 CMake 在屏幕上打印一个九九乘法表

cmake_minimum_required(VERSION 2.8.11)
project(test)

foreach(i RANGE 1 9)
    set(exp "")
    foreach(j RANGE 1 ${i})
        math(EXPR value "${j} * ${i}")
        string(APPEND exp "${j}" "*")
        string(APPEND exp "${i}" "=")
        string(APPEND exp "${value}" " ")
    endforeach()
    message(${exp})
endforeach()

这是 CMakeLists.txt 中的代码,可以看到主要运用了 foreach()、string()、set()、message() 几个命令。

在当前目录运行 cmake .命令。

可以看到终端打印了如下结果

1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
-- Configuring done
-- Generating done

CMake 的命令很多,但只要我们把握大概的方向,在实际编码当中就可以现学现用,掌握了 CMake 的基本语法,我们就可以比较从容去应对在复杂的工程当中配置复杂的 CMakeLists.txt 文件了。

下一篇,讲解如何用 CMake 查找第三方库。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 通信协议之Protocol buffer(Java篇)

    之前一直习惯用json进行数据的传输,觉得很方便。来到新公司后发现同事们用的更多的的协议都不是json,而是Protocol buffer。这个东西之前没有听说...

    Frank909
  • CMake 进行调试

    在 Linux 下开发,可以用 gdb 进行调试,但是如果工程是用 CMake 构建的,那么需要在 CMakeLists.txt 中加入如下代码:

    Frank909
  • Android绘图Canvas十八般武器之Shader详解及实战篇(下)

    上一篇《Android绘图Canvas十八般武器之Shader篇(上)》 我们知道了Bitmap的用法,及TileMode的详细情况。接下来,这一篇作为整个知识...

    Frank909
  • springMVC+ajax 文件上传 带进度条

    肖哥哥
  • win10 uwp 动画移动滑动条的滑块 拿到事件判断是否点击记录之前的值动画

    堆栈网小伙伴问如何点击滑动条的时候,可以通过动画将滑块从原来的坐标移动到用户点击的坐标,同时用户拖动的时候不做动画 在后台代码添加两个事件,一个是按下,一个抬起...

    林德熙
  • Spring的BeanDefinition解析

    在Spring生成bean的过程中,我们提到了Spring会先加载配置文件中的BeanDefinition,然后才会getBean。像普通的<bean>标签,我...

    zhangheng
  • Python的Wand模块

    ubuntu下安装 sudo apt-get install libmagickwand-dev 其他系统安装方法可以参考 http://docs.wand...

    于小勇
  • MySQL中的锁

    对于MyISAM的表锁,主要有以下几点 (1)共享读锁(S)之间是兼容的,但共享读锁(S)和排他写锁(X)之间,以及排他写锁之间(X)是互斥的,也就是说读和写是...

    爱撒谎的男孩
  • jQuery实现上传文件获取进度条

    wePanda
  • 【技巧】ionic后FileTransfer时代的文件传输

    FileTransfer是常用的Codrodva插件之一,在过去的几篇文章中都能看到它的身影:

    IT晴天

扫码关注云+社区

领取腾讯云代金券