Una solución que encontré recientemente es combinar el concepto de compilación fuera del código fuente con un contenedor Makefile.
En mi archivo CMakeLists.txt de nivel superior, incluyo lo siguiente para evitar compilaciones en la fuente:
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()
Luego, creo un Makefile de nivel superior e incluyo lo siguiente:
# -----------------------------------------------------------------------------
# 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
El objetivo predeterminado all
se llama escribiendo make
e invoca el objetivo./build/Makefile
.
Lo primero que hace el objetivo ./build/Makefile
es crear el build
directorio usando $(MKDIR)
, que es una variable para mkdir -p
. El directorio build
es donde realizaremos nuestra compilación fuera de la fuente. Proporcionamos el argumento -p
para asegurarnos de que mkdir
no nos grite por intentar crear un directorio que ya puede existir.
La segunda cosa que hace el objetivo ./build/Makefile
es cambiar los directorios al build
directorio e invocar cmake
.
Volvemos al all
objetivo, invocamos $(MAKE) -C build
, donde $(MAKE)
se genera automáticamente una variable Makefile para make
. make -C
cambia el directorio antes de hacer nada. Por lo tanto, usar $(MAKE) -C build
es equivalente a hacer cd build; make
.
Para resumir, llamar a este contenedor Makefile con make all
o make
es equivalente a hacerlo:
mkdir build
cd build
cmake ..
make
El destino distclean
invoca cmake ..
, luego make -C build clean
, y finalmente, elimina todo el contenido del build
directorio. Creo que esto es exactamente lo que solicitó en su pregunta.
La última parte del Makefile evalúa si el objetivo proporcionado por el usuario es o no distclean
. Si no, cambiará los directorios a build
antes de invocarlo. Esto es muy poderoso porque el usuario puede escribir, por ejemplo make clean
, y el Makefile lo transformará en un equivalente de cd build; make clean
.
En conclusión, este contenedor Makefile, en combinación con una configuración de CMake de compilación obligatoria fuera de la fuente, hace que el usuario nunca tenga que interactuar con el comando cmake
. Esta solución también proporciona un método elegante para eliminar todos los archivos de salida CMake del build
directorio.
PD En el Makefile, usamos el prefijo @
para suprimir la salida de un comando de shell y el prefijo @-
para ignorar los errores de un comando de shell. Cuando se usa rm
como parte del distclean
destino, el comando devolverá un error si los archivos no existen (es posible que ya se hayan eliminado con la línea de comando rm -rf build
o que nunca se hayan generado). Este error de retorno obligará a nuestro Makefile a salir. Usamos el prefijo @-
para evitar eso. Es aceptable si ya se eliminó un archivo; queremos que nuestro Makefile continúe y elimine el resto.
Otra cosa a tener en cuenta: este Makefile puede no funcionar si usa un número variable de variables CMake para construir su proyecto, por ejemplo cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar"
,. Este Makefile asume que invocas CMake de manera consistente, ya sea escribiendo cmake ..
o proporcionando cmake
un número consistente de argumentos (que puedes incluir en tu Makefile).
Finalmente, crédito donde se debe. Este contenedor Makefile se adaptó del Makefile proporcionado por la Plantilla de proyecto de aplicación C ++ .