Forums Casio - Vos tutoriels et astuces

Index du Forum | Vos tutoriels et astuces | Pied à l'étrier pour les Makefile avec GNU Make (gmake)
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Posté le 02/08/2017 00:59

Pied à l'étrier pour les Makefile avec GNU Make (gmake) :

Sous les OS descendant d'Unix tels que Linux, OS X, Solaris, et autres, pour construire un projet, nous utilisons ce que nous appelons des Makefile.

L'idée derrière eux, c'est de faire un livre de cuisine : on décrit comment, à partir d'ingrédients, on arrive à un résultat, pour que lorsque l'utilisateur demande le résultat, l'utilitaire de construction, ici GNU Make, sache ce qu'il doit faire pour parvenir à ce résultat, et de quels ingrédients il a besoin.

Le principe dans un cas préçis : construction d'un add-in

Commençons par décrire le principe dans un cas bien précis, celui qui nous intéresse sur ce site : la construction d'un add-in. L'add-in est au format G1A, un format propriétaire de CASIO, pour le construire, nous allons avoir besoin d'un fichier binaire contenant les données à mettre dans l'add-in, et un utilitaire, g1a-wrapper, que vous avez normalement installé avec le reste.

Sauf que nous avons des fichiers C et des fichiers assembleurs (source). Comment produire ce fichier binaire à partir de fichiers ? Nous savons que nous pouvons compiler les fichiers C et assembleurs (source) en fichiers objets à l'aide du compilateur C (sh3eb-elf-gcc) et de l'assembleur (sh3eb-elf-as). Ces fichiers objets contiennent du binaire avec symboles (au format ELF), et nous voulons les assembler en un fichier binaire pour la mise au format de l'add-in.

Malheureusement, cela ne se fait pas en une passe : il faut d'abord produire un fichier ELF à partir de tous les objets, puis "retirer" les métadonnées dans le fichier ELF (d'où la production d'un fichier intermédiaire "addin.elf").

Nous avons toutes nos règles, et l'enchaînement est bon : nous savons comment faire des fichiers objet à partir des fichiers C et assembleur, le fichier ELF de ces fichiers objet, le fichier binaire de ce fichier ELF, puis l'add-in à partir de ce fichier binaire. Maintenant, il va falloir mettre ça en forme !

Comment on écrit une règle (recette) ?

Chaque règle contient trois éléments : élément à construire, dépendances, commandes liées à la règle. Dans un Makefile, cela se présente de la façon qui suit :

fichier_à_produire: ce_fichier_utile_là ce_fichier_aussi
    commande --produit-un-fichier-avec-le-nom="fichier_à_produire" \
        ce_fichier_utile_là ce_fichier_aussi


À noter que GNU Make n'aime pas vraiment les fichiers dont le nom contient des espaces, donc je vous conseille vraiment d'éviter ça.

Commençons par l'add-in final : le fichier à produire (résultat), c'est le G1A, ce dont nous avons besoin (ingrédients), c'est le fichier binaire, et le contenu de la recette, c'est l'utilisation de g1a-wrapper ! Voilà ce que ça donne :

addin.g1a: addin.bin
    g1a-wrapper -o addin.g1a -n ADDIN --version="12.34.0000" \
        -i chemin/vers/icone.bmp --date="2017.1231.2359"


Au passage, je vous déconseille fortement d'utiliser les quatre derniers chiffres de la version (ils ont une signification spéciale).

Y a plein de trucs qui se répètent, y a pas moyen de faire des variables ?

Si, il est possible d'utiliser des variables ! Il faut les définir en dehors du contenu des règles. Pour cela, faites simplement :

NOM_VARIABLE = contenu de la variable


Ici, des variables utiles à définir pour notre add-in seraient par exemple :

NOM = ADDIN
VERSION = 12.34
ICONE = chemin/vers/icone.bmp
DATE = 2017.1231.2359


Pour les utiliser, il faudra néanmoins utiliser des parenthèses, par exemple, $(NOM), et pas $NOM.

Au-delà de ces variables définies par l'utilisateur, il y a des variables spéciales définies par GNU Make, dites "automatiques". Elles sont définies pour les instructions dans les règles. Parmi d'autres, nous avons :

- $@ : le nom de la cible (résultat), par exemple addin.g1a ;
- $^ : le nom des dépendances (ingrédients), par exemple addin.bin ;
- $< : le nom de la première dépendance.

Nous pouvons donc améliorer notre règle pour qu'elle devienne :

$(NOM).g1a: $(NOM).bin
    g1a-wrapper -o $@ -n $(NOM) --version="$(VERSION).0000" \
        -i $(ICONE) --date="$(DATE)"


