cmake

project()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# zh @ li in ~/tmp/hello [22:45:26]
$ tree
.
├── CMakeLists.txt
└── subhello
└── CMakeLists.txt

1 directory, 2 files

# zh @ li in ~/tmp/hello [22:45:27]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(hello VERSION 0.1.2.3 DESCRIPTION "an hello program" LANGUAGES C HOMEPAGE_URL “https://www.hello.com”)

add_subdirectory(subhello)

# zh @ li in ~/tmp/hello [22:45:34]
$ cat subhello/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(subhello VERSION 3.2.1.0 DESCRIPTION "an subhello program" LANGUAGES C HOMEPAGE_URL “https://www.subhello.com”)

function(print)
foreach(var ${ARGN})
message("${var} = ${${var}}")
endforeach()
endfunction()


print(
PROJECT_NAME # PROJECT_NAME = subhello # 当前
CMAKE_PROJECT_NAME # CMAKE_PROJECT_NAME = hello # 顶层

hello_SOURCE_DIR # hello_SOURCE_DIR = /home/zh/tmp/hello # hello项目
subhello_SOURCE_DIR # subhello_SOURCE_DIR = /home/zh/tmp/hello/subhello # subhello项目
PROJECT_SOURCE_DIR # PROJECT_SOURCE_DIR = /home/zh/tmp/hello/subhello # 当前
CMAKE_SOURCE_DIR # CMAKE_SOURCE_DIR = /home/zh/tmp/hello # 顶层

hello_BINARY_DIR # hello_BINARY_DIR = /home/zh/tmp/hello/build # hello项目
subhello_BINARY_DIR # subhello_BINARY_DIR = /home/zh/tmp/hello/build/subhello # subhello项目
PROJECT_BINARY_DIR # PROJECT_BINARY_DIR = /home/zh/tmp/hello/build/subhello # 当前
CMAKE_BINARY_DIR # CMAKE_BINARY_DIR = /home/zh/tmp/hello/build # 顶层

hello_VERSION # hello_VERSION = 0.1.2.3
subhello_VERSION # subhello_VERSION = 3.2.1.0
PROJECT_VERSION # PROJECT_VERSION = 3.2.1.0
CMAKE_PROJECT_VERSION # CMAKE_PROJECT_VERSION = 0.1.2.3 #顶层

hello_VERSION_MAJOR # hello_VERSION_MAJOR = 0
subhello_VERSION_MAJOR # subhello_VERSION_MAJOR = 3
PROJECT_VERSION_MAJOR # PROJECT_VERSION_MAJOR = 3
CMAKE_PROJECT_VERSION_MAJOR # PROJECT_VERSION_MAJOR = 0 #顶层

hello_VERSION_MINOR # hello_VERSION_MINOR = 1
subhello_VERSION_MINOR # subhello_VERSION_MINOR = 2
PROJECT_VERSION_MINOR # PROJECT_VERSION_MINOR = 2
CMAKE_PROJECT_VERSION_MINOR # PROJECT_VERSION_MINOR = 1 #顶层

hello_VERSION_PATCH # hello_VERSION_PATCH = 2
subhello_VERSION_PATCH # subhello_VERSION_PATCH = 1
PROJECT_VERSION_PATCH # PROJECT_VERSION_PATCH = 1
CMAKE_PROJECT_VERSION_PATCH # PROJECT_VERSION_PATCH = 2 #顶层

hello_VERSION_TWEAK # hello_VERSION_TWEAK = 3
subhello_VERSION_TWEAK # subhello_VERSION_TWEAK = 0
PROJECT_VERSION_TWEAK # PROJECT_VERSION_TWEAK = 0
CMAKE_PROJECT_VERSION_TWEAK # PROJECT_VERSION_TWEAK = 3 #顶层

hello_DESCRIPTION # PROJECT_DESCRIPTION = an hello program
subhello_DESCRIPTION # PROJECT_DESCRIPTION = an subhello program
PROJECT_DESCRIPTION # PROJECT_DESCRIPTION = an subhello program
CMAKE_PROJECT_DESCRIPTION # CMAKE_PROJECT_DESCRIPTION = an hello program # 顶层

hello_HOMEPAGE_URL # hello_HOMEPAGE_URL = “https://www.hello.com”
subhello_HOMEPAGE_URL # subhello_HOMEPAGE_URL = “https://www.subhello.com”
PROJECT_HOMEPAGE_URL # subhello_HOMEPAGE_URL = “https://www.subhello.com”
CMAKE_PROJECT_HOMEPAGE_URL # hello_HOMEPAGE_URL = “https://www.hello.com” # 顶层
)

GNUInstallDirs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# zh @ li in ~/li/tmp/hello [11:19:03]
$ tree
.
└── CMakeLists.txt

0 directories, 1 file

# zh @ li in ~/li/tmp/hello [11:19:04]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(hello VERSION 0.1.2.3 DESCRIPTION "an hello program" LANGUAGES C)

function(print)
foreach(var ${ARGN})
message("${var} # ${var} = ${${var}}")
endforeach()
endfunction()

set(CMAKE_INSTALL_PREFIX "/usr") # 在include(GNUInstallDirs)前设置,否则就是默认/usr/local
include(GNUInstallDirs)
print(
CMAKE_INSTALL_PREFIX # CMAKE_INSTALL_PREFIX = /

CMAKE_INSTALL_BINDIR # CMAKE_INSTALL_BINDIR = usr/bin
CMAKE_INSTALL_FULL_BINDIR # CMAKE_INSTALL_FULL_BINDIR = /usr/bin

CMAKE_INSTALL_SBINDIR # CMAKE_INSTALL_SBINDIR = usr/sbin
CMAKE_INSTALL_FULL_SBINDIR # CMAKE_INSTALL_FULL_SBINDIR = /usr/sbin

CMAKE_INSTALL_LIBEXECDIR # CMAKE_INSTALL_LIBEXECDIR = usr/libexec
CMAKE_INSTALL_FULL_LIBEXECDIR # CMAKE_INSTALL_FULL_LIBEXECDIR = /usr/libexec

CMAKE_INSTALL_SYSCONFDIR # CMAKE_INSTALL_SYSCONFDIR = etc
CMAKE_INSTALL_FULL_SYSCONFDIR # CMAKE_INSTALL_FULL_SYSCONFDIR = /etc

CMAKE_INSTALL_SHAREDSTATEDIR # CMAKE_INSTALL_SHAREDSTATEDIR = usr/com
CMAKE_INSTALL_FULL_SHAREDSTATEDIR # CMAKE_INSTALL_FULL_SHAREDSTATEDIR = /usr/com

CMAKE_INSTALL_LOCALSTATEDIR # CMAKE_INSTALL_LOCALSTATEDIR = var
CMAKE_INSTALL_FULL_LOCALSTATEDIR # CMAKE_INSTALL_FULL_LOCALSTATEDIR = /var

CMAKE_INSTALL_RUNSTATEDIR # CMAKE_INSTALL_RUNSTATEDIR = var/run
CMAKE_INSTALL_FULL_RUNSTATEDIR # CMAKE_INSTALL_FULL_RUNSTATEDIR = /var/run

CMAKE_INSTALL_LIBDIR # CMAKE_INSTALL_LIBDIR = usr/lib
CMAKE_INSTALL_FULL_LIBDIR # CMAKE_INSTALL_FULL_LIBDIR = /usr/lib

CMAKE_INSTALL_INCLUDEDIR # CMAKE_INSTALL_INCLUDEDIR = usr/include
CMAKE_INSTALL_FULL_INCLUDEDIR # CMAKE_INSTALL_FULL_INCLUDEDIR = /usr/include

CMAKE_INSTALL_OLDINCLUDEDIR # CMAKE_INSTALL_OLDINCLUDEDIR = /usr/include
CMAKE_INSTALL_FULL_OLDINCLUDEDIR # CMAKE_INSTALL_FULL_OLDINCLUDEDIR = /usr/include

CMAKE_INSTALL_DATAROOTDIR # CMAKE_INSTALL_DATAROOTDIR = usr/share
CMAKE_INSTALL_FULL_DATAROOTDIR # CMAKE_INSTALL_FULL_DATAROOTDIR = /usr/share

CMAKE_INSTALL_DATADIR # CMAKE_INSTALL_DATADIR = usr/share
CMAKE_INSTALL_FULL_DATADIR # CMAKE_INSTALL_FULL_DATADIR = /usr/share

CMAKE_INSTALL_INFODIR # CMAKE_INSTALL_INFODIR = usr/share/info
CMAKE_INSTALL_FULL_INFODIR # CMAKE_INSTALL_FULL_INFODIR = /usr/share/info

CMAKE_INSTALL_LOCALEDIR # CMAKE_INSTALL_LOCALEDIR = usr/share/locale
CMAKE_INSTALL_FULL_LOCALEDIR # CMAKE_INSTALL_FULL_LOCALEDIR = /usr/share/locale

CMAKE_INSTALL_MANDIR # CMAKE_INSTALL_MANDIR = usr/share/man
CMAKE_INSTALL_FULL_MANDIR # CMAKE_INSTALL_FULL_MANDIR = /usr/share/man

CMAKE_INSTALL_DOCDIR # CMAKE_INSTALL_DOCDIR = usr/share/doc/hello
CMAKE_INSTALL_FULL_DOCDIR # CMAKE_INSTALL_FULL_DOCDIR = /usr/share/doc/hello
)

交叉编译

1
2
3
4
set(CMAKE_SYSTEM_NAME Linux)
set(TOOLCHAIN_PATH /OPT/gcc-arm-linux-gnueabi)
set(CMAKE_C_COMPILER ${tools}/bin/arm-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-linux-gnueabi-g++)

set(CMAKE_SYSTEM_NAME Linux)该指令必须存在,其目的是设置目标机使用的操作系统名称,支持Linux,QNX,WindowsCE,Android等。如果没有操作系统,那么就写 Generic。执行该指令后,cmake 变量——CMAKE_CROSSCOMPILING 会自动被设置为 TRUE,此时 cmake 就会“知道“现在执行的是交叉编译;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# zh @ li in ~/li/tmp/hello [19:44:35]
$ tree
.
├── cmake
│   └── toolchain_x86.cmake
├── CMakeLists.txt
└── src
└── main.c

2 directories, 3 files

# zh @ li in ~/li/tmp/hello [19:44:38]
$ cat cmake/toolchain_x86.cmake
set(CMAKE_SYSTEM_NAME Linux)

set(TOOLCHAIN_PATH /usr/bin)
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)

