cmake 使用例子

cmake 是一个跨平台的编译工具,他本身不能编译链接,只是通过一些规则将各个平台的编译链接指令自动生成。

linux 环境上使用 cmake

其实就是在 make 上再套一层,编写 CMakeLists.txt 规则文件可自动生成 Makefile 文件,最后执行 make

学习 cmake 之前建议会使用 gcc 以及 Makefile 编写,懂得基本的编译、链接

实例1

项目目录树如下

[root@dldl cmake_test]# tree /tmp/cmake_test
/tmp/cmake_test
├── CMakeLists.txt
├── config.h.in
├── dir1
│   └── test_6.py
├── dlbase
│   ├── dlbase.c
│   └── dlbase.h
├── inc
│   └── inc.h
├── inc2
│   └── inc2.h
├── lib
│   ├── lib1.c
│   ├── lib2.c
│   └── lib.h
├── main.c
├── script
│   ├── sub_script
│   │   └── test_4.py
│   ├── test_2.py
│   └── test.py
├── test_3.py
└── test_5.py

CMakeLists.txt  规则如下

#设置最低版本要求
cmake_minimum_required(VERSION 3.10)

#设置项目名和版本号
project(test VERSION 1.0)

#头文件目录即  -Ixxx -Ixxx
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/inc)
include_directories(${PROJECT_SOURCE_DIR}/inc2)
include_directories(${PROJECT_SOURCE_DIR}/lib)
include_directories(${PROJECT_SOURCE_DIR}/dlbase)

#链接目录即 -Lxxx -Lxxx
link_directories(${PROJECT_SOURCE_DIR})
link_directories(${PROJECT_SOURCE_DIR}/lib)

#即包含 cmake 安装路径的 Modules/GNUInstallDirs.cmake 文件
#改文件会根据操作系统环境的不同,生成不同的变量
include(GNUInstallDirs)

#默认选项设置 - OFF
option(TEST "this is test" OFF)

#当运行 cmake ../ -DTEST=ON 时,该条件成立
if(${TEST})
    message("TEST IS ON")
endif()

#设置c的编译选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3 -O0")

#校验变量,变量可以通过 cmake -DCMAKE_ARG=TEST path 来指定
if(CMAKE_ARG MATCHES TEST)
    message("cmake yes")
endif()


#STATIC(default) SHARED
#将 lib1.c lib2.c 编译成 .o 再通过 ar 打包成静态库 libarchive.a
#add_library(archive lib/lib1.c lib/lib2.c)

#遍历 lib 目录,并把里面的所有源文件存入 LIB_SRC 变量
aux_source_directory(lib LIB_SRC)
add_library(archive ${LIB_SRC})

#编译 dlbase 动态库
aux_source_directory(dlbase DLBASE_SRC)
add_library(dlbase SHARED ${DLBASE_SRC})

#添加自定义目标(这里只是为了展示,没实际用处)
#这些目标如果没有被依赖,是不会执行,但是可以通过 make aaaa 来执行
add_custom_target(aaaa
    COMMAND touch aaaa.txt
    COMMAND mkdir -p tmp
    COMMENT "this is target aaaa's comment"
    DEPENDS bbbb
)

add_custom_target(bbbb
    COMMAND touch bbbb.txt
    COMMENT "this is target bbbb's comment"
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/build
)

#添加依赖,这样上面的 aaa 目标才会执行
#由于 aaaa 依赖于 bbbb,所以 bbbb 会先执行
add_dependencies(archive aaaa)


#将 main.c 编译成可执行文件 test
add_executable(test main.c)

# 将静态库 archive 链接到 test
# 将动态库链接到 test,可通过 ldd 查看
target_link_libraries(test archive dlbase)

#添加几个 #define 定义
#/usr/bin/cc -DAAA -DBBB...
add_definitions(-DAAA -DBBB)


set(CMAKE_INSTALL_PREFIX /usr/local/test)

if (CMAKE_INSTALL_PREFIX)
    message("prefix = ${CMAKE_INSTALL_PREFIX}")
endif()


#当我们执行 make 时,就会在当前目录生成 test 可执行文件,它的运行时路径会包含当前路径
#所以直接执行 test 能找到 libdlbase.so 动态库


#当我们执行 make install 时,会按照如下规则,安装到特定目录
#此时,test 可执行文件的运行时路径会取消,所以当我们执行 /usr/local/test/bin/test 时,会报找不到动态库 libdlbase.so
#所以我们如果想正常执行,需要做一些处理,让 test 能找到 libdlbase.so 动态库

#安装库和可执行文件,动态库,静态库(这里的静态库已经编译进了 test 里)
install(TARGETS test archive dlbase
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)

install(FILES ${PROJECT_SOURCE_DIR}/test_3.py ${PROJECT_SOURCE_DIR}/test_5.py
    DESTINATION script
)

