我最近找到的一个解决方案是将“外部构建”概念与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,并包含以下内容:
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
的第一件事是使用
$(MKDIR)
创建
build
目录,其中
$(MKDIR)
是
mkdir -p
的变量。目录
build
是我们将执行外部源生成构建的地方。我们提供参数
-p
以确保
mkdir
不会因为尝试创建可能已经存在的目录而报错。
目标
./build/Makefile
的第二件事是更改目录到
build
目录并调用
cmake
。
回到
all
目标,我们调用
$(MAKE) -C build
,其中
$(MAKE)
是自动生成的
make
Makefile变量。
make -C
在做任何事情之前都会更改目录。因此,使用
$(MAKE) -C build
等同于执行
cd build; make
。
总之,使用
make all
或
make
调用此Makefile包装器相当于执行:
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输出文件。
顺便说一下,在Makefile中,我们使用前缀
@
来抑制来自shell命令的输出,并使用前缀
@-
来忽略shell命令的错误。当使用
rm
作为
distclean
目标的一部分时,如果文件不存在(可能已经使用
rm -rf build
命令行删除或根本没有生成),则该命令将返回错误。此返回错误将强制我们的Makefile退出。我们使用前缀
@-
来防止这种情况发生。如果文件已被删除,则是可以接受的;我们希望我们的Makefile继续并删除其余文件。
还要注意的一件事:如果您使用变量数量的CMake变量构建项目,例如
cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"
,则此Makefile可能无法正常工作。此Makefile假定您以一致的方式调用CMake,即通过键入
cmake ..
或提供
cmake
一致数量的参数(您可以将其包含在Makefile中)。
最后,请给予信用。此Makefile包装器是从
C++ Application Project Template提供的Makefile进行改编的。
此答案最初发布在
此处。我认为它也适用于您的情况。