CMake如何将构建目录设置为与源目录不同

161

我对CMake还比较新,已经阅读了一些关于如何使用它的教程,并编写了一些复杂的50行CMake脚本,以便为3个不同的编译器制作程序。这可能是我在CMake方面的全部知识。

现在我的问题是,我有一些源代码,我不想在制作程序时触及/混乱它的文件夹。我希望所有CMake和make输出文件和文件夹都进入../Compile/,因此我更改了我的CMake脚本中的一些变量,并且当我在笔记本电脑上执行以下操作时,它起作用了:

Compile$ cmake ../src
Compile$ make

我希望我的输出可以干净地放在当前文件夹中,这正是我所需的。

现在我换到了另一台电脑上重新编译了CMake 2.8.11.2,但我几乎回到了起点!它总是将东西编译到我的 CMakeLists.txt 所在目录下的 src 文件夹中。

我在 CMake 脚本中选择目录的部分如下:

set(dir ${CMAKE_CURRENT_SOURCE_DIR}/../Compile/)
set(EXECUTABLE_OUTPUT_PATH ${dir} CACHE PATH "Build directory" FORCE)
set(LIBRARY_OUTPUT_PATH ${dir} CACHE PATH "Build directory" FORCE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dir})
set(CMAKE_BUILD_FILES_DIRECTORY ${dir})
set(CMAKE_BUILD_DIRECTORY ${dir})
set(CMAKE_BINARY_DIR  ${dir})
SET(EXECUTABLE_OUTPUT_PATH ${dir})
SET(LIBRARY_OUTPUT_PATH ${dir}lib)
SET(CMAKE_CACHEFILE_DIR ${dir})

现在它总是以这个结束:

-- Build files have been written to: /.../src

我有遗漏吗?


4
没必要设置你正在设置的所有变量,CMake 会将它们设置为合理的默认值。你绝对不应该修改 CMAKE_BINARY_DIRCMAKE_CACHEFILE_DIR。如果你删除所有这些 set() 调用,只执行 cd Compile; rm -rf *; cmake ../src 命令,会发生什么? - Angew is no longer proud of SO
6
基本上,只要在运行CMake时处于源目录之外,它就不会修改源目录,除非您的CMakeList显式地告诉它这样做。 - Angew is no longer proud of SO
@Angew 谢谢你的提示,真是惊喜!我删除了所有那些行,只使用了 cmake ../src,它就像魔法一样奏效了!这太令人惊讶了,因为我在学习 CMake 的时候之前尝试过,但没有成功。请把你的答案放在正式回复中,以便给你一个大大的勾选标记 :) - The Quantum Physicist
1
救了我的是 @Adam Bowen 的评论:“你不能为源目录创建一个外部源构建,而源目录已经有内部源构建了。” - Aur Saraf
6个回答

159

看起来您想要进行外部构建。有几种方法可以创建外部构建。

  1. 按照您之前的做法,运行

 cd /path/to/my/build/folder
 cmake /path/to/my/source/folder

这将导致cmake在/path/to/my/build/folder中为/path/to/my/source/folder源树生成一个构建树

一旦创建,cmake会记住源文件夹的位置 - 因此您可以使用构建树上的cmake重新运行。

 cmake /path/to/my/build/folder

或者甚至

 cmake .

