【笔记】CMake构建C++工程

一句话结论:本文归纳CMake构建c++工程的基本用法,实现多依赖、多工程、多文件格式的工程编译构建。


1.简介

CMake(cross-platform make)是一个跨平台编译工具,它不能直接生成最终可执行程序,而是构建各平台标准的构建文档(如Linux的Makefile、Windows的sln)。
使用时,用户不再需要直接编写底层Makefile文件,而是编写语法简单的CMakeLists.txt文件,CMake在执行目录下查找CMakeLists.txt来生成Makefile、构建工程,大幅降低工程构建和编译的难度。

2.简单工程

- output
- build
- tests
	- test0
		- main.cc
	- CMakeLists.txt
cmake_minimum_required(VERSION 3.10)

# set c++11 
set(CMAKE_CXX_FLAGS "$GUN_FLAGS -std=c++11")

# set output dir
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../output) # based on current CMakeLists.txt
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../output) # .exe
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../output) # .a

# build test0
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test0 SRC_TEST0) # collect all cxx files in $test0
add_executabel(test0 ${SRC_TEST0})

在项目构建和编译过程中会生成大量中间文件,为保持源代码目录纯净,一般在其他目录新建一个build目

mkdir build
cd build
cmake ../tests/ 
make

3.生成并使用链接库

目的:在A工程下,生成动态链接库,在B工程下调用。
每个带cxx的模块下都需要有CMakeLists.txt来描述当前模块的编译配置。

- output
- build
- root 
	- libs
		- B 
			- B.h
			- B.cc 
			- CMakeLists.txt
    - tests
        - testA
            - main.cc
        - CMakeLists.txt

libs/B下的CMakeLists.txt

