记一次cmake cache引发的编译问题

本文探讨了CMake在条件编译子模块时遇到的问题,分析了CMakeCache.txt的作用及如何避免缓存造成的编译错误。介绍了正确的CMake使用方式,确保编译选项的准确性。

前言

c/c++程序员对cmake肯定都有所耳闻,大型项目的makefile如果要编写起来非常复杂,一般都是通过cmake进行管理。但即使经常接触,但由于cmakelists基本很少改动,但对其一些工作机制还不是很了解,本人前段时间刚好因为对cmake的生疏遇到了一个挺有意思的小问题,记录和分享一下。

问题的出现

假设某项目的代码组织结构如下:

.
└── project
    ├── CMakeLists.txt
    └── src
        ├── CMakeLists.txt
        ├── moduleA
        │   └── CMakeLists.txt
        ├── moduleB
        │   └── CMakeLists.txt
        └── moduleC
            └── CMakeLists.txt

项目名称为project,其内有3个子模块,顶层的CMakeLists.txt如下(仅展示关键内容):

add_subdirectory(moduleA)
add_subdirectory(moduleB)
add_subdirectory(moduleC)

每次触发编译时,cmake ./project 来生成最外层Makefile,再进行make。

假设用个模块编译太过耗时,而有其中一个模块为sdk,发布只需要单独编译出来即可,并不想编译出来起来其他模块。此时需要控制条件编译,由于对cmake不了解,我采取了一个想当然的做法:

option(BUILD_A, "only build moduleA" OFF)

if(BUILD_A)
    add_subdirectory(moduleA)
else()
    add_subdirectory(moduleA)
    add_subdirectory(moduleB)
    add_subdirectory(moduleC)
endif()

然后在编译的时候通过参数来控制编译:

  • cmake ./project && make:全量编译
  • cmake ./project -DBUILD_A=ON && make:单独编译moduleA

起初测试下来发现符合预期没有问题,但是某天突然发现全量编译并没有编译出来moduleB和moduleC的二进制。于是进行一些排查:

  1. 首先查看顶层的Makefile,发现在里面找不到任何moduleB和moduleC的信息,所以确定原因在cmake产生Makefile的过程中
  2. 增加一些打印信息,将顶层CMakeLists.txt修改如下:
option(BUILD_A, "only build moduleA" OFF)

if(BUILD_A)
    message(STATUS "build moduleA only")
    add_subdirectory(moduleA)
else()
    message(STATUS "build all")
    add_subdirectory(moduleA)
    add_subdirectory(moduleB)
    add_subdirectory(moduleC)
endif()

再次编译后发现输出竟然是build moduleA only!但是不是写明了option(BUILD_A, “only build moduleA” OFF)吗?

  1. 清空build目录再试一次全量编译:这次的输出就是build all且所有模块都编译出来了

    至此,基本可以猜到原因了,显然cmake有cache,优先读取cache中的值了。为了验证猜想,看了下编译目录,果然有个CMakeCache.txt,看一下里面是否有BUILD_A的item:

# grep BUILD_A CMakeCache.txt
BUILD_A:BOOL=ON

总结和说明

总结

  • 问题产生的原因:
    在之前我这种写法下,只要先进行一个更小范围的编译,然后不clean就进行下一个更大范围的编译,由于之前的编译选项被cache了,所以下次进行全量编译的时候,并不是预期的用option()的默认值,而是优先读取CMakeCache.txt中的值,导致编译结果不符合预期

  • 两种解决办法:

  1. 每次编译完成后清理编译的CMakeCache.txt
  2. cmake命令中,每次都显示指定编译选项的值

官方说明

最后来看一下官方的说明:

-D :=, -D =
Create a cmake cache entry.
When cmake is first run in an empty build tree, it creates a CMakeCache.txt file and populates it with customizable settings for the project. This option may be used to
specify a setting that takes priority over the project’s default value. The option may be repeated for as many cache entries as desired.
If the : portion is given it must be one of the types specified by the set() command documentation for its CACHE signature. If the : portion is omitted the
entry will be created with no type if it does not exist with a type already. If a command in the project sets the type to PATH or FILEPATH then the will be con‐verted to an absolute path.
This option may also be given as a single argument: -D:= or -D=.

和猜测的一样:使用-D选项时会产生一个cmake cache entry,且该值的优先级高于默认值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值