如果您当前的目录已经是构建文件夹。

  • 对于CMake 3.13或更高版本,请使用这些选项设置源和构建文件夹

  •  cmake -B/path/to/my/build/folder -S/path/to/my/source/folder
    
  • 对于旧版的CMake,请使用一些未记录的选项来设置源代码和构建文件夹

  •  cmake -B/path/to/my/build/folder -H/path/to/my/source/folder
    

    这将完全实现与(1)相同的功能,但不会依赖于当前工作目录。

    CMake默认将其所有输出放置在build tree中,因此,除非您在cmake文件中大量使用${CMAKE_SOURCE_DIR}${CMAKE_CURRENT_SOURCE_DIR},否则它不应该影响您的source tree。

    最大的问题是如果您之前在源目录中生成了一个build tree(即您有一个in source build),那么(1)上述的第二部分就会开始起作用,cmake不会对源或构建位置进行任何更改。因此,对于具有in-source build的源目录,您无法创建out-of-source build。您可以通过从源目录中至少删除CMakeCache.txt来相当容易地解决此问题。还有一些其他文件(主要位于CMakeFiles目录中)由CMake生成,您也应该将其删除,但这些文件不会导致cmake将源树视为build tree。

    由于out-of-source builds通常比in-source builds更可取,因此您可能希望修改cmake以要求进行out-of-source build:

    # Ensures that we do an out of source build
    
    MACRO(MACRO_ENSURE_OUT_OF_SOURCE_BUILD MSG)
         STRING(COMPARE EQUAL "${CMAKE_SOURCE_DIR}"
         "${CMAKE_BINARY_DIR}" insource)
         GET_FILENAME_COMPONENT(PARENTDIR ${CMAKE_SOURCE_DIR} PATH)
         STRING(COMPARE EQUAL "${CMAKE_SOURCE_DIR}"
         "${PARENTDIR}" insourcesubdir)
        IF(insource OR insourcesubdir)
            MESSAGE(FATAL_ERROR "${MSG}")
        ENDIF(insource OR insourcesubdir)
    ENDMACRO(MACRO_ENSURE_OUT_OF_SOURCE_BUILD)
    
    MACRO_ENSURE_OUT_OF_SOURCE_BUILD(
        "${CMAKE_PROJECT_NAME} requires an out of source build."
    )
    

    上述宏来自一个常用的模块,名为MacroOutOfSourceBuild。在谷歌上有很多关于MacroOutOfSourceBuild.cmake的源代码,但我似乎找不到原始版本,这里完整地包括了它。

    不幸的是,CMake通常在调用该宏之前已经写了一些文件,因此尽管它会阻止您实际执行构建,但您仍需要删除CMakeCache.txtCMakeFiles

    您可能会发现将可执行文件、共享库和静态库写入路径中很有用——如果是这样,请参见how do I make cmake output into a 'bin' dir?(免责声明,我在那个问题上有最高票答案……但这就是我知道它的方式)。


    实际上,设置源文件夹的选项是 -S,而不是 -H - smac89
    @smac89 谢谢!看起来他们在3.13中记录了它们 - 我已经更新了答案以反映现代CMake。 - Adam Bowen

    82

    你并不需要设置所有的变量,CMake已经为它们设置了合理的默认值。你绝对不应该修改CMAKE_BINARY_DIR或者CMAKE_CACHEFILE_DIR。请将它们视为只读。

    首先从src目录中删除现有的问题缓存文件:

    cd src
    rm CMakeCache.txt
    cd ..
    

    然后移除所有的set()指令并执行:

    cd Compile && rm -rf *
    cmake ../src
    
    只要在运行CMake时不在源代码目录之内,它就不会修改源代码目录,除非你的CMakeList明确告诉它这样做。一旦你完成这个步骤,你可以查看CMake的默认输出路径,并且仅在你对默认位置不满意(例如EXECUTABLE_OUTPUT_PATH的默认值)时,修改那些你需要的。尝试使用相对于CMAKE_BINARY_DIRCMAKE_CURRENT_BINARY_DIRPROJECT_BINARY_DIR等表达它们。如果你查看CMake文档,你会看到变量被分成语义部分。除了极特殊情况外,在CMakeLists内部,你应该将所有列在“提供信息的变量”下面的变量视为只读。

    2
    我开始使用cmake在src目录中进行构建...这种技术最初失败了。一旦我删除了src目录中的所有cmake构建文件/缓存,这种技术就起作用了。谢谢! - Avi Tevet
    28
    也许最好不要建议使用 rm -rf * 命令。如果 cd Compile 失败的话会很麻烦... - Roman
    3
    @Roman,我认为这种命令行示例基本上就是伪代码。它比键入“进入目录'Compile',删除其中所有内容,然后使用源目录的路径运行CMake”要更简洁和准确。我假设读者具有基本的常识和判断力。 - Angew is no longer proud of SO
    @AviTevet:我认为这是正确的答案。如果在源目录中存在来自先前cmake调用的cmake缓存文件,则除非您从源目录中删除所有旧文件,否则您将无法让cmake选择另一个目录以生成文件。在我看来,这是CMake相当糟糕的行为。为什么不写另一个答案呢? - Jo So

    12

    将我的评论转化为答案:

    如果有人像我一样,从将所有构建文件放入源目录开始:

    cd src
    cmake .
    

    cmake会在src目录中放置一堆构建文件和缓存文件(CMakeCache.txt, CMakeFiles, cmake_install.cmake等)。

    为了切换到源外构建,我必须删除所有这些文件。然后我可以按照@Angew在他的答案中推荐的方法进行操作:

    mkdir -p src/build
    cd src/build
    cmake ..
    

    9
    根据 CMake Wiki:

    CMAKE_BINARY_DIR 如果您是在源码内编译,则与CMAKE_SOURCE_DIR相同;否则,这是您构建树的顶级目录。

    比较这两个变量以确定是否启动了非源代码外构建。

    7
    你的脚本中不应该依赖于硬编码的构建目录名称,所以包含 ../Compile 的那一行必须修改。
    这是因为编译应该由用户决定在哪里进行。
    取而代之的是使用预定义变量之一:http://www.cmake.org/Wiki/CMake_Useful_Variables(查找 CMAKE_BINARY_DIRCMAKE_CURRENT_BINARY_DIR)。

    2
    这正是我正在寻找的——CMAKE_CURRENT_BINARY_DIR,通常会告诉当前二进制构建路径。可能可用于设置依赖库/二进制构建。 - parasrish

    2

    从cmake 3.19开始,您还可以使用预设文件,在预设文件中,除了其他有用的内容外,您也可以指定输出二进制目录:

    {
      "version": 2,
      "cmakeMinimumRequired": {
        "major": 3,
        "minor": 19,
        "patch": 0
      },
      "configurePresets": [
        {
          "name": "default",
          "displayName": "Default",
          "description": "Default build cfg",
          "generator": "Unix Makefiles",
          "binaryDir": "${sourceDir}/Compile",
          "cacheVariables": {
          },
          "environment": {
          }
        }
      ]
    }
    

    然后只需使用--preset参数运行cmake:

    cmake --preset=default
    

    然后只需cd到您的构建目录并运行make,在您的情况下:

    cd ./Compile
    make
    

    他正在询问BUILD目录而不是BINARY目录。我们能否在预设中设置构建目录? - Dariusz
    @Dariusz 我认为它与 binaryDir 相同。 “构建目录是从配置预设中推断出来的,因此构建将在与配置相同的 binaryDir 中进行。” https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html#configure-preset - Martin Flaska

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