Cómo hacer que cmake construya fuera de la fuente sin envolver scripts?

Estoy tratando de hacer que cmake construya en un directorio ‘comstackción’, como en project/build , donde el CMakeLists.txt está en project/ .

Sé que puedo hacer:

 mkdir build cd build cmake ../ 

pero eso es engorroso. Podría ponerlo en un script y llamarlo, pero luego es desagradable proporcionar diferentes argumentos para cmake (como -G “MSYS Makefiles”), o tendría que editar este archivo en cada plataforma.

Preferentemente, haría algo como SET (CMAKE_OUTPUT_DIR build) en el principal CMakeLists.txt. Por favor, dime que esto es posible, y si es así, ¿cómo? ¿O algún otro método de comstackción fuera de la fuente que hace que sea fácil especificar diferentes argumentos?

Puede usar las opciones de AH no documentadas -H y -B para especificar el directorio fuente y binario al invocar cmake :

 cmake -H. -Bbuild -G "MSYS Makefiles" 

Esto buscará CMakeLists.txt en la carpeta actual y creará una carpeta de build (si aún no existe) en ella.

Una solución que encontré recientemente es combinar el concepto de comstackción fuera de la fuente con un contenedor Makefile.

En mi archivo CMakeLists.txt de nivel superior, incluyo lo siguiente para evitar comstackciones in-source:

 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 objective predeterminado es invocar make e invocar el destino ./build/Makefile .

Lo primero que hace el objective ./build/Makefile es crear el directorio de build usando $(MKDIR) , que es una variable para mkdir -p . La build directorio es donde realizaremos nuestra comstackción fuera de la fuente. Proporcionamos el argumento -p para garantizar que mkdir no nos grite por intentar crear un directorio que ya exista.

Lo segundo que hace el objective ./build/Makefile es cambiar directorios al directorio de build e invocar cmake .

Volviendo al objective all , invocamos $(MAKE) -C build , donde $(MAKE) es una variable Makefile generada automáticamente para make . make -C cambia el directorio antes de hacer cualquier cosa. Por lo tanto, usar $(MAKE) -C build es equivalente a hacer cd build; make cd build; make .

Para resumir, llamar a este contenedor de 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 directorio de build . Creo que esto es exactamente lo que solicitó en su pregunta.

La última parte del Makefile evalúa si el objective proporcionado por el usuario es distclean o no. De lo contrario, cambiará directorios para 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 de cd build; make clean cd build; make clean

En conclusión, esta envoltura de Makefile, en combinación con una configuración CMake de comstackción obligatoria fuera de la fuente, lo hace para 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 de CMake del directorio de build .

PS 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. Al usar rm como parte del destino distclean , el comando devolverá un error si los archivos no existen (pueden haberse eliminado ya usando la línea de comando con rm -rf build , o nunca se generaron en primer lugar). Este error de retorno forzará 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 rest.

Otra cosa a tener en cuenta: este Makefile puede no funcionar si utiliza un número variable de variables de CMake para construir su proyecto, por ejemplo, cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar" . Este Makefile asume que invocas CMake de forma consistente, ya sea escribiendo cmake .. o proporcionando cmake una cantidad consistente de argumentos (que puedes incluir en tu Makefile).

Finalmente, crédito donde se debe crédito. Este contenedor Makefile se adaptó del Makefile proporcionado por la Plantilla de proyecto de aplicación C ++ .

Esta respuesta fue publicada originalmente aquí . Pensé que se aplicaba a tu situación también.

Basado en las respuestas anteriores, escribí el siguiente módulo que puede incluir para aplicar una comstackción fuera de la fuente.

 set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output") if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(WARNING "In-source builds not allowed. CMake will now be run with arguments: cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER} ") # Run CMake with out of source flag execute_process( COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) # Cause fatal error to stop the script from further execution message(FATAL_ERROR "CMake has been ran to create an out of source build. This error prevents CMake from running an in-source build.") endif () 

Esto funciona, sin embargo, ya noté dos inconvenientes:

  • Cuando el usuario es flojo y simplemente ejecuta cmake . , siempre verán un FATAL_ERROR . No pude encontrar otra forma de evitar que CMake realice otras operaciones y salir temprano.
  • Cualquier argumento de línea de comando pasado a la llamada original a cmake no se pasará a la “llamada de comstackción fuera de la fuente”.

Sugerencias para mejorar este módulo son bienvenidos.