CMake中的"project"指令应该如何正确使用?

9
我有一个相当大的代码库,构建了几十个库和多个可执行文件。
该代码库按层次结构分解,几乎在每个层次上都构建了库。
我已经在每个目录中放置了一个CMakeLists.txt文件来构建每个库。
在每个CMakeLists.txt文件中,我使用了“project(xxx)”指令。这为我定义了PROJECT_NAME、PROJECT_SOURCE_DIR和PROJECT_BINARY_DIR变量,我会审慎地使用它们。
然而,队伍中的一位成员不喜欢这种方法,因为他找不到任何真实世界中其他人使用过这种方法的例子。他经常引用KitWare示例未使用此方法,因此我们也不应该使用。
他主张的另一种方法是在每个makefile中设置这些变量,这似乎很像“project”给你的方法。
我真的看不出他的观点,并且在说服他方面没有取得什么进展。有人能否阐明在这种方式下使用项目指令的缺点呢?
我向你们请教?

你如何使用 PROJECT_NAMEPROJECT_SOURCE_DIRPROJECT_BINARY_DIR 变量?这些库是独立的还是只是主库/应用程序的一部分? - David Brown
我在生成源代码的宏中使用这些变量。我创建了一个名为${PROJECT_NAME}_SPDEF的顶级目标,其他项目可以依赖它来强制生成这些代码。我还使用_DIR变量将生成的代码放置在适当的位置。 - ScaryAardvark
3个回答

13
首先,它使您可以使用<projectName>_BINARY_DIR<projectName>_SOURCE_DIR,但这并不是主要优势。如果您给CMake一个项目名称,那么它将为每个子项目生成构建目标,并将它们放在自己的目录中。这意味着无论您是使用GNU Make、Eclipse CDT、XCode还是任何其他受支持的生成器,都可以单独构建子项目。例如,使用GNU Make时,每个子项目都有自己的完整构建系统,位于其自己的目录中。
您可以通过PROJECT_NAME访问当前项目名称,通过CMAKE_PROJECT_NAME访问根项目名称。
编辑:我刚意识到以下内容对于任何CMake的构建目标(无论它们是否为项目)都是标准行为。我会将其保留在这里供一般信息参考,但与答案无关:
假设我有一个C++库,并且我可以生成三个二进制可执行文件;Maintests/test1,以及examples/ex1。我可以在我从中调用CMake的目录中运行全部目标的make,运行make ex1,或者我可以更改目录到examples/并从该目录中使用make构建示例。这将构建所有依赖的项目和库,即使它们在目录结构的其他位置,但不会构建Maintests/test1或它们所依赖的任何库,在examples/ex1中没有。然后,如果我从主目录运行make,它不会重新构建examples/ex1依赖的任何库,除非它们的源代码发生了更改。

1
对我来说,能够“cd”到任何构建层次,并执行“make all”,使所有工件从该层次开始构建,这是一个很大的胜利。谢谢。 - ScaryAardvark
@ScaryAardvark 能够自动运行不同编译器的不同命令并包含不同操作系统的不同代码,这真的很好,而且只需要几行代码就可以进行相当高级的操作。我使用CMake压缩资产,这在任何平台/IDE/工具链上都能正常工作。它还使得交叉编译ARM等变得非常容易。您还应该查看CMake的内置测试系统CTest。还有像Jenkins这样的CI系统的CMake插件。 - Martin Foot
是的,我对此很熟悉。我有一个问题,就是cmake如何处理同一编译器的不同版本...即我们的项目树中有一部分只能在sunstudio11下编译,而大部分需要sunstudio12。我想我会提出另一个相关问题,但还是谢谢 :) - ScaryAardvark

0
如果这些库是真正独立的项目,那么使用project命令是有意义的。但如果它们不是,我会将它们作为子目录添加到根CmakeLists.txt中。如果您需要知道当前正在处理的目录,可以使用变量CMAKE_CURRENT_SOURCE_DIRCMAKE_CURRENT_BINARY_DIR

有CMAKE_CURRENT_PROJECT吗?如果确实有的话,我能理解你的观点,但是如果我必须使用set()来创建这个变量(或者类似的东西),为什么不使用“project”,让所有的变量都为我创建。 - ScaryAardvark

0

我今天找到了一个精确使用例子:添加Doxygen文档。

我使用CMake(和Ninja)来构建我的个人C ++项目。我决定随心所欲地向其中一个几乎完成但未记录的项目中添加一些Doxygen文档。我还认为如果能让它尽可能通用,将其添加到其他项目中也很不错。

首先,我生成了一个标准的Doxygen模板并将其重命名。

cd my_projects/projectx
doxygen -g Doxyfile
mv Doxyfile Doxyfile.in

请注意 .in 扩展名。如果我理解正确的话,这可能并不是必要的,但是通常会这样做。
接下来,在我的 CMakeLists.txt 文件中,在定义目标之前,我添加了以下代码块(不确定是否重要,但是 CMake 对某些命令的顺序有时很挑剔)。
FIND_PACKAGE(Doxygen)
IF("${DOXYGEN_FOUND}" MATCHES "^YES$")
    CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
                    ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
                    @ONLY)
    ADD_CUSTOM_TARGET(  doc ALL
                        COMMAND ${DOXYGEN_EXECUTABLE}
                        ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
                        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
                        COMMENT "Doxygenating..."
                        VERBATIM)
ENDIF()

这将创建一个名为doc的新目标。指定ALL将其添加到默认的“all”目标中,但这是可选的。指定@ONLY确保CONFIGURE_FILE不会扩展任何"${variable}"类型的变量,只有"@variable@"类型的变量。有点令人困惑(至少对我来说),CMAKE_CURRENT_SOURCE_DIR似乎是指项目目录,而CMAKE_CURRENT_BINARY_DIR则是指构建目录。

最后,这就是PROJECT_NAME等的作用,我编辑了Doxyfile.in。

这是我新的Doxyfile.in的开头:

DOXYFILE_ENCODING      = UTF-8
PROJECT_NAME           = "@PROJECT_NAME@"
PROJECT_NUMBER         = @PROJECT_VERSION_TWEAK@
PROJECT_BRIEF          =
PROJECT_LOGO           = @CMAKE_CURRENT_SOURCE_DIR@/res/doc_logo-200x55.png
OUTPUT_DIRECTORY       = @CMAKE_CURRENT_SOURCE_DIR@/doc

你已经有了想法,我认为。 一旦这完全泛型化(这是一个词吗?),我就可以将它复制到我的其他项目中,并且只要我标记了我的代码,我就会在任何地方拥有良好的文档。
注意,PROJECT_BRIEF没有指定。 我还没有完成这个,并且还有一些空白需要我考虑。 例如,PROJECT_VERSION_TWEAK实际上还没有包含任何内容。 我必须找到一种方法将我的构建号码放入其中。

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