Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Vos tutoriels et astuces


Index du Forum » Vos tutoriels et astuces » Pied à l'étrier pour les Makefile avec GNU Make (gmake)
Cakeisalie5 En ligne Ancien administrateur Points: 1896 Défis: 11 Message

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

Posté le 02/08/2017 00:59

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 Points: 1447 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 En ligne Ancien administrateur Points: 1896 Défis: 11 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.

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Theprog Hors ligne Membre Points: 1447 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 En ligne Ancien administrateur Points: 1896 Défis: 11 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 $@.

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
-florian66- Hors ligne Ancien rédacteur Points: 2383 Défis: 20 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é
In Arch, I trust ! And you ?
Theprog Hors ligne Membre Points: 1447 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 En ligne Ancien administrateur Points: 1896 Défis: 11 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 !

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Theprog Hors ligne Membre Points: 1447 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 En ligne Ancien administrateur Points: 1896 Défis: 11 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.

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 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.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Theprog Hors ligne Membre Points: 1447 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 En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 04/08/2017 12:05 | #


$(foreach hdr,$(HDR),src/$(hdr))

Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Cakeisalie5 En ligne Ancien administrateur Points: 1896 Défis: 11 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)

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Intelligide Hors ligne Membre de CreativeCalc Points: 49 Défis: 5 Message

Citer : Posté le 12/08/2017 23:06 | #


CMake
Breizh_craft En ligne Modérateur Points: 1157 Défis: 7 Message

Citer : Posté le 12/08/2017 23:07 | #


-20 VDD
Breizh.pm – Un adminsys qui aime les galettes.
Intelligide Hors ligne Membre de CreativeCalc Points: 49 Défis: 5 Message

Citer : Posté le 17/08/2017 23:43 | #


Mais pourquoi ? Cmake est super bien
Cakeisalie5 En ligne Ancien administrateur Points: 1896 Défis: 11 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

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Breizh_craft En ligne Modérateur Points: 1157 Défis: 7 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. »
Breizh.pm – Un adminsys qui aime les galettes.
Intelligide Hors ligne Membre de CreativeCalc Points: 49 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 En ligne Labélisateur Points: 11631 Défis: 176 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

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 106 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd