菜鸟笔记
提升您的技术认知

cmake基础

cmake安装:Download | CMake。

在这之前,我们介绍一下cmake。虽然Make和Makefile简化了手动构建的过程,但是编写Makefile文件仍然是一个麻烦的工作,因此就有了CMake工具。CMake工具用于生成Makefile文件,而如何生成Makefile文件,则由CMakeLists.txt文件指定。它们直接的关系如下图:

使用cmake生成Makefile并编译的流程如下:

  • 编写CMake配置文件CMakeLists.txt。
  • 执行命令 cmake path生成CMakefile。其中,path为CMakeLists.txt所在的目录。
  • 使用make命令进行编译。

指定编译器

特别注意的是,指定编译器的语句需要放在CMakeLists.txt文件最前面。

指定GCC版本

在Linux系统下,gcc一般在/user/bin目录下。在里面可以看gcc的版本。

zhudk@vm1:/usr/bin$ cd
zhudk@vm1:~$ cd /usr/bin
zhudk@vm1:/usr/bin$ ls |grep gcc
c89-gcc
c99-gcc
gcc
gcc-7
gcc-ar
gcc-ar-7
gcc-nm
gcc-nm-7
gcc-ranlib
gcc-ranlib-7
x86_64-linux-gnu-gcc
x86_64-linux-gnu-gcc-7
x86_64-linux-gnu-gcc-ar
x86_64-linux-gnu-gcc-ar-7
x86_64-linux-gnu-gcc-nm
x86_64-linux-gnu-gcc-nm-7
x86_64-linux-gnu-gcc-ranlib
x86_64-linux-gnu-gcc-ranlib-7

如果没有指定编译器的话,默认使用的就是gcc编译器。如果系统中有多个版本gcc,需要指定gcc的版本,可以在环境变量中设置:

export CC=gcc-7      

设置完成后,我们执行cmake命令,会发现如下打印:

zhudk@vm1:/expand/zhudk/demo$ cmake .
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.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: /expand/zhudk/demo
使用交叉编译器

使用结对路径:

set(CROSS_COMPILER_PATH "/expand/zhudk/toolchain/opt/FriendlyARM/toolchain/6.4-aarch64/bin/aarch64-linux-gcc")
set(CMAKE_C_COMPILER ${CROSS_COMPILER_PATH})

或者直接使用编译器名称:

set(CMAKE_C_COMPILER "aarch64-linux-gcc")

项目配置

指定cmake最小版本
cmake_minimum_required (VERSION 3.10)
指定项目名
project (Main)
# 或
project ("Main")
指定编译语言与项目版本
project (Main LANGUAGES CXX VERSION 1.0.0)

编译配置

添加编译选项

方式1

set(CMAKE_CXX_FLAGS   "-no-pie")
set(CMAKE_C_FLAGS   "-no-pie") # 命令行方式传入:-DCMAKE_C_FLAGS=-no-pie

方式2

add_compile_options (<option> ...)
添加搜索路径

头文件

include_directories (inc)

库文件

link_directories (lib)
添加库文件

Note:target_link_libraries需要放于add_executable之后

静态库

target_link_libraries(project_name mxnet) #添加libmxnet.a

动态库

target_link_libraries(project_name -lmxnet) #添加libmxnet.so
添加子目录编译
add_subdirectory (lib)
添加程序宏定义

为当前路径以及下层路径的目标加入编译器命令行定义,相当于在**C/C++**程序中使用#define

对全部生成目标而言

add_definitions (-DFOO -DBAR ...)

对单个生成目标而言

target_compile_definitions(<target>
                           <INTERFACE|PUBLIC|PRIVATE> [items1...]
                           [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]
                          )

生成目标

编译生成可执行文件

必须放在project的前面

add_executable (Main main.cpp)
编译生成库

Note:默认生成静态库,除非特意指定生成库的类型

  • 静态库
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)
  • 动态库
add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)

经典示例

多目录多源文件

# set(CROSS_COMPILER_PATH "/expand/zhudk/toolchain/opt/FriendlyARM/toolchain/6.4-aarch64/bin/aarch64-linux-gcc")
# set(CMAKE_C_COMPILER ${CROSS_COMPILER_PATH})
set(CMAKE_C_COMPILER "aarch64-linux-gcc")

project(haha)
cmake_minimum_required(VERSION 3.10)
set(CMAKE_C_FLAGS "-g -Wall -O2")

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)

aux_source_directory(. SRC)
aux_source_directory(./aaa SRC)
aux_source_directory(./bbb SRC)
aux_source_directory(./ccc SRC)
add_executable(${PROJECT_NAME} ${SRC})

上述项目结构是一个不同目录下多个源文件的情况,根据代码功能不同放在不同的目录下。

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)