#将 dir1 和 script/* 复制到 ${CMAKE_INSTALL_PREFIX}/script 目录里去
#排除 ex 目录
install(DIRECTORY dir1 script/ DESTINATION script
    PATTERN "ex" EXCLUDE
    PATTERN "script/*"
    PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ
)

#安装完成之后我们可以执行一些收尾动作
install(CODE "execute_process(COMMAND echo install done)")

编写完 CMakeLists.txt 之后就可以开始生成 Makefile 文件并开始实际编译

mkdir build
cd build
cmake ../

此时已经生成了 Makefile,我们执行 make 并过滤出一些有用的信息

[root@dldl build]# make VERBOSE=1 | egrep '/cc |/ar '
/usr/bin/cc -DAAA -DBBB -Ddlbase_EXPORTS -I/tmp/cmake_test -I/tmp/cmake_test/inc -I/tmp/cmake_test/inc2 -I/tmp/cmake_test/lib -I/tmp/cmake_test/dlbase  -g3 -O0 -fPIC   -o CMakeFiles/dlbase.dir/dlbase/dlbase.c.o   -c /tmp/cmake_test/dlbase/dlbase.c
/usr/bin/cc -fPIC  -g3 -O0  -shared -Wl,-soname,libdlbase.so -o libdlbase.so CMakeFiles/dlbase.dir/dlbase/dlbase.c.o  -L/tmp/cmake_test  -L/tmp/cmake_test/lib 
/usr/bin/cc -DAAA -DBBB -I/tmp/cmake_test -I/tmp/cmake_test/inc -I/tmp/cmake_test/inc2 -I/tmp/cmake_test/lib -I/tmp/cmake_test/dlbase  -g3 -O0   -o CMakeFiles/archive.dir/lib/lib1.c.o   -c /tmp/cmake_test/lib/lib1.c
/usr/bin/cc -DAAA -DBBB -I/tmp/cmake_test -I/tmp/cmake_test/inc -I/tmp/cmake_test/inc2 -I/tmp/cmake_test/lib -I/tmp/cmake_test/dlbase  -g3 -O0   -o CMakeFiles/archive.dir/lib/lib2.c.o   -c /tmp/cmake_test/lib/lib2.c
/usr/bin/ar qc libarchive.a  CMakeFiles/archive.dir/lib/lib1.c.o CMakeFiles/archive.dir/lib/lib2.c.o
/usr/bin/cc -DAAA -DBBB -I/tmp/cmake_test -I/tmp/cmake_test/inc -I/tmp/cmake_test/inc2 -I/tmp/cmake_test/lib -I/tmp/cmake_test/dlbase  -g3 -O0   -o CMakeFiles/test.dir/main.c.o   -c /tmp/cmake_test/main.c
/usr/bin/cc  -g3 -O0   CMakeFiles/test.dir/main.c.o  -o test  -L/tmp/cmake_test  -L/tmp/cmake_test/lib -Wl,-rpath,/tmp/cmake_test:/tmp/cmake_test/lib:/tmp/cmake_test/build: libarchive.a libdlbase.so

执行 make install

[root@dldl build]# make install
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/test/bin/test
-- Set runtime path of "/usr/local/test/bin/test" to ""
-- Installing: /usr/local/test/lib/libarchive.a
-- Installing: /usr/local/test/lib/libdlbase.so
-- Up-to-date: /usr/local/test/script/test_3.py
-- Up-to-date: /usr/local/test/script/test_5.py
-- Up-to-date: /usr/local/test/script/dir1
-- Up-to-date: /usr/local/test/script/dir1/test_6.py
-- Up-to-date: /usr/local/test/script
-- Up-to-date: /usr/local/test/script/test.py
-- Up-to-date: /usr/local/test/script/test_2.py
-- Up-to-date: /usr/local/test/script/sub_script
-- Up-to-date: /usr/local/test/script/sub_script/test_4.py
install done

然后我们看下安装路径目录结构

/usr/local/test
├── bin
│   └── test
├── lib
│   ├── libarchive.a
│   └── libdlbase.so
└── script
    ├── dir1
    │   └── test_6.py
    ├── sub_script
    │   └── test_4.py
    ├── test_2.py
    ├── test_3.py
    ├── test_5.py
    └── test.py

所有安装的文件都在 install_manifest.txt 文件里,如果想卸载直接执行如下操作

cat install_manifest.txt | xargs rm -f

注意此时执行 build/test成功,而执行 /usr/local/test/bin/test 会报找不到 libdlbase.so

从上面我们可以看出在 make 时最后链接时加上了运行时库搜索路径 -Wl,-rpath,/tmp/cmake_test:/tmp/cmake_test/lib:/tmp/cmake_test/build:

而当我们 make install 时去掉了库搜索路径(上面标红的输出)


部分常用变量的解释,请参照 变量

CMAKE_INSTALL_PREFIX        #安装路径前缀
PROJECT_SOURCE_DIR          #项目根目录



上一篇: 库编译选项生成 pkg-config 以及运行时库查找路径
下一篇: gcc 常见用法
作者邮箱: 203328517@qq.com