我正在尝试将cmake构建到目录“build”中,就像在project/build
中一样,CMakeLists.txt在project/
中。
我知道我能做到:
mkdir build
cd build
cmake ../
但这很麻烦。我可以把它放在一个脚本中并调用它,但是为cmake提供不同的参数(比如-G“MSYS Makefiles”)是不愉快的,或者我需要在每个平台上编辑这个文件。
最好我会在主CMakeLists.txt中执行类似SET(CMAKE_OUTPUT_DIR构建)的操作。请告诉我这是可能的,如果是的话,怎么样?或者其他一些源代码构建方法可以很容易地指定不同的参数?
答案 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 -p
是build
的变量。目录-p
是我们执行源外构建的地方。我们提供参数mkdir
以确保./build/Makefile
不会因为尝试创建可能已存在的目录而尖叫。
目标build
做的第二件事是将目录更改为cmake
目录并调用all
。
回到$(MAKE) -C build
目标,我们调用$(MAKE)
,其中make
是为make -C
自动生成的Makefile变量。 $(MAKE) -C build
在执行任何操作之前更改目录。因此,使用cd build; make
相当于执行make all
。
总而言之,使用make
或mkdir 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”。欢迎提出改进此模块的建议。