IngleseFranceseSpagnolo

Ad


Favicon di OnWorks

makepp_cookbook - Online nel cloud

Esegui makepp_cookbook nel provider di hosting gratuito OnWorks su Ubuntu Online, Fedora Online, emulatore online Windows o emulatore online MAC OS

Questo è il comando makepp_cookbook che può essere eseguito nel provider di hosting gratuito OnWorks utilizzando una delle nostre molteplici workstation online gratuite come Ubuntu Online, Fedora Online, emulatore online Windows o emulatore online MAC OS

PROGRAMMA:

NOME


makepp_cookbook -- Il modo migliore per impostare i makefile per varie situazioni

DESCRIZIONE


Ho scoperto che praticamente nessuno legge mai un manuale per uno strumento di fabbricazione, perché francamente
nessuno è realmente interessato al processo di creazione stesso: a noi interessano solo i risultati.
Quindi questo libro di cucina è stato messo insieme nella speranza che le persone siano in grado di ottenere ciò di cui hanno bisogno
rapidamente dagli esempi senza guadare il manuale. Questo mostra come digitare
domande, mentre le istruzioni di installazione e gli ostacoli si trovano nel
Domande frequenti.

Costruzione biblioteche
Do Tu veramente bisogno a biblioteca?

Ho visto una serie di grandi programmi che consistono in un gran numero di moduli, ciascuno dei quali
che vive nella propria directory. Comunemente, ogni directory viene inserita nella propria libreria,
e poi il programma finale si collega a tutte le librerie.

In molti casi, penso che piuttosto che usare una libreria, ci sia un approccio migliore. Biblioteche
non sono proprio la soluzione giusta se ogni modulo non può o non sarà riutilizzato in nessun altro
programma, perché poi ottieni tutti gli inconvenienti delle librerie e nessuno dei
vantaggi. Le biblioteche sono utili nei seguenti casi:

1. Quando hai un mucchio di subroutine che devono essere collegate con diverse
programmi e nessun programma utilizza effettivamente il 100% delle subroutine: ogni programma utilizza a
sottoinsieme diverso. In questo caso, probabilmente è una buona idea usare una libreria statica (a
.a file o un file di archivio).

2. Quando hai un modulo che dovrebbe essere collegato a diversi programmi e tu
desidera caricarlo dinamicamente in modo che ogni programma non debba avere una copia separata di
la Biblioteca. Le librerie dinamiche possono risparmiare spazio sui file eseguibili e talvolta migliorarle
prestazioni del sistema perché è stata caricata una sola copia della libreria per tutti i
diversi programmi che lo utilizzano.

3. Quando il tempo di collegamento è proibitivo, utilizzare librerie condivise per grandi pezzi di
il programma può velocizzare notevolmente il collegamento.

