31

我试图让 CMake 构建到一个目录“build”中,如project/buildCMakeLists.txt 所在的目录project/

我知道我可以做到:

mkdir build
cd build
cmake ../

但这很麻烦。我可以把它放在一个脚本中并调用它,但是向 CMake 提供不同的参数(比如 -G "MSYS Makefiles")是不愉快的,或者我需要在每个平台上编辑这个文件。

最好我会SET(CMAKE_OUTPUT_DIR build)在主 CMakeLists.txt 中执行类似的操作。请告诉我这是可能的,如果可以,怎么做?或者其他一些可以轻松指定不同参数的源代码构建方法?

4

3 回答 3

53

CMake 3.13 或更高版本支持命令行选项 -S-B分别指定源和二进制目录。

cmake -S . -B build -G "MSYS Makefiles"

这将CMakeLists.txt在当前文件夹中查找并在其中创建一个build文件夹(如果它尚不存在)。

对于旧版本的 CMake,您可以使用未记录的 CMake 选项-H-B在调用时指定源和二进制目录cmake

cmake -H. -Bbuild -G "MSYS Makefiles"

请注意,选项和目录路径之间不能有空格字符。

于 2012-06-21T18:05:20.553 回答
6

我最近发现的一个解决方案是将外源构建概念与 Makefile 包装器相结合。

在我的顶级 CMakeLists.txt 文件中,我包含以下内容以防止源内构建:

if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} )
    message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." )
endif()

然后,我创建一个顶级 Makefile,并包括以下内容:

# -----------------------------------------------------------------------------
# CMake project wrapper Makefile ----------------------------------------------
# -----------------------------------------------------------------------------

SHELL := /bin/bash
RM    := rm -rf
MKDIR := mkdir -p

all: ./build/Makefile
    @ $(MAKE) -C build

./build/Makefile:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake ..)

distclean:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1)
    @- $(MAKE) --silent -C build clean || true
    @- $(RM) ./build/Makefile
    @- $(RM) ./build/src
    @- $(RM) ./build/test
    @- $(RM) ./build/CMake*
    @- $(RM) ./build/cmake.*
    @- $(RM) ./build/*.cmake
    @- $(RM) ./build/*.txt

ifeq ($(findstring distclean,$(MAKECMDGOALS)),)
    $(MAKECMDGOALS): ./build/Makefile
    @ $(MAKE) -C build $(MAKECMDGOALS)
endif

默认目标all通过键入make调用,并调用目标./build/Makefile

目标做的第一件事./build/Makefile是使用 来创建build目录$(MKDIR),它是mkdir -p. 该目录build是我们将执行我们的源代码外构建的地方。我们提供参数-p以确保mkdir不会因为试图创建可能已经存在的目录而对我们大喊大叫。

目标所做的第二件事./build/Makefile是将目录更改为build目录并调用cmake.

回到all目标,我们调用$(MAKE) -C build,其中$(MAKE)是为 自动生成的 Makefile 变量makemake -C在做任何事情之前更改目录。因此,使用$(MAKE) -C build相当于做cd build; make

总而言之,用make allor调用这个 Makefile 包装器make相当于:

mkdir build
cd build
cmake ..
make 

目标distclean调用cmake .., 然后make -C build clean, 最后从build目录中删除所有内容。我相信这正是您在问题中所要求的。

Makefile 的最后一部分评估用户提供的目标是否是distcleanbuild如果没有,它将在调用它之前将目录更改为。这非常强大,因为用户可以键入,例如,make cleanMakefile 会将其转换为cd build; make clean.

总之,这个 Makefile 包装器与强制的源外构建 CMake 配置相结合,使用户永远不必与命令交互cmakebuild此解决方案还提供了一种从目录中删除所有 CMake 输出文件的优雅方法。

PS 在 Makefile 中,我们使用前缀@来抑制 shell 命令的输出,并使用前缀@-来忽略 shell 命令的错误。当rm作为distclean目标的一部分使用时,如果文件不存在,该命令将返回错误(它们可能已经使用带有 的命令行被删除rm -rf build,或者它们从一开始就没有生成)。这个返回错误将强制我们的 Makefile 退出。我们使用前缀@-来防止这种情况。如果文件已经被删除是可以接受的;我们希望我们的 Makefile 继续运行并删除其余部分。

另一件需要注意的事情:如果您使用可变数量的 CMake 变量来构建项目,则此 Makefile 可能不起作用,例如,cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar". 此 Makefile 假定您以一致的方式调用 CMake,通过键入cmake ..或提供cmake一致数量的参数(可以包含在 Makefile 中)。

最后,信用到期。这个 Makefile 包装器改编自C++ Application Project Template提供的 Makefile 。

这个答案最初发布在这里。我认为它也适用于你的情况。

于 2015-04-16T15:10:59.873 回答
3

根据之前的答案,我编写了以下模块,您可以包含该模块以强制执行源外构建。

set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output")

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
    message(WARNING "In-source builds not allowed. CMake will now be run with arguments:
        cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
")

    # Run CMake with out of source flag
    execute_process(
            COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
            WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})

    # Cause fatal error to stop the script from further execution
    message(FATAL_ERROR "CMake has been ran to create an out of source build.
This error prevents CMake from running an in-source build.")
endif ()

这行得通,但是我已经注意到两个缺点:

  • 当用户懒惰并简单地运行cmake .时,他们总是会看到一个FATAL_ERROR. 我找不到另一种方法来阻止 CMake 执行任何其他操作并提前退出。
  • 传递给原始调用的任何命令行参数cmake都不会传递给“源外构建调用”。

欢迎提出改进此模块的建议。

于 2016-05-31T08:35:30.033 回答