Ainsi, vous aurez moins d'emplacements à changer à chaque fois.

Et si je veux faire des trucs un peu plus compliqués, y a des fonctions ?

Y en a pas mal avec GNU Make. On citera par exemple $(wildcard pattern), qui permet d'évaluer une wildcard (chemins avec des étoiles '*' et autres métacaractères) pour récupérer plusieurs fichiers. Par exemple, on peut faire $(wildcard source/*.c) pour récupérer tous les fichiers C dans le dossier source/.

Pour le reste, ceci est un tutoriel rapide et n'a pas vocation à devenir une référence complète : vous saurez chercher par vous-mêmes

Y a des trucs cool à faire avec les dépendances ?

L'idée avec les dépendances, c'est que si un fichier n'existe pas dedans, ça va faire une erreur, et si un fichier a été mis à jour depuis la dernière construction, ça va refaire le fichier source, et tout ce qui dépend de ce fichier source (donc seulement les fichiers objets concernés, et l'ELF/binaire/add-in).

Donc il est possible de faire une règle du type truc.o: truc.c machin.h pour que si machin.h est mis à jour, ça considère que ça doive reconstruire truc.o (ce qui est une bonne chose, puisque des structures ou autres choses prototypes ont peut-être été mis à jour !).

Conclusion

L'usage des commandes de compilation, de production des fichiers objet, du fichier binaire, et de l'add-in en lui-même, c'est sur le tutoriel d'installation du cross-compilateur, étapes 7 et 8.

gint requiert des commandes propres, et c'est probablement renseigné sur le topic de celui-ci.

À partir d'ici, vous avez les armes de base pour décider comment vous allez organiser votre projet. Vous devriez avoir ce qu'il faut pour avoir tout dans le même dossier, mais peut-être voudrez-vous avoir un dossier src/ ou source/ pour séparer les sources du reste ? Peut-être avec un dossier obj/ ou objets/ pour séparer les objets des sources, histoire que ça fasse moins cracra ? De là, vous avez le contrôle et la documentation, je vous fais confiance




Theprog
Hors ligne
Membre
Niveau: Intermédiaire
Points: 1435
Défis: 20
Message
Citer : Posté le 02/08/2017 12:13 | #
Merci pour le tutoriel

J'ai deux trois questions qui vont venir au fur et à mesure. J'en ai déjà 2:

