我觉的Cmake比较核心的一些东西就是
比如我当前项目源码根目录下面有这些源码目录
├── arch├── cfg├── doc├── drivers├── fs├── include├── kernel├── library├── sample└── utils
我会在根目录放置一个 CMakeLists.txt,之后在每个需要管理的目录下面也放置一个CMakeLists.txt,如下所示(这边没有把include包含进来是因为头文件cmake会自动去构建不需要手动添加,拿C语言来举例,所有包含在.C中的.H文件,cmake全部会帮你自动计算出来,不需要手动去添加)
├── CMakeLists.txt├── arch│ └── CMakeLists.txt├── cfg│ └── CMakeLists.txt├── doc│ └── CMakeLists.txt├── drivers│ └── CMakeLists.txt├── fs│ └── CMakeLists.txt├── include├── kernel│ └── CMakeLists.txt├── library│ └── CMakeLists.txt├── sample│ └── CMakeLists.txt└── utils└── CMakeLists.txt├── cmake├── build
需要说明下这边我多加了两个目录cmake和build,cmake目录一般用来放置一些通用的cmake源码模块(比如通用编译环境设置,编译链接的一些选项配置等等),build用来存放项目构建过程中所有的输出文件。
根目录下面CMakeLists.txt大致就可以按下面这样撰写,就可以把整个骨架搭起来。
#cmake最低版本要求cmake_minimum_required(VERSION 3.10.0)#项目名字为testproject(test NONE)#包含通用的编译环境模块到顶层目录include(${CMAKE_SOURCE_DIR}/cmake/base.cmake)#下一级的编译目录add_subdirectory(arch)add_subdirectory(cfg)add_subdirectory(doc)add_subdirectory(drivers)add_subdirectory(fs)add_subdirectory(kernel)add_subdirectory(library)add_subdirectory(sample)add_subdirectory(utils)
进入build目录运行整个编译框架
cd build && cmake .. && make
用到的相关命令
命令标签格式 | 说明 |
---|---|
cmake_minimum_required(VERSION major.minor[.patch[.tweak]] [FATAL_ERROR]) | 设置最低版本的cmake要求,一般放在第一行 |
project(<PROJECT-NAME> [LANGUAGES] [<language-name>...]) | 为项目设置名字,版本,启用的编程语言,我上面没有启用任何编程语言所以用NONE |
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) | 增加一个子目录到编译系统 |
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <VAR>][NO_POLICY_SCOPE]) | 从给出的file文件加载和运行cmake代码 |
#定义一个mytool 可执行程序输出目标,目前只有一个源文件 mytool.cppadd_executable(mytool mytool.cpp)#往mytool可执行程序输出目标添加源文件,现在mytool目标里面包含两个源文件target_sources(mytool PRIVATE mytool2.cpp)#定义一个动态库archive 输出目标,文件有三个源文件add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)#定义一个静态库archive 输出目标,也可以不指定STATIC 因为add_library默认输出目标是#静态库add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)#从给出的源文件直接生成object文件,比如linux下C语言的.o文件add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
用到的相关命令
命令标签格式 | 说明 |
---|---|
add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...]) | 定义一个名字为name可执行程序输出目标,其中 source1 [source2 ...]是源文件,比如C语言是.C的源文件,.H的源文件不需要指定,cmake会自动去搜索 |
add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...]) add_library(<name> OBJECT <src>...) | 定义一个名字为name的库输出目标 |
target_sources(<target><INTERFACE|PUBLIC|PRIVATE> [items1...][<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]) | 往一个目标里面添加源文件,这个目标名字target是在add_executable() 或者add_library() 中定义的name |
命令标签格式 | 说明 |
---|---|
target_include_directories(<target> [SYSTEM] [BEFORE]<INTERFACE|PUBLIC|PRIVATE> [items1...] [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]) | Include的头文件的查找目录,也就是Gcc的[-Idir...]选项 |
target_compile_definitions(<target> <INTERFACE|PUBLIC|PRIVATE> [items1...][<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]) | 通过命令行定义的宏变量,也就是gcc的[-Dmacro[=defn]...]选项 |
target_compile_options(<target> [BEFORE] <INTERFACE|PUBLIC|PRIVATE> [items1...] [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...] | gcc其他的一些编译选项指定,比如-fPIC |
# gcc头文件查找目录,相当于-I选项,e.g -I/foo/bar#CMAKE_SOURCE_DIR是cmake内置变量表示当前项目根目录target_include_directories(test_elf PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/syscalls)# 编译的宏定义,e.g 相当于-D选项 e.g -Dmacro=defnset(MONITOR_OMIT_BSS_INIT "0")set(MONITOR_OMIT_DATA_INIT "0")set(MONITOR_OMIT_T_CHECKS "0")target_compile_definitions(test_elf PRIVATE MONITOR_OMIT_BSS_INIT=${MONITOR_OMIT_BSS_INIT} MONITOR_OMIT_DATA_INIT=${MONITOR_OMIT_DATA_INIT} MONITOR_TRAP_NT_IRQS=${MONITOR_TRAP_NT_IRQS} )# 其他编译选项定义,e.g -fPICtarget_compile_options(test_elf PRIVATE -std=c99 -Wall -Wextra -Werror)
命令标签格式 | 说明 |
---|---|
target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) | item可以是链接到该目标的库的名字(去掉前缀lib和扩展名后缀之后的库名字),也就是gcc链接器的-llibrary选项 Item也可以是链接选项以-开始的item会被认为是链接选项(除了 -l和-framework) Item也可以是要链接到该目标的库文件的完整路径(针对非标准路径的库时可以这样指定,当然也可以用link_directories命令来为链接器增加搜索库的搜索路径) |
# 链接选项设置target_link_libraries(test_elf PRIVATE -msoft-float -static -nostdlib)#设置链接的标准路径的库target_link_libraries(test_elf PRIVATE gcc)#设置链接的本project自己输出的目标库libkernel.atarget_link_libraries(test_elf PRIVATE kernel)#设置链接非标准路径中别人编译好的库target_link_libraries(test_elf PRIVATE $ENV{HOME}/lib/libtest.a)
备注:也可以用其他方式来设置编译链接的一些参数,比如直接设置cmake内置变量或者其他命令,我这里选择这些基于目标的命令来操作主要是因为,基于目标来管理更容易维护和管理,不会污染到其他的作用域。
具体的含义如下:
PRIVATE | 只给自己用,不给依赖者用 |
---|---|
PUBLIC | 自己和依赖者都可以用 |
INTERFACE | 自己不用,给依赖着用 |
add_library(archive archive.cpp)target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)add_library(serialization serialization.cpp)target_compile_definitions(serialization INTERFACE USING_SERIALIZATION_LIB)add_library(archiveExtras extras.cpp)target_link_libraries(archiveExtras PUBLIC archive)target_link_libraries(archiveExtras PRIVATE serialization)# archiveExtras is compiled with -DUSING_ARCHIVE_LIB# and -DUSING_SERIALIZATION_LIBadd_executable(consumer consumer.cpp)# consumer is compiled with -DUSING_ARCHIVE_LIBtarget_link_libraries(consumer archiveExtras)
命令标签格式 | 说明 |
---|---|
add_custom_command(TARGET <target> PRE_BUILD | PRE_LINK | POST_BUILD COMMAND command1 [ARGS] [args1...] [COMMAND command2 [ARGS] [args2...] ...] [BYPRODUCTS [files...]] [WORKING_DIRECTORY dir] [COMMENT comment] [VERBATIM] [USES_TERMINAL]) | Target是库或者可执行文件输出目标 PRE_BUILD只有Visual Studio 8或者之后的版本才支持,其他情况等同于PRE_LINK PRE_LINK在编译之后,链接之前运行, POST_BUILD,该目标的所有构建规则都执行完之后才运行 COMMAND ,指定要执行的命令行命令和参数 |
#在test_elf链接之前,先拷贝一些test_elf需要用到的文件到编译目录,在进行编译add_custom_command(TARGET test_elf PRE_LINKCOMMANDcp ${CMAKE_BINARY_DIR}/cfg/start.o ${CMAKE_BINARY_DIR}/. && cp ${CMAKE_SOURCE_DIR}/target/imx6_gcc/imx6.ld ${CMAKE_BINARY_DIR}/.)#把编译输出的test_elf进行进一步处理,生成最终的bin文件add_custom_command(TARGET test_elf POST_BUILD COMMAND ${CUSTOM_CMD_OBJCOPY} -O binary -S test_elf test_elf.bin)
#先为这个第三方工具设置一个自定义目标,这个目标并没有实际输出文件,#只是一个目标的名字比如这边的autoconfig add_custom_target(autoconfig ALL)#利用这个目标去运行这个第三方工具,这边是cfg,后面是这个工具需要的参数add_custom_command(TARGET autoconfig PRE_BUILDCOMMAND cfg --pass ${pass} ${CFG_ELF_INCLUDE} ${rom_image} ${symbol_table} ${T_file} ${CFG_TABLES}) #autoconfig和test_elf这两目标,没有直接的关系(像前面介绍的可执行程序,可能依赖其#他库输出目标,这里没有这样的关系),需要指定目标输出顺序,先对autoconfig进行输#出(自动生成源码),在进行test_elf输出(编译和链接)add_dependencies(test_elf autoconfig)#把自动生成的源文件cfg_out.c加到test_elf目标去编译target_sources(test_elf PRIVATE cfg_out.c)#指明cfg_out.c是自动生成的文件,否则会认为找不到文件而出错set_source_files_properties(cfg_out.c PROPERTIES GENERATED 1)
命令标签格式 | 说明 |
---|---|
add_custom_target(Name [ALL] [command1 [args1...]] [COMMAND command2[args2...] ...] [DEPENDS depend depend depend ... ] [BYPRODUCTS [files...]] [WORKING_DIRECTORY dir] [COMMENT comment] [VERBATIM] [USES_TERMINAL] [COMMAND_EXPAND_LISTS] [SOURCES src1 [src2...]])[items2...] ...]) | 增加一个名字为Name的目标,该目标没有输出,所以它总是被构建。 ALL选项表明该目标被添加到默认的构建目标,所以它每次都会运行 |
add_dependencies(<target> [<target-dependency>]...) | 在目标target在构建之前,后面的其他target-dependency依赖目标必须先被构建完成Target或者target-dependency目标是由add_executable(),add_library(), add_custom_target()命令输出的目标 |
set_source_files_properties([file1 [file2 [...]]] PROPERTIES prop1 value1 [prop2 value2 [...]]) | 通过键/值对( key/value)来设置源文件的一些属性 |
#支持的编程语言配置enable_language(ASM)#设置汇编源文件的编译器,我这边配置成和C语言的编译器一样set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})#把汇编源码和C源码一起放置在待编译的源文件列表里面就行set(SRC_KERNEL_LIB ${CMAKE_SOURCE_DIR}/target/imx6_gcc/target_support.S ${CMAKE_SOURCE_DIR}/arch/arm_gcc/mpcore/chip_support.S ${CMAKE_SOURCE_DIR}/arch/arm_gcc/common/core_support.S ${CMAKE_SOURCE_DIR}/drivers/mmu_table.c)
命令标签格式 | 说明 |
---|---|
enable_language(<lang> [OPTIONAL] ) | 启用某种编程语言 |
#配置交叉编译变量set(CMAKE_SYSTEM_NAME Linux)set(CMAKE_SYSTEM_PROCESSOR arm)set(CMAKE_CROSSCOMPILING TRUE)#不使用动态链接 -rdyamicset(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set(CMAKE_C_COMPILER "$ENV{CROSS_COMPILE_ROOT_PATH}/bin/$ENV{CROSS_COMPILE_PREFIX}-gcc")set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})set(CMAKE_AR "$ENV{CROSS_COMPILE_ROOT_PATH}/bin/$ENV{CROSS_COMPILE_PREFIX}-ar")set(CMAKE_RANLIB "$ENV{CROSS_COMPILE_ROOT_PATH}/bin/$ENV{CROSS_COMPILE_PREFIX}-ranlib")set(CUSTOM_CMD_OBJCOPY "$ENV{CROSS_COMPILE_ROOT_PATH}/bin/$ENV{CROSS_COMPILE_PREFIX}-objcopy")set(CUSTOM_CMD_NM "$ENV{CROSS_COMPILE_ROOT_PATH}/bin/$ENV{CROSS_COMPILE_PREFIX}-nm") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)set(CMAKE_FIND_ROOT_PATH $ENV{CROSS_COMPILE_ROOT_PATH})
CMakeCInformation.cmake里面的编译器/链接器/归档器的变量定义如下
#编译C源文件if(NOT CMAKE_C_COMPILE_OBJECT) set(CMAKE_C_COMPILE_OBJECT "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")endif()#链接C源文件if(NOT CMAKE_C_LINK_EXECUTABLE) set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")endif()#静态库的链接过程if(NOT DEFINED CMAKE_C_ARCHIVE_CREATE) set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>")endif()if(NOT DEFINED CMAKE_C_ARCHIVE_APPEND) set(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")endif()if(NOT DEFINED CMAKE_C_ARCHIVE_FINISH) set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")endif()
#自定义的ar生成规则(cmake默认的ar打包参数和我项目源码定义参数不一样,所以自己重新定义)set(CMAKE_STATIC_LINKER_FLAGS "-rcs")set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> <LINK_FLAGS> <TARGET> <OBJECTS>")
联系客服