CMake实战教程(二)

前言

从上一篇的文章中,相信大家也了解了CMake这个东西,但是呢它不是只是看一下就能会的,这种东西还是要实践才能学会,那么如果你已经实践过了,就会体会到上一篇文章在实际中使用到底是多么的难搞,比如:

  • 生成很多垃圾文件,这是我的第一个体会…
  • 需要手动去指定编译的文件
  • 无法指定编译器,编译选项等等很多东西…
  • 如果有子目录也不能完全适用…
  • 以及一些其他的问题…

总的来说就是在真正项目中压根就没法适用的工程,那为什么我要写呢,因为那是我学习的过程,总不能一口吞下一个大胖子是不是,接下来的一系列文章我就会让CMake变得能在实际中使用,越来越自动化,更方便构建。。。

外部构建

第一个问题,在运行cmake .后会产生很多垃圾文件,那么我们可以让它在一个build目录下去编译,生成的垃圾文件放在这个目录下就好了,不需要的时候直接清除即可。

可能有人会问,它不能像Makefile一样直接make clean 或者make distclean清除编译的垃圾文件吗,我当时学的时候也谷歌过,但是,很遗憾没有,使用我才让它产生的垃圾文件放在build目录下,其实不能说是垃圾文件,只不过是一些中间文件,记录某些东西的,我用不上它,所以认为是垃圾…仅此而已。

对此官方的解释是:

CMakeLists.txt 可以执行脚本并通过脚本生成一些临时文件,但是却没有办法来跟踪这些临时文件到底是哪些,因此,没有办法提供一个可靠的 clean 方案。

那怎么办呢?很简单,从CMake的语法我们就知道,它在构建的时候指定了PATH,也就是顶层CMakeLists.txt 入口的路径。

cmake PATH

那么很显然,它可以是相对路径而不是绝对路径,毕竟‘.’ 表示当前路径, 点点‘..’ 表示上一级路径,那么我们可以新建一个build目录,然后在build目录下去运行:

cmake ..

这在CMake中称之为外部构建(out-of-source build),而 CMake 强烈推荐的就是外部构建!

我自己也写了个build的脚本,内容非常简单,主要做两件事:

  1. 创建一个build目录(存在就不会重新创建的)
  2. 进入build目录
  3. 然后外部构建cmake
  4. 生成Makefile文件后运行make命令编译
#!/bin/bash

mkdir -p build
cd build
cmake ..
make

因此在编译的时候直接运行这个脚本即可,生成的内容全部都在build目录下。

  • 这是原始目录
.
├── build.sh
├── CMakeLists.txt
└── main.c

0 directories, 3 files

编译后在build目录下生成很多文件,包括 Makefile、section2(可执行程序)

CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile  section2

补充一点,如果你想看到cmake生成的垃圾文件比你源码还多的时候,你就会爱死外部构建这种骚操作了~

自动查找源码

不得不说,cmake是个很好的自动化构建工具,既然是自动化,那么很的东西都是自动的,比如查找源码,cmake就提供查找源码的命令:

aux_source_directory(<dir> <variable>)

它的主要作用就是:查找在某个路径下的所有源文件,注意,是所有源码文件,当你的目录下有很多个源码文件的时候,他就主动去查找了,哦,当然,它也只会查找源码文件,比如*.c 、 *.cpp 、*.cc啦,反正只要是源码就可以了,但是什么txt 、 *.h文件这些它是不会记录下来的。

  • dir : 指定的目录(可以是绝对路径也可以是相对路径)
  • variable:将输出结果列表储存在指定的<variable>变量中。

反正这个命令就很方便,我在某个目录下有啥源码文件,我都会被记录到<variable>变量,然后在CMake直接使用即可。当然后续也有其他的命令去找源码文件,一口吃不了一个大胖子,先了解这个先,后续慢慢学习~

变量

CMake中,变量是十分常见的,我正在就简单讲解下基本的语法吧:

定义变量常用的函数是:

set(VARIABLE_NAME VARIABLE)

取消定义变量是

unset(VARIABLE_NAME )
  • VARIABLE_NAME : 变量名字
  • VARIABLE:变量的值

变量的的值始终是string(字符串)类型,变量名字是区分大小写的,一般变量命名还是正常点比较好,别搞太多乱七八糟的特殊符号,只要数字、字母,下划线"_" 、横线"-"就差不多了,变量的作用域也是有全局与局部之分,与C语言、Java都差不多,我也不多说了。我的例程中全局变量是全部大写,局部变量是全小写的,也是比较好区分。

变量引用的形式为${variable_name},变量引用被变量的值替换,或者如果变量没有被设置,则由空字符串替换。变量引用可以嵌套,例如${outer_${inner_variable}veriable};环境变量引用的形式为$ENV{VARIABLE},并在相同的上下文中作为正常变量引用。

打印日志

CMake构建的时候,你可能不知道某些变量是啥内容,那么就在终端打印出来看看就好了,这根我们写代码中的printf函数差不多,给直接打一串字符串出来瞧瞧…

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

首先呢,<mode>是指定消息的类型:

  • (无) = 重要消息;
  • STATUS = 非重要消息;
  • WARNING = CMake 警告, 会继续执行;
  • AUTHOR_WARNING = CMake 警告 (dev), 会继续执行;
  • SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤;
  • FATAL_ERROR = CMake 错误, 终止所有处理过程;

正常情况下我都是输出一些状态信息——STATUS,打印个变量啦,打印下代码的执行顺序啦等等…

后面就是有些字符串信息了,变量在这里直接引用就好,毕竟变量本身就是字符串…

section3

给出个实例代码:
当前目录存在2个c文件,分别是main.cpower.c就是简单计算x的y次方,纯粹是个demo,我自己也懒得写,并不是因为代码有多高深,所以这代码我是从网上找的,来自潘伟洲大神的cmake测试代码:https://github.com/wzpan/cmake-demo

  • main.c
#include <stdio.h>
#include <stdlib.h>
#include "power.h"

int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
    double result = power(base, exponent);
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}
  • power.c
double power(double base, int exponent)
{
    int result = base;
    int i;

    if (exponent == 0) {
        return 1;
    }
    
    for(i = 1; i < exponent; ++i){
        result = result * base;
    }

    return result;
}

然后就是CMakeLists.txt文件:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (section3)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(section3 ${DIR_SRCS})

相比于上一篇文章,我这个CMakeLists.txt文件只是添加了aux_source_directory命令去自动扫描当前目录下的源码文件,并且保存到DIR_SRCS 变量中,仅此而已!!

然后在add_executable命令中,用${DIR_SRCS}变量代替指定的源码文件source1 source2 ... sourceN,当然你也可以打印一下${DIR_SRCS}变量到底保存了什么,如果不出意外的话,它保存的就是./main.c; ./power.c,表示的是当前目录下的main.cpower.c这两个文件,而分号代表它是一个list,后续会讲解怎么去提取list的内容…

message(STATUS "${DIR_SRCS}")

然后用外部构建的方式去编译代码:

jie@pc:~/github/cmake/section3$ ./build.sh 
-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jie/github/cmake/section3/build
Scanning dependencies of target section3
[ 33%] Building C object CMakeFiles/section3.dir/main.c.o
[ 66%] Building C object CMakeFiles/section3.dir/power.c.o
[100%] Linking C executable section3
[100%] Built target section3

很明显生成了正确可执行文件~

代码下载

https://github.com/jiejieTop/cmake

未完待续…

发布了115 篇原创文章 · 获赞 283 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/jiejiemcu/article/details/103359625