在库中使用 CMake "set(XXXX CACHE ...)" 是一个错误吗?

Is CMake "set(XXXX CACHE ...)" a bug when used in a library?

我在我的项目中使用 CMake,我的项目的第三方库之一(为方便起见包含在 add_subdirectory() 中)也是如此。我一直遇到奇怪的构建问题,我想我已经将它们追踪到第三方库顶级 CMakeLists.txt:

中的以下行
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "")

这会为我的整个项目设置 CMAKE_DEBUG_POSTFIX(明确设置的任何子树除外),这会破坏我的构建。更糟糕的是,构建行为是顺序和时间相关的,在干净构建之后,而不是在重建之后,值在我的构建中间被更改。 (追踪很有趣。)

本库设置了很多名称以“XXXX_”开头的缓存变量,其中“XXXX”是本库的名称。这对我来说很好,因为这些变量不太可能被其他人的代码使用。但是,当您的代码本应成为其他人项目的组成部分时,在全局范围内设置常用变量似乎是反社会的。它也很脆弱;如果我在顶层 CMakeLists.txt 中使用 set(XXXX <aValue>),那么库的 set(XXXX CACHE...) 语句将被忽略。

相反,库应该只使用 set(CMAKE_DEBUG_POSTFIX "d"),它为库的所有代码树设置变量,而不是其他人的。

这是图书馆构建代码中的错误吗?旨在成为优秀 CMake 公民的库是否应该避免 CACHE 变量,除了它们明确命名的私有变量?

对于打算用作子项目的库(通过 add_subdirectoryFetchContent),我会说 设置此类的错误缓存变量而不检查项目是否是顶级的。另一方面,not 打算以这种方式使用的项目在这种情况下应该明确检查并发出致命错误(或者可能是作者警告)。因此,无论哪种方式,我都认为存在错误,您应该通知维护者。

在 CMake 3.21+ 中,变量 PROJECT_IS_TOP_LEVEL 有效。在早期版本中,您可以这样写:

string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL)

获取相同的变量。然后检查:

if (PROJECT_IS_TOP_LEVEL)
  # Either this: (AUTHOR_WARNING acceptable, too)
  message(FATAL_ERROR "Subproject inclusion not supported")
  # or:
  set(CMAKE_* ... CACHE ...)
endif ()