file(GLOB_RECURSE SRC_LIBS_B
	${CMAKE_CURRENT_SOURCE_DIR}/B/*.h
	${CMAKE_CURRENT_SOURCE_DIR}/B/*.cc
)
add_library(B SHARED ${SRC_LIBS_B}) # shared->.so  static->.a  object->.o

tests下的CMakeLists

... # "简单工程"中CMakeLists.txt的常规配置
include_libraries({CMAKE_CURRENT_SOURCE_DIR}/../libs/B)

aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test0 SRC_TEST0) # collect all cxx files in $test0

# link B.so
add_executabel(testA ${SRC_TESTA})
target_link_libraries(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libB.so)

# link B.a
link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libB.a)
add_executable(testA ${SRC_TESTA})

# linke B/*.o
add_executable(testA ${SRC_TESTA} $<TARGET_OBJECTS:B>)

4. 超多依赖模块库

目的:
A -> B.so ->C.a->D.a
B.so->C.a B.so->D.a
C.a->D.a
A->C.a A->D.a
在这里插入图片描述

- output
- build
- exlibs
	- D 
		- D.h
		- D.cc
		- CMakeLists.txt
- root
	- statics
		- C
			- C.h
			- C.cc
			- CMakeLists.txt   
	- libs
		- B 
			- B.h
			- B.cc 
			- CMakeLists.txt
    - tests
        - testA
            - main.cc
        - CMakeLists.txt
- CMakeLists.txt

项目构建时,脚本从外往里调,从里往外构。最终依赖exlibs/D/CMakeLists.txt。

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/D)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/D SRC_LIBD)
add_library(D STATIC ${SRC_LIBD}) # 最外层CMakeLists会定义libD的输出目录

statics/C/CMakeLists.txt

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../exlibs/D) # include D.header
include_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a) # 使用静态链接库

#include_directories(${CMAKE_CURRENT_SOURCE_DIR})
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC_LIBC)
add_library(C STATIC ${SRC_LIBD}) # 最外层CMakeLists会定义libC.a的输出目录

libs/B/CMakeLists.txt

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../exlibs/D) # include D.header
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../statics/C) # include C.header
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC_LIBB)

link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libC.a ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a) # 静态链接库,左边依赖右边

add_library(B SHARED ${SRC_LIBD})

tests/CMakeLists.txt

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../extlibs/D)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../statics/C)

# C&D都是静态链接库的形式,这里还需要再次包含一遍
link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libC.a ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a) # 静态链接库,左边依赖右边

file(GLOB_RECURSE SRC_TESTA
	${CMAKE_CURRENT_SOURCE_DIR/testA/*.h}
	${CMAKE_CURRENT_SOURCE_DIR/testA/*.cc}
)
add_executable(testA ${SRC_TESTA})
target_link_libraries(testA PUBLIC ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libB.so) # 使用动态链接库B

最外层CMakeLists.txt

cmake_minium_version(VERSION 3.10)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJEC_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJEC_SOURCE_DIR}/output)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJEC_SOURCE_DIR}/output)

set(CMAKE_CXX_FALGS "${GUN_FLAGS} -std=c++11 -fPIC -Wl,--no-as-needed -ldl -lphread") # 使用c++11标准;-fPIC 动态库中使用静态库;-Wl,--no-as-needed 链接dlsym所需系统库;-lphread使用多线程库phread

add_subdirectory(exlibs/D)
add_subdirectory(root/statics/C)
add_subdirectory(root/libs/B)
add_subdirectory(root/tests)

5.超多依赖库II

同样的依赖关系,不想把C构建成单独的静态库,而是直接使用C编译成的.o。此时只需要在构建B时将C/*.o链接。
1.修改statics/C/CMakeLists.txt最后一行

# add_library(staticC STATIC ${SRC_LIBD})
add_library(objC OBJECT ${SRC_LIBD}) # 将C直接编译成.o备用

2.修改libs/B/CMakeLists.txt的最后2行

# link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libC.a ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a) # 静态链接库,左边依赖右边
# add_library(staticC SHARED ${SRC_LIBD})

link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a) # 只链接静态库
add_library(B SHARED 
	${SRC_LIBD}
	$<TARGET_OBJECTS:objC>
) # 直接把C编到libB.so里,后续使用时,只需要链接libB.so就可以用C里的符号

3.修改tests/CMakelists.txt,去除对libC.a的依赖

# # C&D都是静态链接库的形式,这里还需要再次包含一遍
# link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libC.a ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a) # 静态链接库,左边依赖右边

# 去除对libC.a的依赖
link_libraries(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libD.a}

6.总结

除此之外,还需要指定cmake_cxx_flag编译指令以实现链接系统库等个性化需求;也可以引入xxx.cmake美化cmake的CMakelists文件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用CMake构建C++工程的示例。 我们假设这个工程有如下目录结构: ``` . ├── CMakeLists.txt ├── include │ └── mylib.h ├── src │ ├── main.cpp │ └── mylib.cpp └── test ├── CMakeLists.txt └── mylib_test.cpp ``` 其中: - `include` 目录包含头文件 `mylib.h` - `src` 目录包含源文件 `main.cpp` 和 `mylib.cpp` - `test` 目录包含测试代码 `mylib_test.cpp` 下面是 `CMakeLists.txt` 文件的内容: ```cmake # 设置 CMake 最低版本需求 cmake_minimum_required(VERSION 3.10) # 设置项目名称和版本号 project(myproject VERSION 1.0) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) # 添加一个库 target,包含 mylib.cpp 和 mylib.h add_library(mylib src/mylib.cpp include/mylib.h) # 添加一个可执行文件 target,包含 main.cpp,以及 mylib 库 add_executable(myapp src/main.cpp) target_link_libraries(myapp PRIVATE mylib) # 添加一个测试 target,包含 mylib_test.cpp,以及 mylib 库 enable_testing() find_package(GTest REQUIRED) add_executable(mylib_test test/mylib_test.cpp) target_link_libraries(mylib_test PRIVATE mylib GTest::GTest GTest::Main) add_test(NAME mylib_test COMMAND mylib_test) ``` 其中: - `cmake_minimum_required(VERSION 3.10)` 声明了本工程需要 CMake 最低版本 3.10。 - `project(myproject VERSION 1.0)` 声明了本工程的名称和版本号。 - `set(CMAKE_CXX_STANDARD 11)` 设置了 C++ 标准为 C++11。 - `add_library(mylib src/mylib.cpp include/mylib.h)` 添加了一个名为 `mylib` 的库 target,包含 `mylib.cpp` 和 `mylib.h`。 - `add_executable(myapp src/main.cpp)` 添加了一个名为 `myapp` 的可执行文件 target,包含 `main.cpp` 和 `mylib` 库。 - `enable_testing()` 启用了测试功能。 - `find_package(GTest REQUIRED)` 查找 Google Test 库。 - `add_executable(mylib_test test/mylib_test.cpp)` 添加了一个名为 `mylib_test` 的测试 target,包含 `mylib_test.cpp` 和 `mylib` 库。 - `target_link_libraries(mylib_test PRIVATE mylib GTest::GTest GTest::Main)` 将 `mylib` 和 `GTest` 库链接到 `mylib_test` 中。 - `add_test(NAME mylib_test COMMAND mylib_test)` 添加了一个名为 `mylib_test` 的测试。 现在你可以使用以下命令构建和运行工程: ```bash mkdir build cd build cmake .. make ./myapp ctest ``` `make` 命令将编译所有 target,并生成可执行文件和库文件。`./myapp` 命令将运行 `myapp` 可执行文件。`ctest` 命令将运行所有测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值