AnglaisFrançaisEspagnol

Ad


Icône de favori OnWorks

makepp_cookbook - En ligne dans le Cloud

Exécutez makepp_cookbook dans le fournisseur d'hébergement gratuit OnWorks sur Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS

Il s'agit de la commande makepp_cookbook qui peut être exécutée dans le fournisseur d'hébergement gratuit OnWorks en utilisant l'un de nos multiples postes de travail en ligne gratuits tels que Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS

PROGRAMME:

Nom


makepp_cookbook -- La meilleure façon de configurer des makefiles pour diverses situations

DESCRIPTION


J'ai découvert que pratiquement personne ne lisait jamais le manuel d'un outil de fabrication, car franchement
personne ne s'intéresse vraiment au processus de fabrication lui-même - nous ne sommes intéressés que par les résultats.
Ce livre de cuisine a donc été conçu dans l'espoir que les gens puissent obtenir ce dont ils ont besoin
rapidement à partir des exemples sans parcourir le manuel. Cela montre comment taper
questions, tandis que les instructions d'installation et les pierres d'achoppement se trouvent dans le
Questions fréquemment posées.

Développement bibliothèques
Do you vraiment need a bibliothèque?

J'ai vu un certain nombre de grands programmes qui consistent en un grand nombre de modules, chacun de
qui vit dans son propre répertoire. Généralement, chaque répertoire est placé dans sa propre bibliothèque,
puis le programme final est lié à toutes les bibliothèques.

Dans de nombreux cas, je pense que plutôt que d'utiliser une bibliothèque, il existe une meilleure approche. Bibliothèques
ne sont pas vraiment la bonne solution si chaque module ne peut pas ou ne sera pas réutilisé dans un autre
programme, car vous obtenez alors tous les inconvénients des bibliothèques et aucun des
avantages. Les bibliothèques sont utiles dans les cas suivants :

