我最近发现的一个解决方案是将外源构建概念与 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 变量make
。make -C
在做任何事情之前更改目录。因此,使用$(MAKE) -C build
相当于做cd build; make
。
总而言之,用make all
or调用这个 Makefile 包装器make
相当于:
mkdir build
cd build
cmake ..
make
目标distclean
调用cmake ..
, 然后make -C build clean
, 最后从build
目录中删除所有内容。我相信这正是您在问题中所要求的。
Makefile 的最后一部分评估用户提供的目标是否是distclean
。build
如果没有,它将在调用它之前将目录更改为。这非常强大,因为用户可以键入,例如,make clean
Makefile 会将其转换为cd build; make clean
.
总之,这个 Makefile 包装器与强制的源外构建 CMake 配置相结合,使用户永远不必与命令交互cmake
。build
此解决方案还提供了一种从目录中删除所有 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 。
这个答案最初发布在这里。我认为它也适用于你的情况。