Cmake vs faire des exemples de codes?

118

Je me demandais s'il y avait un exemple de code pour Makefiles ( make) et CMakeLists.txt( cmake) qui font tous les deux la même chose (la seule différence étant que l'un est écrit makeet l'autre cmake).

J'ai essayé de chercher «cmake vs make», mais je n'ai jamais trouvé de comparaison de code. Il serait vraiment utile de comprendre les différences, ne serait-ce que pour un cas simple.

jlo
la source
20
+1 C'est une bonne question; quand je commençais, cmakeje le voulais aussi. Mais je doute que vous le trouviez parce que les capacités ne correspondent tout simplement pas si bien l'une à l'autre. Si vous essayez de faire cmakecomme makeça, vous vous rendrez fou, sérieusement. Il vaut mieux partir de zéro. Les choses qui sont insignifiantes en makesont assez impliquées cmake, et vice-versa.
Ernest Friedman-Hill le
1
@ ErnestFriedman-Hill avez-vous plus de détails à ce sujet? Alors, makeet cmakesont-ils si distincts qu'ils devraient être davantage considérés comme complémentaires que comme des outils concurrents?
Ehtesh Choudhury
2
@Shurane - cmake ne construit rien lui-même; il crée des Makefiles (et d'autres scripts de construction similaires), que vous exécutez ensuite. Par conséquent, chaque fois que vous écrivez des fichiers cmake, vous devez vous demander si une commande doit s'appliquer pendant la génération ou pendant la génération. Certaines actions - copier un ensemble de fichiers génériques au moment de la construction, par exemple - sont assez compliquées, comparées au " cp *.x $(OUTDIR)" que vous écririez dans un Makefile. Peut-être la partie la plus ennuyeuse pour moi est que les Makefiles générés sont par conception complètement non portables et rigides (suite)
Ernest Friedman-Hill
1
(suite) Vous ne pouvez même pas déplacer le répertoire source sur la même machine sans relancer cmake pour régénérer le Makefile! Le choix n'est donc pas entre cmake et make, mais plutôt entre écrire des Makefiles portables vous-même ou utiliser cmake pour générer des fichiers non portables sur chaque machine de construction (et étant donné que vous pouvez utiliser Cygwin ou mingw sous Windows, je trouve généralement que le premier est plus facile. )
Ernest Friedman-Hill
1
une bonne question, mais il n'y a pas de réponse spécifique, car les deux outils tentent de résoudre un problème différent. cmake prend des informations sur la façon de construire des programmes et génère des makefiles qui construisent le programme. Par conséquent, cmake est un langage avec des règles de construction abstraites et gnu make est une résolution de dépendances qui exécute des programmes sur un parcours de graphe acyclique dirigé.
Alex

Réponses:

118

Le Makefile suivant construit un exécutable nommé à progpartir des sources prog1.c, prog2.c, prog3.c and main.c. progest lié libmystatlib.a et libmydynlib.soqui sont tous deux également construits à partir des sources. En outre, progutilise la bibliothèque libstuff.adans stuff/libet son en-tête dans stuff/include. Le Makefile construit par défaut 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 un CMakeLists.txtqui 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:

  • CMake reconnaît quels 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 comme $(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 / du système de construction.

  • Drapeaux de débogage sont inclus en réglant soit la variable CMAKE_BUILD_TYPEà « Debug », ou en le passant à CMake lors de l' appel du programme: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • CMake propose également l'inclusion indépendante de la plate-forme du drapeau '-fPIC' (via la POSITION_INDEPENDENT_CODEpropriété) et bien d'autres. Pourtant, des paramètres plus obscurs peuvent être implémentés à la main dans CMake aussi bien que dans un Makefile (en utilisant COMPILE_FLAGS et des propriétés similaires). Bien sûr, CMake commence vraiment à briller lorsque des bibliothèques tierces (comme OpenGL) sont incluses de manière portable.

  • Le processus de construction comporte une étape si vous utilisez un Makefile, à savoir la saisie makesur 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, soit en exécutant un client GUI). Cela crée un Makefile ou quelque chose d'équivalent, selon le système de construction de votre choix (par exemple make sur 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 .

Roberto
la source
3
Le Makefile n'est-il pas trop compliqué? En utilisant CPPFLAGSau lieu d' INCDIRune seule, on aurait pu utiliser les règles intégrées et l'appel explicite du compilateur aurait été redondant. De même pour la gestion de ar, les règles intégrées peuvent également couvrir cela. Aussi, pourquoi définir CPPet CCexplicitement? Ils sont déjà définis sur de bonnes valeurs par make, ce sont des variables prédéfinies. makereconnaît également quel compilateur utiliser pour quel type de source, les règles intégrées sont nombreuses.
Christian Hujer
1
Et beaucoup de ces variables doivent être attribuées avec :=au lieu de =.
Christian Hujer
1
En regardant la description, cmakeest plus comparable à automakeque make.
ivan_pozdeev
Le Makefile fourni pourrait être réduit à 3/4 lignes. Vous DEVRIEZ spécifier INCLUDESau lieu de INCDIR. Vous n'avez pas besoin des règles% .o:%. C.
shuva
6

Prenez un logiciel qui utilise CMake comme système de construction (il y a beaucoup de projets open source parmi lesquels choisir comme exemple). Obtenez le code source et configurez-le à l'aide de CMake. Lisez les makefiles résultants et profitez-en.

Une chose à garder à l'esprit que ces outils ne mappent pas un à un. La différence la plus évidente est que CMake recherche les dépendances entre les différents fichiers (par exemple l'en-tête C et les fichiers source), alors que make laisse cela aux auteurs du makefile.