是设置生成的可执行文件的路径,PROJECT_SOURCE_DIR:工程的根目录

aux_source_directory(dir var)

是把指定目录下所有源文件存储在一个变量中(追加在这个变量中)。

add_executable(${PROJECT_NAME} ${SRC})

生成可执行文件。

关于头文件的引用,在aaa.c文件中:

#include "aaa.h"
#include "../bbb/bbb.h"
#include "../ccc/ccc.h"

算是手动引用的。如果不想在include的时候手动引用,则可以在CMakeLists.txt中提前添加头文件搜索目录。

include_directories(./bbb)

现在我们来运行cmake。注意,因为运行cmake的时候会生成很多附带文件,如果直接在根目录下运行cmake的话,会对程序目录造成污染,所以,建议在./build目录下运行cmake,然后可执行文件存放在./build/bin

zhudk@vm1:/expand/zhudk/demo/build$ cmake ..
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.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: /expand/zhudk/demo/build
zhudk@vm1:/expand/zhudk/demo/build$ make
Scanning dependencies of target haha
[ 20%] Building C object CMakeFiles/haha.dir/main.c.o
[ 40%] Building C object CMakeFiles/haha.dir/aaa/aaa.c.o
[ 60%] Building C object CMakeFiles/haha.dir/bbb/bbb.c.o
[ 80%] Building C object CMakeFiles/haha.dir/ccc/ccc.c.o
[100%] Linking C executable bin/haha
[100%] Built target haha
生成库

aux_source_directory(. libsrc)
#设置库输出路径
set(LIBRARY_OUTPUT_PATH ../lib)

#生成静态库 最后的库名称会为libhello.a
#add_library(hello STATIC ${libsrc})
#生成动态库 最后的库名称会为libhello.so
#add_library(hello SHARED ${libsrc})

#只是连续2次使用add_library指定库名称时(第一个参数),这个名称不能相同
#所以上述写法 不会生成动态库。可以先不使用相同的名称,再修改库的名称

add_library(hello_static STATIC ${libsrc})
add_library(hello_shared SHARED ${libsrc})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME hello)
set_target_properties(hello_shared PROPERTIES OUTPUT_NAME hello)
链接库

还是上述的库文件。

project(haha)
cmake_minimum_required(VERSION 3.10)
set(CMAKE_C_FLAGS "-g -Wall -O2")
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)

aux_source_directory(. SRC)
aux_source_directory(./aaa SRC)
aux_source_directory(./bbb SRC)
aux_source_directory(./ccc SRC)

include_directories(./include)
find_library(lib libhello.a HINTS ${PROJECT_SOURCE_DIR}/lib)

add_executable(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME} ${lib})
  • find_library: 在指定目录下查找指定库,并把库的绝对路径存放到变量里,其第一个参数是变量名称,第二个参数是库名称,第三个参数是HINTS,第4个参数是路径,其它用法可以参考cmake文档
  • target_link_libraries: 把目标文件与库文件进行链接。

使用find_library的好处是在执行cmake ..时就会去查找库是否存在,这样可以提前发现错误,不用等到链接时。

添加控制选项

在CMakeLists.txt中使用变量
option(debug "a debug option" off) #0或者OFF 1或者ON

if(debug)
	message("debug is ${debug}")
else()
	message("debug is ${debug}")	
endif()

加入想在CMakeLists.txt文件中控制一些语句,可以使用option定义变量。

option命令,其第一个参数是这个option的名字,第二个参数是字符串,用来描述这个option是来干嘛的,第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF

在执行cmake命令的时候,可以设置其值。cmake .. -Ddebug=1

在源码中使用option变量

在CMakeLists.txt中定义宏,可以直接在源码中使用。

add_definitions(-Ddebug)
#include <stdio.h>
#include "./aaa/aaa.h"
#include "./bbb/bbb.h"
#include "./ccc/ccc.h"
#include "hello.h"
int main(void)
{
  
	printf("Hello World!\n");
	aaa();
	bbb();
	ccc();
	hello();

#ifdef debug
	printf("debug\n");
#endif
	return 0;
}

可以直接在源码中使用,会打印debug

在源码中使用option变量

在CMakeLists.txt中定义宏,可以直接在源码中使用。

add_definitions(-Ddebug)
#include <stdio.h>
#include "./aaa/aaa.h"
#include "./bbb/bbb.h"
#include "./ccc/ccc.h"
#include "hello.h"
int main(void)
{
  
	printf("Hello World!\n");
	aaa();
	bbb();
	ccc();
	hello();

#ifdef debug
	printf("debug\n");
#endif
	return 0;
}

可以直接在源码中使用,会打印debug

仔细想了一下,如果真的需要修改宏的时候,还不如直接修改源码,修改后直接make就可以了,如果修改了CMakeLists.txt文件,那么还重新cmake。