CMake多模块的构建方式

前些天用 C 和 CPP 写了一个较为大型的项目(可能也没有那么大型),其中模块间有相互依赖关系,配环境时候卡了很久,主要是对 CMake 的多模块不熟悉,查资料、抄 llvm、瞎测搞出了一些规律,本文不保证很准确,选择性地阅读。

本文不保证很准确,请选择性地阅读。

一、单个模块和基本语法

基本属性

cmake_minimum_required(VERSION 3.13)

表示版本号,一般无所谓。

project(%s, C/CXX)

表示该项目是什么语言写的,这个很重要,因为编译出来的符号可能是不一致的。

1
2
SET(CMAKE_C_FLAGS "-m32")
SET(CMAKE_CXX_FLAGS "-m32")

设置这两个变量,SET 是一个常见的语法,表示赋值;如果要表示追加的话,可以使用这句 SET(CMAKE_C_FLAGS ${CMAKE_C_FLAG}"aaa") ,结果是 "-m32aaa" 如果用空格隔开,结果是 "-m32;aaa" ,还是有区别的。

构建动态链接库 add_library

add_library(A_shared SHARED fileA.c include/fileA.h)

这样会在目录下生成 libA_shared.dylib (或者libA_shared.so )

构建静态链接库 add_library

add_library(B_static STATIC fileB.c include/fileB.h)

这样会在目录下生成 libB_static.a

构建可执行文件 add_executable

1
2
add_executable(C_execute main.c)
target_link_library(C_execute A_shared B_static)

编译 main.c 的源文件,再将A 连接进去,将 B 连接进去,最终生成可执行文件 C_exetute 。

指定当前 project 的 include 目录 include_directories

include_directories(%s)

一般指定一些第三方依赖,或者为了结构化,指定include文件夹,这样可以在c代码里将 #include "include/hello.h" 省略为 #include "hello.h" 。(不保证这句话的准确性)

指定动态/静态链接库对外暴露的头文件

target_include_directories(A_shared PUBLIC ${PROJECT_SOURCE_DIR}/include)

这样在其他的 project 里,就可以访问到 include 目录下的一些文件。

二、添加子模块

吼!有这些基础知识后,就可以讲一下多模块的构建了!

用这句话添加子模块:add_subdirectory(sub1)

目录结构:子模块里一定也要有 CMakeLists.txt ,即 sub1/CMakeLists.txt 。

在sub1/CMakeLists.txt 里,为所欲为,该干嘛干嘛,这里先假设自闭的情况,不和父模块、兄弟模块交互。 而且,会从父模块继承一些东西下来,继承什么我不大清楚。

三、模块相互依赖关系

以子模块sub2依赖子模块sub1为例。 目录结构为:

1
2
3
4
5
6
7
8
- CMakeLists.txt
- sub1
- CMakeLists.txt
- f1.c
- f1.h
- sub2
- CMakeLists.txt
- main.c

主要内容为:

cat CMakeLists.txt

1
2
3
cmake_minimum_required(VERSION 3.13)
add_subdirectory(sub1)
add_subdirectory(sub2)

cat sub1/CMakeLists.txt

1
2
3
4
5
6
7
8
project(sub1 C)

add_library(shared_1
SHARED
f1.c
f1.h
)
target_include_directories(shared_1 PUBLIC ${PROJECT_SOURCE_DIR})

cat sub2/CMakeLists.txt

1
2
3
project(sub2)
add_executable(exec_2 main.c)
target_link_libraries(exec_2 shared_1)

有几条规则: 子模块的 project之间是相互可以访问到的,不需要额外操作,例如exec_2可以访问到shared_1。 target_include_directories 是为了让exec_2 访问到shared_1 的头文件,否则只能访问到共享库本身。

四、总结

有这些应该够用了,反正我开发是够了。