Uma solução que encontrei recentemente é combinar o conceito de compilação fora da fonte com um wrapper Makefile.
No meu arquivo CMakeLists.txt de nível superior, incluo o seguinte para evitar compilações na fonte:
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()
Em seguida, crio um Makefile de nível superior e incluo o seguinte:
# -----------------------------------------------------------------------------
# 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
O destino padrão all
é chamado digitando make
e chama o destino./build/Makefile
.
A primeira coisa que o destino ./build/Makefile
faz é criar o build
diretório usando $(MKDIR)
, que é uma variável mkdir -p
. O diretório build
é onde executaremos nossa compilação fora da fonte. Nós fornecemos o argumento -p
para garantir que mkdir
não nos grite por tentar criar um diretório que já possa existir.
A segunda coisa que o destino ./build/Makefile
faz é alterar os diretórios para o build
diretório e invocar cmake
.
De volta ao all
destino, chamamos $(MAKE) -C build
, onde $(MAKE)
é uma variável Makefile gerada automaticamente make
. make -C
altera o diretório antes de fazer qualquer coisa. Portanto, usar $(MAKE) -C build
é equivalente a fazer cd build; make
.
Para resumir, chamar esse wrapper Makefile com make all
ou make
é equivalente a:
mkdir build
cd build
cmake ..
make
O destino distclean
chama cmake ..
e make -C build clean
, por fim, remove todo o conteúdo do build
diretório. Acredito que isso é exatamente o que você solicitou na sua pergunta.
A última parte do Makefile avalia se o destino fornecido pelo usuário é ou não distclean
. Caso contrário, ele mudará os diretórios para build
antes de invocá-lo. Isso é muito poderoso, porque o usuário pode digitar, por exemplo make clean
, eo Makefile transformará isso em um equivalente a cd build; make clean
.
Concluindo, esse wrapper Makefile, em combinação com uma configuração obrigatória de CMake de compilação de origem, faz com que o usuário nunca precise interagir com o comando cmake
. Essa solução também fornece um método elegante para remover todos os arquivos de saída do CMake do build
diretório.
PS No Makefile, usamos o prefixo @
para suprimir a saída de um comando shell, e o prefixo @-
para ignorar erros de um comando shell. Ao usarrm
como parte do distclean
destino, o comando retornará um erro se os arquivos não existirem (eles podem já ter sido excluídos usando a linha de comando rm -rf build
ou nunca foram gerados). Este erro de retorno forçará nosso Makefile a sair. Usamos o prefixo @-
para impedir isso. É aceitável se um arquivo já tiver sido removido; queremos que o nosso Makefile continue e remova o resto.
Outro ponto a ser observado: esse Makefile pode não funcionar se você usar um número variável de variáveis do CMake para criar seu projeto, por exemplo, cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"
. Esse Makefile pressupõe que você invoque o CMake de maneira consistente, digitando cmake ..
ou fornecendo cmake
um número consistente de argumentos (que você pode incluir no Makefile).
Finalmente, credite onde o crédito é devido. Esse wrapper Makefile foi adaptado do Makefile fornecido pelo C ++ Application Project Template .