我最近发现的一个解决方案是将外源构建概念与 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
通过键入make 调用默认目标all,并调用目标./build/Makefile。
目标./build/Makefile 所做的第一件事是使用$(MKDIR) 创建build 目录,这是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 变量来构建您的项目,此 Makefile 可能不起作用,例如,cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"。此 Makefile 假定您以一致的方式调用 CMake,或者通过键入 cmake .. 或通过提供 cmake 一致数量的参数(可以包含在 Makefile 中)。
最后,信用到期。这个 Makefile 包装器改编自 C++ Application Project Template 提供的 Makefile。
此答案最初发布于here。我认为它也适用于您的情况。