- Pour ce qui est de la manipulation des variables, si je veux lister tout les .o en fonction des .c, est-ce que je peux faire quelque chose de ce style:
SRC= $(wildcard src/*.c)
OBJ= $(SRC:.c=.o,scr=build)


- Pour les dépendances, si j'ai par exemple un projet avec un main.c, main.h et const.h qui est inclut dans main.h et qui stocke des variables importantes, est-ce que si je modifie le fichier const.h et que mon Makefile a une instruction de ce type:
main.o : main.c main.h
         blabla

le fichier const.h est compté dans les dépendances ? (bon en fait en écrivant ça j'ai une petite idée mais histoire d'être sûr)

Ajouté le 02/08/2017 à 12:20 :
Ah et pour la date, c'est du troll ou c'est vraiment le format genre mmhh:ddmm:yyyy ?
----------------------------------
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Citer : Posté le 02/08/2017 12:31 | #
Theprog a écrit :
Pour ce qui est de la manipulation des variables, si je veux lister tout les .o en fonction des .c, est-ce que je peux faire quelque chose de ce style:
SRC= $(wildcard src/*.c)
OBJ= $(SRC:.c=.o,src=build)

Pas directement. Je te conseille de faire ça :

SRC = $(wildcard src/*.c)
SRC = $(SRC:src/%=%)
OBJ = $(SRC:%.c=build/%.o)

Ici, SRC contiendra par exemple truc.c machin.c, et OBJ contiendra build/truc.o build/machin.o.

Theprog a écrit :
Pour les dépendances, si j'ai par exemple un projet avec un main.c, main.h et const.h qui est inclut dans main.h et qui stocke des variables importantes, est-ce que si je modifie le fichier const.h et que mon Makefile a une instruction de ce type :

main.o : main.c main.h
         blabla


le fichier const.h est compté dans les dépendances ? (bon en fait en écrivant ça j'ai une petite idée mais histoire d'être sûr)

GNU Make n'explore pas directement les sources C pour voir les fichiers qu'ils incluent. Y a moyen de faire ça avec GCC, mais je conseille pas à votre niveau : contentez-vous de faire en sorte que n'importe quel fichier header (.h) modifié fasse recompiler tous les fichiers C.

Theprog a écrit :
Ah et pour la date, c'est du troll ou c'est vraiment le format genre mmhh:ddmm:yyyy ?

J'ai fait ça de tête, et j'aurais pas dû. Le format c'est YYYY.MMDD.HHmm. J'ai corrigé le tutoriel.
----------------------------------
Theprog
Hors ligne
Membre
Niveau: Intermédiaire
Points: 1435
Défis: 20
Message
Citer : Posté le 02/08/2017 14:33 | #
Merci de ton aide
Bon voilà donc le fichier auquel j'ai abouti:
NOM= moisland
VERSION= 1.0
ICONE= moisland.bmp
DATE= 02.08.2017

HDR = $(wildcard src/*.h)
HDR = $(HDR:src/%=%)
SRC = $(wildcard src/*.c)
SRC = $(SRC:src/%=%)
OBJC = $(SRC:%.c=%.o)

IMG = $(wildcard assets/image-*.bmp)
IMG = $(IMG:assets/%=%)
OBJI = $(IMG:%.bmp=%.bmp.o)
FNT = $(wildcard assets/font-*.bmp)
FNT = $(FNT:assets/%=%)
OBJF = $(FNT:%.bmp=%.bmp.o)


$(NOM).g1a: build/$(NOM).bin
    g1a-wrapper $< -o $@ -i $(ICONE) --version="$(VERSION).0000"

build/$(NOM).bin: build/$(NOM).elf
    sh3eb-elf-objcopy -R .comment -R .bss -O binary $< $@

build/$(NOM).elf: build/$(OBJC) build/$(OBJI) build/$(OBJF)
    sh3eb-elf-gcc $^ -o $@ `fxsdk --cflags --libs`


build/%.o: src/%.c src/$(HDR)
    sh3eb-elf-gcc -c $^ -o $@ `fxsdk --cflags`

build/%.bmp.o: assets/%.bmp
    ifeq ($(findstring font, %),)
    fxconv -image $< -o $@ -n assets_%
    else
    fxconv -font $< -o $@ -n assets_%
    endif
    



.PHONY: clean mrproper

clean:
    rm -rf build/*.o
    rm -rf build/*.elf
    rm -rf build/*.bin

mrproper: clean
    rm -rf $(NOM).g1a


Quand je lance la commande make, j'ai une erreur: "Makefile:9: *** La variable récursive « SRC » se référence elle-même (à la fin). Arrêt."
Si je comprend bien ça vient de la modification de la variable à partir d'elle même mais bon ...

Sinon dans l'ensemble, cela vous semble-t-il correct ?
J'ai notamment un doute sur la variable %, il faut mettre $(%) ?
----------------------------------
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Citer : Posté le 02/08/2017 14:59 | #
La version doit vraiment être au format MM.mm.0000, donc il faut faire 01.00 au lieu de 1.0. N'oublies pas d'utiliser ta variable DATE avec g1a-wrapper !

Pour ton erreur, en fait, il y a différents types d'affectations, j'en ai pas parlé et je me suis dit que GNU Make comprendrait, mais apparemment non.

= c'est une affectation avec référence, donc par exemple, VERSION = $(TRUC) ça lira TRUC au moment de l'évaluation de VERSION, donc plus tard, i.e. si tu changes TRUC après, ça prendra en compte. Mais du coup, ici, il vaut mieux utiliser :=, qui évalue les variables référencées à l'affectation et non plus tard.

Pour les variables automatiques, tu peux faire $<caractère>, par exemple $@.
----------------------------------
-florian66-
Hors ligne
Rédacteur
Niveau: Aucun
Points: 2245
Défis: 19
Message
Citer : Posté le 02/08/2017 17:09 | #
Merci Cake pour ce tutoriel, même si mes makefiles sont (déjà) faits, c'est toujours bien d'avoir quelques explications complémentaires à côté
Theprog
Hors ligne
Membre
Niveau: Intermédiaire
Points: 1435
Défis: 20
Message
Citer : Posté le 02/08/2017 19:17 | #
Bon du coup j'ai presque fini, simplement il me reste une erreur:
g1a-wrapper build/moisland.bin -o moisland.g1a -i moisland.bmp --version="01.00.0000" --date="2017.0101.0000"
g1a-wrapper: warning: version string "01.00.0000" does not have expected format 'MM.mm.pppp'
g1a-wrapper: warning: date string "2017.0101.0000" does not have expected format 'yyyy.MMdd.hhmm'

La j'avoue que je vois pas trop où il veut en venir
----------------------------------
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Citer : Posté le 02/08/2017 19:21 | #
Ce n'est pas une erreur, c'est juste Lephenixnoir qui a la flemme de corriger. Je lui ai report ça il y a quelques mois, et c'est toujours là apparemment. Mais ne t'inquiètes pas, le g1a est bien produit !
----------------------------------
Theprog
Hors ligne
Membre
Niveau: Intermédiaire
Points: 1435
Défis: 20
Message
Citer : Posté le 02/08/2017 19:22 | #
Dac impec alors
Merci

Ajouté le 03/08/2017 à 23:45 :
Rebonjour,

Alors voilà un nouveau problème que je comprend pas trop sur un Makefile:
NOM= AGBSW
VERSION= 01.00
ICONE= MainIcon.bmp
DATE= 2017.0101.0000

HDR = $(wildcard src/*.h)
HDR := $(HDR:src/%=%)
SRC = $(wildcard src/*.c*)
SRC := $(SRC:src/%=%)
OBJC := $(SRC:%=%.o)

IMG = $(wildcard assets/image_*.bmp)
IMG := $(IMG:assets/%=%)
OBJI := $(IMG:%.bmp=%.bmp.o)
FNT = $(wildcard assets/font_*.bmp)
FNT := $(FNT:assets/%=%)
OBJF := $(FNT:%.bmp=%.bmp.o)

$(NOM).g1a: build/$(NOM).bin
    g1a-wrapper $< -o $@ -i $(ICONE) --version='$(VERSION).0000' --date='$(DATE)'

build/$(NOM).bin: build/$(NOM).elf
    sh3eb-elf-objcopy -R .comment -R .bss -O binary $< $@

build/$(NOM).elf: build/$(OBJC) $(if $(OBJI), build/$(OBJI)) $(if $(OBJF), build/$(OBJF))
    sh3eb-elf-gcc $^ -o $@ `fxsdk --cflags --libs`


build/%.c.o: src/%.c $(if $(HDR), src/$(HDR))
    sh3eb-elf-gcc -c $< -o $@ `fxsdk --cflags`

build/%.cpp.o: src/%.cpp $(if $(HDR), src/$(HDR))
    sh3eb-elf-gcc -c $< -o $@ `fxsdk --cflags`

build/%.bmp.o: assets/%.bmp
ifeq ($(findstring font, $<),)
    fxconv -image $< -o $@ -n $(<:assets/%.bmp=assets_%)
else
    fxconv -font $< -o $@ -n $(<:assets/%.bmp=assets_%)
endif
    



.PHONY: clean mrproper

clean:
    rm -rf build/*.o
    rm -rf build/*.elf
    rm -rf build/*.bin

mrproper: clean
    rm -rf $(NOM).g1a


En fait ce qui m'espante quand je le lance c'est qu'il me donne l'erreur suivante:
make: ***  Aucune règle pour fabriquer la cible « build/syscall.c.o », nécessaire pour « build/AGBSW.elf ». Arrêt.

alors que le fichier est bel et bien présent au milieu des autres fichiers sources, et que la même commande dans la console directement marche... (sh3eb-elf-gcc -c src/syscall.c -o build/syscall.c.o fxsdk --cflags)
----------------------------------
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Citer : Posté le 04/08/2017 00:37 | #
Les règles avec patterns ('%') ne matchent que si les dépendances existent.
Essaies de faire $(info $(if $(HDR), src/$(HDR))) juste avant la règle pour build/%.c.o, et regardes si les fichiers existent bien (si ta variable est bonne).

J'en profite pour dire que gint n'offre pas le support pour le C++, donc inutile d'ajouter les fichiers .cpp pour le moment.
----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10113
Défis: 130
Message
Citer : Posté le 04/08/2017 10:15 | #
Tu peux parfaitement compiler du C++ avec gint, à condition de faire mon boulot à ma place (c'est-à-dire ajouter les extern "C" {} où il faut dans les headers). Il n'y a certes pas de STL, pas de string, pas même de new/new[]/delete/delete[], mais on peut compiler du C++ normalement.

Après, Smash' m'avait parlé de problèmes sérieux sur ces points, donc je déconseille de le faire. Mais si vous êtes forts et que vous voulez creuser un peu, c'est parfaitement possible.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Theprog
Hors ligne
Membre
Niveau: Intermédiaire
Points: 1435
Défis: 20
Message
Citer : Posté le 04/08/2017 11:59 | #
Bon en effet il y a un bien un problème à cet endroit là. En fait le truc c'est que mettre le chemin avec src/ en faisant src/$(HDR) ne s'applique qu'au premier header et non pas aux autres Mais bon la je sais pas comment faire pour éviter ça.
src/piaf.h pig.h attractor.h draw.h level.h memory.h bow.h object.h mobile.h MonochromeLib.h control.h AGBSW.h syscall.h
----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10113
Défis: 130
Message
Citer : Posté le 04/08/2017 12:05 | #
$(foreach hdr,$(HDR),src/$(hdr))
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Citer : Posté le 04/08/2017 12:07 | #
Ou tout simplement $(HDR:%=src/%). Les substitutions, c'est joli. (ou plus indirectement, patsubst, mais c'est substentiellement la même chose)
----------------------------------
Intelligide
Hors ligne
Banni de la Shoutbox
Niveau: Aucun
Points: 33
Défis: 5
Message
Citer : Posté le 12/08/2017 23:06 | #
CMake
----------------------------------
┌∩┐(ಠ_ಠ)┌∩┐
Breizh_craft
Hors ligne
Modérateur
Niveau: Confirmé
Points: 581
Défis: 6
Message
Citer : Posté le 12/08/2017 23:07 | #
-20 VDD
----------------------------------
Intelligide
Hors ligne
Banni de la Shoutbox
Niveau: Aucun
Points: 33
Défis: 5
Message
Citer : Posté le 17/08/2017 23:43 | #
Mais pourquoi ? Cmake est super bien
----------------------------------
┌∩┐(ಠ_ಠ)┌∩┐
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1424
Défis: 8
Message
Citer : Posté le 17/08/2017 23:45 | #
N'hésites pas à en faire un tutoriel dans ce cas ! De préférence persistent, après, c'est toi qui vois
----------------------------------
Breizh_craft
Hors ligne
Modérateur
Niveau: Confirmé
Points: 581
Défis: 6
Message
Citer : Posté le 17/08/2017 23:49 | #
Flemme de réfléchir, alors je cite.

« cmake (written in C++) - so huge and bloated, compilation takes longer than compiling GCC (!). It’s not even possible to create freestanding Makefiles, since the generated Makefiles call back into the cmake binary itself. »
----------------------------------
Intelligide
Hors ligne
Banni de la Shoutbox
Niveau: Aucun
Points: 33
Défis: 5
Message
Citer : Posté le 19/08/2017 20:52 | #
@Cake: déso, pas déso, j'ai trop d'boulot

@breizh: Cmake met moins de temps pour compiler que GCC ( Et encore moins que cette fils de ****rie de VS Studio de sa maman la s***** ) et y a pas besoin de créer une makefile custom vu que Cake s'en charge. C'est comme dire qu'un char d'assaut n'a pas de mitraillette Et il permet d'avoir une compilation multiplateforme optimale et le plus important, IL AFFICHE LA PROGRESSION DE LA COMPILATION *0*
----------------------------------
┌∩┐(ಠ_ಠ)┌∩┐
Dark storm
Hors ligne
Administrateur
Niveau: Aucun
Points: 9615
Défis: 170
Message
Citer : Posté le 19/08/2017 22:59 | #
Nan mais Intel, t'as pas du comprendre ce que ça implique x)
« It’s not even possible to create freestanding Makefiles, since the generated Makefiles call back into the cmake binary itself »

En gros tu peux pas partager ton projet à quelqu'un qui n'a pas cmake. C'est dommage…
Après, le multiplateforme osef, le fxsdk est dispo uniquement sous Gnunux.
Et la progression, vu que ça compile rarement plus de 10 secondes pour un addin, voilà quoi.

On en revient au même point : pourquoi une usine à gaz pour un truc trivial ? Après, chacun a sa propre réponse, mais moi je suis plutôt du style à utiliser l'outil le plus adapté
----------------------------------
Finir est souvent bien plus difficile que commencer. — Jack Beauregard


Index du Forum | Vos tutoriels et astuces | Pied à l'étrier pour les Makefile avec GNU Make (gmake)
Publicité et partenaires
Casio Education
Casio éducation

TI-Planet
Casiopeia
Casiopeia
CasioFan, la communauté ClassPad
CasioFan
CodeWalrus
CodeWalrus

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2015 | Il y a 35 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements
Rugby Manager | Jeu de basket | Jeu de handball | Jeu de tennis | Nova Raider | Réparation téléphone | Soccer Rush | Tasty Tale

Planète Casio est un site communautaire indépendant, géré bénévolement et n'est donc pas affilié à Casio | Toute reproduction de Planète Casio, même partielle, est interdite
Les fichiers, programmes et autres publications présents sur Planète Casio restent la propriété de leurs auteurs respectifs et peuvent être soumis à des licences ou des copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd