在CMake中从父级CMakeLists.txt覆盖默认选项(...)值

82

我正在尝试将几个第三方库包含到我的源代码树中,以便于升级时最小化对其构建系统的更改。它们都使用CMake,而我也是,所以在我的CMakeLists.txt中,我可以使用add_subdirectory(extern/foo)来添加libfoo。

但是foo的CMakeLists.txt编译了测试用例、构建文档和一个我不需要的共享库等内容。libfoo的作者预见到了这一点,并通过选项来控制它们 - 例如option(FOO_BUILD_SHARED "Build libfoo shared library" ON) - 这意味着我可以通过CMake命令行设置它们。但我希望默认关闭并可通过命令行覆盖。

我曾尝试在add_subdirectory(extern/foo)之前执行set(FOO_BUILD_SHARED OFF)。这会导致在第二次及后续构建尝试中不尝试构建共享库,但在我真正需要加速的第一次构建中仍会构建。

这种操作是否可行,还是我需要为这些项目维护分叉的CMakeLists.txt?

3个回答

95
尝试在CACHE中设置变量。
SET(FOO_BUILD_SHARED OFF CACHE BOOL "Build libfoo shared library")

注意:您需要指定变量类型和描述,这样CMake才知道如何在GUI中显示此条目。


13
将它放在add_subdirectory命令之前,这样它将设置变量的默认值。 - tibur
12
有时候你需要使用“FORCE”。 - Rajish
8
专业提示(给自己):FORCE应该放在文档字符串之后,而不是随意放在中间... - Claudiu
9
如果变量由于之前的OPTIONSET(CACHE)调用或上一次配置的CMakeCache.txt已经被缓存,那么您需要使用SET(FOO_BUILD_SHARED OFF CACHE BOOL "Build libfoo shared library" FORCE)来源 - Abai
内部的不行吗? - Zingam
如果想要隐藏选项(从CMake GUI中),请使用INTERNAL而不是BOOL。那么,cmake_dependent_option呢?它也适用吗? - Top-Master

10

这个问题很旧了,但是谷歌把我带到了这里。

使用SET(<变量名称> <值> CACHE BOOL "" FORCE)的问题在于它会在整个项目范围内设置选项。如果您想要使用一个子项目(库)并且您想要为子项目(ParentLibrary)设置BUILD_STATIC_LIBS,则使用SET(... CACHE BOOL "" FORCE)会将该值设置为所有项目。

我正在使用以下项目结构:

|CMakeLists.txt (root)
|- dependencies
   | CMakeLists.txt (dependencies)
   |- ParentLibrary
      | CMakeLists.txt (parent)
|- lib
   | CMakeLists.txt (lib)

现在我有一个名为CMakeLists.txt(依赖)的文件,它长这样:

# Copy the option you want to change from ParentLibrary here
option (BUILD_SHARED_LIBS "Build shared libraries" ON)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(ParentLibrary)

优点是我不需要修改ParentLibrary,而且我可以仅针对该项目设置选项。

必须显式复制option命令,否则在执行CMake配置时,变量的值会首先由set命令设置,然后再被option命令覆盖,因为缓存中没有值。当第二次执行CMake配置时,option命令将被忽略,因为缓存中已经有一个值,并且将使用来自set命令的值。这将导致两个CMake运行之间的配置不同的一些奇怪行为。


好主意,但确切地说,该选项将适用于依赖项下的所有子项目,而不仅仅是一个项目。 - stackprotector

4

为了给出更加实时的答案,因为新项目使用更新的CMake版本(可能使用旧版CMake版本的库)。

从CMake 3.13开始,有一个选项可以在父项目中简单地设置变量,从而在所有子项目中设置变量。这基本上就是您尝试过的,但现在是可能的。版本3.13引入了CMP0077

option()命令通常用于创建缓存条目以允许用户设置选项。但是,在调用option()命令之前,项目中可能定义了与选项同名的普通(非缓存)变量。例如,将另一个项目作为子目录嵌入的项目可能希望硬编码子项目的选项以按其需要构建。
...
在CMake 3.13及以上版本中,当给定名称的普通变量已经存在时,option()命令更喜欢什么也不做。它不会创建或更新缓存条目,也不会删除普通变量。新行为在构建树中的第一次和后续运行之间保持一致。此策略提供了与未更新以期望新行为的项目的兼容性。

所以,您可以简单地使用set在父项目内硬编码一个选项,只需在add_subdirectory之前添加以下内容:

set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(FOO_BUILD_SHARED OFF)

...

add_subdirectory(extern/foo)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接