CMake是如何工作的
[toc]
出错不要慌,首先一定要仔细看报错。在根据报错去解决问题。一定要学会用二分法先定位到问题是哪个地方出来的。
首先从图中可以知道,出错是在编译 transport_receiver这个target的时候。但是去找transport_receiver的时候发现包含了头文件目录。
仔细看FAILED 的信息
Building CXX object build_report_sdk/CMakeFiles/reportsdk.dir///dispatch_queue.cpp.o
因为CMakeFiles中的文件才是最后编译的参数,进入找到相应的文件,然后根据错误再去修正。
根据错误,知道是CMakeFiles下面的reportsdk这个target编译的时候找不到头文件。之后找到这个generator产生的编译表示产物,看一下是否真的没有-I参数指定对应的目录。如果没有就找到该targrt添加并重新编译。
CMake由来
Cmake是一个build system。经过多面的发展已经发展为一个系列。开发工具包括CMake、Cpack、CTest、CDash。具体可以看cmake gitlab链接。
CMake是一个是Build工具,负责可执行文件的构建
CTest是一个测试驱动工具,用于运行测试
CPack是一个打包工具,用于创建用特定平台用Cmake编译的软件安装包
CDash是一个Web应用,用于显示测试结果和执行在持续集成测试参考
CMake是如何实现的
如前所述,CMake是一个元构建系统,可用于为许多其他构建工具创建构建文件.
使用cmake –help可以查看其生成器。
CMake处理过程
CMake主要有两个阶段
Configure CMake处理输入给它的一切,同时创建执行Build的内部表示
Generate 创建真正的编译文件
CMake Configure步骤
检测CMakeCache.txt是否存在,如果存在就读取
读取源目录的根目录下的CMakeLists.txt文件,CMake语言解析器会去解析CMakeLists.txt文件。每一个文件中被发现的命令都会被执行,另外CMakeLists.txt文件可以通过CMake命令
include
和add_subdirectory
触发解析。CMake会为每一个命令生成一个可以在CMake语言中使用的C++对象,例如add_library
if
include
等命令。其实CMake整个语言以命令调用的形式实现的。CMake的语言解析器就干了一件事情,就是把CMake输入的文件转换成命令的调用和这些命令需要的参数(字符串列表)。
CMake其实就是将用户写的CMake语句进行执行,执行完了后在内存中就会生成文件(表示项目的构建),这些文件中包含了库、可执行文件、自定义命令、选择generator需要的信息。这些都在CMakeCache.txt文件里面。
内存中的文件有所以需要构建的target(比如库、可执行文件),用户也可以自定义target(用户可以定义他们的输入、输出、编写他们在运行时自定义的命令或脚本)。CMake把每一个target都保存到一个CmTarget对象中。
这些对象依次存放到cmMakefile对象中,也就是是在source tree的给定目录中找到的所有目标都会存储到这里。
总结就是,cmMakeifle包含了所有cmTarget目标。
CMake Generate步骤
在configure步骤执行之后就可以执行gennerate步骤了。这个步骤就是使用用户指定的编译工具去得到编译的文件和目标产物。在这一步targets(库、可执行文件、用户自定义target)会被转换为IDE编译工具的输入或者是一系列的Makefiles(被make执行)。
CMake会自动构建依赖,对于generator是Makefiles的项目,Cmake会把项目依赖信息存放在以下四个文件
depend.cmake 存储所有目标的依赖信息
flags.cmake 包含目标源文件的编译选项信息,被改变了就需要重新编译
build.cmake 编译这些依赖的规则
DependInfo.cmake 为了保持编译信息的是最新的,也包含项目这部分的文件,以及他们的语言是什么。当一个目标的依赖过时的时候,就会重新计算目标的依赖。
后面如果想看懂make文件,就需要了解makefile的知识
举例
我有如下CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(native)
find_package(Boost CONFIG REQUIRED)
message(${Boost_VERSION})
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_MACOSX_RPATH 0)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
add_library(testNative SHARED
common/seg.cc
)
add_executable(${PROJECT_NAME} main.cc)
target_include_directories(${PROJECT_NAME} PUBLIC
./common
)
target_link_libraries(${PROJECT_NAME} testNative)
这时候创建build文件夹,执行cmake .. make之后得到如下表示
打开其中一个target文件,结果如下
XXX.o.d文件包含了inclue文件的地址。
这时候添加一个test.h文件进去
报错
fatal error: 'test.h' file not found #include "test.h"
发现是在编译
[ 25%] Building CXX object CMakeFiles/testNative.dir/common/seg.cc.o
找到testNative目标,查看flags.make
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.22
# compile CXX with /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
CXX_DEFINES = -DtestNative_EXPORTS
CXX_INCLUDES =
CXX_FLAGS = -O0 -Wall -g -ggdb -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -mmacosx-version-min=12.2 -fPIC
发现确实没有include进去,这时候修改testNative在CMakeLists.txt的内容,使得包含该头文件。
重新编译查看testNative target下的flags.make
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.22
# compile CXX with /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
CXX_DEFINES = -DtestNative_EXPORTS
CXX_INCLUDES = -I/Users/bytedance/Documents/Code/C++/native/common/test
CXX_FLAGS = -O0 -Wall -g -ggdb -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -mmacosx-version-min=12.2 -fPIC
指定gennerator
我们这时候用的是默认的gennerator。我们可以指定gennerator比如Ninja
Cmake -G Ninja ..
Ninja
同样是编译出错,看一下CMakefiles
参考Ninja学习资料,主要是rule.ninja 和 build.ninja文件
其中rule.ninjaa定义了rule,也就是编译执行的命令
build.ninja 执行真正的build,会给rule中的命令提供输入与输出
查看ninja.build中对应target的写法,发现
INCLUDES = -I/Users/bytedance/Documents/Code/C++/native/./common
确实没有头文件,添加之后
INCLUDES = -I/Users/bytedance/Documents/Code/C++/native/common/test -I/Users/bytedance/Documents/Code/C++/native/./common
就包含在内,问题完美解决。所以一切的根源就是要充分理解问题,定位问题,再去查找问题原因,再解决。不然后面两步会浪费大量的时间与精力。