让cmake在没有包装脚本的情况下构建源代码?

时间:2012-06-21 16:57:24

标签: configuration cmake

我正在尝试将cmake构建到目录“build”中,就像在project/build中一样,CMakeLists.txt在project/中。

我知道我能做到:

mkdir build
cd build
cmake ../

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

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

3 个答案:

答案 0 :(得分:43)

CMake 3.13或更新版本支持command line options -S-B分别指定源和二进制目录。

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

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

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

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

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

答案 1 :(得分: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 -pbuild的变量。目录-p是我们执行源外构建的地方。我们提供参数mkdir以确保./build/Makefile不会因为尝试创建可能已存在的目录而尖叫。

目标build做的第二件事是将目录更改为cmake目录并调用all

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

总而言之,使用makemkdir build cd build cmake .. make 调用此Makefile包装器等同于:

distclean

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

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

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

P.S。在Makefile中,我们使用前缀@-来抑制shell命令的输出,使用前缀rm来忽略shell命令中的错误。当distclean作为rm -rf build目标的一部分使用时,如果文件不存在(可能已使用@-的命令行删除了它们,则该命令将返回错误,或者它们从来没有产生过。)此返回错误将强制我们的Makefile退出。我们使用前缀cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"来防止这种情况发生。如果已经删除了文件,则可以接受;我们希望Makefile继续运行并删除其余部分。

需要注意的另一点是:如果使用可变数量的CMake变量来构建项目,则此Makefile可能无效,例如cmake ..。此Makefile假定您以一致的方式调用CMake,方法是键入cmake或提供{{1}}一致数量的参数(可以包含在Makefile中)。

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

此答案最初发布于here。我认为它也适用于你的情况。

答案 2 :(得分:2)

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

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原始调用的任何命令行参数都不会传递给“out-of-source build call”。

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