# zh @ li in ~/li/tmp/hello [19:44:41]
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(hello VERSION 0.1.2.3 DESCRIPTION "an hello program" LANGUAGES C)

FILE(GLOB SRC_FILES "src/*.c")

add_executable(${PROJECT_NAME} ${SRC_FILES})

# zh @ li in ~/li/tmp/hello [19:44:43]
$ cat src/main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) { printf("hello world\n"); return 0; }

# zh @ li in ~/li/tmp/hello [19:44:46]
$ mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain_x86.cmake ..
-- The C compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/zh/li/tmp/hello/build

# zh @ li in ~/li/tmp/hello/build [19:44:53]
$ make
[ 50%] Building C object CMakeFiles/hello.dir/src/main.c.o
[100%] Linking C executable hello
[100%] Built target hello

注意:-DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain_x86.cmake 不能直接写入CMakeLists.txtinclude(toolchain_x86.cmake)

需要链接系统库或第三方库时,cmake 提供了FIND_PROGRAM()FIND_LIBRARY()FIND_FILE()FIND_PATH()FIND_PACKAGE() 来查找库路径或包。默认情况下,上述指令查找的是主机上的文件,其并不适用于目标机器。为此cmake提供了相应的变量:

set(CMAKE_FIND_ROOT_PATH path1 path2 path3 ...):设置其值为多个目录,这样 FIND_XXX() 就会从这些目录中查找。