Tadeusz A. Kadłubowski
la source
4

Si cette question concerne un exemple de Makefilesortie du CMakeList.txtfichier, veuillez vérifier les sources cmake-backend et en générer une Makefile. Si ce n'est pas le cas, j'ajoute à la réponse de @Roberto, j'essaie de simplifier les choses en cachant les détails.

Fonction CMake

Alors que Makec'est un outil flexible pour les règles et les recettes, CMakec'est une couche d'abstraction qui ajoute également la fonction de configuration.

Ma plaine CMakeLists.txtressemblera à ce qui suit,

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

Notez que cela CMakecache howla construction peut être fait. Nous avons seulement spécifié whatest l'entrée et la sortie.

Le CMakeLists.txtcontient la liste des appels de fonction définis par cmake.

(Fonction CMake) Vs Créer des règles

Dans Makefilele rules and recipessont utilisés à la place de functions. En plus de la functionfonction-like, rules and recipesfournissez le chaînage. Mon minimaliste Makefileressemblera à ce qui suit,

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

Alors que le executable.mkressemblera à 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 Makefilecomme ce qui suit,

all: testapp.bin

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

.PHONY: all clean

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

J'ai récupéré cet extrait d' ici et l' ai modifié. Notez que certaines règles implicites sont ajoutées à ce fichier qui peut être trouvé dans la documentation makefile. Certaines variables implicites sont également pertinentes ici.

Notez que cela Makefilefournit les détails recipemontrant que howla construction peut être effectuée. Il est possible d'écrire executable.mkpour conserver les détails définis dans un seul fichier. De cette façon, le makefile peut être réduit comme je l'ai montré plus tôt.

Variables internes dans CMakeetMake

Maintenant peu avancé, CMakenous pouvons définir un indicateur de compilateur comme le suivant,

set(CMAKE_C_FLAGS "-Wall")

Veuillez en savoir plus sur les CMakevariables par défaut dans le CMakeCache.txtfichier. Le CMakecode ci-dessus sera équivalent au Makecode ci-dessous,

CFLAGS = -Wall

Notez qu'il CFLAGSs'agit d'une variable interne dans Make, de la même manière, CMAKE_C_FLAGSest une variable interne dans CMake.

ajout d'un chemin d'inclusion et de bibliothèque dans CMake

Nous pouvons le faire en cmakeutilisant des fonctions.

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

Vs ajouter un chemin d'inclusion et de bibliothèque dans Make

Nous pouvons ajouter des include et des bibliothèques en ajoutant des lignes comme celle-ci,

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 dépende pas des outils de configuration automatique)

CMake configurer / tweek

Normalement, il est possible de générer des config.hfichiers comme des auto-configoutils en utilisant configure_filefunction. Il est possible de faire plus de trucs en écrivant des fonctions personnalisées. Et enfin on peut sélectionner une config comme la suivante,

cmake --build . --config "Release"

Il est possible d'ajouter une option configurable en utilisant la optionfonction.

Makefile configurer / modifier

Si d'une manière ou d'une autre nous devons le compiler avec un indicateur de débogage, nous pouvons invoquer la méthode makesimilaire,

make CXXFLAGS=NDEBUG

Je pense que les variables internes, Makefile-ruleset CMake-functionssont un bon début pour la comparaison, bonne chance avec plus de fouille.

Shuva
la source