Ch3nyang's blog collections_bookmark

post

person

about

category

category

local_offer

tag

rss_feed

rss

CMake教程

calendar_month 2020-04
archive 工具
tag cmake tag cpp tag c

本文为 cmake 基础使用。

cmake

安装

通常来讲,我们更推荐在 Linux 下安装使用。

sudo apt-get install cmake

Windows 下可以直接去官网下载安装。单实际使用时,笔者发现其有一定几率 make 无法执行且不报错。所以如果一定要在 Windows下运行,可以使用 VSCode 里面的cmake插件,一键编译运行,甚至可以说更加方便。

本文全部以 Linux 为例。由于笔者比较懒,都是在 Windows 的 WSL 跑的 Ubuntu 20.04,如果与正常安装的Linux有所出入,还请见谅。

初涉 CMake:单文件编译运行

我们首先熟悉一下最简单的应用场景:只有一个cpp文件。创建 test.cpp 并编写程序

touch test.cpp
vim test.cpp
#include<iostream>
using namespace std;
int main()
{
        cout<<"Hello World"<<endl;
        return 0;
}

就像 gcc 后面需要跟的那一长串说明,CMake也需要知道编译的一些参数。它使用的是 CmakeLists。创建 CMakeLists.txt 并编写内容

touch CMakLists.txt
vim CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(hello)
add_executable(test test.cpp)

上面的CMakeLists文件中,

  • cmake_minimum_required(VERSION 2.8)
    

    指定了CMake所需要的最小版本,即2.8,低于这个版本将无法正常运行。

  • project(hello)
    

    指定了项目名称为hello,这个名称可以任意设定。

  • add_executable(test test.cpp)
    

    则指定了可执行文件。

现在我们来编译并运行我们的项目。在这之前请先确认一下当前文件夹中已经创建并编写好了如下两个文件

$ ls
CMakeLists.txt  test.cpp

然后我们

$ cmake .
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.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: {dir}
$ make
Scanning dependencies of target test
make[2]: Warning: File 'CMakeFiles/test.dir/depend.make' has modification time 0.33 s in the future
[ 50%] Building CXX object CMakeFiles/test.dir/test.cpp.o
[100%] Linking CXX executable test
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
[100%] Built target test

接着运行

./hello

就能看到程序运行结果了。

执行 make 指令时,注意到有警告

make[2]: Warning: File 'CMakeFiles/test.dir/depend.make' has modification time 0.33 s in the future

这种类似的警告是由于在虚拟机上运行时,与宿主机时钟不同步导致的。有时这一警告会导致无法正常生成可执行文件。这时同步时间并重新编译运行即可:

$ find . -type f |xargs -n 5 touch
$ make clean
$ cmake .
$ make

基础应用:带子目录的编译

如果只有一个文件的话,gcc 一行就能解决,显然比 cmke 方便得多。而 cmake 的优势正是在于多文件编译。

我们来脱裤子放屁,编译如下几个文件

//test.h
#ifndef TEST_H
#define TEST_H

class test{
    void print();
};

#endif
//test.cpp
#include<iostream>
#include "test.h"
using namespace std;

void test::print()
{
    cout<<"Hello World"<<endl;
}
//main.cpp
#include "test.h"

int main()
{
    test T;
    t.print();
    return 0;
}

并按照如下结构存储文件

$ tree .
.
├── CMakeLists.txt
├── include
│   └── test.h
└── src
    ├── main.cpp
    └── test.cpp

2 directories, 4 files

修改 CMakeLists 文件为

cmake_minimum_required(VERSION 2.8)

project(directory_test)

include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_executable(testDirectories ${SOURCES})

相较于上一次我们使用的 CMakeLists 文件,有了如下改动

  • file(GLOB SOURCES "src/*.cpp")
    

    指定了头文件的位置

  • file(GLOB SOURCES "src/*.cpp")
    

    指定了源文件位置,当然,这句语句也可以写成

    set(SOURCES "src/*.cpp")
    
  • add_executable(testDirectories ${SOURCES})
    

    当中,我们使用 ${SOURCES} 直接引用了 source 变量。

下面我们开始编译运行。

通常来讲我们习惯把编译结果和源代码分开,单独放入 build 文件夹中,这样方便管理。因此我们创建文件夹

mkdir build
cd ./build

然后编译运行

$ cmake ..
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.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: {dir}/build
$ make
Scanning dependencies of target testDirectories
make[2]: Warning: File 'CMakeFiles/testDirectories.dir/depend.make' has modification time 0.2 s in the future
[ 33%] Building CXX object CMakeFiles/testDirectories.dir/src/main.cpp.o
[ 66%] Building CXX object CMakeFiles/testDirectories.dir/src/test.cpp.o
[100%] Linking CXX executable testDirectories
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
[100%] Built target testDirectories

注意,第一个命令是 cmake .. 而不是 cmake .

然后运行

$ ./testDirectories
Hello World

