2012-06-21 8 views
17

Estoy tratando de hacer que cmake construya en un directorio 'compilación', como en project/build, donde el CMakeLists.txt está en project/.¿Cómo hacer que cmake construya fuera de la fuente sin envolver las secuencias de comandos?

Sé que puedo hacer:

mkdir build 
cd build 
cmake ../ 

pero que 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.

Preferiblemente haría algo así 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 compilación fuera de la fuente que hace que sea fácil especificar diferentes argumentos?

+0

Tengo la misma arquitectura de proyecto que usted. Mi dir de "compilación" siempre está aquí. Personalmente encuentro que escribir _cmake .._ no es tan importante. – Offirmo

Respuesta

26

Puede utilizar las opciones CRealice indocumentados -H y -B para especificar la fuente y el directorio de binarios al invocar cmake:

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

Este buscará el CMakeLists.txt en la carpeta actual y crear una carpeta build (si todavía no existe) en él.

+3

Buenas opciones. Pero Max dijo "es desagradable proporcionar diferentes argumentos para cmake". Él lo quiere en el CMakeList. – Offirmo

4

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

En mi archivo CMakeLists.txt de nivel superior, I incluyen los siguientes prevenir la entrada fuente construye:

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() 

A continuación, se crea un Makefile superior, e incluyen los siguientes:

# ----------------------------------------------------------------------------- 
# 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 

Se llama al destino predeterminado all escribiendo make, e invoca el destino ./build/Makefile.

Lo primero que el objetivo ./build/Makefile hace es crear el directorio build 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 exista.

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

Volver al destino 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.

En resumen, llamando a este envoltorio Makefile con make all o make es equivalente a hacer:

mkdir build 
cd build 
cmake .. 
make 

El objetivo distclean invoca cmake .., entonces make -C build clean, y, finalmente, elimina todos los contenidos del directorio build. 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 es 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, esta envoltura de Makefile, en combinación con una configuración 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 directorio build.

P.S. 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 objetivo 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 resto.

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

Por último, crédito donde se debe crédito. Este contenedor Makefile fue adaptado del Makefile proporcionado por el C++ Application Project Template.

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

1

Basado en las respuestas anteriores, escribí el siguiente módulo que puede incluir para aplicar una compilació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 yo ya di cuenta de dos inconvenientes:

  • Cuando el usuario es perezoso y simplemente corre cmake ., siempre verá una 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 compilación fuera de la fuente".

Sugerencias para mejorar este módulo son bienvenidas.

Cuestiones relacionadas