Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 20/02/2008, à 01:14

Le Farfadet Spatial

Makefile avancé

Bonjour.

   Je me suis lancé dans une exploration la plus profonde possible des Makefiles (depuis le temps que je me disais qu'il fallait que je le fasse). Donc, voici l'organisation du projet :

      -- le répertoire racine ;

      -- un répertoire nommé « headers, » dans lequel sont stockées tous les en-têtes ;

      -- un répertoire nommé « src, » dans lequel sont stockées toutes les sources, à l'exclusion des en-têtes ;

      -- un répertoire nommé « obj, » dans lequel sont stockés tous les fichiers objet (.o).

   L'exécutable au final est stocké dans le répertoire racine.

   Voici les Makefiles de chaque répertoire :

   Racine :

DEBUG=yes
export CC=g++
export LD=g++
export RM=rm -rf
ifeq ($(DEBUG),yes)
	export COPTIONAL=-pedantic -ansi -Wall -g
else
	export COPTIONAL=-O3 -fomit-frame-pointer -DNDEBUG
endif
VPATH=src
OBJ_DIR=obj
HEADERS_DIR=headers
export EXEC=testgtkmm

.PHONY: all clean mrproper

all: $(EXEC)
ifeq ($(DEBUG),yes)
	@echo "Generating debug version"
else
	@echo "Generating release version"
endif

$(EXEC):
	@(cd $(VPATH) && $(MAKE))

clean:
	@(cd $(OBJ_DIR) && $(MAKE) $@)

mrproper: clean
	@(cd $(VPATH) && $(MAKE) $@)
	@(cd $(OBJ_DIR) && $(MAKE) $@)
	@(cd $(HEADERS_DIR) && $(MAKE) $@)
	@$(RM) $(EXEC)
	@$(RM) *~

src :

HEADERS_DIR=../headers
CFLAGS=$(COPTIONAL) -I$(HEADERS_DIR) `pkg-config gtkmm-2.4 --cflags`
SRC=$(wildcard *.cpp)
export OBJ=$(SRC:.cpp=.o)

.PHONY: all mrproper

all: $(EXEC)

$(EXEC): $(OBJ)
	@(cd $(OBJ_DIR) && $(MAKE))

main.o: $(HEADERS_DIR)/helloworld.hpp

helloworld.o: $(HEADERS_DIR)/helloworld.hpp

%.o: %.cpp
	@$(CC) -o $@ -c $< $(CFLAGS)
	@mv $@ $(OBJ_DIR)

mrproper:
	@$(RM) *~

obj :

LDFLAGS=`pkg-config gtkmm-2.4 --libs`
EXEC_DIR=..

.PHONY: all clean mrproper

all: $(EXEC)

$(EXEC): $(OBJ)
	@$(LD) -o $@ $^ $(LDFLAGS)
	@mv $(EXEC) $(EXEC_DIR)

clean:
	@$(RM) *.o

mrproper: clean
	@$(RM) *~

headers :

.PHONY: mrproper

mrproper:
	@$(RM) *~

Dans l'ensemble, ça fonctionne pas mal. Cependant, j'ai trois interrogations :

      1. J'ai l'impression de ne pas être au top au niveau des dépendances, notamment pour les fichiers .o. Quelqu'un peut-il m'aiguiller là-dessus ?

      2. Plutôt que de modifier le Makefile, j'aimerais que l'on puisse passer en argument si l'on veut une version de déboguage. Cependant, pour ça, la seule solution que je vois c'est de rajouter une cible de ce genre dans le Makefile racine :

debug:
	@echo Generating debug version
	@export COPTIONAL=-pedantic -ansi -Wall -g
	@(cd $(VPATH) && $(MAKE))

Mais ça ne me satisfait pas, car il faut redoubler les commandes de la cible « $(EXEC) » et, pour peu que le processus de compilation devienne complexe, c'est un coup à faire des erreurs. Quelqu'un aurait-il une proposition ?

      3. N'y aurait-il pas des noms plus standard pour les variables que j'utilise ? Dans le même ordre d'idée, est-ce que quelqu'un connaît des moyens de plus automatiser les choses et rendre le Makefile portable, sans pour autant en venir aux auto-tools ni CMake (ce sera l'étape suivante) ?

   Merci d'avance à tous ceux qui voudront bien essayer de m'aider.

   À bientôt.

                                                                                                                                             Le Farfadet Spatial

Hors ligne

#2 Le 20/02/2008, à 10:08

Aurel34

Re : Makefile avancé

quelques idées en vrac:

tu devrais éviter les makefiles récursifs: http://miller.emu.id.au/pmiller/books/rmch/

même si la cible mrproper est assez classique (et m'a toujours fait rire), une cible "clean" fait plus sérieux à  mon avis smile

si créer une cible ne te plait pas tu peux utiliser les variables d'environnement avec "-e"

enfin au niveau portabilité tu ne devrais pas avoir de problème tant que tu restes sur une plateforme gnu (gnu make etc.), par contre au-delà  c'est un travail long et fastidieux qu'il vaut mieux laisser à  des outils comme autotools.

Hors ligne

#3 Le 20/02/2008, à 10:27

Le Farfadet Spatial

Re : Makefile avancé

Salut à  tous !

Aurel34 a écrit :

tu devrais éviter les makefiles récursifs: http://miller.emu.id.au/pmiller/books/rmch/

Heu... Si mon fichier Makefile principal appelle bien des sous Makefiles, il n'y a pas d'appel récursif : à  aucun moment, un fichier appelé sera appelé à  nouveau. De plus, en l'occurrence, l'idée n'est pas vraiment de savoir si l'organisation du projet est bonne, mais d'être capable d'utiliser toutes les fonctions de make et d'envoyer les résultats ailleurs que dans le répertoire o๠se trouve le Makefile.

   Néanmoins, pour un cas plus concret (pas un test), l'article que tu donnes à  l'air intéressant et je ne le connaissais pas. Je te remercie.

même si la cible mrproper est assez classique (et m'a toujours fait rire), une cible "clean" fait plus sérieux à  mon avis smile

Il y a bien une cible clean dans mes Makefiles : elle surprime les fichier d'extension .o. Simplement, la cible mrproper est là  pour mettre en place la version de distribution : elle appelle la cible clean, puis supprime aussi les fichiers de secours (*~) et l'exécutable.

si créer une cible ne te plait pas tu peux utiliser les variables d'environnement avec "-e"

Oui, je pense que je vais me rabattre là -dessus, parce que la solution consistant à  faire une cible spéciale pour la version de déboguage, quel que soit le sens dans lequel on la prend, pose problème.

enfin au niveau portabilité tu ne devrais pas avoir de problème tant que tu restes sur une plateforme gnu (gnu make etc.), par contre au-delà  c'est un travail long et fastidieux qu'il vaut mieux laisser à  des outils comme autotools.

Je suis au courant, mais sait-on jamais : je ne peux pas prétendre connaà®tre parfaitement la totalité des possibilités de make, il y a peut-être des possibilités pour étendre la portabilité, au moins sur plateforme POSIX, que je ne connais pas.

   à€ bientà´t.

                                                                                                                                             Le Farfadet Spatial

Hors ligne

#4 Le 20/02/2008, à 11:16

trucutu

Re : Makefile avancé

Le Farfadet Spatial a écrit :

Je me suis lancé dans une exploration la plus profonde possible des Makefiles (depuis le temps que je me disais qu'il fallait que je le fasse).

Salut,

Au vu de ta démarche, et puisque tu à  l'air de gérer l'écriture à  la main des Makefile, pourquoi tu ne t'attaquerais pas au Automake etc... ?


La chanson du dimanche - "La pêche !"
PC acheté chez Novatux : entièrement satisfait.
Faire des recherches solidaires !

Hors ligne

#5 Le 20/02/2008, à 11:40

Aurel34

Re : Makefile avancé

Le Farfadet Spatial a écrit :

Salut à  tous !
Heu... Si mon fichier Makefile principal appelle bien des sous Makefiles, il n'y a pas d'appel récursif
...

si, et c'est justement le but de l'article: aller contre l'approche récursive de make, en pseudo (pseudo) code:
make
  for_each subdir
    (cd $subdir ; make)
  end

c'est les appels à  "make" qui sont récursif (make s'appelle lui-même) et c'est mal. L'article essaye de montrer que comme ça chaque makefile à  une version de plus en plus locale du projet, et que ça finit par merder. Automake génère ce genre de makefiles, mais a (à  son niveau) une vision globale du projet.

Il y a bien une cible clean dans mes Makefiles...

mea culpa

Simplement, la cible mrproper est là  pour mettre en place la version de distribution : elle appelle la cible clean, puis supprime aussi les fichiers de secours (*~) et l'exécutable.

make distclean ?

Oui, je pense que je vais me rabattre là -dessus, parce que la solution consistant à  faire une cible spéciale pour la version de déboguage, quel que soit le sens dans lequel on la prend, pose problème.

tu as pensé à  générer des ".o de débuggage", ce qui permettrait d'accélérer la compilation des deux, genre:

SOURCES=machin.c...
OBJS=$(SOURCES:.c=.o)
DBGOBJS=$(SOURCES:.c=.od)

...

release: $(NAME)

debug: $(DBGNAME)

...

$(NAME): $(OBJS)
    $(LD) -o $(NAME) $(OBJS) $(LDFLAGS)


$(DBGNAME): $(DBGOBJS)
    $(LD) -o $(DBGNAME) $(DBGOBJS) $(DBGLDFLAGS)

...
%.o: %.cpp
    @$(CC) -o $@ -c $< $(CFLAGS)
    @mv $@ $(OBJ_DIR)

...
%.od: %.cpp
    @$(CC) -o $@ -c $< $(DBGCFLAGS)
    @mv $@ $(OBJ_DIR)

make, il y a peut-être des possibilités pour étendre la portabilité, au moins sur plateforme POSIX, que je ne connais pas.

la doc des dev autotools est une bonne référence, mais c'est drastique, du genre: "ne pas utiliser les pipes dans les scripts shell inclus (pas portable, genre sur ultrix)". De plus POSIX n'est pas une garantie, toutes les plateformes ne l'implémentent pas complètement, et linux par exemple utilise les normes de l'opengroup qui sont "dérivées" (les originales sont payantes). Perso, je suis d'avis de ne pas trop se prendre la tête sur ce genre de trucs si d'autres l'ont déjà  fait ...

sinon, juste un truc au niveau protabilité des makefiles: je crois que la syntaxe

.c.o

est plus portable que

%.o : %.c

enfin, as-tu essayé makedepend pour tes dépendances ? tu vas dans ton rep et tu fais

makedepend

ou (je préfère)

makedepend -Y.

c'est bien pratique (et tu peux le mettre dans une cible)

à€ bientà´t.

de même wink

Dernière modification par Aurel34 (Le 20/02/2008, à 11:46)

Hors ligne

#6 Le 20/02/2008, à 12:08

Le Farfadet Spatial

Re : Makefile avancé

Salut à  tous !

trucutu a écrit :

Au vu de ta démarche, et puisque tu à  l'air de gérer l'écriture à  la main des Makefile, pourquoi tu ne t'attaquerais pas au Automake etc... ?

Je te cite la fin de mon premier message :

Le Farfadet Spatial a écrit :

sans pour autant en venir aux auto-tools ni CMake (ce sera l'étape suivante)

En fait, à  force d'utiliser des Makefiles générés automatiquement, en plus à  partir de modèles tout fait, je me suis rendu compte que je ne maà®trisais plus vraiment mes outils de compilation. Du coup, je reprends à  la base. J'en suis à  la première étape : me remettre à  jour sur make. Ensuite, je reviendrais aux auto-tools. Enfin, je regarderais CMake, qui a l'air vraiment prometteur, mais que je ne connais pas.

   à€ bientà´t.
                                                                                                                         Le Farfadet Spatial

Hors ligne

#7 Le 20/02/2008, à 12:11

trucutu

Re : Makefile avancé

Ah ok..
aux temps pour moi.
Bon courage dans ce retour aux sources ... smile


La chanson du dimanche - "La pêche !"
PC acheté chez Novatux : entièrement satisfait.
Faire des recherches solidaires !

Hors ligne

#8 Le 20/02/2008, à 12:20

Le Farfadet Spatial

Re : Makefile avancé

Re-salut à  tous !

Aurel34 a écrit :

c'est les appels à  "make" qui sont récursif (make s'appelle lui-même)

D'accord.

L'article essaye de montrer que comme ça chaque makefile à  une version de plus en plus locale du projet, et que ça finit par merder. Automake génère ce genre de makefiles, mais a (à  son niveau) une vision globale du projet.

Je ne doute pas de la pertinence de l'article, je le lirais de toute façon.

   Cependant, dans ce cas particulier, je garde tout de même les appels récursifs !

make distclean ?

Pourquoi pas.

tu as pensé à  générer des ".o de débuggage"

Oui, j'y ai songé, mais j'avais écarté cette idée. Cela dit, je ne me souviens plus des raisons qui m'avait poussé à  ça. Je devrais envisager cette possibilité de nouveau.

De plus POSIX n'est pas une garantie, toutes les plateformes ne l'implémentent pas complètement, et linux par exemple utilise les normes de l'opengroup qui sont "dérivées" (les originales sont payantes).

Je suis au courant de cet état de fait, mais c'est une première référence. De toute façon, sans auto-tools ou CMake, je ne vois pas comment faire quelque chose de complètement portable. Simplement, je veux faire l'effort d'en arriver à  la portabilité maximum possible.

Perso, je suis d'avis de ne pas trop se prendre la tête sur ce genre de trucs si d'autres l'ont déjà  fait ...

Je suis plutà´t d'accord avec toi, mais cela ne colle pas avec l'exercice que je me suis imposé !

sinon, juste un truc au niveau protabilité des makefiles: je crois que la syntaxe

.c.o

est plus portable que

%.o : %.c

Je crois bien que tu as raison, la deuxième syntaxe ayant été introduite par GNU Make.

as-tu essayé makedepend pour tes dépendances ?

Je ne le connaissais pas ! Donc, je vais de ce pas taper dans une console :

man makedepend

à€ bientà´t.

                                                                                                                     Le Farfadet Spatial

Hors ligne

#9 Le 29/02/2008, à 04:33

Le Farfadet Spatial

Re : Makefile avancé

Salut à tous !

   Aurel34, ta référence au sujet des Makefiles récursif est vraiment très intéressante, finalement je vais intégrer ce qui y est présenté dans ce petit projet de test.

   Cela dit, je n'en suis pas là.

   En fait, toujours grâce à Aurel34, j'ai découvert les joies de makedepend, petit outil auquel je m'adonne avec une joie non feinte : c'est vraiment génial. Cela dit, si je l'ai intégré sans problème dans des petits projets avec un unique répertoire, ça marche beaucoup moins bien dans ce projet de test. En fait, dans ma version avec ce projet, lorsque je tape :

make depend

Ça ne s'arrête jamais, je suis obligé d'interrompre avec [ctrl] + [c] et je ne comprends pas pourquoi ça se passe ainsi.

   Pour que vous puissiez m'aider, voire essayer d'exécuter ça chez vous, ci-après les différents sources de mon programme. Il nécessite que Boost et Gtkmm soient installés.

   Dans le répertoire racine :

      -- Makefile

DEBUG=yes
export CC=g++
export LD=g++
export RM=rm -rf
ifeq ($(DEBUG),yes)
	export COPTIONAL=-pedantic -ansi -Wall -g
else
	export COPTIONAL=-O3 -fomit-frame-pointer -DNDEBUG
endif
VPATH=src
OBJ_DIR=obj
HEADERS_DIR=headers
export EXEC=testgtkmm

.PHONY: all depend clean distclean

all: $(EXEC)
ifeq ($(DEBUG),yes)
	@echo "Generating debug version"
else
	@echo "Generating release version"
endif

$(EXEC):
	@(cd $(VPATH) && $(MAKE))

depend:
	@(cd $(VPATH) && $(MAKE) $@)

clean:
	@(cd $(OBJ_DIR) && $(MAKE) $@)

distclean: clean
	@(cd $(VPATH) && $(MAKE) $@)
	@(cd $(OBJ_DIR) && $(MAKE) $@)
	@(cd $(HEADERS_DIR) && $(MAKE) $@)
	@$(RM) $(EXEC)
	@$(RM) *~

Dans le répertoire src :

      -- Makefile

HEADERS_DIR=../headers
CFLAGS=$(COPTIONAL) -I$(HEADERS_DIR) `pkg-config gtkmm-2.4 --cflags`
SRC=$(wildcard *.cpp)
export OBJ=$(SRC:.cpp=.o)

.PHONY: all depend distclean

all: $(EXEC)

$(EXEC): $(OBJ)
	@(cd $(OBJ_DIR) && $(MAKE))

depend:
	@makedepend -- $(CFLAGS) -- $(SRC)

.cpp.o:
	@$(CC) -o $@ -c $< $(CFLAGS)
	@mv $@ $(OBJ_DIR)

distclean:
	@$(RM) *~
	@$(RM) *.bak

-- helloworld.cpp

#include <helloworld.hpp>
#include <iostream>

GTKExample::HelloWorld::HelloWorld (void):
  m_button ("Hello world") {  // Creates a new button with label "Hello World"
  // Sets the border width of the window.
  set_border_width(10);

  /* When the button receives the "clicked" signal, it will call the
     on_button_clicked() method defined below. */
  m_button.signal_clicked().connect(sigc::mem_fun(*this,
					       &HelloWorld::on_button_clicked));

  // This packs the button into the Window (a container).
  add(m_button);

  // The final step is to display this newly created widget.
  m_button.show();
}  // GTKExample::HelloWorld::HelloWorld (void)

GTKExample::HelloWorld::~HelloWorld (void) {}

void GTKExample::HelloWorld::on_button_clicked (void) {
  std::cout << "Hello World" << std::endl;
}  // void GTKExample::HelloWorld::on_button_clicked (void)

-- main.cpp

#include <boost/test/included/prg_exec_monitor.hpp>
#include <gtkmm/main.h>
#include <iostream>
#include <helloworld.hpp>

int cpp_main (int argc, char **argv) {
  try {
    Gtk::Main kit(argc, argv);

    GTKExample::HelloWorld helloWorld;
    // Shows the window and returns when it is closed.
    Gtk::Main::run(helloWorld);

    return 0;
  }
  catch (const std::exception &e) {
    std::cerr << "Exception caught: " << e.what() << std::endl;
    return -1;
  }
}  // int cpp_main (int, char **)

Répertoire obj :

      -- Makefile

LDFLAGS=`pkg-config gtkmm-2.4 --libs`
EXEC_DIR=..

.PHONY: all clean distclean

all: $(EXEC)

$(EXEC): $(OBJ)
	@$(LD) -o $@ $^ $(LDFLAGS)
	@mv $(EXEC) $(EXEC_DIR)

clean:
	@$(RM) *.o

distclean: clean
	@$(RM) *~

Répertoire headers :

      -- Makefile

.PHONY: distclean

distclean:
	@$(RM) *~

-- helloworld.hpp

#ifndef GTKMM_EXAMPLE_HELLOWORLD_H
#define GTKMM_EXAMPLE_HELLOWORLD_H

#include <gtkmm/button.h>
#include <gtkmm/window.h>

namespace GTKExample {
  class HelloWorld: public Gtk::Window {
  public:
    HelloWorld (void);
    virtual ~HelloWorld (void);

  protected:
    // Signal handlers:
    virtual void on_button_clicked (void);

    // Member widgets:
    Gtk::Button m_button;
  };  // class HelloWorld
}  // namespace GTKExample

#endif  // #ifndef GTKMM_EXAMPLE_HELLOWORLD_H

Voilà, je crois bien que tout y est. Merci d'avance si vous pouvez m'aider.

   À bientôt.

                                                                                                                     Le Farfadet Spatial

[EDIT] Quelques coquilles.

Dernière modification par Le Farfadet Spatial (Le 29/02/2008, à 15:02)

Hors ligne

#10 Le 02/03/2008, à 10:55

Aurel34

Re : Makefile avancé

re smile

pour makedepend, il fait une récursion parmis tous les headers et ça peut être long. Je n'ai pas encore testé ton truc (un peu la flemme là) mais as-ty testé

makedepend -Y

si je me rappelle bien, la commande "-Y" lui indique de ne chercher les dépendances qu'à partir du rep courant (pas deans /usr/include), ce qui est en général suffisant.
sinon tu as bien mis les include guards donc il n'y a pas de raison que ça boucle bêtement...

Hors ligne

#11 Le 02/03/2008, à 18:49

Le Farfadet Spatial

Re : Makefile avancé

Salut à tous !

Aurel34 a écrit :

pour makedepend, il fait une récursion parmis tous les headers et ça peut être long. Je n'ai pas encore testé ton truc (un peu la flemme là) mais as-ty testé

makedepend -Y

si je me rappelle bien, la commande "-Y" lui indique de ne chercher les dépendances qu'à partir du rep courant (pas deans /usr/include), ce qui est en général suffisant.

D'après la page man, -Y est l'option qui permet d'indiquer le répertoire où se trouve la bibliothèque standard. Si on met simplement -Y, du coup, il n'y a pas de répertoire et makedepend ne cherche donc pas cette bibliothèque.

   J'ai essayé avec cette option et ça n'a rien changé...

   Merci d'avoir au moins jeté un œil.

   À bientôt.

                                                                                                                                                Le Farfadet Spatial

Hors ligne

#12 Le 06/03/2008, à 18:26

Le Farfadet Spatial

Re : Makefile avancé

Salut à tous !

   Je fais remonter un peu ce sujet au cas où. Si quelqu'un a une idée, son aide ne sera pas de refus.

   À bientôt.

                                                                                       Le Farfadet Spatial

Hors ligne

#13 Le 06/09/2012, à 18:40

Alopekos

Re : Makefile avancé

Bonjour !

Après 4 ans, je ne relance pas le sujet je souhaite le compléter car j'ai eu des problèmes aussi d'utilisation de makedepend.

Cet outils est puissant certe, mais il n'est pas toujours disponible (sous d'autres systèmes d'exploitations notamment). De plus, il pose problème lors de l'utilisation de makefile récursif (je sais le topic précise bien que ce n'est pas bien, mais dans certains cas c'est beaucoup plus aisé (la résursivité se limite à 1 niveau de profondeur seulement)) Ainsi il existe un outil similaire (pour ne pas dire exactement pareil) fourni par le compilateur gcc lui même : il s'agit de l'option -M. Par exemple :

gcc -M src/* -I includes/ >> depends

Génère une liste de toutes les dépendances des fichier src/*, recherchée dans le dossier includes, redirigée dans le fichier depends. De cette façon il n'y a plus qu'à l'inclure dans le fichier makefile à l'aide de la commande "include nom_de_fichier"  (précédez la commande d'un tiret "-" pour que la commande ne renvoie pas d'erreur si le fichier n'existe pas)

-include depends

A bientôt chers internautes
Alopekos

Dernière modification par Alopekos (Le 06/09/2012, à 18:43)

Hors ligne