如果你在中途某个地方出错了而没有很好的解决方法,请重置make并重新编译运行

$ make clean

或者直接删去cmake生成的文件。

库的运用:动态库

如果一个项目中只有几个或几十个文件,那么上面的方法是没有问题的。而面对几十万个文件的项目,其编译时间将会达到数小时。如果每次做了微小的改动后都重新编译,这是不可接受的。因此我们需要对项目中的某些文件创建库。动态库和静态库的区别在此不再赘述。

构建如下目录

$ tree
.
├── CMakeLists.txt
├── build
├── include
│   └── test.h
├── src
│   └── test.cpp
└── test
    └── main.cpp

5 directories, 4 files
$ cd ./build

修改 CMakeLists

cmake_minimum_required(VERSION 2.8)

project(directory_test)
set(CMAKE_BUILD_TYPE Release)

include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_library(testDirectories SHARED ${SOURCES})

install(TARGETS testDirectories DESTINATION "{dir}/bin")//这里的地址改为你想输出的地址

由于我们需要生成库文件而不是可执行文件,所以之前的 add_executable 改为了 add_library

install(TARGETS testDirectories DESTINATION "{dir}/bin")

指定了动态链接库的安装目录。

编译并安装该库

chenyang@Chenyang:/mnt/e/code/cmake/build$ cmake ..
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.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: {dir}/build
$ make
Scanning dependencies of target testDirectories
make[2]: Warning: File 'CMakeFiles/testDirectories.dir/depend.make' has modification time 0.33 s in the future
[ 50%] Building CXX object CMakeFiles/testDirectories.dir/src/test.cpp.o
[100%] Linking CXX shared library libtestDirectories.so
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
[100%] Built target testDirectories
$ sudo make install
[100%] Built target testDirectories
Install the project...
-- Install configuration: "Release"
-- Installing: {dir}/bin/libtestDirectories.so

其中,sudo make install 即为安装库。

然后我们使用库。

修改 CMakeLists 为

cmake_minimum_required(VERSION 2.8)

project(lib_test)
set(CMAKE_BUILD_TYPE Release)

set(PROJECT_LINK_LIBS libtestDirectories.so)
link_directories("{dir}")//这里的地址改为库的地址

include_directories("{dir}/include")//这里的地址改为头文件的地址

add_executable(libtest test/main.cpp)
target_link_libraries(libtest ${PROJECT_LINK_LIBS})

然后编译运行

$ make clean
$ cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: {dir}/build
$ make
[ 50%] Building CXX object CMakeFiles/libtest.dir/test/main.cpp.o
[100%] Linking CXX executable libtest
[100%] Built target libtest
$ ./libtest
Hello World

库的运用:静态库

与动态库相同,只需把 CMakeLists 当中的 SHARED 改为 STATIC

cmake_minimum_required(VERSION 2.8)

project(directory_test)
set(CMAKE_BUILD_TYPE Release)

include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_library(testDirectories STATIC ${SOURCES})

install(TARGETS testDirectories DESTINATION "{dir}/bin")//这里的地址改为你想输出的地址

然后编译安装

$ make clean
$ cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: {dir}/build
$ make
Scanning dependencies of target testDirectories
[ 50%] Building CXX object CMakeFiles/testDirectories.dir/src/test.cpp.o
[100%] Linking CXX static library libtestDirectories.a
[100%] Built target testDirectories
$ sudo make install
[100%] Built target testDirectories
Install the project...
-- Install configuration: "Release"
-- Installing: {dir}/bin/libtestDirectories.a

可以看到,已经生成了链接库

$ ls ../bin
libtestDirectories.a  libtestDirectories.so

然后我们使用库。

修改 CMakeLists 为

cmake_minimum_required(VERSION 2.8)

project(lib_test)
set(CMAKE_BUILD_TYPE Release)

set(PROJECT_LINK_LIBS libtestDirectories.a)
link_directories("{dir}")//这里的地址改为库的地址

include_directories("{dir}/include")//这里的地址改为头文件的地址

add_executable(libtest test/main.cpp)
target_link_libraries(libtest ${PROJECT_LINK_LIBS})

然后编译运行

$ make clean
$ cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: {dir}/build
$ make
[ 50%] Building CXX object CMakeFiles/libtest.dir/test/main.cpp.o
[100%] Linking CXX executable libtest
[100%] Built target libtest
$ ./libtest
Hello World

还有更多…

本文只是 CMake 的冰山一角。CMake 以其不易于学习著称,我们这里只是让大家快速入门,学会基本操作,并且在探索剩余内容时不至于因为缺乏基础知识而看不懂。

CMake 剩余的东西我建议大家对照着常用变量和环境变量的表了解,这样会比较方便。网上不少教程都是上来就把这种非常概念性的内容扔给读者,这是中式教程的通病:把教程当作 document。希望我这样一份“教程”能够帮你入门 CMake,并且能看懂其他教程。

Comments

Share This Post