¿Cmake vs hacer códigos de muestra?

117

Me preguntaba si había algún código de muestra para Makefiles ( make) y CMakeLists.txt( cmake) que ambos hagan lo mismo (la única diferencia es que uno está escrito makey el otro cmake).

Intenté buscar 'cmake vs make', pero nunca encontré ninguna comparación de código. Sería de gran ayuda comprender las diferencias, aunque solo sea por un caso sencillo.

jlo
fuente
20
+1 Esta es una buena pregunta; cuando estaba empezando cmakeyo también quería esto. Pero dudo que lo encuentre porque las capacidades simplemente no se asignan tan bien entre sí. Si intentas cmakeactuar como make, te volverás loco, en serio. Lo mejor es empezar de cero. Las cosas que son triviales makeestán bastante envueltas cmakey viceversa.
Ernest Friedman-Hill
1
@ ErnestFriedman-Hill, ¿tiene más detalles al respecto? Por lo tanto makey cmakeson tan distintos que deben ser vistos más como un complemento en lugar de herramientas de la competencia?
Ehtesh Choudhury
2
@Shurane - cmake no construye nada por sí mismo; crea Makefiles (y otros scripts de compilación similares), que luego ejecuta. Por lo tanto, siempre que esté escribiendo archivos cmake, debe pensar si un comando debe aplicarse durante el tiempo de generación o durante el tiempo de compilación. Algunas acciones (copiar un conjunto de archivos con comodines en el momento de la compilación, por ejemplo) son bastante complicadas, en comparación con " cp *.x $(OUTDIR)" lo que escribirías en un Makefile. Quizás la parte más molesta para mí es que los Makefiles generados son, por diseño, completamente no portátiles e inflexibles (continuación)
Ernest Friedman-Hill
1
(continuación) ¡Ni siquiera puede mover el directorio fuente en la misma máquina sin volver a ejecutar cmake para regenerar el Makefile! Por lo tanto, la elección no es entre cmake y make, sino entre escribir Makefiles portátiles usted mismo o usar cmake para generar archivos no portátiles en cada máquina de compilación (y dado que puede usar Cygwin o mingw en Windows, generalmente encuentro que lo primero es más fácil. )
Ernest Friedman-Hill
1
una buena pregunta, pero no hay una respuesta específica, porque ambas herramientas intentan resolver un problema diferente. cmake toma información sobre cómo construir programas genera archivos MAKE que construyen el programa. Por lo tanto, cmake es un lenguaje con reglas de construcción abstractas y gnu make es una resolución de dependencia que ejecuta programas en un recorrido de grafo acíclico dirigido.
Alex

Respuestas:

118

El siguiente Makefile crea un ejecutable con el nombre progde las fuentes prog1.c, prog2.c, prog3.c and main.c. progestá vinculado con libmystatlib.a y libmydynlib.soque también se construyen desde la fuente. Además, progusa la biblioteca libstuff.aen stuff/liby su encabezado en stuff/include. El Makefile por defecto crea un objetivo de lanzamiento, pero también ofrece un objetivo de depuración:

#Makefile    
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3 
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)

PROGOBJS = prog1.o prog2.o prog3.o

