首页
学习
活动
专区
圈层
工具
发布

深入浅出CMake(一):基础篇

什么是 CMake?

CMake 是一个跨平台的编译构建工具,用来自动化生成 Makefile 之类的构建文件的。

一般在 unix 类系统上开发,我们用 gcc 或者 g++ 编译源码。

代码语言:javascript
代码运行次数:0
复制
g++ hello.cpp  world.cpp 

针对很小的工程,处理的源码文件就这么几个,我们完全手写编译脚步就好了。

但是如果工程量变大,情况就变得复杂了,我们就需要用 make 工具,并编写 Makefile 记录好源码与依赖之间的关系。

make 配合 Makefile 使用,威力很大,但是手写代码很烦恼,而 cmake 似乎更现代化,它能够自动生成 Makefile,并且逻辑似乎更清晰。

你可以简单地认为,cmake 的使用比手写 Makefile 更简单。

当然,我并不是说 cmake 比 make 更高级,更好,只是相对于新手而言,它是很友好的,我们都希望把精力花在编写具体的业务代码上,而不是炫技一般编写复杂的 Makefile 文件,我决定学习 CMake 也是看到 OpenCV 改用它编译了,另外 Android 的源码好像也是它,这也让我不得不去学习它。

至于 CMake 和其它编译工具孰好孰坏,这里我不做评价。

一个最简单的 CMake 例子

要构建一个 CMake 编译系统,首先需要在代码根目录创建一个 CMakeLists.txt 文件,这个文件是用来描述构建过程的,可以看做是一个高级版的 Makefile 文件。

假设整个工程只有 hello.cpp 这个文件.

代码语言:javascript
代码运行次数:0
复制
#include <iostream>

using namespace std;


int main(int argc,char** argv)
{
    cout << "Hello for cmake!" << endl;
    return 0;
}

如果要编译的话,我们可以这样编写 CMakeLists.txt 。

代码语言:javascript
代码运行次数:0
复制
cmake_minimum_required(VERSION 2.8.11)
project(HELLO)

add_executable(hello hello.cpp)

3 行代码就搞定了。

cmake_minimum_required()这句代码是指定 cmake 的最低版本 project()这句代码是指定工程的名称 add_executable()这个指示生成的可执行文件,上面的例子是编译 hello.cpp 生成 hello 这个可执行文件

CMakeLists.txt 写好后,在当前目录执行 cmake .命令,.代表在当前目录执行操作,如果 CMakeLists.txt 在别处,需要将路径添加在 cmake 后面。

代码语言:javascript
代码运行次数:0
复制
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.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/frank/exercises/cmake

命令完成后,我们可以发现当前目录多了 1 个名为 CMakeFiles 的文件,也多了一个 Makefile 文件。

我们再执行 make 命令,最终可以发现生成了可执行文件 hello.

代码语言:javascript
代码运行次数:0
复制
[ 50%] Building CXX object CMakeFiles/hello.dir/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

在终端执行 ./hello,可以看见屏幕打印了

代码语言:javascript
代码运行次数:0
复制
Hello fo cmake!

这正式 hello.cpp 中的输出。

这是最简单的 cmake 编译例子,但实际工作中是不会有这么简单的,实际工作中会涉及到很多的源文件,还有很多的动态的库,静态的库。

下面讲解一个稍微复杂但完整的例子。

一个稍微复杂的 CMake 例子

假设 hello.cpp 需要调用一个 so 中的 api,这个 so 库是由 world.cpp 编译生成的。

这个应该可以代表大多数开发场景,因为 C/C++ 工程开发中,避免不了要调用其他的 so 或者是 .a 文件,也要和 .h 头文件打交道。

代码语言:javascript
代码运行次数:0
复制
#ifndef __WORLD_H__
#define __WORLD_H__

void say_hello();

#endif

上面是 world.h 的代码,它位于工程中 ./include文件夹。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>
#include "include/world.h"


void say_hello()
{
    std::cout << "I\'m frank909! blog is frank909.blog.csdn.net." << std::endl;
}

上面是 world.cpp 的代码。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>
#include "include/world.h"

using namespace std;


int main(int argc,char** argv)
{
    cout << "Hello for cmake!" << endl;
    say_hello();
    return 0;
}

上面是 hello.cpp 修改后的代码,它直接调用了 say_hello()方法,这个时候我们需要仔细考虑怎么写 CMakeLists.txt 文件了。

代码语言:javascript
代码运行次数:0
复制
cmake_minimum_required(VERSION 2.8.11)
project(HELLO)

include_directories("include")
add_library(world world.cpp)

add_executable(hello hello.cpp)
target_link_libraries(hello world)

include_directories()这句代码指定了头文件地址 add_library()的作用是生成库文件,默认是 .a 的文件,也就是静态库,如何生成动态库接下来的部分会讲。 target_lingk_libraries()这句代码的意思也很容易懂,那就是为可执行文件 hello 链接 libworld.a 这个库。

执行 cmake .操作可以看到会正常生成 MakeFile 文件。

再执行 make动作,可以看见目标正常生成。

代码语言:javascript
代码运行次数:0
复制
Scanning dependencies of target world
[ 25%] Building CXX object CMakeFiles/world.dir/world.cpp.o
[ 50%] Linking CXX static library libworld.a
[ 50%] Built target world
Scanning dependencies of target hello
[ 75%] Building CXX object CMakeFiles/hello.dir/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

生成了两个文件 libworld.a 和 hello.

我们执行 ./hello 可以看到如下结果。

代码语言:javascript
代码运行次数:0
复制
Hello for cmake!
I'm frank909! blog is frank909.blog.csdn.net.

以上的例子就基本覆盖了我们日常开发的模式。

指定头文件路径

指定动态库或者是静态库的路径,然后链接。

我们已经具备自己编写简单的 CMakeLists.txt 的能力了,但为了更好的理解和灵活运用我们还需要学习一些关于 cmake 的基本概念。

cmake 中的 target

cmake 的主要工作大多是为了操作各种各样的 target,cmake 把二进制可执行文件和库都称为 target。

target 就是指 cmake 要编译的目标。

可执行文件

add_executable()指定了可执行文件,它是 unix 上 a.out 之类的文件和 windows 平台 .exe 文件

动态库和静态库都使用 add_library()命令生成,也就是 unix 平台上的 .so 和 .a 文件,window 上的 .DLL 文件等等。

add_library()默认生成 .a 文件,如果要生成 .so 文件用 add_library(test SHARED test.cpp)这样的形式,要指定 SHARED 模型,才会生成 libtest.so 文件。 默认的效果等同于 add_library(test STATIC test.cpp)生成的是 libtest.a 文件。

链接库

link_libraries(hello test)cmake 通过 link_libraries() 命令指定了目标间的依赖关系,示例代码中 hello 是可执行文件,test 是库。

指定头文件

include_directories()指定了编译系统的头文件地址

处理好了头文件、库的生成和链接、可执行文件的生成,cmake 就基本 OK 了。

如果,遇到更复杂的情况的话,还是需要深入了解 CMake 构建机制,我就陆续编写更详细的内容。

下一篇
举报
领券