跟随该变量的有下述 3 个变量,它们的值为 NEVERONLYBOTH

CMAKE_FIND_ROOT_PATH_MODE_PROGRAM若设为

  • NEVER,则FIND_PROGRAM() 不会在 CMAKE_FIND_ROOT_PATH 指定的目录中寻找
  • ONLY,则FIND_PROGRAM() 只会从 CMAKE_FIND_ROOT_PATH 指定的目录中寻找
  • BOTH,则FIND_PROGRAM() 会优先从 CMAKE_FIND_ROOT_PATH 指定的目录中寻找,再从默认的目录中寻找

FIND_PROGRAM() 通常用于寻找可执行程序,给后续的 EXECUTE_PROCESS()ADD_CUSTOM_COMMAND() 指令使用。并且,只有主机在生成编译文件时使用该可执行程序。因此通常设置 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

CMAKE_FIND_ROOT_PATH_MODE_LIBRARY:交叉编译只能使用 FIND_LIBRARY() 查找符合目标机器的库文件,因此设置set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

CMAKE_FIND_ROOT_PATH_MODE_INCLUDE:同上,设为 ONLY

编译选项

1
2
3
add_definitions(-std=c++11)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -Wl,-rpath=./:./lib") #-Wl,-rpath=./
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wl,-rpath=./:./lib") #-Wall
1
cmake -DCMAKE_PREFIX_PATH=/path/to -DCMAKE_LIBRARY_PATH=/path/to -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_C_FLAGS=-m32

find_xxx()

  1. set(CMAKE_PREFIX_PATH "/path/libs/xxx-install")之后,pkg_search_module()就能去/path/libs/xxx-install找到库的libxxx.pc文件
  2. set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/my/path/to/pkgconfig")之后,也能找找到libxxx.pc

pkg_search_module()

旧版本: pkg_search_module(PKG_XXX REQUIRED xxx)生成3个变量

  • PKG_XXX_INCLUDE_DIRS:编译时xxx库提供的头文件目录
  • PKG_XXX_LIBRARIES:只提供xxx库的名字,不提供路径
  • PKG_XXX_LDFLAGS:包含xxx的路径和库

你可以:target_link_libraries(my_proj ${PKG_XXX_LDFLAGS})

新版本: pkg_search_module(PKG_XXX REQUIRED IMPORTED_TARGET opencv)里面的IMPORTED_TARGET会创建一个供你直接在项目中使用的目标
你可以: target_link_libraries(my_proj PRIVATE PkgConfig::PKG_XXX)