CMake¶
cmake不同工程类型的样例,所有样例可在GitHub仓库中找到
Exec工程¶
cmake通过目录下的CMakeLists.txt
文件,将整个工程串联起来。在主目录的CMakeLists.txt
文件中,通过add_subdirectory
命令加入子一级的目录,以此类推。
主配置文件¶
- 主配置文件主要配置一些全局信息。例如,项目名称,C++库版本等。同时加入子一级目录。
cmake_minimum_required(VERSION 3.13) # Setting a project name project(exec VERSION 1.0 DESCRIPTION "EXEC Project" LANGUAGES CXX) # set the C++ standard set(CMAKE_CXX_STANDARD 20) # Use STD c++ instead of GNU set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) message(STATUS "[ROOT] CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR}") message(STATUS "[ROOT] CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR}") message(STATUS "[ROOT] CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}") ###################### # Add Targets ###################### add_subdirectory(src)
Exec目标配置文件¶
- 为了统一,可选择
set_target_properties
命令,为此目标配置所有的编译信息- 编译和链接选项通过分号隔离
- 默认文件输出路径就是
CMAKE_CURRENT_BINARY_DIR
set_target_properties(EXEC PROPERTIES COMPILE_OPTIONS "-Wall;-Werror;-Wpedantic;-I." LINK_OPTIONS "-Wl,-Map=output.map,--print-memory-usage;-L." RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
- 如果不想用分号隔离编译和链接选项,可以通过
separate_arguments
命令将空格转换成分号,从而选项的写法就不会那么奇怪,下面的写法和上面的的写法是相同的,但是可读性更好也更不容易出错:separate_arguments(COMPILE_OPTION UNIX_COMMAND "-Wall -Werror -Wpedantic -I.") separate_arguments(LINK_OPTION UNIX_COMMAND "-Wl,-Map=output.map,--print-memory-usage -L.") set_target_properties(EXEC PROPERTIES COMPILE_OPTIONS "${COMPILE_OPTION}" LINK_OPTIONS "${LINK_OPTION}" RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
- 开启
verbose
后,可得到完整的编译链接命令- 编译命令
[ 50%] Building CXX object src/CMakeFiles/EXEC.dir/main.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/exec/build/src && /usr/bin/c++ -g -Wall -Werror -Wpedantic -I. -std=c++20 -MD -MT src/CMakeFiles/EXEC.dir/main.cpp.o -MF CMakeFiles/EXEC.dir/main.cpp.o.d -o CMakeFiles/EXEC.dir/main.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/exec/src/main.cpp
- 链接命令
[100%] Linking CXX executable EXEC cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/exec/build/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/EXEC.dir/link.txt --verbose=1 /usr/bin/c++ -g -Wl,-Map=output.map,--print-memory-usage -L. CMakeFiles/EXEC.dir/main.cpp.o -o EXEC Memory region Used Size Region Size %age Used
- 编译命令
cmake目标¶
cmake配置完成后,可通过cmake
生成对应的Makefile
。此工程的目标有:
build> make help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... edit_cache
... rebuild_cache
... EXEC
EXEC
是我们定义的目标,其他的是由cmake
自己生成的。
动态库工程¶
动态库通过add_library
命令添加目标,而EXEC工程通过add_executable
添加工程。除了这个区别,其他配置两者基本一致。
动态库配置文件¶
- 为了方便查找动态库的头文件,通过
target_include_directories
命令,将目录文件输出file(GLOB_RECURSE SRC_CPP ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp") # Set SHARED to build so library add_library(mylib SHARED ${SRC_CPP}) # Set public so that its dependency can get the include directory target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
- 编译命令
- 先编译
.o
文件 - 再将所有
.o
文件链接成.so
文件[ 25%] Building CXX object src/CMakeFiles/mylib.dir/hello.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build/src && /usr/bin/c++ -Dmylib_EXPORTS -I/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/src -g -fPIC -std=c++20 -MD -MT src/CMakeFiles/mylib.dir/hello.cpp.o -MF CMakeFiles/mylib.dir/hello.cpp.o.d -o CMakeFiles/mylib.dir/hello.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/src/hello.cpp [ 50%] Linking CXX shared library libmylib.so cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/mylib.dir/link.txt --verbose=1 /usr/bin/c++ -fPIC -g -shared -Wl,-soname,libmylib.so -o libmylib.so CMakeFiles/mylib.dir/hello.cpp.o make[3]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build'
- 先编译
动态库测试配置文件¶
- 通过
target_link_libraries
命令就可以链接到动态库,由于动态库工程已经将其头文件目录PUBLIC
输出,所以此处能找到头文件target_link_libraries(myTest PRIVATE mylib )
- 编译命令
[ 75%] Building CXX object test/CMakeFiles/myTest.dir/main.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build/test && /usr/bin/c++ -I/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/src -g -std=c++20 -MD -MT test/CMakeFiles/myTest.dir/main.cpp.o -MF CMakeFiles/myTest.dir/main.cpp.o.d -o CMakeFiles/myTest.dir/main.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/test/main.cpp [100%] Linking CXX executable myTest cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build/test && /usr/bin/cmake -E cmake_link_script CMakeFiles/myTest.dir/link.txt --verbose=1 /usr/bin/c++ -g CMakeFiles/myTest.dir/main.cpp.o -o myTest -Wl,-rpath,/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build/src ../src/libmylib.so make[3]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/libso/build'
静态库工程¶
静态库工程和动态工程除了在add_library
命令中使用STATIC
选项以外,其他和动态库工程完全一样。
静态库配置文件¶
- 通过
add_library(mylib STATIC ${SRC_CPP})
配置了静态库目标 - 编译命令
[ 25%] Building CXX object src/CMakeFiles/mylib.dir/hello.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/src && /usr/bin/c++ -I/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/src -g -std=c++20 -MD -MT src/CMakeFiles/mylib.dir/hello.cpp.o -MF CMakeFiles/mylib.dir/hello.cpp.o.d -o CMakeFiles/mylib.dir/hello.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/src/hello.cpp [ 50%] Linking CXX static library libmylib.a cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/src && /usr/bin/cmake -P CMakeFiles/mylib.dir/cmake_clean_target.cmake cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/mylib.dir/link.txt --verbose=1 /usr/bin/ar qc libmylib.a CMakeFiles/mylib.dir/hello.cpp.o /usr/bin/ranlib libmylib.a make[3]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build'
源代码导入工程¶
将其他目录下的源代码和当前目录的源代码一起编译,可以排除因编译器版本不同,而造成目标文件的兼容问题。cmake提供了:
* aux_source_directory(<dir> <variable>)
* 用于将指定目录下所有的源文件记录在<varible>
中,以便和当前目录的源文件同时编译
导入源代码配置文件¶
- 先通过
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src1 SRCS1)
命令,将src1
目录下的所有源代码文件记录在SRCS1
变量中 - 再通过
add_executable(SRC_IMPORT_EXEC ${SRCS1} ${SRCS2} main.cpp)
命令,将其他目录下的源代码和当前目录下的源代码一起编译
自定义工程¶
除了常规目标(EXEC,静态库,动态库),cmake还提供了一些命令用于用户自定义的目标:
add_custom_command
- 用于产生当前目标的依赖文件,本文的例子就用此命令执行了自动生成的代码,用于当前目标的编译
add_custom_target
- 创建一个非常规的目标。例如,我们可以用其创建一个
doxygen
目标用来生成代码文档
- 创建一个非常规的目标。例如,我们可以用其创建一个
add_dependencies
- 建立自定义依赖关系。cmake对库依赖会自动建立依赖关系,但是如果我们想建立一个非常规的依赖关系,就需要用到此命令。例如,建立自定义目标的依赖关系
- 当依赖关系建立好后,系统会按照依赖关系进行按顺序编译
自定义目标配置文件¶
本例的编译顺序如下,可参考简单的Makefile文件:
- 先通过
python
的cog
命令自动生成CPP源代码 - 再将
main.cpp
与生成的代码进行编译 - 最终生成一个可执行文件
自定义目标的配置文件如下:
set(CODEGEN_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/internal")
set(CODEGEN_SRC "${CODEGEN_BINARY_DIR}/animal.cpp")
add_executable(main main.cpp ${CODEGEN_SRC})
add_custom_command(
OUTPUT ${CODEGEN_SRC}
# Copy all files in this folder to $(CODEGEN_BINARY_DIR)
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/internal ${CODEGEN_BINARY_DIR}
COMMAND make -C ${CODEGEN_BINARY_DIR}
COMMENT "Generate C++ code with cog python tool"
VERBATIM
)
set_target_properties(main PROPERTIES
COMPILE_OPTIONS "-I${CODEGEN_BINARY_DIR}"
LINK_OPTIONS ""
RUNTIME_OUTPUT_DIRECTORY ""
)
internal
文件夹用于代码生成,在执行前被拷贝到目标工作目录CMAKE_CURRENT_BINARY_DIR
下- 由于当前目标依赖生成的源文件,所以在编译的时候要加入头文件目录,以防止自动生成了依赖的头文件在编译时无法找到
- 编译命令
- 源文件自动生成命令
[ 25%] Generate C++ code with cog python tool cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src && /usr/bin/cmake -E copy_directory /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/src/internal /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src && make -C /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal make[4]: Entering directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal' python3 -m cogapp -d -s " //cog generated" -o animal.cpp animal.cpp.cog make[4]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal'
- 编译链接命令
[ 50%] Building CXX object src/CMakeFiles/main.dir/main.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src && /usr/bin/c++ -g -I/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal -std=c++20 -MD -MT src/CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/src/main.cpp [ 75%] Building CXX object src/CMakeFiles/main.dir/internal/animal.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src && /usr/bin/c++ -g -I/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal -std=c++20 -MD -MT src/CMakeFiles/main.dir/internal/animal.cpp.o -MF CMakeFiles/main.dir/internal/animal.cpp.o.d -o CMakeFiles/main.dir/internal/animal.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src/internal/animal.cpp [100%] Linking CXX executable main cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/main.dir/link.txt --verbose=1 /usr/bin/c++ -g CMakeFiles/main.dir/main.cpp.o CMakeFiles/main.dir/internal/animal.cpp.o -o main make[3]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/custom/build'
- 源文件自动生成命令
WebAssembly工程¶
另一篇文章“WebAssembly”介绍了WebAssembly和C/C++的用法。当时的例子是用Makefile
集成编译的。通过emcmake
结合cmake
命令可以方便地将C/C++ cmake
工程编译成“WebAssembly”工程,命令如下:
web: clean
emcmake cmake -S . -B build
cmake --build ./build --verbose
修改静态库工程¶
通过将静态库工程的cmake
配置命令变成emcmake cmake
,就可直接编译成“WebAssembly”工程,可参考其Makefile中的web目标。
- 编译命令
- 静态库
[ 25%] Building CXX object src/CMakeFiles/mylib.dir/hello.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/src && /home/yuxiangw/emsdk/upstream/emscripten/em++ @CMakeFiles/mylib.dir/includes_CXX.rsp -std=c++20 -MD -MT src/CMakeFiles/mylib.dir/hello.cpp.o -MF CMakeFiles/mylib.dir/hello.cpp.o.d -o CMakeFiles/mylib.dir/hello.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/src/hello.cpp [ 50%] Linking CXX static library libmylib.a cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/src && /usr/bin/cmake -P CMakeFiles/mylib.dir/cmake_clean_target.cmake cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/mylib.dir/link.txt --verbose=1 /home/yuxiangw/emsdk/upstream/emscripten/emar qc libmylib.a CMakeFiles/mylib.dir/hello.cpp.o /home/yuxiangw/emsdk/upstream/emscripten/emranlib libmylib.a make[3]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build'
- 链接命令存在于
CMakeFiles/mylib.dir/link.txt
文件,通过emar
命令进行压缩/home/yuxiangw/emsdk/upstream/emscripten/emar qc libmylib.a CMakeFiles/mylib.dir/hello.cpp.o /home/yuxiangw/emsdk/upstream/emscripten/emranlib libmylib.a
- 链接命令存在于
- WASM和JS文件
[ 75%] Building CXX object test/CMakeFiles/myTest.dir/main.cpp.o cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/test && /home/yuxiangw/emsdk/upstream/emscripten/em++ @CMakeFiles/myTest.dir/includes_CXX.rsp -std=c++20 -MD -MT test/CMakeFiles/myTest.dir/main.cpp.o -MF CMakeFiles/myTest.dir/main.cpp.o.d -o CMakeFiles/myTest.dir/main.cpp.o -c /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/test/main.cpp [100%] Linking CXX executable myTest.js cd /home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build/test && /usr/bin/cmake -E cmake_link_script CMakeFiles/myTest.dir/link.txt --verbose=1 /home/yuxiangw/emsdk/upstream/emscripten/em++ @CMakeFiles/myTest.dir/objects1.rsp -o myTest.js @CMakeFiles/myTest.dir/linklibs.rsp make[3]: Leaving directory '/home/yuxiangw/GitHub/learning_book/docs/topics/cmake/code/liba/build'
- 同样,链接命令也存在与
CMakeFiles/myTest.dir/link.txt
文件中
- 同样,链接命令也存在与
- 静态库