前些天用 C 和 CPP 写了一个较为大型的项目
(可能也没有那么大型),其中模块间有相互依赖关系,配环境时候卡了很久,主要是对 CMake 的多模块不熟悉,查资料、抄 llvm、瞎测搞出了一些规律,本文不保证很准确,选择性地阅读。
本文不保证很准确,请选择性地阅读。
一、单个模块和基本语法
基本属性
1 |
cmake_minimum_required(VERSION 3.13) |
表示版本号,一般无所谓。
1 |
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
1 |
add_library(A_shared SHARED fileA.c include/fileA.h) |
这样会在目录下生成 libA_shared.dylib (或者 libA_shared.so )
构建静态链接库 add_library
1 |
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
1 |
include_directories(%s) |
一般指定一些第三方依赖,或者为了结构化,指定include文件夹,这样可以在c代码里将 #include "include/hello.h" 省略为 #include "hello.h" 。(不保证这句话的准确性)
指定动态/静态链接库对外暴露的头文件
1 |
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 |
主要内容为:
1 2 3 4 |
# cat CMakeLists.txt cmake_minimum_required(VERSION 3.13) add_subdirectory(sub1) add_subdirectory(sub2) |
1 2 3 4 5 6 7 8 9 |
# cat sub1/CMakeLists.txt project(sub1 C) add_library(shared_1 SHARED f1.c f1.h ) target_include_directories(shared_1 PUBLIC ${PROJECT_SOURCE_DIR}) |
1 2 3 4 5 |
# cat sub2/CMakeLists.txt 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 的头文件,否则只能访问到共享库本身。
四、总结
有这些应该够用了,反正我开发是够了。