L'uso di librerie statiche ha uno svantaggio principale: su alcuni sistemi (es. Linux), l'ordine
in cui colleghi le librerie è di fondamentale importanza. Il linker elabora le librerie
nell'ordine specificato sulla sua riga di comando. Prende tutto ciò di cui pensa di aver bisogno
ogni libreria, quindi passa alla libreria successiva. Se qualche libreria successiva fa riferimento a a
simbolo che non è stato ancora incorporato da una libreria precedente, il linker no
sapere di tornare indietro e prenderlo dalla libreria precedente. Di conseguenza, può essere necessario
per elencare la libreria più volte sulla riga di comando del linker. (Ho lavorato su un progetto
dove abbiamo dovuto ripetere l'intero elenco delle biblioteche tre volte. Questo progetto è ciò che ha fatto
preferisco l'approccio alternativo suggerito di seguito, quello del collegamento incrementale.)

L'utilizzo di librerie dinamiche presenta diversi svantaggi. Innanzitutto, il tuo programma può essere leggermente
più lento ad avviarsi se la libreria non è già utilizzata da qualche altro programma, perché
deve essere trovato e caricato. In secondo luogo, può essere una vera seccatura ottenere tutta la dinamica
librerie installate nelle posizioni corrette; non puoi semplicemente copiare l'eseguibile del programma,
devi anche assicurarti di copiare tutte le sue librerie. Terzo, su alcuni sistemi, è
è difficile eseguire il debug del codice all'interno delle librerie condivise perché i debugger non supportano
loro bene.

Se il tuo modulo non verrà mai utilizzato in nessun altro programma, ci sono poche ragioni per usarlo
una libreria: ottieni tutti gli svantaggi dell'uso delle librerie e nessuno dei vantaggi.
La tecnica che preferisco consiste nell'utilizzare il collegamento incrementale, ove disponibile.

Ecco come puoi farlo su Linux:

mio_modulo.o : $(filter_out mio_modulo.o, $(carattere jolly *.o))
ld -r -o $(output) $(input)

Quello che farà è crearne un altro .o file chiamato mio_modulo.o, che consisterà in
tutte le .o file in questa sottodirectory. Il linker risolverà il maggior numero di
riferimenti come può, e lascerà i riferimenti rimanenti da risolvere in a
successiva fase di collegamento. Al livello più alto, quando finalmente crei il tuo programma,
invece di collegare con libmy_module.a or libmy_module.so, ti collegheresti semplicemente con
mio_modulo.o. Quando ti colleghi .o file, non hai problemi con la dipendenza dall'ordine nel
riga di comando del linker.

Letting makepp figura su quale biblioteca moduli sono di applicazione

Anche se hai una vera libreria, in cui un determinato programma ha bisogno solo di pochi file da esso
(piuttosto che ogni singolo modulo), makepp potrebbe essere in grado di capire quali sono i moduli
necessario dalla libreria e includere solo quelli nella build. Questo può salvare la compilazione
tempo se stai sviluppando la libreria insieme a un programma, perché non ti preoccupi di
compila i moduli della libreria che non sono necessari per il particolare programma su cui stai lavorando.

Se la tua libreria osserva rigorosamente la convenzione che tutte le funzioni o le classi dichiarate in
un file xyz.h sono completamente implementati in un file sorgente che viene compilato in xyz.o (cioè, tu
non suddividere l'implementazione in xyz1.o ed xyz2.o), allora puoi usare il
Funzione "$(infer_objects)" per dire a makepp di estrarre solo i moduli rilevanti dal
biblioteca. Questo può funzionare sorprendentemente bene per le librerie con anche dozzine di file di inclusione.
Fondamentalmente, "$(infer_objects)" esamina l'elenco di .h file che sono inclusi, e guarda
per corrispondente .o File. Se stai sviluppando rapidamente una libreria e un programma
insieme, questo può risparmiare tempo di compilazione, perché non ti preoccupi mai di compilare moduli di
la libreria che il programma non usa.

Ecco un esempio del modo in cui lo uso:

mio_programma: $(infer_objects *.o, $(LIB1)/*.o $(LIB2)/*.o)
$(CXX) $(input) -o $(output) $(SYSTEM_LIBRARIES)

La funzione "$(infer_objects )" restituisce il suo primo argomento (dopo aver eseguito il carattere jolly
espansione su di esso), e controlla anche l'elenco dei file nel suo secondo argomento, per
file il cui nome è lo stesso del nome di any .h file inclusi da qualsiasi file nella sua prima
discussione. Se vengono trovati file di questo tipo, questi vengono aggiunti all'elenco.

Costruzione a statico biblioteca

Se sei sicuro di aver effettivamente bisogno di una libreria e il collegamento incrementale non è disponibile oppure
non è quello che vuoi fare, ci sono un paio di modi per farlo. Innanzitutto, ecco un esempio
dove tutti i file sono elencati esplicitamente:

LIBRARY_FILES = abcde

libmine.a: $(LIBRARY_FILES).o
&rm -f $(uscita)
$(AR) cr $(output) $(input)
ranlib $(output) # Potrebbe non essere necessario, a seconda del sistema operativo.

&rm è il comando "rm" integrato di makepp. Se sei abituato a scrivere makefile, potresti esserlo
un po' sorpreso da questo comando; potresti essere abituato a qualcosa di più simile a questo:

libmine.a: $(LIBRARY_FILES).o
$(AR) ru $@ $? # Non consigliato!!!!!!!
ranlib $(uscita)

dove $? (noto anche come "$(changed_inputs)") è una variabile automatica che indica qualsiasi file
che sono cambiati dall'ultima volta che è stata creata la libreria e $@ è più o meno lo stesso
come "$(uscita)".

Questo approccio non è raccomandato per diversi motivi:

· Supponiamo di rimuovere un file sorgente dalla directory corrente. È ancora in
libreria, perché non hai ricostruito la libreria da zero. Di conseguenza, qualsiasi cosa
che i collegamenti con questa libreria avranno lo stantio .o file, e questo può rovinare il tuo
costruisce. (Una volta mi sono completamente confuso da questo quando stavo cercando di rimuovere il codice morto
da un progetto: ho continuato a eliminare i file ed è ancora collegato, quindi ho pensato che il codice fosse
morto. Tuttavia, quando qualcun altro ha ricostruito il progetto da zero, non ha collegato nessuno
Di più! Il problema era che il vecchio .o i file erano ancora nell'archivio.)

Inoltre, a seconda delle tue opzioni per "ar" e della tua implementazione di "ar" (ad esempio, se tu
usa l'opzione "q" invece di "r"), puoi finire per avere diverse versioni del
stesso .o all'interno .a file. Se le diverse versioni definiscono globals differenti, il
il linker potrebbe provare a richiamarli entrambi. Questa è probabilmente una brutta cosa.

Questo è il motivo per cui prima rimuoviamo il file della libreria e lo creiamo da zero. Questo sarà
impiegare un po' più di tempo rispetto al semplice aggiornamento dei moduli in una libreria, ma non molto di più; Su
un computer moderno, la quantità di tempo consumata dal ar il programma è minuscolo rispetto
a ciò che il compilatore C prende in una build tipica, quindi non vale la pena preoccuparsi
di.

· Uno dei modi in cui makepp tenta di garantire build corrette è che lo farà
ricostruire automaticamente se la riga di comando per costruire un determinato obiettivo è cambiata. Ma
usando il $? variabile può causare problemi, perché ogni volta che la libreria viene aggiornata,
il comando build è diverso. (Puoi sopprimerlo usando
":build_check ignore_action"; vedere makepp_build_check per i dettagli.)

· Aggiornare l'archivio invece di ricostruirlo renderà impossibile per makepp farlo
metti il ​​file correttamente in una cache di build (vedi makepp_build_cache per i dettagli).

A volte potresti scoprire che elencare tutti i file è un po' una seccatura, specialmente se a
progetto è in fase di rapido sviluppo e l'elenco dei file è in continua evoluzione. Esso
potrebbe essere più semplice creare la libreria usando i caratteri jolly, in questo modo:

libmine.a: $(solo_bersagli *.o)
&rm $(uscita)
$(AR) cr $(output) $(input)

Questo mette tutto il .o file nella directory corrente nella libreria. Il jolly
corrisponde a qualsiasi .o file che esiste o può essere compilato, quindi funzionerà anche se i file non lo fanno
esistono ancora.

La funzione "only_targets" viene utilizzata per escludere .o file che non hanno una corrispondenza
file sorgente più. Supponi di avere un file chiamato xyz.c che mettevi nel tuo
biblioteca. Questo significa che c'è un xyz.o file in giro. Ora cancelli xyz.c
perché è obsoleto, ma ti dimentichi di eliminare xyz.o. Senza "only_targets"
funzione, xyz.o sarebbe comunque incluso nell'elenco di .o file inclusi nella libreria.

Costruzione a dinamico biblioteca

Il processo di creazione di librerie dinamiche è interamente dipendente dal sistema. lo farei altamente
consiglia di utilizzare libtool per creare una libreria dinamica (vedi
<http://www.gnu.org/software/libtool/>), così non devi capire come farlo su
la tua piattaforma, e in modo che il tuo makefile continui a funzionare anche quando passi a a
sistema operativo diverso. Vedere la documentazione di libtool per i dettagli. Ecco un esempio di Makefile:

LIBTOOL :=libtool

libflick.la : $(solo_bersagli *.lo)
$(LIBTOOL) --mode=link $(CC) $(input) -o $(output)

%.lo: %.c
$(LIBTOOL) --mode=compila $(CC) $(CFLAGS) $(INCLUDE) -c $(input) -o $(output)

Costruzione on alcuni diverso macchine or reti
Uno dei problemi più fastidiosi con i makefile è che non funzionano quasi mai quando tu
passare a una macchina diversa o a una rete diversa. Se i tuoi makefile devono lavorare su
ogni possibile macchina sul pianeta, allora probabilmente hai bisogno di una sorta di configurazione
sceneggiatura. Ma se devi lavorare solo su poche macchine diverse, ci sono diversi modi
puoi affrontare questo problema:

Usa il a diverso includere filetto in contro tutti i , il ambienti

All'inizio di ogni makefile, puoi includere una riga come questa:

includi system_defs.mk

Il file system_defs.mk normalmente si trova in un posto diverso per ciascuno
ambiente. Se vuoi che le tue directory di build siano identiche su tutte le macchine, allora metti
system_defs.mk in una directory sopra le directory di compilazione, oppure fornire un percorso di inclusione
a makepp usando l'opzione della riga di comando "-I".

Questo di solito è un po' doloroso da fare, ma funziona bene se c'è un numero enorme di
differenze.

Usa il if dichiarazioni

Questo è il modo più brutto per farlo, ma di solito funziona.

ifsys i386
CC := gcc
altro ifsys sun4u
cc := cc
altro ifsys hpux11
CC = c89
endif

Se tutto ciò che devi fare è trovare alcuni programmi o librerie o includere file in diversi
posti, ci possono essere modi migliori (vedi sotto).

trova_programma, primo_disponibile, trova file

Queste funzioni possono cercare in diverse directory del sistema per trovare il
file appropriati. Questo non è potente come uno script di configurazione, ovviamente, ma lo trovo
utile. Ad esempio, faccio quanto segue:

CXX ;= $(trova_programma g++ c++ pg++ cxx CC aCC)
# Scegli il primo compilatore C++ disponibile in PATH.
# (Per inciso, se non definisci affatto CXX, questo
# è il modo in cui è definito.)
TCL_INCLUDE ;= -I$(dir_noslash $(trovafile 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 ) cerca tcl.h in ognuno dei campi indicati
# directory e restituisce il percorso completo. Questo è allora
# convertito in un'opzione di compilazione rimuovendo il
# nomefile (lasciando la directory) e preceduto da -I.
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(TCL_INCLUDE) $(ingresso) -o $(uscita)

TCL_LIB ;= $((primo_disponibile
/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))
# Trova dove si trova la libreria Tcl. Questo è poi esplicitamente
# elencato nel comando link:
mio_programma : *.o
$(CXX) $(CXXFLAGS) $(input) -o $(output) $(TCL_LIB)

Fai vantaggio of Perl config informazioni

Le tecniche di cui sopra potrebbero non essere sufficienti se hai bisogno di ulteriori informazioni su
il tuo sistema, ad esempio se esiste un doppio lungo o qual è l'ordine dei byte. Però,
perl ha già calcolato queste cose, quindi puoi semplicemente usare le sue risposte.

Lo script di configurazione automatica di Perl rende disponibili tutte le sue informazioni di configurazione tramite
l'hash %Config. Non esiste una sintassi per accedere a un hash Perl direttamente in makepp, ma puoi farlo
cadere in Perl e impostare le variabili scalari, che sono direttamente accessibili da makepp:

inizio_perl
# Recupera i valori dall'hash di configurazione.
usa Configurazione;
$CC = $Config{'cc'}; # compilatore C utilizzato da perl;
$byteorder_flags = "-DBYTEORDER=$Config{'byteorder'}";
$longdouble_defined = $Config{'d_longdbl'} eq 'definire';
$CFLAGS_for_shared_libs = $Config{'cccdlflags'};
$LDFLAGS_for_shared_libs = $Config{'ccdlflags'};
perl_end

Inoltre, una volta che hai fatto 'use Config', puoi usare l'istruzione "$(perl )", come
Questo:

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

Digita "perldoc Config" per vedere quali informazioni sono disponibili tramite l'hash %Config.

La configurazione di Perl è un buon posto per ottenere cose come informazioni sui tipi interi, byte
order e altre cose che di solito richiedono uno script di configurazione separato da individuare. Un po 'di
le sue informazioni relative alla presenza di cose nel file system potrebbero non essere
valido. Ad esempio, $Config{'cc'} si riferisce al compilatore C con cui è stato compilato perl,
che potrebbe non essere lo stesso compilatore C che vuoi usare. In effetti, potrebbe anche non esistere
sul tuo sistema, poiché probabilmente hai installato Perl tramite un pacchetto binario.

Suggerimenti: per utilizzando jolly
accoppiamento contro tutti i file con l’esclusione di a certo sottoinsieme

I caratteri jolly di Makepp non hanno alcun modo al momento di abbinare tutti i file con l’esclusione di un certo
set, ma puoi farlo con una combinazione di funzioni.

Ad esempio, supponi di avere un programma di test per ogni modulo in una libreria, ma non lo fai
desidera includere i programmi di test nella libreria. Se tutti i programmi di test iniziano con
test, quindi puoi escluderli in questo modo:

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

Le funzioni "$(filter )" e "$(filter_out )" sono un set di filtri molto potente da fare
tutti i tipi di operazioni di intersezione e differenza di insiemi. Per esempio,

SUBDIR ;= $(filter_out *test* *$(ARCH)*, $(shell find . -type d -print))
# Restituisce tutte le sottodirectory che non hanno
# "test" o $(ARCH) in essi.

$(filter $(patsubst test_dir/test_%.o, %.o, $(jolly test_dir/*.o)), \
$(carattere jolly *.o))
# Restituisce un elenco di file .o nella corrente
# directory per la quale esiste un corrispondente
# file test_*.o nella sottodirectory test_dir.
$(filter_out $(patsubst man/man3/%.3, %.o, $(carattere jolly man/man3/*.3)), \
$(carattere jolly *.o))
# Restituisce un elenco di file .o nella corrente
# directory per la quale non esiste una pagina di manuale
# con lo stesso nome file nella sottodirectory man/man3.

utilizzando , il "$(only_targets )" function a eliminato stantio .o file

Supponi di creare un programma o una libreria con un comando build come questo:

programma: *.o
$(CC) $(input) -o $(output)

Supponiamo ora di eliminare un file sorgente. Se dimentichi di eliminare il corrispondente .o file,
sarà ancora collegato anche se non c'è più modo di costruirlo. Nel
futuro, makepp probabilmente riconoscerà automaticamente questa situazione e la escluderà da
l'elenco dei caratteri jolly, ma al momento devi dirgli di escluderlo manualmente:

programma: $(only_targets *.o)
$(CC) $(ingressi) -o $(uscite)

Makepp non conosce alcun modo per costruire lo stantio .o file più poiché il suo file sorgente è
sparito, quindi la funzione "$(only_targets )" lo escluderà dall'elenco delle dipendenze.

Suggerimenti: per multiplo directory
Uno dei motivi principali per scrivere makepp era semplificare la gestione di più
directory. Makepp è in grado di combinare comandi di compilazione da più makefile, quindi può
gestire correttamente una regola in un makefile che dipende da un file che è costruito da a
makefile diverso.

Che a do in posto of ricorsiva make

Makepp supporta la creazione ricorsiva per la compatibilità con le versioni precedenti, ma è altamente raccomandato
per cui non usalo. Se non sai cos'è, bene.

Vedi "Sistema migliore per build gerarchiche" in makepp per i dettagli sul perché non vuoi
usa make ricorsivo, oppure cerca sul web "make ricorsivo considerato dannoso".

Invece di eseguire un make ricorsivo per creare il target "all" in ogni makefile, è
di solito è più facile far capire a makepp quali obiettivi dovranno essere effettivamente costruiti.
Inoltre, se metti tutto il tuo .o e i file della libreria nella stessa directory del
makefile, allora makepp capirà automaticamente anche quali makefile sono necessari--il
l'unica cosa che è necessaria è che il tuo livello più alto faccia un elenco dei file necessari
per il passaggio finale di collegamento. Vedere gli esempi di seguito.

Uno makefile per ogni directory: con implicito Caricamento in corso

Il modo più comune per gestire più directory è mettere un makefile in ogni directory
che descrive come compilare tutto in o da quella directory. Se metti .o file in
la stessa directory dei file sorgente, quindi il caricamento implicito (vedi "Caricamento implicito" in
makepp_build_algorithm) troverà automaticamente tutti i makefile. Se metti il ​​tuo .o
file in una directory diversa (ad esempio, in una sottodirectory dipendente dall'architettura), quindi
probabilmente dovrà caricare tutti i makefile rilevanti usando l'istruzione "load_makefile".

Ecco un esempio di makefile di primo livello per una gerarchia di directory che utilizza il caricamento implicito
per costruire un programma che consiste in molte librerie condivise (ma vedi "Hai davvero bisogno di un
library?" in makepp_cookbook, perché creare un programma da un gruppo di librerie condivise
non è necessariamente una buona idea):

# Makefile di primo livello:
program : main.o **/*.la # Link nelle librerie condivise da tutte le sottodirectory.
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(input) -o $(output) $(LIBS)

Questo è praticamente tutto ciò di cui hai bisogno nel makefile di livello superiore. In ogni sottodirectory, tu
probabilmente farebbe qualcosa del genere:

# Makefile in ogni sottodirectory:
include standard_defs.mk # Ricerche ., .., ../ .., ecc. finché non
# trova il file di inclusione indicato.
# sovrascrive alcune definizioni di variabili qui
SPECIAL_FLAGS := -fai_qualcosa_diverso

Ogni makefile può probabilmente essere più o meno lo stesso se i comandi per costruire i target
sono abbastanza simili.

Infine, dovresti inserire quanto segue nel standard_defs.mk file (che probabilmente dovrebbe
trovarsi nella directory di primo livello):

# Impostazioni variabili comuni e regole di compilazione per tutte le directory.
FLAG := -g -O2
INCLUDE_DIR := $(find_upwards include)
# Ricerche ., .., ../ .., ecc. per un file o
# directory chiamata include, quindi se metti
# tutti i tuoi file di inclusione lì dentro, questo lo farà
# trovali.
INCLUDE := -I$(INCLUDE_DIR)

%.lo: %.c
$(LIBTOOL) --mode=compila $(CC) $(CFLAGS) $(INCLUDE) -c $(input) -o $(output)

lib$(relativo_a ., ..).la: $(solo_bersagli *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(output) $(input)
# $(relativo_a ., ..) restituisce il nome della corrente
# sottodirectory relativa al livello superiore
# sottocartella. Quindi se questo makefile è xyz/Makefile,
# questa regola creerà xyz/libxyz.la.

# Pubblica i file include pubblici nella directory include di primo livello:
$(INCLUDE_DIR)/pubblico_%.h : pubblico_%.h
:build_check symlnk
&ln -fr $(ingresso) $(uscita)

Uno makefile per ogni directory: esplicito Caricamento in corso

Se vuoi mettere tutto il tuo .o file in una sottodirectory dipendente dall'architettura, quindi
l'esempio sopra dovrebbe essere modificato per essere qualcosa del genere:

# Makefile di primo livello:
MAKEFILES := $(carattere jolly **/Makeppfile) # Elenco di tutte le sottodirectory da
# ottieni i makefile da.

load_makefile $(MAKEFILES) # Caricali tutti in.

include standard_defs.mk # Ottieni il comando di compilazione per main.o.

programma : $(ARCH)/main.o */**/$(ARCH)/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(input) -o $(output) $(LIBS)
# */**/$(ARCH) esclude la sottodirectory
# $(ARCH), dove non vogliamo costruire
# una libreria condivisa.

Ogni makefile sarebbe esattamente lo stesso di prima:

# Makefile in ogni sottodirectory:
includi standard_defs.mk
# ... la variabile sovrascrive qui

Ed infine, standard_defs.mk conterrebbe qualcosa del genere:

# Impostazioni variabili comuni e regole di compilazione per tutte le directory.
ARCH ;= $(shell uname -s)-$(shell uname -m)-$(shell uname -r)
# A volte le persone usano solo $(shell uname -m), ma
# sarà lo stesso per FreeBSD e Linux su
# un x86. L' -r non è molto utile su Linux,
# ma è importante per altri sistemi operativi: binari per
# SunOS 5.8 in genere non funziona su SunOS 5.7.
&mkdir -p $(ARCH) # Assicurati che la directory di output esista.
FLAG := -g -O2
INCLUDE_DIR := $(find_upwards include)
# Ricerche ., .., ../ .., ecc. per un file o
# directory chiamata include, quindi se metti
# tutti i tuoi file di inclusione lì dentro, questo lo farà
# trovali.
INCLUDE := -I$(INCLUDE_DIR)

$(ARCH)/%.lo : %.c
$(LIBTOOL) --mode=compila $(CC) $(CFLAGS) $(INCLUDE) -c $(input) -o $(output)

$(ARCO)/ lib$(relativo_a ., ..).la: $(solo_bersagli *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(output) $(input)
# $(relativo_a ., ..) restituisce il nome della corrente
# sottodirectory relativa al livello superiore
# sottocartella. Quindi se questo makefile è xyz/Makefile,
# questa regola creerà xyz/$(ARCH)/libxyz.la.

# Copia i file include pubblici nella directory include di primo livello:
$(INCLUDE_DIR)/pubblico_%.h : pubblico_%.h
&cp $(ingresso) $(uscita)

Automaticamente fabbricazione , il makefile

Se i tuoi makefile sono tutti estremamente simili (come nell'esempio sopra), puoi dire a Makepp
per costruirli automaticamente se non esistono. Basta aggiungere quanto segue al tuo livello superiore
makefile:

SUBDIR := $(filter_out indesiderati_dir1 indesiderati_dir2, $(carattere jolly */**))
$(foreach)/Makeppfile: : foreach $(SOTTODIR)
&echo "include defs_standard.mk" -o $(output)
&echo "_include advanced_defs.mk" -o >>$(output)
# Se esiste il file advanced_defs.mk, allora
# sarà incluso, ma se non esiste,
# l'istruzione _include verrà ignorata.

Ora i makefile stessi verranno creati automaticamente.

Uno makefile esclusivamente at , il top livello

Se tutti i tuoi makefile sono identici, potresti chiedere: perché dovrei avere un makefile in ognuno?
livello? Perché non metti tutto questo nel makefile di primo livello?

Sì, questo può essere fatto. Lo svantaggio principale è che diventa più difficile da specificare
diverse opzioni di compilazione per ogni sottodirectory. Un secondo svantaggio è che il tuo
makefile diventerà probabilmente un po' più difficile da leggere.

Ecco un esempio di come fare proprio questo:

# Makefile di primo livello per la gerarchia di directory. Costruisce il programma
# da un insieme di librerie condivise come esempio. (Vedi avvertenze sopra
# per il motivo per cui potresti voler utilizzare il collegamento incrementale o altro
# approccio piuttosto che librerie condivise.)
makepp_percent_subdirs := 1 # Consenti a % di corrispondere a più directory.
SUBDIR := $(filter_out *CVS* other-unwanted_dirs $(carattere jolly **))
FLAG := -g -O2
INCLUDE := -Include

%.lo: %.c
$(LIBTOOL) --mode=compila $(CC) $(INCLUDE) $(CFLAGS) -c $(input) -o $(output)

$(per ogni)/ lib$(notdir $(foreach)).la: $(foreach)/*.lo : foreach $(SOTTODIR)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(output) $(input)
# Regola per creare tutte le librerie.

programma: main.o **/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(output) $(input)

include/$(notdir $(foreach)) : $(foreach) : foreach **/public_*.h
&cp $(ingresso) $(uscita)
# Regola di esempio per copiare pubblicamente
# file .h accessibili nel posto giusto.

A cavedano bersaglio

I makefile tradizionali contengono un target pulito, che consente di rimuovere tutto ciò che era
costruito. Ci sono tre ragioni per cui non dovresti farlo con makepp:

1. Makepp fa di tutto per garantire una build corretta. Quindi il disperato "non lo faccio"
sapere cosa c'è che non va", farti venire voglia di ricominciare da capo è una cosa del passato.

2. A volte le persone cercheranno di risparmiare tempo facendo due cose contraddittorie contemporaneamente:
"pulire tutto". Questo può confondere il sistema smart wildcard di makepp, perché lo farà
prima ottenere i fatti prima di fare qualsiasi cosa. Poi arriva l'azione pulita, che fa
non dire a makepp cosa fa (in effetti non può, perché annulla qualcosa -- il
contrariamente a ciò che serve uno strumento di compilazione). Poi arriva "tutti", ma i file aggiornati,
che dove là, sono misteriosamente spariti.

3. C'è il comando "makeppclean", che fa la stessa cosa e in modo più efficiente.

Tuttavia conserviamo questa sezione storica, poiché ti dice qualcosa sul
modo in cui funziona makepp: un target fasullo chiamato "clean" è solo un nome per una serie di comandi per
rimuovere tutti i file risultanti dal processo di creazione. Di solito un bersaglio pulito sembra
qualcosa come questo:

$(falso pulito):
&rm -fm $(carattere jolly *.o .makepp_log)
# -m e .makepp_log eliminano tutta la spazzatura di makepp.

Invece di elencare esplicitamente i file che vuoi eliminare, puoi anche dire a makepp di
rimuovi tutto ciò che sa costruire, in questo modo:

$(falso pulito):
&rm -fm .makepp_log $(only_targets *)

Questo ha il vantaggio che se uno qualsiasi dei tuoi file sorgente può essere creato da altri file,
verranno cancellati anche loro; d'altra parte, stantio .o file (file che prima erano
costruibile ma il cui file sorgente è stato successivamente rimosso) non verrà eliminato.

Se hai una build che coinvolge makefile in diverse directory, il tuo top-
level makefile può fare riferimento al target "pulito" (o a qualsiasi altro target fasullo) in un diverso
makefile:

# Makefile di primo livello
SUBDIR := sub1 sub2

# crea regole qui

# Pulisci dopo la compilazione:
$(falso pulito): $(SUBDIRS)/pulito
&rm -fm .makepp_log $(only_targets *)

In alternativa, puoi mettere il tuo target "pulito" solo nel makefile di primo livello e averlo
elaborare tutte le directory, in questo modo:

$(falso pulito):
&rm -fm $(only_targets **/*)

utilizzando Qt's potenza preprocessore
Questo esempio mostra un makefile per un'utilità che usa la libreria Qt GUI di Nokia (vedi
<http://qt.nokia.com>). L'unica cosa leggermente insolita di questo è che tu
deve eseguire un preprocessore chiamato "moc" sulla maggior parte dei file ".h" che contengono definizioni di widget,
ma non vuoi eseguire "moc" su nessun file ".h" che non usa la macro "Q_OBJECT".

Automaticamente determinazione quale file bisogno potenza file

Potresti, ovviamente, semplicemente elencare tutti i file ".h" che devono avere "moc" in esecuzione su di essi.
Se stai sviluppando rapidamente nuovi widget, tuttavia, potrebbe essere una seccatura
continua ad aggiornare l'elenco nel makefile. Puoi aggirare la necessità di elencare il moc
moduli esplicitamente con qualcosa del genere:

MOC := $(QTDIR)/bin/moc
MODULI := qualunque modulo tu abbia nel tuo programma
MOC_MODULES := $(patsubst %.h, moc_%, $(&grep -l /Q_OBJECT/ *.h))
# Scansiona tutti i file .h per la macro Q_OBJECT.

mio_programma: $(MODULES).o $(MOC_MODULES).o
$(CXX) $(input) -o $(output)

moc_%.cxx: %.h # Crea i file moc dai file .h.
$(MOC) $(ingresso) -o $(uscita)

%.o: %.cxx
$(CXX) $(CXXFLAGS) -c $(ingresso) -o $(uscita)

Questo approccio scansiona ciascuno dei tuoi .h file ogni volta che viene eseguito makepp, cercando il
Macro "Q_OBJECT". Sembra costoso, ma probabilmente non ci vorrà molto. (Il .h
i file dovranno essere comunque caricati dal disco durante il processo di compilazione, quindi lo faranno
essere memorizzato nella cache.)

#includere , il .moc filetto

Un altro approccio è "#include" l'output dal preprocessore "moc" nel tuo widget
dossier di implementazione. Ciò significa che devi ricordarti di scrivere "#include", ma ha
il vantaggio che ci sono meno moduli da compilare, e quindi la compilazione va più veloce.
(Per la maggior parte delle compilazioni C++, la maggior parte del tempo viene spesa leggendo i file di intestazione e
l'output del preprocessore deve includere quasi tanti file quanti sono i tuoi widget
comunque.) Ad esempio:

// mio_widget.h
classe MyWidget : QWidget pubblico {
Q_OGGETTO
// ...
}

// mio_widget.cpp

#include "mio_widget.h"
#include "my_widget.moc" // my_widget.moc è l'output del
// preprocessore moc.
// Altre cose di implementazione qui.
MyWidget::MyWidget(QWidget * genitore, const char * nome):
QWidget (genitore, nome)
{
// ...
}

Ora devi avere una regola nel tuo makefile per creare tutti i file ".moc", in questo modo:

MOC := $(QTDIR)/bin/moc
# Regola per creare file .moc:
%.moc: %.h
$(MOC) $(ingresso) -o $(uscita)

Makepp è abbastanza intelligente da rendersi conto che deve creare "my_widget.moc" se non lo fa
esiste già o se non è aggiornato.

Questo secondo approccio è quello che uso solitamente perché velocizza la compilazione.

Sostituzioni per deprecato make idiomi
FARECMDGOALS

A volte le persone hanno regole nel loro makefile che dipendono dal target che stanno costruendo,
utilizzando la variabile speciale "MAKECMDGOALS". Ad esempio, a volte si vedono cose come
Questo:

ifneq ($(produzione del filtro, $(MAKECMDGOALS)),)
FLAG := -O2
altro
CLAGS := -g
endif

Funzionerà bene con makepp. Tuttavia, raccomando di non usare "MAKECMDGOALS" per questo
casi (e così fa il manuale GNU make). Faresti meglio a mettere il tuo ottimizzato e
compilato per il debug .o file in directory separate o assegnando loro prefissi diversi o
suffissi, o usando i repository, per tenerli separati.

Probabilmente l'unica volta in cui potresti effettivamente voler fare riferimento a "MAKECMDGOALS" è se lo è
richiede molto tempo per caricare i tuoi makefile e non ne hai bisogno per il tuo obiettivo "pulito"
(ma non hai bisogno di un obiettivo pulito). Per esempio,

ifneq ($(MAKECMDGOALS),pulito)
load_makefile $(carattere jolly **/Makeppfile)
altro
no_implicit_load . # Impedisce il caricamento automatico di qualsiasi altro makefile.
endif

$(falso pulito):
&rm -f $(carattere jolly **/*.o)

Ricorsivo make a costruire in diverso directory

Vedi "Suggerimenti per più directory" in makepp_cookbook.

Ricorsivo make a il cambiamento APPREZZIAMO of a variabile

Alcuni makefile si richiamano nuovamente con un valore diverso di una variabile, ad esempio il debug
target nel seguente frammento di makefile

.PHONY: tutto il debug

ottimizzato:
$(MAKE) programma CFLAGS=-O2

eseguire il debug:
$(MAKE) programma CFLAGS=-g

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

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

Se l'utente digita "make debug", costruisce il programma in modalità predefinita con il debug abilitato
invece che con l'ottimizzazione.

Un modo migliore per farlo è creare due programmi diversi, con due diversi set di
file oggetto, in questo modo:

FLAG := -O2
DEBUG_FLAGS := -g
MODULI := ab

programma: $(MODULES).o
$(CC) $(CFLAGS) $(input) -o $(output)

debug/programma: debug/$(MODULES).o
$(CC) $(DEBUG_FLAGS) $(input) -o $(output)

%.o: %.c
$(CC) $(CFLAGS) -c $(ingresso) -o $(uscita)

debug/%.o: %.c
$(CC) $(DEBUG_FLAGS) -c $(ingresso) -o $(uscita)

$(debug fasullo): debug/programma

Il vantaggio di farlo in questo modo è (a) non è necessario ricostruire tutto quando si
passare da debug a ottimizzato e viceversa; (B)

Quanto sopra può essere scritto in modo un po' più conciso usando i repository. Il seguente
makefile è esattamente equivalente:

repository debug=. # Fa sembrare la sottodirectory debug una copia di
# la sottodirectory corrente.
caricamento_makefile debug CFLAGS=-g
# Override CFLAGS quando invocato nella sottodirectory di debug
CFLAGS := -O2 # Valore di CFLAGS quando invocato in questa sottodirectory

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

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

$(debug fasullo): debug/programma
# Se l'utente digita "makepp debug", costruisce
# debug/programma invece di programma.

Miscellanea suggerimenti
Come do I costruire prima parte diversamente ad appena una volta?

Makepp lo rende difficile da fare perché il risultato è incoerente rispetto alle regole.
Ma ci sono situazioni in cui potresti aver bisogno di questo, ad esempio per compilare un solo modulo con
pesanti informazioni di debug. Puoi raggiungere questo obiettivo in due passaggi costruendo prima il
dipendenza separatamente, e quindi escludendola dalla fase di collegamento:

makepp DEBUG=3 buggy.o # Costruiscilo con un'altra opzione.
makepp --dont-build=buggy.o buggy # Usalo, nonostante l'opzione di compilazione "sbagliata".

Come do I make sicuro my produzione directory esistere?

Puoi specificare una regola per creare la directory di output, quindi assicurati che ogni file che
va nella directory di output dipende da questo. Ma di solito è più facile fare qualcosa del genere
Questo:

# Il modo classico
fittizio := $(shell test -d $(OUTPUT_DIRECTORY) || mkdir -p $(OUTPUT_DIRECTORY))
# Di solito è più facile che far dipendere tutti i file
# $(OUTPUT_DIRECTORY) e avere una regola per farlo.
# Nota che devi usare := invece di = per forzarlo a
# eseguire immediatamente.
# Un approccio alternativo: usare codice Perl, OUTPUT_DIRECTORY variabile locale
inizio_perl
-d $OUTPUT_DIRECTORY o mkdir $OUTPUT_DIRECTORY;
perl_end
# Il modo moderno, non fa nulla per le directory esistenti
&mkdir -p $(OUTPUT_DIRECTORY)

Una di queste istruzioni dovrebbe essere vicino alla parte superiore del tuo makefile, quindi vengono eseguite
prima di tutto ciò che potrebbe aver bisogno della directory.

Come do I forza a command a eseguire on ogni costruire?

Il modo più semplice è non usare affatto il meccanismo delle regole, ma semplicemente eseguirlo, come
Questo:

fittizio := $(data shell > last_build_timestamp)

Oppure inseriscilo in un blocco perl, in questo modo:

inizio_perl
system("comando da eseguire");
perl_end

Questo approccio ha lo svantaggio che verrà eseguito anche se un obiettivo non correlato è
essere eseguito.

Un secondo approccio consiste nel dichiarare il file come target fasullo, anche se si tratta di un file reale.
Questo costringerà makepp a rieseguire il comando per compilarlo ogni volta, ma solo se lo fa
appare nell'elenco delle dipendenze di alcune regole.

Come do I accorciare , il visualizzati costruire comandi?

Spesso ci sono così tante opzioni per i comandi di compilazione che ciò che viene visualizzato sul
schermo è illeggibile. È possibile modificare ciò che viene visualizzato sopprimendo la visualizzazione del
l'intero comando, quindi stampare esplicitamente la parte interessante del comando. È
facile stampare solo la parte rilevante del comando usando "$(filter_out )", come
Questo:

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

%.o: %.c
@&echo $(notdir $(CC)) ... \
$(filtro_out -I* $(ADDL_CXX_FLAGS), $(ALL_CFLAGS)) \
-c $(ingresso)
@$(CC) $(ALL_CFLAGS) -c $(ingresso) -o $(uscita)

(Il "@" davanti al comando sopprime la stampa del comando.)

Questo ti permetterà di vedere la maggior parte delle opzioni interessanti ma non tutte le
includere le directory (di cui spesso ce ne sono moltissime!). Se la parte che ti interessa
in è contiguo al tuo comando, puoi anche usare la funzione "stampa" (che aggiunge a
newline, quindi non vuoi molti di loro):

bersaglio:
@... $(stampa parte interessante) ...

Come do I convertire a filetto ai miglioramenti dipendenze?

Per alcuni formati di file oscuri non vale la pena implementare uno scanner. In un progetto
abbiamo file xml, diciamo foobar.xml che contiene le dipendenze per foobar.out:


un
B
C


Abbiamo deciso di aderire a questo semplice layout, quindi non abbiamo bisogno di analizzare xml. Con il
builtin &sed, ecco cosa facciamo con tre semplici sostituzioni per i tre tipi di
Linee:

%.d: %.xml
&sed 's! !$(radice).out: \\! || S! (.+) !$$1 \\! || S! !# Vuoto!' \
$(ingresso) -o $(uscita)

includi foobar.d

Il tentativo di includere questo, produce prima "foobar.d":

foobar.out: \
un \
B \
c\
# Vuoto

La riga vuota (solo un commento o davvero vuota) evita di doversi preoccupare del finale
barra rovesciata. Un'alternativa per produrre un elenco multilinea è:

%.d: %.xml
&sed 's! !$(radice).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(ingresso) -o $(uscita)

includi foobar.d

Questo produce un equivalente:

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

Se hai una riscrittura più complessa da fare, definisci una funzione all'interno del makefile o in a
modulo che includi. Ad esempio, non definire $_ salterà le righe di input:

sub miofiltro {
return undef $_ if /
mio $radice = f_gambo;
S! !$stem.out: \$((! || s! !))! || s!<.+?>!!g;
}

%.d: %.xml
&sed 's! !$(radice).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(ingresso) -o $(uscita)

includi foobar.d

Usa makepp_cookbook online utilizzando i servizi onworks.net


Server e workstation gratuiti

Scarica app per Windows e Linux

Comandi Linux

Ad