Je me demandais s'il y avait un exemple de code pour Makefile
s (make
) et CMakeLists.txt
(cmake
) que les deux fassent la même chose (la seule différence est que l’un est écrit en make
et l’autre en cmake
).
J'ai essayé de rechercher 'cmake vs make', mais je n'ai jamais trouvé de comparaison de code. Il serait vraiment utile de comprendre les différences, même s'il ne s'agit que d'un cas simple.
Le Makefile suivant construit un exécutable nommé prog
à partir des sources prog1.c, prog2.c, prog3.c and main.c
. prog
est lié à libmystatlib.a
et libmydynlib.so
qui sont tous deux également construits à partir de la source. De plus, prog
utilise la bibliothèque libstuff.a
dans stuff/lib
et son en-tête dans stuff/include
. Le Makefile par défaut construit une cible de publication, mais offre également une cible de débogage:
#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 $@
Voici une CMakeLists.txt
qui fait (presque) exactement la même chose, avec quelques commentaires pour souligner les similitudes avec le 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")
Dans cet exemple simple, les différences les plus importantes sont les suivantes:
CMake reconnaît les compilateurs à utiliser pour quel type de source. En outre, il appelle la bonne séquence de commandes pour chaque type de cible. Par conséquent, il n'y a pas de spécification explicite de commandes telles que $ (CC) ..., $ (RANLIB) ... et ainsi de suite.
Tous les indicateurs habituels du compilateur/éditeur de liens traitant de l'inclusion de fichiers d'en-tête, de bibliothèques, etc. sont remplacés par des commandes indépendantes de la plate-forme/indépendantes du système.
Les indicateurs de débogage sont inclus en définissant la variable CMAKE_BUILD_TYPE sur "Debug" ou en la passant à CMake lors de l’appel du programme: cmake -DCMAKE_BUILD_TYPE:STRING=Debug
.
CMake offre également l'inclusion, indépendamment de la plate-forme, de l'indicateur '-fPIC' (via la propriété POSITION_INDEPENDENT_CODE) et de nombreux autres. Néanmoins, des paramètres plus obscurs peuvent être implémentés manuellement dans CMake, ainsi que dans un Makefile (en utilisant COMPILE_FLAGS
et propriétés similaires). Bien sûr, CMake commence vraiment à briller lorsque des bibliothèques tierces (telles que OpenGL) sont incluses de manière portable.
Le processus de construction comporte une étape si vous utilisez un Makefile, à savoir taper make
sur la ligne de commande. Pour CMake, il y a deux étapes: Tout d’abord, vous devez configurer votre environnement de construction (soit en tapant cmake <source_dir>
dans votre répertoire de construction ou en exécutant un client graphique). Cela crée un Makefile ou quelque chose d’équivalent, selon le système de construction de votre choix (par exemple make sous Unixes ou VC++ ou MinGW + Msys sous Windows). Le système de construction peut être passé à CMake en tant que paramètre; Cependant, CMake fait des choix par défaut raisonnables en fonction de la configuration de votre système. Deuxièmement, vous effectuez la construction réelle dans le système de construction sélectionné.
Les sources et les instructions de construction sont disponibles sur https://github.com/rhoelzel/make_cmake .
Prenez un logiciel qui utilise CMake comme système de construction (il y a beaucoup de projets opensource parmi lesquels choisir). Obtenez le code source et configurez-le en utilisant CMake. Lire les makefiles résultants et en profiter.
Une chose à garder à l'esprit que ces outils ne mappent pas un à un. La différence la plus évidente est que CMake analyse les dépendances entre différents fichiers (par exemple, les fichiers source et en-tête C), alors que make laisse cela aux auteurs du makefile.
Si cette question concerne un exemple Makefile
, la sortie du CMakeList.txt
fichier alors veuillez vérifier les sources de cmake-backend et générer un tel Makefile
. Si ce n'est pas le cas, alors j'ajoute à la réponse de @Roberto j'essaie de simplifier les choses en cachant les détails.
Alors que Make
est un outil flexible pour les règles et les recettes, CMake
est une couche d'abstraction qui ajoute également la fonctionnalité de configuration.
Ma plaine CMakeLists.txt
ressemblera à ce qui suit,
cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})
Notez que CMake
cache how
la construction peut être faite. Nous avons uniquement spécifié que what
est l’entrée et la sortie.
Le CMakeLists.txt
contient la liste des appels de fonction définis par cmake
.
Dans Makefile
le rules and recipes
sont utilisés à la place de functions
. En plus de la fonctionnalité semblable à function
, rules and recipes
fournir un chaînage. Mon minimaliste Makefile
ressemblera à ce qui suit,
-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}
Tandis que le executable.mk
ressemblera à ce qui suit,
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)
En partant de zéro, je commencerai par un Makefile
comme celui-ci,
all: testapp.bin
testapp.bin:sourcea.o sourcb.o
$(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)
.PHONY: all clean
clean:
$(RM) $(OBJECTS) testapp.bin
J'ai eu cet extrait de ici et l'ai modifié. Notez que certaines règles implicites sont ajoutées à ce fichier, que vous pouvez trouver dans la documentation de makefile. Certaines variables implicites sont également pertinentes ici.
Notez que Makefile
fournit le détail recipe
montrant how
que la construction peut être faite. Il est possible d'écrire executable.mk
pour conserver les détails définis dans un fichier. De cette façon, le makefile peut être réduit comme je l’ai déjà montré.
CMake
et Make
Maintenant que nous sommes un peu avancés, dans CMake
, nous pouvons définir un drapeau de compilateur comme celui-ci,
set(CMAKE_C_FLAGS "-Wall")
Veuillez en savoir plus sur les variables CMake
par défaut dans CMakeCache.txt
fichier. Le code CMake
ci-dessus sera équivalent à Make
code ci-dessous,
CFLAGS = -Wall
Notez que CFLAGS
est une variable interne dans Make
, de la même manière, CMAKE_C_FLAGS
est une variable interne dans CMake
.
Nous pouvons le faire dans cmake
en utilisant des fonctions.
target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})
Nous pouvons ajouter des bibliothèques include et en ajoutant des lignes comme suit:
INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest
Notez que les lignes ci-dessus peuvent être générées à partir d’outils de génération automatique ou de pkg-config. (bien que Makefile ne soit pas dépendant des outils de configuration automatique)
Normalement, il est possible de générer quelques config.h
fichier comme auto-config
outils en utilisant configure_file
une fonction. Il est possible de faire plus de truc en écrivant des fonctions personnalisées. Et enfin, nous pouvons sélectionner une configuration comme celle-ci,
cmake --build . --config "Release"
Il est possible d’ajouter des options configurables à l’aide de la fonction option
.
Si d’une manière ou d’une autre nous avons besoin de le compiler avec un indicateur de débogage, nous pouvons invoquer le make
comme,
make CXXFLAGS=NDEBUG
Je pense que les variables internes, Makefile-rules
et CMake-functions
sont un bon début pour la comparaison, bonne chance pour creuser davantage.