1. Lorsque vous avez un tas de sous-programmes qui doivent être liés à plusieurs
programmes, et aucun programme n'utilise en fait 100% des sous-programmes--chaque programme utilise un
sous-ensemble différent. Dans ce cas, c'est probablement une bonne idée d'utiliser une bibliothèque statique (un
.a fichier ou un fichier d'archive).

2. Lorsque vous avez un module qui doit être lié à plusieurs programmes différents, et que vous
voulez le charger dynamiquement afin que chaque programme n'ait pas besoin d'avoir une copie distincte de
la bibliothèque. Les bibliothèques dynamiques peuvent économiser de l'espace sur les fichiers exécutables et parfois améliorer
performances du système car il n'y a qu'une seule copie de la bibliothèque chargée pour tous les
différents programmes qui l'utilisent.

3. Lorsque votre temps de liaison est excessivement long, utilisez des bibliothèques partagées pour de gros morceaux de
le programme peut considérablement accélérer le lien.

L'utilisation de bibliothèques statiques a un inconvénient majeur : sur certains systèmes (par exemple Linux), l'ordre
dans lequel vous liez les bibliothèques est d'une importance cruciale. L'éditeur de liens traite les bibliothèques
dans l'ordre spécifié sur sa ligne de commande. Il attrape tout ce dont il pense avoir besoin de
chaque bibliothèque, puis passe à la bibliothèque suivante. Si une bibliothèque ultérieure fait référence à un
symbole qui n'a pas encore été incorporé à partir d'une bibliothèque précédente, l'éditeur de liens ne
savoir revenir en arrière et le récupérer dans la bibliothèque précédente. En conséquence, il peut être nécessaire
pour répertorier la bibliothèque plusieurs fois sur la ligne de commande de l'éditeur de liens. (j'ai travaillé sur un projet
où nous avons dû répéter trois fois la liste complète des bibliothèques. Ce projet est ce qui a fait
Je préfère l'approche alternative suggérée ci-dessous, celle de la liaison incrémentale.)

L'utilisation de bibliothèques dynamiques présente plusieurs inconvénients. Premièrement, votre programme peut être légèrement
plus lent à démarrer si la bibliothèque n'est pas déjà utilisée par un autre programme, car
il doit être trouvé et chargé. Deuxièmement, il peut être très compliqué d'obtenir toute la dynamique
bibliothèques installées aux bons emplacements ; vous ne pouvez pas simplement copier l'exécutable du programme,
vous devez également vous assurer que vous copiez toutes ses bibliothèques. Troisièmement, sur certains systèmes, il
est difficile de déboguer le code à l'intérieur des bibliothèques partagées car les débogueurs ne prennent pas en charge
eux bien.

Si votre module ne sera jamais utilisé dans un autre programme, alors il y a peu de raisons d'utiliser
une bibliothèque : vous obtenez tous les inconvénients de l'utilisation des bibliothèques et aucun des avantages.
La technique que je préfère consiste à utiliser la liaison incrémentielle, lorsqu'elle est disponible.

Voici comment procéder sous Linux :

mon_module.o : $(filter_out mon_module.o, $(wildcard *.o))
ld -r -o $(sortie) $(entrées)

Ce que cela va faire, c'est créer un autre .o fichier appelé mon_module.o, qui consistera en
tous les .o fichiers dans ce sous-répertoire. L'éditeur de liens résoudra autant de
références que possible, et laissera les références restantes à résoudre dans un
étape ultérieure de la liaison. Au niveau supérieur, lorsque vous construisez enfin votre programme,
au lieu de lier avec libmy_module.a or libmy_module.so, vous feriez simplement un lien avec
mon_module.o. Lorsque vous liez .o fichiers, vous n'avez pas de problèmes de dépendance à l'ordre dans le
ligne de commande de l'éditeur de liens.

Location makepp comprendre ande qui bibliothèque modules nécessaire

Même si vous avez une vraie bibliothèque, où un programme donné n'a besoin que de quelques fichiers
(plutôt que chaque module), makepp pourrait être en mesure de déterminer quels modules sont
nécessaires à partir de la bibliothèque et inclure uniquement ceux dans le build. Cela peut sauver la compilation
temps si vous développez la bibliothèque avec un programme, parce que vous ne vous souciez pas de
compiler des modules de bibliothèque qui ne sont pas nécessaires pour le programme particulier sur lequel vous travaillez.

Si votre bibliothèque respecte strictement la convention selon laquelle toutes les fonctions ou classes déclarées dans
un fichier xyz.h sont complètement implémentés dans un fichier source qui se compile en xyz.o (c'est-à-dire vous
ne divisez pas la mise en œuvre en xyz1.o ainsi que xyz2.o), alors vous pouvez utiliser le
Fonction "$(infer_objects)" pour dire à makepp de ne retirer que les modules pertinents du
une bibliothèque. Cela peut fonctionner étonnamment bien pour les bibliothèques contenant même des dizaines de fichiers d'inclusion.
Fondamentalement, "$(infer_objects)" examine la liste des .h fichiers qui sont inclus, et regarde
pour correspondant .o des dossiers. Si vous développez rapidement une bibliothèque et un programme
ensemble, cela peut économiser du temps de compilation, car vous ne vous embêtez jamais à compiler des modules de
la bibliothèque que le programme n'utilise pas.

Voici un exemple de la façon dont je l'utilise :

mon_programme : $(infer_objects *.o, $(LIB1)/*.o $(LIB2)/*.o)
$(CXX) $(entrées) -o $(sortie) $(SYSTEM_LIBRARIES)

La fonction "$(infer_objects )" renvoie son premier argument (après avoir fait le joker
expansion dessus), et parcourt également la liste des fichiers dans son deuxième argument, pour
fichiers dont le nom est le même que le nom de n'importe quel .h fichiers inclus par n'importe quel fichier dans son premier
argument. Si de tels fichiers sont trouvés, ils sont ajoutés à la liste.

Développement a statique bibliothèque

Si vous êtes sûr d'avoir réellement besoin d'une bibliothèque et que la liaison incrémentielle n'est pas disponible ou
n'est pas ce que vous voulez faire, il y a plusieurs façons de le faire. Voici d'abord un exemple
où tous les fichiers sont explicitement répertoriés :

LIBRARY_FILES = abcde

libmine.a : $(LIBRARY_FILES).o
&rm -f $(sortie)
$(AR) cr $(sortie) $(entrées)
ranlib $(output) # Peut ne pas être nécessaire, selon votre système d'exploitation.

Le &rm est la commande "rm" intégrée de makepp. Si vous avez l'habitude d'écrire des makefiles, vous êtes peut-être
un peu surpris par cette commande ; vous êtes peut-être habitué à quelque chose comme ça :

libmine.a : $(LIBRARY_FILES).o
$(AR) ru $@ $ ? # Non recommandé!!!!!!!
ranlib $(sortie)

où $? (également connu sous le nom de "$(changed_inputs)") est une variable automatique qui désigne tous les fichiers
qui ont changé depuis la dernière création de la bibliothèque, et $@ est à peu près le même
comme "$(sortie)".

Cette approche n'est pas recommandée pour plusieurs raisons :

· Supposons que vous supprimiez un fichier source du répertoire actuel. C'est encore dans le
bibliothèque, car vous n'avez pas reconstruit la bibliothèque à partir de zéro. En conséquence, n'importe quoi
que les liens avec cette bibliothèque seront périmés .o fichier, et cela peut gâcher votre
construit. (J'ai été une fois complètement confus par cela lorsque j'essayais de supprimer le code mort
d'un projet : j'ai continué à supprimer des fichiers et c'était toujours lié, alors j'ai pensé que le code était
morte. Cependant, lorsque quelqu'un d'autre a reconstruit le projet à partir de zéro, il n'a lié aucun
Suite! Le problème était que l'ancien .o fichiers étaient toujours dans l'archive.)

De plus, en fonction de vos options pour "ar" et de votre implémentation de "ar" (par exemple, si vous
utilisez l'option "q" au lieu de "r"), vous pouvez finir par avoir plusieurs versions du
même .o à l'intérieur de l' .a déposer. Si les différentes versions définissent des globales différentes, le
l'éditeur de liens peut essayer de tirer dans les deux. C'est probablement une mauvaise chose.

C'est pourquoi nous supprimons d'abord le fichier de bibliothèque et le créons à partir de zéro. Cette volonté
prend un peu plus de temps que la simple mise à jour des modules d'une bibliothèque, mais pas beaucoup plus longtemps ; au
un ordinateur moderne, la quantité de temps consommée par le ar le programme est minuscule comparé
à ce que le compilateur C prend dans une construction typique, donc ça ne vaut pas la peine de s'inquiéter
sur.

· L'un des moyens par lesquels makepp tente de garantir des versions correctes est qu'il
reconstruire automatiquement si la ligne de commande pour construire une cible donnée a changé. Mais
en utilisant le $? variable peut poser problème, car à chaque mise à jour de la bibliothèque,
la commande build est différente. (Vous pouvez supprimer cela en utilisant
":build_check ignore_action" ; voir makepp_build_check pour plus de détails.)

· Mettre à jour l'archive plutôt que de la reconstruire empêchera makepp de
placez le fichier correctement dans un cache de construction (voir makepp_build_cache pour plus de détails).

Parfois, vous pouvez trouver que la liste de tous les fichiers est un peu pénible, surtout si un
projet connaît un développement rapide et la liste des fichiers est en constante évolution. Ce
peut être plus facile de construire la bibliothèque en utilisant des caractères génériques, comme ceci :

libmine.a : $(only_targets *.o)
&rm $(sortie)
$(AR) cr $(sortie) $(entrées)

Cela met tous les .o fichiers du répertoire courant dans la bibliothèque. Le joker
correspond à n'importe quel .o fichier qui existe ou peut être construit, il fonctionnera donc même si les fichiers ne
existent encore.

La fonction "only_targets" est utilisée pour exclure .o fichiers qui n'ont pas de correspondance
fichiers sources plus. Supposons que vous ayez un fichier appelé xyz.c que vous mettiez dans votre
une bibliothèque. Cela signifie qu'il y a un xyz.o fichier qui traîne. Maintenant tu supprimes xyz.c
parce que c'est obsolète, mais vous oubliez de supprimer xyz.o. Sans les "only_targets"
fonction, xyz.o figurerait toujours sur la liste des .o fichiers inclus dans la bibliothèque.

Développement a Dynamic bibliothèque

Le processus de création de bibliothèques dynamiques dépend entièrement du système. je serais hautement
recommande d'utiliser libtool pour construire une bibliothèque dynamique (voir
<http://www.gnu.org/software/libtool/>), vous n'avez donc pas à trouver comment le faire sur
votre plate-forme, et pour que votre makefile continue de fonctionner même lorsque vous passez à un
système d'exploitation différent. Voir la documentation de libtool pour plus de détails. Voici un exemple de Makefile :

LIBTOOL := libtool

libflick.la : $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(entrées) -o $(sortie)

%.lo : %.c
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(INCLUDES) -c $(entrée) -o $(sortie)

Développement on plusieurs différent machines or réseaux
L'un des problèmes les plus ennuyeux avec les makefiles est qu'ils ne fonctionnent presque jamais lorsque vous
passer à une autre machine ou à un autre réseau. Si vos makefiles doivent fonctionner
toutes les machines possibles sur la planète, alors vous avez probablement besoin d'une sorte de configuration
scénario. Mais si vous ne devez travailler que sur quelques machines différentes, il existe plusieurs façons
vous pouvez aborder ce problème:

Utilisez a différent comprendre filet in TOUTE le environnements

Au début de chaque makefile, vous pouvez inclure une ligne comme celle-ci :

inclure system_defs.mk

Le fichier system_defs.mk serait normalement situé dans un endroit différent pour chaque
environnement. Si vous voulez que vos répertoires de construction soient identiques sur toutes les machines, alors mettez
system_defs.mk dans un répertoire au-dessus des répertoires de construction, ou bien fournir un chemin d'inclusion
à makepp en utilisant l'option de ligne de commande "-I".

C'est généralement assez pénible à faire, mais cela fonctionne bien s'il y a un grand nombre de
différences.

Utilisez if déclarations

C'est la façon la plus laide de le faire, mais cela fonctionnera généralement.

ifsys i386
CC := gcc
sinon ifsys sun4u
cc := cc
sinon ifsys hpux11
CC = c89
fin si

Si tout ce que vous avez à faire est de trouver quelques programmes ou bibliothèques ou d'inclure des fichiers dans différents
endroits, il peut y avoir de meilleurs moyens (voir ci-dessous).

trouver_programme, premier_disponible, trouver un fichier

Ces fonctions peuvent rechercher différents répertoires dans votre système pour trouver le
fichiers appropriés. Ce n'est pas aussi puissant qu'un script de configuration, bien sûr, mais je le trouve
utile. Par exemple, je fais ce qui suit :

CXX ;= $(trouver_programme g++ c++ pg++ cxx CC aCC)
# Choisissez le premier compilateur C++ disponible dans PATH.
# (Par ailleurs, si vous ne définissez pas du tout CXX, cela
# est la façon dont il est défini.)
TCL_INCLUDE ;= -I$(dir_noslash $(findfile tcl.h, \
/usr/local/stow/tcl-8.4.5-nothread/include\
/usr/include/tcl8.4 /usr/include/tcl\
/net/na1/tcl8.4a3/include /net/na1/tcl8.4a3/include))
# $(findfile ) recherche tcl.h dans chacun des
# répertoires et renvoie le chemin complet. C'est alors
# converti en une option de compilation en supprimant le
# nom de fichier (en quittant le répertoire) et préfixé par -I.
%.o : %.cpp
$(CXX) $(CXXFLAGS) $(TCL_INCLUDE) $(entrée) -o $(sortie)

TCL_LIB ;= $((premier_disponible
/usr/local/stow/tcl-8.4.5-nothread/lib/libtcl8.4.so
/usr/lib/libtcl8.4.so /usr/lib/libtcl.so
/net/na1/tcl8.4a3/lib/libtcl8.4.a
/net/na1/tcl8.4a3/lib/libtcl8.4.sl))
# Trouvez où se trouve la bibliothèque Tcl. C'est alors explicitement
# répertorié sur la commande de lien :
mon_programme : *.o
$(CXX) $(CXXFLAGS) $(entrées) -o $(sortie) $(TCL_LIB)

Prenez avantage of De Perl config d'information

Les techniques ci-dessus peuvent ne pas être suffisantes si vous avez besoin d'informations supplémentaires sur
votre système, par exemple si un long double existe ou quel est l'ordre des octets. Cependant,
perl a déjà calculé ces choses, vous pouvez donc simplement utiliser ses réponses.

Le script de configuration automatique de Perl rend toutes ses informations de configuration disponibles via
le hachage %Config. Il n'y a pas de syntaxe pour accéder à un hachage Perl directement dans makepp, mais vous pouvez
entrez dans Perl et définissez des variables scalaires, qui sont directement accessibles depuis makepp :

perl_begin
# Récupérer les valeurs du hachage de configuration.
utilisez la configuration ;
$CC = $Config{'cc'} ; # compilateur C utilisé par perl ;
$byteorder_flags = "-DBYTEORDER=$Config{'byteorder'}" ;
$longdouble_defined = $Config{'d_longdbl'} eq 'define' ;
$CFLAGS_for_shared_libs = $Config{'cccdlflags'} ;
$LDFLAGS_for_shared_libs = $Config{'ccdlflags'} ;
perl_end

De plus, une fois que vous avez terminé 'use Config', vous pouvez utiliser l'instruction "$(perl )", comme
ce:

SHARED_LIB_EXTENSION := $(perl $Config{'dlext'})

Tapez "perldoc Config" pour voir quelles informations sont disponibles via le hachage %Config.

La configuration de Perl est un bon endroit pour obtenir des informations sur les types entiers, les octets
commande, et d'autres choses qui nécessitent généralement un script de configuration séparé pour localiser. Une partie de
ses informations relatives à la présence d'éléments dans le système de fichiers peuvent ne pas être
valide. Par exemple, $Config{'cc'} fait référence au compilateur C avec lequel perl a été construit,
qui n'est peut-être pas le même compilateur C que vous souhaitez utiliser. En fait, il n'existe peut-être même pas
sur votre système, puisque vous avez probablement installé Perl via un paquet binaire.

Pourboires en en utilisant caractères génériques
Des TOUTE fichiers sauf a certaines sous-ensemble

Les jokers de Makepp n'ont actuellement aucun moyen de faire correspondre tous les fichiers sauf un certain
défini, mais vous pouvez le faire avec une combinaison de fonctions.

Par exemple, supposons que vous ayez un programme de test pour chaque module d'une bibliothèque, mais que vous ne
voulez inclure les programmes de test dans la bibliothèque. Si tous les programmes de test commencent par
tester, alors vous pouvez les exclure comme ceci :

libproduction.a : $(filter_out test*, $(wildcard *.o))

Les fonctions "$(filter )" et "$(filter_out )" sont un ensemble de filtres très puissant à faire
toutes sortes d'opérations d'intersection et de différence définies. Par exemple,

SUBDIRS ;= $(filter_out *test* *$(ARCH)*, $(shell find . -type d -print))
# Renvoie tous les sous-répertoires qui n'ont pas
# "test" ou $(ARCH) en eux.

$(filter $(patsubst test_dir/test_%.o, %.o, $(wildcard test_dir/*.o)), \
$(caractère générique *.o))
# Renvoie une liste des fichiers .o dans le
# répertoire pour lequel il existe un correspondant
# fichier test_*.o dans le sous-répertoire test_dir.
$(filter_out $(patsubst man/man3/%.3, %.o, $(wildcard man/man3/*.3)), \
$(caractère générique *.o))
# Renvoie une liste des fichiers .o dans le
# répertoire pour lequel il n'y a pas de page de manuel
# avec le même nom de fichier dans le sous-répertoire man/man3.

En utilisant le "$(seulement_cibles )" fonction à éliminé rassis .o fichiers

Supposons que vous construisez un programme ou une bibliothèque avec une commande de construction comme celle-ci :

programme : *.o
$(CC) $(entrées) -o $(sortie)

Supposons que vous supprimiez maintenant un fichier source. Si vous oubliez de supprimer le .o fichier,
il sera toujours lié même s'il n'y a plus moyen de le construire. Dans le
à l'avenir, makepp reconnaîtra probablement cette situation automatiquement et l'exclura de
la liste des caractères génériques, mais à l'heure actuelle, vous devez lui dire de l'exclure manuellement :

programme : $(only_targets *.o)
$(CC) $(entrées) -o $(sorties)

Makepp ne connaît aucun moyen de construire le rassis .o fichier plus puisque son fichier source est
disparu, donc la fonction "$(only_targets )" l'exclura de la liste des dépendances.

Pourboires en plusieurs répertoires
L'une des principales raisons d'écrire makepp était de simplifier la gestion de plusieurs
répertoires. Makepp est capable de combiner des commandes de construction à partir de plusieurs makefiles, il peut donc
traiter correctement une règle dans un makefile qui dépend d'un fichier qui est construit par un
makefile différent.

Quoi à do in endroit of récursif faire

Makepp prend en charge le make récursif pour la compatibilité descendante, mais il est fortement recommandé
que vous ne sauraient utilise le. Si vous ne savez pas ce que c'est, tant mieux.

Voir "Un meilleur système pour les builds hiérarchiques" dans makepp pour plus de détails sur les raisons pour lesquelles vous ne voulez pas
utilisez make récursif, ou bien recherchez sur le Web « make récursif considéré comme nuisible ».

Au lieu de faire un make récursif pour faire la cible "all" dans chaque makefile, il est
généralement plus facile de laisser makepp déterminer quelles cibles devront réellement être construites.
De plus, si vous mettez tous vos .o et les fichiers de bibliothèque dans le même répertoire que le
makefiles, makepp déterminera automatiquement quels makefiles sont également nécessaires - le
la seule chose dont vous avez besoin est d'avoir votre liste de création de niveau supérieur les fichiers nécessaires
pour la dernière étape de liaison. Voir les exemples ci-dessous.

UN makefile en chacun annuaire: avec implicitement chargement

La façon la plus courante de gérer plusieurs répertoires est de mettre un makefile dans chaque répertoire
qui décrit comment tout construire dans ou à partir de ce répertoire. Si tu mets .o les fichiers
même répertoire que les fichiers sources, puis chargement implicite (voir "Chargement implicite" dans
makepp_build_algorithm) trouvera automatiquement tous les makefiles. Si vous mettez votre .o
fichiers dans un répertoire différent (par exemple, dans un sous-répertoire dépendant de l'architecture), alors vous
devra probablement charger tous les makefiles pertinents en utilisant l'instruction "load_makefile".

Voici un exemple de makefile de niveau supérieur pour une hiérarchie de répertoires qui utilise le chargement implicite
pour construire un programme qui se compose de nombreuses bibliothèques partagées (mais voir "Avez-vous vraiment besoin d'un
bibliothèque ?" dans makepp_cookbook, car créer un programme à partir d'un ensemble de bibliothèques partagées
n'est pas forcément une bonne idée):

# Makefile de niveau supérieur :
programme : main.o **/*.la # Lien dans les bibliothèques partagées de tous les sous-répertoires.
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(entrées) -o $(sortie) $(LIBS)

C'est à peu près tout ce dont vous avez besoin dans le makefile de haut niveau. Dans chaque sous-répertoire, vous
ferait probablement quelque chose comme ça :

# Makefile dans chaque sous-répertoire :
inclure standard_defs.mk # Recherches ., .., ../ .., etc. jusqu'à ce qu'il
# trouve le fichier d'inclusion indiqué.
# remplacer certaines définitions de variables ici
SPECIAL_FLAGS := -do_something_différent

Chaque makefile peut probablement être à peu près le même si les commandes pour construire les cibles
sont assez similaires.

Enfin, vous mettriez ce qui suit dans le standard_defs.mk fichier (qui devrait probablement
être situé dans le répertoire de niveau supérieur) :

# Paramètres de variables communs et règles de construction pour tous les répertoires.
CLAPET := -g -O2
INCLUDE_DIR := $(find_upwards inclut)
# Recherches ., .., ../ .., etc. pour un fichier ou
# répertoire appelé includes, donc si vous mettez
# tous vos fichiers d'inclusion là-dedans, cela
# les trouver.
INCLUT := -I$(INCLUDE_DIR)

%.lo : %.c
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(INCLUDES) -c $(entrée) -o $(sortie)

lib$(relative_to ., ..).la : $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(sortie) $(entrées)
# $(relative_to ., ..) renvoie le nom du
# sous-répertoire relatif au niveau supérieur
# sous-répertoire. Donc, si ce makefile est xyz/Makefile,
# cette règle construira xyz/libxyz.la.

# Publiez les fichiers d'inclusion publics dans le répertoire d'inclusion de niveau supérieur :
$(INCLUDE_DIR)/public_%.h : public_%.h
:build_check symbolenk
&ln -fr $(entrée) $(sortie)

UN makefile en chacun annuaire: explicite chargement

Si vous voulez mettre tous vos .o fichiers dans un sous-répertoire dépendant de l'architecture, puis
l'exemple ci-dessus doit être modifié pour ressembler à ceci :

# Makefile de niveau supérieur :
MAKEFILES := $(wildcard **/Makeppfile) # Liste de tous les sous-répertoires à
# récupère les makefiles à partir de.

load_makefile $(MAKEFILES) # Chargez-les tous.

include standard_defs.mk # Obtenir la commande de compilation pour main.o.

programme : $(ARCH)/main.o */**/$(ARCH)/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(entrées) -o $(sortie) $(LIBS)
# */**/$(ARCH) exclut le sous-répertoire
# $(ARCH), où nous ne voulons pas construire
# une bibliothèque partagée.

Chaque makefile serait exactement le même qu'avant :

# Makefile dans chaque sous-répertoire :
inclure standard_defs.mk
# ... remplacements de variables ici

Et enfin, standard_defs.mk contiendrait quelque chose comme ce qui suit :

# Paramètres de variables communs et règles de construction pour tous les répertoires.
ARCH ;= $(shell uname -s)-$(shell uname -m)-$(shell uname -r)
# Parfois, les gens n'utilisent que $(shell uname -m), mais
# ce sera la même chose pour FreeBSD et Linux sur
# un x86. Le -r n'est pas vraiment utile sous Linux,
# mais est important pour les autres OS : binaires pour
# SunOS 5.8 ne fonctionnera généralement pas sur SunOS 5.7.
&mkdir -p $(ARCH) # Assurez-vous que le répertoire de sortie existe.
CLAPET := -g -O2
INCLUDE_DIR := $(find_upwards inclut)
# Recherches ., .., ../ .., etc. pour un fichier ou
# répertoire appelé includes, donc si vous mettez
# tous vos fichiers d'inclusion là-dedans, cela
# les trouver.
INCLUT := -I$(INCLUDE_DIR)

$(ARCH)/%.lo : %.c
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(INCLUDES) -c $(entrée) -o $(sortie)

$(ARCH)/ lib$(relative_to ., ..).la : $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(sortie) $(entrées)
# $(relative_to ., ..) renvoie le nom du
# sous-répertoire relatif au niveau supérieur
# sous-répertoire. Donc, si ce makefile est xyz/Makefile,
# cette règle construira xyz/$(ARCH)/libxyz.la.

# Copiez les fichiers d'inclusion publics dans le répertoire d'inclusion de niveau supérieur :
$(INCLUDE_DIR)/public_%.h : public_%.h
&cp $(entrée) $(sortie)

automatiquement Rendre le le fichiers make

Si vos makefiles sont tous extrêmement similaires (comme dans l'exemple ci-dessus), vous pouvez dire à Makepp
pour les construire automatiquement s'ils n'existent pas. Ajoutez simplement ce qui suit à votre niveau supérieur
makefile :

SUBDIRS := $(filter_out rép_désiré1 rép_désiré2, $(caractère générique */**))
$(foreach)/Makeppfile : : foreach $(SUBDIRS)
&echo "include standard_defs.mk" -o $(sortie)
&echo "_include additional_defs.mk" -o >>$(sortie)
# Si le fichier additional_defs.mk existe, alors
# il sera inclus, mais s'il n'existe pas,
# l'instruction _include sera ignorée.

Maintenant, les makefiles eux-mêmes seront automatiquement construits.

UN makefile uniquement at le top niveau

Si tous vos makefiles sont identiques, vous pouvez demander : pourquoi devrais-je avoir un makefile à chaque
niveau? Pourquoi ne pas mettre tout cela dans le makefile de niveau supérieur ?

Oui, cela peut être fait. Le principal inconvénient est qu'il devient plus difficile de spécifier
différentes options de construction pour chaque sous-répertoire. Un deuxième inconvénient est que votre
makefile deviendra probablement un peu plus difficile à lire.

Voici un exemple pour faire exactement cela :

# Makefile de niveau supérieur pour la hiérarchie des répertoires. Construit le programme
# sur un ensemble de bibliothèques partagées à titre d'exemple. (Voir les mises en garde ci-dessus
# pour savoir pourquoi vous pourriez vouloir utiliser la liaison incrémentielle ou autre
# approche plutôt que des bibliothèques partagées.)
makepp_percent_subdirs := 1 # Autoriser % à correspondre à plusieurs répertoires.
SUBDIRS := $(filter_out *CVS* other-unwanted_dirs $(wildcard **))
CLAPET := -g -O2
INCLUT := -Iinclus

%.lo : %c
$(LIBTOOL) --mode=compile $(CC) $(INCLUDES) $(CFLAGS) -c $(entrée) -o $(sortie)

$(pour chaque)/ lib$(notdir $(foreach)).la : $(foreach)/*.lo : foreach $(SUBDIRS)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(sortie) $(entrées)
# Règle pour faire toutes les bibliothèques.

programme : main.o **/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(sortie) $(entrées)

include/$(notdir $(foreach)) : $(foreach) : foreach **/public_*.h
&cp $(entrée) $(sortie)
# Exemple de règle pour copier le publiquement
# fichiers .h accessibles au bon endroit.

A espace extérieur plus propre, l'objectif

Les makefiles traditionnels contiennent une cible propre, ce qui permet de supprimer tout ce qui était
construit. Il y a trois raisons pour lesquelles vous ne devriez pas faire cela avec makepp :

1. Makepp fait de grands efforts pour assurer une construction correcte. Alors le désespéré "Je ne
savoir ce qui ne va pas", vous donner envie de repartir de zéro appartient au passé.

2. Les gens essaieront parfois de gagner du temps en faisant deux choses contradictoires à la fois :
"faire tout nettoyer". Cela peut perturber le système de caractères génériques intelligents de makepp, car il
obtenez d'abord les faits avant de faire quoi que ce soit. Vient ensuite l'action propre, qui ne
ne pas dire à makepp ce qu'il fait (en fait, il ne peut pas, car il défait quelque chose - le
contrairement à ce à quoi sert un outil de build). Vient ensuite "tout", mais les fichiers à jour,
qui où là, sont mystérieusement partis.

3. Il y a la commande "makeppclean", qui fait la même chose, et plus efficacement.

Néanmoins, nous retenons cette section historique, car elle vous dit quelque chose sur le
façon dont makepp fonctionne : une cible bidon appelée « clean » n'est qu'un nom pour un ensemble de commandes à
supprimez tous les fichiers résultant du processus de création. Habituellement, une cible propre a l'air
quelque chose comme ça:

$(faux nettoyage) :
&rm -fm $(caractère générique *.o .makepp_log)
# -m et .makepp_log se débarrassent de tous les fichiers indésirables de makepp.

Au lieu de lister explicitement les fichiers que vous souhaitez supprimer, vous pouvez également demander à makepp de
supprimer tout ce qu'il sait construire, comme ceci :

$(faux nettoyage) :
&rm -fm .makepp_log $(seulement_cibles *)

Cela a l'avantage que si l'un de vos fichiers source peut être construit à partir d'autres fichiers,
ils seront également supprimés ; d'autre part, rassis .o fichiers (fichiers qui étaient
constructible mais dont le fichier source a depuis été supprimé) ne sera pas supprimé.

Si vous avez une construction qui implique des makefiles dans plusieurs répertoires différents, votre top-
makefile de niveau peut référencer la cible "propre" (ou toute autre cible bidon) dans un autre
makefile :

# Makefile de haut niveau
SUBDIRS := sous1 sous2

# règles de construction ici

# Nettoyer après la compilation :
$(phony clean) : $(SUBDIRS)/clean
&rm -fm .makepp_log $(seulement_cibles *)

Alternativement, vous pouvez mettre votre cible "propre" uniquement dans le makefile de niveau supérieur, et l'avoir
traiter tous les répertoires, comme ceci :

$(faux nettoyage) :
&rm -fm $(seulement_cibles **/*)

En utilisant Qt's moqueur préprocesseur
Cet exemple montre un makefile pour un utilitaire qui utilise la bibliothèque Qt GUI de Nokia (voir
<http://qt.nokia.com>). La seule chose un peu inhabituelle à ce sujet est que vous
doit exécuter un préprocesseur appelé "moc" sur la plupart des fichiers ".h" qui contiennent des définitions de widgets,
mais vous ne voulez pas exécuter "moc" sur des fichiers ".h" qui n'utilisent pas la macro "Q_OBJECT".

automatiquement détermination qui fichiers need moqueur fichiers

Vous pouvez, bien sûr, simplement lister tous les fichiers ".h" qui doivent exécuter "moc" dessus.
Cependant, si vous développez rapidement de nouveaux widgets, il peut être ennuyeux de
continuez à mettre à jour la liste dans le makefile. Vous pouvez contourner le besoin de lister le moc
modules explicitement avec quelque chose comme ceci :

MOC := $(QTDIR)/bin/moc
MODULES := quels que soient les modules que vous avez dans votre programme
MOC_MODULES := $(patsubst %.h, moc_%, $(&grep -l /Q_OBJECT/ *.h))
# Analyse tous les fichiers .h pour la macro Q_OBJECT.

mon_programme : $(MODULES).o $(MOC_MODULES).o
$(CXX) $(entrées) -o $(sortie)

moc_%.cxx: %.h # Crée les fichiers moc à partir des fichiers .h.
$(MOC) $(entrée) -o $(sortie)

%.o : %cxx
$(CXX) $(CXXFLAGS) -c $(entrée) -o $(sortie)

Cette approche analyse chacun de vos .h fichiers chaque fois que makepp est exécuté, à la recherche du
Macro "Q_OBJECT". Cela semble cher, mais cela ne prendra probablement pas longtemps du tout. (Les .h
les fichiers devront tous être chargés à partir du disque de toute façon par le processus de compilation, ils seront donc
être mis en cache.)

#comprendre le .moc filet

Une autre approche consiste à "#inclure" la sortie du préprocesseur "moc" dans votre widget
dossier de mise en oeuvre. Cela signifie que vous devez vous rappeler d'écrire le "#include", mais il a
l'avantage qu'il y a moins de modules à compiler, et donc la compilation va plus vite.
(Pour la plupart des compilations C++, la majorité du temps est consacrée à la lecture des fichiers d'en-tête, et
la sortie du préprocesseur doit inclure presque autant de fichiers que votre widget
de toute façon.) Par exemple :

// mon_widget.h
classe MonWidget : QWidget public {
Q_OBJET
/ / ...
}

// mon_widget.cpp

#include "mon_widget.h"
#include "my_widget.moc" // my_widget.moc est la sortie du
// préprocesseur moc.
// Autres choses d'implémentation ici.
MyWidget::MyWidget(QWidget * parent, const char * nom) :
QWidget(parent, nom)
{
/ / ...
}

Maintenant, vous devez avoir une règle dans votre makefile pour créer tous les fichiers ".moc", comme ceci :

MOC := $(QTDIR)/bin/moc
# Règle pour créer des fichiers .moc :
%.moc : %h
$(MOC) $(entrée) -o $(sortie)

Makepp est assez intelligent pour se rendre compte qu'il doit créer "my_widget.moc" s'il ne le fait pas
existent déjà ou s'ils sont obsolètes.

Cette seconde approche est celle que j'utilise habituellement car elle accélère la compilation.

Remplaçants en obsolète faire expressions idiomatiques
MAKECMD OBJECTIFS

Parfois, les gens ont des règles dans leur makefile qui dépendent de la cible qu'ils construisent,
en utilisant la variable spéciale "MAKECMDGOALS". Par exemple, on voit parfois des choses comme
ce:

ifneq ($(production de filtres, $(MAKECMDGOALS)),)
CLAPET := -O2
d'autre
CLAGS := -g
fin si

Cela fonctionnera très bien avec makepp. Cependant, je recommande de ne pas utiliser "MAKECMDGOALS" pour de tels
cas (tout comme le manuel GNU make). Vous feriez mieux de mettre votre optimisé et
débogage-compilé .o fichiers dans des répertoires séparés, ou en leur donnant des préfixes ou
suffixes, ou en utilisant des référentiels, pour les garder séparés.

Probablement le seul moment où vous voudrez peut-être faire référence à "MAKECMDGOALS" est s'il
prend beaucoup de temps pour charger vos makefiles, et vous n'en avez pas besoin pour votre cible "propre"
(mais vous n'avez pas besoin d'une cible propre). Par exemple,

ifneq ($(MAKECMDGOALS),propre)
load_makefile $(caractère générique **/Makeppfile)
d'autre
no_implicit_load . # Empêche le chargement automatique de tout autre makefile.
fin si

$(faux nettoyage) :
&rm -f $(caractère générique **/*.o)

Récursif faire à construire in différent répertoires

Voir "Conseils pour plusieurs répertoires" dans makepp_cookbook.

Récursif faire à Change Plus-value of a variable

Certains makefiles se réinvoquent avec une valeur différente d'une variable, par exemple, le debug
cible dans le fragment de makefile suivant

.PHONY : tout débogage

optimisé :
Programme $(MAKE) CFLAGS=-O2

déboguer:
$(MAKE) programme CFLAGS=-g

programme : ao bo
$(CC) $(CFLAGS) $^ -o $@

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

Si l'utilisateur tape "make debug", il construit le programme en mode par défaut avec le débogage activé
au lieu d'une optimisation.

Une meilleure façon de le faire est de créer deux programmes différents, avec deux ensembles différents de
fichiers objets, comme ceci :

CLAPET := -O2
DEBUG_FLAGS := -g
MODULES := ab

programme : $(MODULES).o
$(CC) $(CFLAGS) $(entrées) -o $(sortie)

debug/program : debug/$(MODULES).o
$(CC) $(DEBUG_FLAGS) $(entrées) -o $(sortie)

%.o : %.c
$(CC) $(CFLAGS) -c $(entrée) -o $(sortie)

debug/%.o : %.c
$(CC) $(DEBUG_FLAGS) -c $(entrée) -o $(sortie)

$(phony debug) : déboguer/programmer

L'avantage de procéder ainsi est que (a) vous n'avez pas besoin de tout reconstruire lorsque vous
passer du débogage à l'optimisation et vice-versa ; (b)

Ce qui précède peut être écrit de manière un peu plus concise en utilisant des référentiels. Ce qui suit
makefile est exactement équivalent :

débogage du référentiel=. # Fait ressembler le sous-répertoire debug à une copie de
# le sous-répertoire courant.
load_makefile débogage CFLAGS=-g
# Remplacer CFLAGS lorsqu'il est invoqué dans le sous-répertoire de débogage
CFLAGS := -O2 # Valeur de CFLAGS lorsqu'il est invoqué dans ce sous-répertoire

programme : ao bo
$(CC) $(CFLAGS) $^ -o $@

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

$(phony debug) : déboguer/programmer
# Si l'utilisateur tape "makepp debug", construit
# déboguer/programmer au lieu de programmer.

Divers conseils
Comment do I construire UN partie différemment juste une fois?

Makepp rend cela difficile à faire car le résultat est incohérent par rapport aux règles.
Mais il y a des situations où vous pouvez en avoir besoin, par exemple pour compiler un seul module avec
informations de débogage lourdes. Vous pouvez y parvenir en deux étapes en créant d'abord le
dépendance séparément, puis en l'excluant de la phase de liaison :

makepp DEBUG=3 buggy.o # Construisez-le avec une autre option.
makepp --dont-build=buggy.o buggy # Utilisez-le, malgré l'option de construction "incorrecte".

Comment do I faire sûr my sortie répertoires exister?

Vous pouvez spécifier une règle pour créer le répertoire de sortie, puis vous assurer que chaque fichier qui
va dans le répertoire de sortie en dépend. Mais il est généralement plus facile de faire quelque chose comme
ce:

# La voie classique
factice := $(shell test -d $(OUTPUT_DIRECTORY) || mkdir -p $(OUTPUT_DIRECTORY))
# C'est généralement plus facile que de faire dépendre tous les fichiers de
# $(OUTPUT_DIRECTORY) et avoir une règle pour le faire.
# Notez que vous devez utiliser := au lieu de = pour le forcer à
# exécuter immédiatement.
# Une approche alternative : en utilisant du code Perl, OUTPUT_DIRECTORY local var
perl_begin
-d $OUTPUT_DIRECTORY ou mkdir $OUTPUT_DIRECTORY ;
perl_end
# La manière moderne, ne fait rien pour les répertoires existants
&mkdir -p $(RÉPERTOIRE_SORTIE)

L'une de ces instructions doit se trouver en haut de votre makefile, elles sont donc exécutées
avant tout ce qui pourrait éventuellement avoir besoin du répertoire.

Comment do I forcer a commander à exécuter on chaque construire?

Le moyen le plus simple est de ne pas utiliser du tout le mécanisme de règle, mais simplement de l'exécuter, comme
ce:

dummy := $(shell date > last_build_timestamp)

Ou mettez-le dans un bloc perl, comme ceci :

perl_begin
system("commande à exécuter");
perl_end

Cette approche a l'inconvénient qu'elle sera exécutée même si une cible non liée est
étant exécuté.

Une deuxième approche consiste à déclarer le fichier comme une cible bidon, même s'il s'agit d'un vrai fichier.
Cela forcera makepp à réexécuter la commande pour la construire à chaque fois, mais seulement si elle
apparaît dans la liste des dépendances d'une règle.

Comment do I raccourcir le dans construire commandes?

Souvent, il y a tellement d'options pour les commandes de compilation que ce qui est affiché sur le
l'écran est illisible. Vous pouvez modifier ce qui est affiché en supprimant l'affichage du
commande entière, puis imprimez explicitement la partie intéressante de la commande. C'est
facile d'imprimer uniquement la partie pertinente de la commande en utilisant "$(filter_out )", comme
ce:

ALL_CFLAGS = $(CFLAGS) $(INCLUDE) $(ADDL_CXX_FLAGS) $(DEBUG_FLAGS)

%.o : %.c
@&écho $(notdir $(CC)) ... \
$(filter_out -I* $(ADDL_CXX_FLAGS), $(ALL_CFLAGS)) \
-c $(entrée)
@$(CC) $(ALL_CFLAGS) -c $(entrée) -o $(sortie)

(Le "@" devant la commande supprime l'impression de la commande.)

Cela vous permettra de voir la plupart des options intéressantes mais n'affichera pas toutes les
inclure des répertoires (dont ils sont souvent très nombreux !). Si la partie qui vous intéresse
in est contigu dans votre commande, vous pouvez également utiliser la fonction "print" (qui ajoute un
newline, donc vous n'en voulez pas plusieurs):

Cible:
@... $(imprimer la partie intéressante) ...

Comment do I convertir a filet développement dépendances ?

Pour certains formats de fichiers obscurs, il ne vaut pas la peine d'implémenter un scanner. Dans un projet
nous avons des fichiers xml, disons foobar.xml qui contient les dépendances pour foobar.out:


une
b
c


Nous avons décidé d'adhérer à cette disposition simple, nous n'avons donc pas besoin d'analyser xml. Avec le
&sed intégré, voici ce que nous faisons avec trois substitutions simples pour les trois types de
lignes:

%.d : %xml
&sed ! !$(stem).out: \\! || s! (.+) !$$1 \\! || s! !# Vide!' \
$(entrée) -o $(sortie)

inclure foobar.d

En essayant d'inclure ceci, produit d'abord "foobar.d":

foobar.out : \
une \
b\
c\
# Vide

Une ligne vide (juste un commentaire ou vraiment vide) évite d'avoir à se soucier de la fin
barre oblique inverse. Une alternative produisant une liste multiligne est :

%.d : %xml
&sed ! !$(stem).out : \$$((! || s! !)) ! || s!<.+?>!!g' \
$(entrée) -o $(sortie)

inclure foobar.d

Cela produit un équivalent :

foobar.out : $((
a
b
c
))

Si vous avez une réécriture plus complexe à faire, définissez une fonction dans le makefile ou dans un
module que vous incluez. Par exemple, indéfinir $_ sautera les lignes d'entrée :

sous monfiltre {
renvoie undef $_ si /
mon $tige = f_tige;
s! !$stem.out : \$((! || s! !)) ! || s!<.+?>!!g;
}

%.d : %xml
&sed ! !$(stem).out : \$$((! || s! !)) ! || s!<.+?>!!g' \
$(entrée) -o $(sortie)

inclure foobar.d

Utilisez makepp_cookbook en ligne en utilisant les services onworks.net


Serveurs et postes de travail gratuits

Télécharger des applications Windows et Linux

Commandes Linux

Ad