prog: main.o $(PROGOBJS) mystatlib mydynlib
    $(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog 
debug: CFLAGS=$(DEBUG)
debug: prog

mystatlib: mystatlib.o
    $(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
    $(CPP) -shared mydynlib.o -o libmydynlib.so

%.o: %.c
    $(CC) $(CFLAGS) $(INCDIR) $< -o $@ 
%.o: %.cpp
    $(CPP) $(CFLAGS) $(INCDIR) -fPIC  $< -o $@ 

Aquí hay un CMakeLists.txtque hace (casi) exactamente lo mismo, con algunos comentarios para subrayar las similitudes con el Makefile:

#CMakeLists.txt     
cmake_minimum_required(VERSION 2.8)                    # stuff not directly
project(example)                                       # related to building

include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib)        # -L flags for linker

set(PROGSRC prog1.c prog2.c prog3.c)                   # define variable 

add_executable(prog main.c ${PROGSRC})                 # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff)   # -l flags for linking prog target

add_library(mystatlib STATIC mystatlib.c)              # define static library target mystatlib, specify sources

add_library(mydynlib SHARED mydynlib.cpp)              # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")

En este sencillo ejemplo, las diferencias más importantes son:

  • CMake reconoce qué compiladores utilizar para qué tipo de fuente. Además, invoca la secuencia correcta de comandos para cada tipo de objetivo. Por lo tanto, no existe una especificación explícita de comandos como $(CC) ..., $(RANLIB) ...etc.

  • Todos los indicadores habituales del compilador / enlazador que se ocupan de la inclusión de archivos de encabezado, bibliotecas, etc. se reemplazan por comandos independientes de la plataforma / del sistema de compilación.

  • Los indicadores de depuración se incluyen configurando la variable CMAKE_BUILD_TYPEen "Debug" o pasándola a CMake al invocar el programa:cmake -DCMAKE_BUILD_TYPE:STRING=Debug .

  • CMake ofrece también la inclusión independiente de la plataforma del indicador '-fPIC' (a través de la POSITION_INDEPENDENT_CODEpropiedad) y muchos otros. Aún así, se pueden implementar configuraciones más oscuras a mano en CMake tan bien como en un Makefile (usandoCOMPILE_FLAGS propiedades similares). Por supuesto, CMake realmente comienza a brillar cuando se incluyen bibliotecas de terceros (como OpenGL) de manera portátil.

  • El proceso de compilación tiene un paso si usa un Makefile, es decir, escribir makeen la línea de comandos. Para CMake, hay dos pasos: Primero, necesita configurar su entorno de compilación (ya sea escribiendocmake <source_dir> su directorio de compilación o ejecutando algún cliente GUI). Esto crea un Makefile o algo equivalente, dependiendo del sistema de compilación de su elección (por ejemplo, make en Unixes o VC ++ o MinGW + Msys en Windows). El sistema de compilación se puede pasar a CMake como parámetro; sin embargo, CMake hace elecciones predeterminadas razonables según la configuración de su sistema. En segundo lugar, realiza la compilación real en el sistema de compilación seleccionado.

Las fuentes y las instrucciones de compilación están disponibles en https://github.com/rhoelzel/make_cmake .

Roberto
fuente
3
¿No es el Makefile demasiado complicado? Al usar en CPPFLAGSlugar de INCDIRuno, podría haber usado las reglas integradas y la invocación explícita del compilador habría sido redundante. De manera similar, para el manejo de ar, las reglas integradas también pueden cubrir eso. Además, ¿por qué establecer CPPy CCexplícitamente? Ya están establecidos en buenos valores por make, son variables predefinidas. maketambién reconoce qué compilador usar para qué tipo de fuente, las reglas integradas son muchas.
Christian Hujer
1
Y muchas de estas variables deben asignarse con en :=lugar de =.
Christian Hujer
1
En cuanto a la descripción, cmakees más comparable a la automakede make.
ivan_pozdeev
El Makefile proporcionado podría reducirse a 3/4 líneas. DEBERÍA especificar en INCLUDESlugar de INCDIR. No necesita las reglas% .o:%. C.
shuva
6

Elija algún software que use CMake como su sistema de compilación (hay muchos proyectos de código abierto para elegir como ejemplo). Obtenga el código fuente y configúrelo usando CMake. Lea los archivos MAKE resultantes y disfrute.

Una cosa a tener en cuenta es que esas herramientas no se asignan uno a uno. La diferencia más obvia es que CMake busca dependencias entre diferentes archivos (por ejemplo, encabezado C y archivos fuente), mientras que make deja eso a los autores del archivo Make.

Tadeusz A. Kadłubowski
fuente
4

Si esta pregunta es sobre una Makefilesalida de muestra del CMakeList.txtarchivo, verifique las fuentes de cmake-backend y genere una Makefile. Si no es así, agregando a la respuesta de @Roberto, estoy tratando de simplificarlo ocultando los detalles.

Función CMake

Si bien Makees una herramienta flexible para reglas y recetas, CMakees una capa de abstracción que también agrega la función de configuración.

Mi llanura CMakeLists.txtse verá así,

cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})

Tenga en cuenta que se puede CMakeocultar howla compilación. Solo especificamos whatla entrada y la salida.

La CMakeLists.txtlista de llamadas de función-que se definen por contiene cmake.

(Función CMake) Vs Make reglas

En Makefileel rules and recipesse utilizan en lugar de functions. Además de la functionfunción -like, rules and recipesproporciona encadenamiento. Mi minimalista Makefilese verá así,

-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}

Si bien executable.mkse verá como el siguiente,

SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)

%.bin:$(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) $(DEPS) $(TARGETS)

-include $(DEPS)

Comenzando desde cero, comenzaré con algo Makefilecomo el siguiente,

all: testapp.bin

testapp.bin:sourcea.o sourcb.o
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) testapp.bin

Tengo este fragmento de aquí y lo modifiqué. Tenga en cuenta que se agregan algunas reglas implícitas a este archivo que se pueden encontrar en la documentación de makefile. Algunas variables implícitas también son relevantes aquí.

Tenga en cuenta que Makefileproporciona los detalles que recipemuestran que howse puede realizar la compilación. Es posible escribir executable.mkpara mantener los detalles definidos en un archivo. De esa manera, el archivo MAKE se puede reducir como mostré anteriormente.

Variables internas en CMakeyMake

Ahora avanzando un poco, CMakepodemos establecer una bandera de compilador como la siguiente,

set(CMAKE_C_FLAGS "-Wall")

Obtenga más información sobre las CMakevariables predeterminadas en el CMakeCache.txtarchivo. El CMakecódigo anterior será equivalente al Makecódigo siguiente,

CFLAGS = -Wall

Tenga en cuenta que CFLAGSes una variable interna en Make, de la misma manera, CMAKE_C_FLAGSes una variable interna enCMake .

agregando incluir y ruta de biblioteca en CMake

Podemos hacerlo cmakeusando funciones.

target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
    mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})

Vs agregar inclusión y ruta de biblioteca en Make

Podemos agregar inclusión y bibliotecas agregando líneas como las siguientes,

INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest

Tenga en cuenta que estas líneas anteriores se pueden generar a partir de herramientas de generación automática o pkg-config. (aunque Makefile no depende de las herramientas de configuración automática)

CMake configure / tweek

Normalmente, es posible generar algunos config.harchivos como auto-configherramientas usando configure_filefunction. Es posible hacer más trucos escribiendo funciones personalizadas. Y finalmente podemos seleccionar una configuración como la siguiente,

cmake --build . --config "Release"

Es posible agregar alguna opción configurable usando la optionfunción.

Makefile configurar / ajustar

Si de alguna manera necesitamos compilarlo con alguna bandera de depuración, podemos invocar algo makesimilar,

make CXXFLAGS=NDEBUG

Creo que las variables internas, Makefile-rulesy CMake-functionsson un buen comienzo para la comparación, buena suerte con más indagaciones.

shuva
fuente