EnglezăFrancezăSpaniolă

Ad


Favicon OnWorks

makepp_cookbook - Online în cloud

Rulați makepp_cookbook în furnizorul de găzduire gratuit OnWorks prin Ubuntu Online, Fedora Online, emulator online Windows sau emulator online MAC OS

Aceasta este comanda makepp_cookbook care poate fi rulată în furnizorul de găzduire gratuit OnWorks folosind una dintre multiplele noastre stații de lucru online gratuite, cum ar fi Ubuntu Online, Fedora Online, emulator online Windows sau emulator online MAC OS

PROGRAM:

NUME


makepp_cookbook -- Cel mai bun mod de a configura fișiere make pentru diferite situații

DESCRIERE


Am descoperit că practic nimeni nu citește vreodată un manual pentru un instrument de fabricare, pentru că sincer
nimeni nu este cu adevărat interesat de procesul de fabricare în sine - ne interesează doar rezultate.
Așa că această carte de bucate a fost creată în speranța că oamenii vor putea obține ceea ce au nevoie
rapid din exemple fără a trece prin manual. Aceasta arată cum se tastează
întrebări, în timp ce instrucțiunile de instalare și blocaje vor fi găsite în
întrebări frecvente.

Clădire biblioteci
Do tu într-adevăr nevoie a bibliotecă?

Am văzut o serie de programe mari care constau dintr-un număr mare de module, fiecare dintre ele
care locuiește în propriul director. De obicei, fiecare director este pus în propria bibliotecă,
iar apoi programul final se leagă de toate bibliotecile.

În multe cazuri, cred că, în loc să folosesc o bibliotecă, există o abordare mai bună. Biblioteci
nu sunt cu adevărat soluția potrivită dacă fiecare modul nu poate sau nu va fi reutilizat în niciunul
program, pentru că atunci obțineți toate dezavantajele bibliotecilor și niciunul dintre
avantaje. Bibliotecile sunt utile în următoarele cazuri:

1. Când aveți o grămadă de subrutine care trebuie să fie legate cu mai multe diferite
programe și niciun program nu utilizează de fapt 100% din subrutine - fiecare program folosește un
subset diferit. În acest caz, probabil că este o idee bună să folosiți o bibliotecă statică (a
.a fișier sau un fișier de arhivă).

2. Când aveți un modul care ar trebui să fie conectat la mai multe programe diferite, și dvs
doresc să-l încărcați dinamic, astfel încât fiecare program să nu aibă nevoie de o copie separată a
Librăria. Bibliotecile dinamice pot economisi spațiu în fișierele executabile și uneori pot îmbunătăți
performanța sistemului deoarece există o singură copie a bibliotecii încărcată pentru toate
diferite programe care îl folosesc.

3. Când timpul de conectare este prohibitiv de lung, folosiți biblioteci partajate pentru bucăți mari de
programul poate accelera semnificativ legătura.

Folosirea bibliotecilor statice are un dezavantaj principal: pe unele sisteme (de ex. Linux), ordinea
în care conectați bibliotecile este de o importanță critică. Linker-ul procesează biblioteci
în ordinea specificată pe linia sa de comandă. Preia tot ce crede că are nevoie
fiecare bibliotecă, apoi trece la următoarea bibliotecă. Dacă o bibliotecă ulterioară se referă la a
simbol care nu a fost încă încorporat dintr-o bibliotecă anterioară, linkerul nu
știți să vă întoarceți și să o luați din biblioteca anterioară. Ca urmare, poate fi necesar
pentru a lista biblioteca de mai multe ori pe linia de comandă a linkerului. (Am lucrat la un proiect
unde a trebuit să repetăm ​​de trei ori întreaga listă de biblioteci. Acest proiect este ceea ce a făcut
prefer abordarea alternativă sugerată mai jos, cea a conectării incrementale.)

Utilizarea bibliotecilor dinamice are mai multe dezavantaje. În primul rând, programul tău poate fi ușor
mai lent la pornire dacă biblioteca nu este deja utilizată de un alt program, deoarece
trebuie găsit și încărcat. În al doilea rând, poate fi o adevărată bătaie de cap să obții toată dinamica
biblioteci instalate în locațiile corecte; nu poți doar să copiați executabilul programului,
de asemenea, trebuie să vă asigurați că copiați toate bibliotecile sale. În al treilea rând, pe unele sisteme, acesta
este dificil să depanați codul în bibliotecile partajate, deoarece depanatoarele nu acceptă
le bine.

Dacă modulul dvs. nu va fi folosit niciodată în niciun alt program, atunci există puține motive pentru utilizare
o bibliotecă: aveți toate dezavantajele utilizării bibliotecilor și niciunul dintre avantaje.
Tehnica pe care o prefer este să folosesc legăturile incrementale, acolo unde este disponibilă.

Iată cum puteți face acest lucru pe Linux:

my_module.o : $(filter_out my_module.o, $(wildcard *.o))
ld -r -o $(ieșire) $(intriri)

Ceea ce va face aceasta este să creeze altul .o fișier numit modulul_meu.o, care va consta din
toate din .o fișierele din acest subdirector. Linkerul va rezolva cât mai multe dintre ele
referințe după cum poate și va lăsa referințele rămase să fie rezolvate în a
etapa ulterioară a legăturii. La nivelul superior, când în sfârșit îți construiești programul,
în loc să se conecteze cu libmy_module.a or libmy_module.so, pur și simplu te-ai conecta cu
modulul_meu.o. Când faci legătura .o fișiere, nu aveți probleme cu dependența de ordine în
linia de comandă a linkerului.

închirierea makepp figura afară care bibliotecă module sunt necesar

Chiar dacă aveți o bibliotecă adevărată, în care un anumit program are nevoie doar de câteva fișiere din el
(mai degrabă decât fiecare modul), makepp ar putea să-și dea seama care sunt modulele
necesare din bibliotecă și includeți numai cele din build. Acest lucru poate salva compilația
timp dacă dezvoltați biblioteca împreună cu un program, pentru că nu vă deranjați
compilați module de bibliotecă care nu sunt necesare pentru programul particular la care lucrați.

Dacă biblioteca dvs. respectă cu strictețe convenția în care sunt declarate toate funcțiile sau clasele
un fișier xyz.h sunt complet implementate într-un fișier sursă care se compilează în xyz.o (adică tu
nu împărțiți implementarea în xyz1.o și xyz2.o), apoi puteți utiliza
Funcția „$(infer_objects)” pentru a spune makepp să scoată numai modulele relevante din
bibliotecă. Acest lucru poate funcționa surprinzător de bine pentru biblioteci cu chiar și zeci de fișiere incluse.
Practic, „$(infer_objects)” examinează lista de .h fișierele care sunt incluse și arată
pentru corespondență .o fișiere. Dacă dezvoltați rapid o bibliotecă și un program
împreună, acest lucru poate economisi timp de compilare, deoarece nu vă deranjați niciodată să compilați module ale
biblioteca pe care programul nu o folosește.

Iată un exemplu de modul în care îl folosesc:

programul_meu: $(infer_objects *.o, $(LIB1)/*.o $(LIB2)/*.o)
$(CXX) $(intrări) -o $(ieșire) $(SYSTEM_LIBRARIES)

Funcția „$(infer_objects )” returnează primul argument (după ce face wildcard
extinderea pe acesta), și, de asemenea, se uită prin lista de fișiere din al doilea argument, pentru
fișiere al căror nume este același cu numele oricăror .h fișiere incluse de orice fișier în primul său
argument. Dacă se găsesc astfel de fișiere, acestea sunt adăugate la listă.

Clădire a static bibliotecă

Dacă sunteți sigur că aveți nevoie într-adevăr de o bibliotecă și conectarea incrementală nu este disponibilă sau
nu este ceea ce vrei să faci, există câteva moduri de a face asta. În primul rând, iată un exemplu
unde toate fișierele sunt listate în mod explicit:

LIBRARY_FILES = abcde

libmine.a: $(LIBRARY_FILES).o
&rm -f $(ieșire)
$(AR) cr $(ieșire) $(intriri)
ranlib $(ieșire) # Poate să nu fie necesar, în funcție de sistemul de operare.

&rm este comanda încorporată "rm" a makepp-ului. Dacă sunteți obișnuit să scrieți fișiere make, este posibil să fiți
putin surprins de aceasta comanda; ai putea fi obisnuit cu ceva mai de genul asta:

libmine.a: $(LIBRARY_FILES).o
$(AR) ru $@ $? # Nu se recomandă!!!!!!!
ranlib $(ieșire)

unde $? (cunoscut și ca „$(changed_inputs)”) este o variabilă automată care înseamnă orice fișiere
care s-au schimbat de la ultima dată când a fost construită biblioteca și $@ este aproximativ același
ca „$(ieșire)”.

Această abordare nu este recomandată din mai multe motive:

· Să presupunem că eliminați un fișier sursă din directorul curent. Este încă în
biblioteca, pentru că nu ați reconstruit biblioteca de la zero. Ca rezultat, orice
care legăturile cu această bibliotecă vor avea învechit .o fișier și asta vă poate strica
construieste. (Odată am fost complet confuz de asta când încercam să elimin codul mort
dintr-un proiect: am continuat să șterg fișiere și încă se lega, așa că am crezut că codul era
mort. Cu toate acestea, când altcineva a reconstruit proiectul de la zero, acesta nu a legat niciunul
Mai mult! Problema era că vechiul .o fișierele erau încă în arhivă.)

De asemenea, în funcție de opțiunile dvs. pentru „ar” și de implementarea „ar” (de exemplu, dacă dvs
folosiți opțiunea „q” în loc de „r”), puteți ajunge să aveți mai multe versiuni ale
acelaşi .o în interiorul .a fişier. Dacă versiunile diferite definesc valori globale diferite,
linkerul poate încerca să le atragă pe ambele. Acesta este probabil un lucru rău.

Acesta este motivul pentru care mai întâi eliminăm fișierul bibliotecă și îl creăm de la zero. Asta va
durează puțin mai mult decât actualizarea modulelor dintr-o bibliotecă, dar nu mult mai mult; pe
un computer modern, cantitatea de timp consumată de ar programul este minuscul comparat
la ceea ce compilatorul C preia într-o versiune tipică, așa că nu merită să vă faceți griji
cu privire la.

· Una dintre modalitățile prin care makepp încearcă să garanteze build-uri corecte este că va face acest lucru
reconstruiește automat dacă linia de comandă pentru a construi o anumită țintă s-a schimbat. Dar
folosind $? variabila poate cauza probleme, deoarece de fiecare dată când biblioteca este actualizată,
comanda build este diferită. (Puteți suprima acest lucru folosind
„:build_check ignore_action”; vezi makepp_build_check pentru detalii.)

· Actualizarea arhivei, mai degrabă decât reconstruirea acesteia, va face imposibil pentru makepp
puneți fișierul corect într-un cache de compilare (consultați makepp_build_cache pentru detalii).

Uneori s-ar putea să descoperiți că listarea tuturor fișierelor este puțin chiar dureroasă, mai ales dacă a
proiectul este în curs de dezvoltare rapidă, iar lista de fișiere este în continuă schimbare. Aceasta
poate fi mai ușor să construiți biblioteca folosind metacaractere, ca acesta:

libmine.a: $(only_targets *.o)
&rm $(ieșire)
$(AR) cr $(ieșire) $(intriri)

Aceasta pune toate .o fișierele din directorul curent în bibliotecă. Wildcardul
se potrivește cu oricare .o fișier care există sau poate fi construit, deci va funcționa chiar dacă fișierele nu
exista inca.

Funcția „only_targets” este folosită pentru a exclude .o fișiere care nu au corespunzătoare
mai multe fișiere sursă. Să presupunem că ai apelat un fișier xyz.c pe care obișnuiai să le pui în ta
bibliotecă. Aceasta înseamnă că există o xyz.o dosar întins în jur. Acum ștergeți xyz.c
pentru că este învechit, dar uitați să ștergeți xyz.o. Fără „only_targets”
funcţie, xyz.o ar fi în continuare incluse în lista de .o fișierele incluse în bibliotecă.

Clădire a dinamic bibliotecă

Procesul de construire a bibliotecilor dinamice este în întregime dependent de sistem. aș face foarte mult
recomandăm utilizarea libtool pentru a construi o bibliotecă dinamică (vezi
<http://www.gnu.org/software/libtool/>), astfel încât nu trebuie să vă dați seama cum să o faceți
platforma dvs. și astfel încât fișierul dvs. make să continue să funcționeze chiar și atunci când treceți la a
sistem de operare diferit. Consultați documentația libtool pentru detalii. Iată un exemplu de Makefile:

LIBTOOL := libtool

libflick.la : $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(intrări) -o $(ieșire)

%.lo : %.c
$(LIBTOOL) --mode=compilați $(CC) $(CFLAGS) $(INCLUDE) -c $(intrare) -o $(ieșire)

Clădire on câteva diferit Mașini or rețele
Una dintre cele mai enervante probleme cu makefile-urile este că aproape niciodată nu funcționează când tu
comutați la o altă mașină sau la o altă rețea. Dacă fișierele dvs. make trebuie să funcționeze
fiecare mașină posibilă de pe planetă, atunci probabil că aveți nevoie de un fel de configurație
scenariu. Dar dacă trebuie să lucrați doar pe câteva mașini diferite, există mai multe moduri
poti aborda aceasta problema:

Utilizare a diferit include fişier in toate il medii

La începutul fiecărui makefile, puteți include o linie ca aceasta:

includ system_defs.mk

Fișierul system_defs.mk ar fi în mod normal situat într-un loc diferit pentru fiecare
mediu inconjurator. Dacă doriți ca directoarele dvs. de compilare să fie identice pe toate mașinile, atunci puneți
system_defs.mk într-un director deasupra directoarelor de compilare, sau furnizați o cale de includere
pentru a makepp folosind opțiunea de linie de comandă „-I”.

Acest lucru este de obicei destul de dureros de făcut, dar funcționează bine dacă există un număr mare de
diferențe.

Utilizare if Declarații

Acesta este cel mai urat mod de a o face, dar de obicei va funcționa.

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

Dacă tot ce trebuie să faceți este să găsiți câteva programe sau biblioteci sau să includeți fișiere în diferite
locuri, pot exista moduri mai bune (vezi mai jos).

find_program, first_available, Gaseste fisier

Aceste funcții pot căuta în diferite directoare din sistemul dumneavoastră pentru a găsi
fișiere adecvate. Acesta nu este la fel de puternic ca un script de configurare, desigur, dar îl găsesc
util. De exemplu, fac următoarele:

CXX ;= $(find_program g++ c++ pg++ cxx CC aCC)
# Alegeți primul compilator C++ care este disponibil în PATH.
# (De altfel, dacă nu definiți deloc CXX, aceasta
# este modul în care este definit.)
TCL_INCLUDE ;= -I$(dir_noslash $(găsește fișierul 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 ) caută tcl.h în fiecare dintre cele indicate
# directoare și returnează calea completă. Aceasta este atunci
# convertit într-o opțiune de compilare prin eliminarea
# nume de fișier (parăsind directorul) și prefixarea cu -I.
%.o : %.cpp
$(CXX) $(CXXFLAGS) $(TCL_INCLUDE) $(intrare) -o $(ieșire)

TCL_LIB ;= $((first_available
/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))
# Găsiți unde este biblioteca Tcl. Aceasta este atunci în mod explicit
# listat pe comanda link:
programul_meu : *.o
$(CXX) $(CXXFLAGS) $(intrări) -o $(ieșire) $(TCL_LIB)

Lua avantaj of a lui Perl config informații

Tehnicile de mai sus pot să nu fie suficiente dacă aveți nevoie de informații suplimentare despre
sistemul dvs., cum ar fi dacă există un dublu lung sau care este ordinea octeților. In orice caz,
perl a calculat deja aceste lucruri, așa că puteți folosi doar răspunsurile sale.

Scriptul de autoconfigurare al lui Perl pune la dispoziție toate informațiile de configurare prin
hash-ul %Config. Nu există o sintaxă pentru a accesa un hash Perl direct în makepp, dar poți
treceți în Perl și setați variabile scalare, care sunt direct accesibile din makepp:

perl_begin
# Preluați valori din hashul de configurare.
utilizați Config;
$CC = $Config{'cc'}; # Compilatorul C folosit de 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 asemenea, odată ce ați făcut „utilizați configurația”, puteți utiliza instrucțiunea „$(perl )”, cum ar fi
acest:

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

Tastați „perldoc Config” pentru a vedea ce informații sunt disponibile prin hash-ul %Config.

Configurația lui Perl este un loc bun pentru a obține lucruri precum informații despre tipurile întregi, octeți
ordine și alte lucruri care necesită de obicei un script de configurare separat pentru a le localiza. Unele
informațiile sale care se referă la prezența lucrurilor în sistemul de fișiere ar putea să nu fie
valabil. De exemplu, $Config{'cc'} se referă la compilatorul C cu care a fost construit perl,
care ar putea să nu fie același compilator C pe care doriți să îl utilizați. De fapt, s-ar putea să nu existe
pe sistemul dvs., deoarece probabil ați instalat Perl printr-un pachet binar.

sfaturi pentru folosind metacaractere
De potrivire toate fișiere cu excepția a sigur submult

Caracterele jokere ale lui Makepp nu au nicio modalitate în prezent de a potrivi toate fișierele cu excepția un anumit
set, dar o puteți face cu o combinație de funcții.

De exemplu, să presupunem că aveți un program de testare pentru fiecare modul dintr-o bibliotecă, dar nu aveți
doriți să includeți programele de testare în bibliotecă. Dacă toate programele de testare încep cu
test, atunci le puteți exclude astfel:

libproduction.a: $(test de filtrare*, $(caracterul metalic *.o))

Funcțiile „$(filter )” și „$(filter_out )” sunt un set foarte puternic de filtre de făcut
tot felul de operații de intersecție și diferență a seturilor. De exemplu,

SUBDIRS ;= $(filter_out *test* *$(ARCH)*, $(shell find . -type d -print))
# Returnează toate subdirectoarele care nu au
# „test” sau $(ARCH) în ele.

$(filtru $(patsubst test_dir/test_%.o, %.o, $(wildcard test_dir/*.o)), \
$(caracter metalic *.o))
# Returnează o listă de fișiere .o din actualul
# director pentru care există un corespondent
# test_*.o fișier în subdirectorul test_dir.
$(filter_out $(patsubst man/man3/%.3, %.o, $(wildcard man/man3/*.3)), \
$(caracter metalic *.o))
# Returnează o listă de fișiere .o din actualul
# director pentru care nu există o pagină de manual
# cu același nume de fișier în subdirectorul man/man3.

Utilizarea il „$(only_targets )" funcţie la elimina stătut .o fișiere

Să presupunem că construiți un program sau o bibliotecă cu o comandă de compilare ca aceasta:

program: *.o
$(CC) $(intrări) -o $(ieșire)

Să presupunem că acum ștergeți un fișier sursă. Dacă uitați să ștergeți codul corespunzător .o fişier,
va fi în continuare conectat chiar dacă nu mai există nicio modalitate de a-l construi. În
viitor, makepp va recunoaște probabil această situație automat și o va exclude din
lista de wildcard, dar în prezent, trebuie să îi spuneți să o excludă manual:

program: $(only_targets *.o)
$(CC) $(intrări) -o $(ieșiri)

Makepp nu știe nicio modalitate de a construi vechea .o fișier mai mult, deoarece fișierul sursă este
dispărut, așa că funcția „$(only_targets )” o va exclude din lista de dependențe.

sfaturi pentru multiplu directoare
Unul dintre principalele motive pentru a scrie makepp a fost simplificarea gestionării multiplelor
directoare. Makepp este capabil să combine comenzi de compilare din mai multe fișiere make, așa că poate
să se ocupe corect de o regulă într-un fișier makefile care depinde de un fișier care este construit de a
makefile diferit.

Ce la do in loc of recursiv face

Makepp acceptă makep recursiv pentru compatibilitate cu versiunile inverse, dar este foarte recomandat
Ca Tu nu foloseste-l. Dacă nu știi ce este, bine.

Consultați „Sistem mai bun pentru versiuni ierarhice” în makepp pentru detalii despre motivul pentru care nu doriți
folosiți recursive make sau căutați pe web „recursive make considerate dăunătoare”.

În loc să faci un make recursiv pentru a face ținta „toate” în fiecare makefile, este
de obicei, mai ușor să-l lași pe makepp să-și dea seama care ținte vor trebui de fapt construite.
În plus, dacă puneți tot .o și fișiere de bibliotecă în același director ca și
makefiles, apoi makepp își va da seama automat ce fișiere make sunt necesare și
singurul lucru de care este necesar este să aveți la nivel superior să faceți o listă a fișierelor necesare
pentru etapa finală de conectare. Vezi exemplele de mai jos.

O makefile pentru fiecare director: cu implicit încărcare

Cel mai obișnuit mod de a gestiona mai multe directoare este de a pune un makefile în fiecare director
care descrie cum să construiți totul în sau din acel director. Daca pui .o fișiere în
același director ca și fișierele sursă, apoi încărcarea implicită (consultați „Încărcarea implicită” în
makepp_build_algorithm) va găsi automat toate makefile-urile. Daca pui ta .o
fișiere într-un director diferit (de exemplu, într-un subdirector dependent de arhitectură), apoi dvs
va trebui probabil să încarce toate makefile-urile relevante folosind instrucțiunea „load_makefile”.

Iată un exemplu de makefile de nivel superior pentru o ierarhie de directoare care utilizează încărcare implicită
pentru a construi un program care constă din multe biblioteci partajate (dar consultați „Chiar aveți nevoie de un
bibliotecă?" în makepp_cookbook, deoarece creează un program dintr-o grămadă de biblioteci partajate
nu este neapărat o idee bună):

# Makefile de nivel superior:
program : main.o **/*.la # Link în bibliotecile partajate din toate subdirectoarele.
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(intrări) -o $(ieșire) $(LIBS)

Acesta este cam tot ce aveți nevoie în fișierul makefile de nivel superior. În fiecare subdirector, tu
probabil ar face ceva de genul asta:

# Makefile în fiecare subdirector:
include standard_defs.mk # Căutări ., .., ../ .., etc până când acesta
# găsește fișierul include indicat.
# suprascrieți câteva definiții de variabile aici
SPECIAL_FLAGS := -fa_ceva_diferit

Fiecare makefile poate fi probabil cam la fel dacă comenzile pentru a construi ținte
sunt destul de asemănătoare.

În cele din urmă, ați pune următoarele în standard_defs.mk fișier (care ar trebui probabil
să fie localizat în directorul de nivel superior):

# Setări comune ale variabilelor și reguli de construcție pentru toate directoarele.
CFLAGS := -g -O2
INCLUDE_DIR := $(find_upwards include)
# Căutări ., .., ../ .., etc. pentru un fișier sau
# director numit include, deci dacă puneți
# toate fișierele dvs. incluse acolo, acest lucru va fi
# gaseste-i.
INCLUDE := -I$(INCLUDE_DIR)

%.lo : %.c
$(LIBTOOL) --mode=compilați $(CC) $(CFLAGS) $(INCLUDE) -c $(intrare) -o $(ieșire)

lib$(relativ_la ., ..).la: $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(ieșire) $(intriri)
# $(relativ_la ., ..) returnează numele curentului
# subdirectorul relativ la nivelul superior
# subdirector. Deci, dacă acest makefile este xyz/Makefile,
# această regulă va construi xyz/libxyz.la.

# Publicați fișierele includ publice în directorul de includere de nivel superior:
$(INCLUDE_DIR)/public_%.h : public_%.h
:build_check symlnk
&ln -fr $(intrare) $(ieșire)

O makefile pentru fiecare director: explicit încărcare

Dacă vrei să pui tot .o apoi fișierele într-un subdirector dependent de arhitectură
exemplul de mai sus ar trebui modificat pentru a fi ceva de genul acesta:

# Makefile de nivel superior:
MAKEFILES := $(wildcard **/Makeppfile) # Lista tuturor subdirectoarelor către
# obțineți makefile de la.

load_makefile $(MAKEFILES) # Încărcați-le pe toate în.

include standard_defs.mk # Obține comanda de compilare pentru main.o.

program : $(ARCH)/main.o */**/$(ARCH)/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(intrări) -o $(ieșire) $(LIBS)
# */**/$(ARCH) exclude subdirectorul
# $(ARCH), unde nu vrem să construim
# o bibliotecă comună.

Fiecare makefile ar fi exact la fel ca înainte:

# Makefile în fiecare subdirector:
includ standard_defs.mk
# ... variabila suprascrie aici

Şi, în sfârşit, standard_defs.mk ar conține ceva de genul următor:

# Setări comune ale variabilelor și reguli de construcție pentru toate directoarele.
ARCH ;= $(shell uname -s)-$(shell uname -m)-$(shell uname -r)
# Uneori oamenii folosesc doar $(shell uname -m), dar
# acesta va fi același pentru FreeBSD și Linux activat
# un x86. -r nu este chiar util pe Linux,
# dar este important pentru alte sisteme de operare: binare pentru
# SunOS 5.8 de obicei nu va rula pe SunOS 5.7.
&mkdir -p $(ARCH) # Asigurați-vă că există directorul de ieșire.
CFLAGS := -g -O2
INCLUDE_DIR := $(find_upwards include)
# Căutări ., .., ../ .., etc. pentru un fișier sau
# director numit include, deci dacă puneți
# toate fișierele dvs. incluse acolo, acest lucru va fi
# gaseste-i.
INCLUDE := -I$(INCLUDE_DIR)

$(ARCH)/%.lo : %.c
$(LIBTOOL) --mode=compilați $(CC) $(CFLAGS) $(INCLUDE) -c $(intrare) -o $(ieșire)

$(ARCH)/ lib$(relativ_la ., ..).la: $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(ieșire) $(intriri)
# $(relativ_la ., ..) returnează numele curentului
# subdirectorul relativ la nivelul superior
# subdirector. Deci, dacă acest makefile este xyz/Makefile,
# această regulă va construi xyz/$(ARCH)/libxyz.la.

# Copiați fișierele includ publice în directorul de includere de nivel superior:
$(INCLUDE_DIR)/public_%.h : public_%.h
&cp $(intrare) $(ieșire)

Automat efectuarea il makefiles

Dacă fișierele dvs. make sunt toate extrem de similare (ca în exemplul de mai sus), puteți spune Makepp
pentru a le construi automat dacă nu există. Doar adăugați următoarele la nivelul dvs. superior
makefile:

SUBDIRS:= $(filter_out unwanted_dir1 unwanted_dir2, $(caracter metalic */**))
$(foreach)/Makeppfile: : foreach $(SUBDIRS)
&echo „include standard_defs.mk” -o $(ieșire)
&echo „_include additional_defs.mk” -o >>$(ieșire)
# Dacă fișierul additional_defs.mk există, atunci
# va fi inclus, dar dacă nu există,
# instrucțiunea _include va fi ignorată.

Acum makefile-urile în sine vor fi construite automat.

O makefile afară at il top nivel

Dacă toate makefile-urile dvs. sunt identice, vă puteți întreba: de ce ar trebui să am câte un makefile la fiecare
nivel? De ce să nu puneți totul în fișierul makefile de nivel superior?

Da, acest lucru se poate face. Principalul dezavantaj este că devine mai greu de specificat
diferite opțiuni de construcție pentru fiecare subdirector. Un al doilea dezavantaj este că dvs
makefile va deveni probabil puțin mai greu de citit.

Iată un exemplu de a face exact asta:

# Makefile de nivel superior pentru ierarhia directoarelor. Construiește programul
# dintr-un set de biblioteci partajate ca exemplu. (Vezi avertismentele de mai sus
# de ce ați putea dori să utilizați linkuri incrementale sau altele
# abordare în loc de biblioteci partajate.)
makepp_percent_subdirs := 1 # Permite % să se potrivească cu mai multe directoare.
SUBDIRS := $(filter_out *CVS* other-nedorted_dirs $(wildcard **))
CFLAGS := -g -O2
INCLUDE := -I include

%.lo: %.c
$(LIBTOOL) --mode=compilați $(CC) $(INCLUDE) $(CFLAGS) -c $(intrare) -o $(ieșire)

$(pentru fiecare)/ lib$(notdir $(foreach)).la: $(foreach)/*.lo : foreach $(SUBDIRS)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(ieșire) $(intriri)
# Regula pentru a face toate bibliotecile.

program : main.o **/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(ieșire) $(intriri)

includes/$(notdir $(foreach)) : $(foreach) : foreach **/public_*.h
&cp $(intrare) $(ieșire)
# Exemplu de regulă pentru copierea publicului
# fișiere .h accesibile la locul potrivit.

A curat ţintă

Makefile-urile tradiționale conțin o țintă curată, care permite eliminarea a tot ceea ce a fost
construit. Există trei motive pentru care nu ar trebui să faci asta cu makepp:

1. Makepp face tot posibilul pentru a asigura o construcție corectă. Deci disperatul „nu vreau
știi ce e în neregulă”, a te face să vrei să începi de la zero este de domeniul trecutului.

2. Oamenii vor încerca uneori să economisească timp făcând două lucruri contradictorii simultan:
„curățați totul”. Acest lucru poate încurca sistemul smart wildcard al makepp, pentru că o va face
mai întâi afla faptele înainte de a face ceva. Apoi vine acțiunea curată, care face
nu-i spune lui makepp ce face (de fapt, nu poate, pentru că anulează ceva -- the
contrar a ceea ce servește un instrument de construcție). Apoi urmează „toate”, dar fișierele actualizate,
care acolo unde sunt, au dispărut în mod misterios.

3. Există comanda „makeppclean”, care face același lucru și mai eficient.

Cu toate acestea, păstrăm această secțiune istorică, deoarece vă spune ceva despre
felul în care funcționează makepp: o țintă falsă numită „curat” este doar un nume pentru un set de comenzi
eliminați toate fișierele care rezultă din procesul de creare. De obicei, o țintă curată arată
ceva de genul:

$(curat fals):
&rm -fm $(wildcard *.o .makepp_log)
# -m și .makepp_log scapă de toate deșeurile makepp.

În loc să enumerați în mod explicit fișierele pe care doriți să le ștergeți, puteți, de asemenea, să îi spuneți makepp
eliminați tot ce știe să construiască, astfel:

$(curat fals):
&rm -fm .makepp_log $(numai_ținte *)

Acest lucru are avantajul că, dacă oricare dintre fișierele dvs. sursă poate fi construit din alte fișiere,
vor fi si ele sterse; pe de altă parte, învechit .o fișiere (fișiere care erau înainte
buildable dar al cărui fișier sursă a fost eliminat de atunci) nu va fi șters.

Dacă aveți o versiune care implică makefile în mai multe directoare diferite,
makefile de nivel poate face referire la ținta „curată” (sau orice altă țintă falsă) într-o altă țintă
makefile:

# Makefile de nivel superior
SUBDIRS := sub1 sub2

# construiți reguli aici

# Curățați după construcție:
$(curat fals): $(SUBDIRS)/curat
&rm -fm .makepp_log $(numai_ținte *)

Alternativ, puteți pune ținta „curată” numai în fișierul makefile de nivel superior și o aveți
procesează toate directoarele, astfel:

$(curat fals):
&rm -fm $(numai_ținte **/*)

Utilizarea Qt's putere preprocesor
Acest exemplu arată un makefile pentru un utilitar care utilizează biblioteca Nokia Qt GUI (vezi
<http://qt.nokia.com>). Singurul lucru care este ușor neobișnuit la asta este că tu
trebuie să ruleze un preprocesor numit „moc” pe majoritatea fișierelor „.h” care conțin definiții widget,
dar nu doriți să rulați „moc” pe niciun fișier „.h” care nu utilizează macrocomanda „Q_OBJECT”.

Automat determinarea care fișiere nevoie putere fișiere

Puteți, desigur, să enumerați toate fișierele „.h” care trebuie să ruleze „moc” pe ele.
Dacă dezvoltați rapid widget-uri noi, totuși, poate fi ceva deranjant
continua să actualizezi lista din makefile. Puteți ocoli nevoia de a enumera moc
module explicit cu ceva de genul acesta:

MOC := $(QTDIR)/bin/moc
MODULE := orice module se întâmplă să ai în programul tău
MOC_MODULES := $(patsubst %.h, moc_%, $(&grep -l /Q_OBJECT/ *.h))
# Scanează toate fișierele .h pentru macrocomanda Q_OBJECT.

programul_meu: $(MODULE).o $(MOC_MODULES).o
$(CXX) $(intrări) -o $(ieșire)

moc_%.cxx: %.h # Face fișierele moc din fișierele .h.
$(MOC) $(intrare) -o $(ieșire)

%.o: %.cxx
$(CXX) $(CXXFLAGS) -c $(intrare) -o $(ieșire)

Această abordare scanează fiecare dintre dvs .h de fiecare dată când se rulează makepp, căutând fișierele
Macrocomandă „Q_OBJECT”. Sună scump, dar probabil că nu va dura deloc mult. (Cel .h
Fișierele vor trebui oricum încărcate de pe disc prin procesul de compilare, așa că o vor face
fi stocat în cache.)

#include il .moc fişier

O altă abordare este să „#include” rezultatul de la preprocesorul „moc” în widget-ul tău
dosar de implementare. Aceasta înseamnă că trebuie să vă amintiți să scrieți „#include”, dar a făcut-o
avantajul că există mai puține module de compilat și astfel compilarea merge mai rapid.
(Pentru majoritatea compilațiilor C++, cea mai mare parte a timpului este petrecută citind fișierele antet și
ieșirea de la preprocesor trebuie să includă aproape la fel de multe fișiere ca și widgetul dvs
oricum.) De exemplu:

// widgetul_meu.h
clasa MyWidget : public QWidget {
Q_OBJECT
// ...
}

// my_widget.cpp

#include „my_widget.h”
#include „my_widget.moc” // my_widget.moc este rezultatul din
// preprocesor moc.
// Alte lucruri de implementare aici.
MyWidget::MyWidget(QWidget * părinte, caracter constant * nume):
QWidget (părinte, nume)
{
// ...
}

Acum trebuie să aveți o regulă în makefile pentru a crea toate fișierele „.moc”, astfel:

MOC := $(QTDIR)/bin/moc
# Regulă pentru a face fișiere .moc:
%.moc: %.h
$(MOC) $(intrare) -o $(ieșire)

Makepp este suficient de inteligent pentru a realiza că trebuie să creeze „my_widget.moc” dacă nu
există deja sau dacă este depășit.

Această a doua abordare este cea pe care o folosesc de obicei, deoarece accelerează compilarea.

înlocuiri pentru depreciată face idiomuri
FĂCEM OBIECTIVE

Uneori, oamenii au reguli în makefile depind de ținta pe care o construiesc,
folosind variabila specială „MAKECMDGOALS”. De exemplu, uneori se vede lucruri de genul
acest:

ifneq ($(producție de filtru, $(MAKECMDGOALS)),)
CFLAGS := -O2
altfel
CFLAGS := -g
endif

Acest lucru va funcționa bine cu makepp. Cu toate acestea, recomand să nu folosiți „MAKECMDGOALS” pentru astfel de lucruri
cazuri (la fel și GNU face manual). Este mai bine să vă puneți optimizat și
compilat prin depanare .o fișiere în directoare separate sau oferindu-le prefixe diferite sau
sufixe sau folosind depozite, pentru a le păstra separate.

Probabil că singurul moment în care s-ar putea să vrei să faci referire la „MAKECMDGOALS” este dacă
durează mult timp pentru a vă încărca fișierele make și nu aveți nevoie de asta pentru ținta „curată”.
(dar nu aveți nevoie de o țintă curată). De exemplu,

ifneq ($(MAKECMDGOALS),clean)
load_makefile $(wildcard **/Makeppfile)
altfel
fără_încărcare_implicită . # Preveniți încărcarea automată a oricăror alte fișiere make.
endif

$(curat fals):
&rm -f $(caracter metalic **/*.o)

recursive face la construi in diferit directoare

Consultați „Sfaturi pentru mai multe directoare” în makepp_cookbook.

recursive face la Schimbare valoare of a variabil

Unele fișiere make se reinvocă cu o valoare diferită a unei variabile, de exemplu, depanarea
țintă în următorul fragment makefile

.PHONY: toate depanate

optimizat:
Programul $(MAKE) CFLAGS=-O2

depanare:
programul $(MAKE) CFLAGS=-g

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

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

Dacă utilizatorul scrie „make debug”, acesta construiește programul în modul implicit cu depanarea activată
în loc de optimizare.

O modalitate mai bună de a face acest lucru este să construiți două programe diferite, cu două seturi diferite de
fișiere obiect, astfel:

CFLAGS := -O2
DEBUG_FLAGS := -g
MODULE := ab

program: $(MODULE).o
$(CC) $(CFLAGS) $(intrări) -o $(ieșire)

depanare/program: depanare/$(MODULE).o
$(CC) $(DEBUG_FLAGS) $(intrări) -o $(ieșire)

%.o : %.c
$(CC) $(CFLAGS) -c $(intrare) -o $(ieșire)

depanare/%.o : %.c
$(CC) $(DEBUG_FLAGS) -c $(intrare) -o $(ieșire)

$(depanare falsă): depanare/program

Avantajul de a face acest lucru este (a) că nu trebuie să reconstruiți totul atunci când aveți
trece de la depanare la optimizat și înapoi; (b)

Cele de mai sus pot fi scrise ceva mai concis folosind depozite. Următoarele
makefile este exact echivalent:

depanare depozit=. # Face ca subdirectorul de depanare să arate ca o copie a
# subdirectorul curent.
load_makefile depanare CFLAGS=-g
# Suprascrie CFLAGS atunci când este invocat în subdirectorul de depanare
CFLAGS := -O2 # Valoarea CFLAGS atunci când este invocată în acest subdirector

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

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

$(depanare falsă): depanare/program
# Dacă utilizatorul tasta „makepp debug”, se construiește
# depanare/program în loc de program.

Diverse Sfaturi
Cum do I construi unu parte diferit doar o singura data?

Makepp face acest lucru greu de realizat, deoarece rezultatul este inconsecvent în ceea ce privește regulile.
Dar există situații în care este posibil să aveți nevoie de acest lucru, de exemplu, pentru a compila un singur modul cu
informații grele de depanare. Puteți realiza acest lucru în doi pași, construind mai întâi
dependență separat și apoi excluzând-o din faza de legătură:

makepp DEBUG=3 buggy.o # Construiește-l cu altă opțiune.
makepp --dont-build=buggy.o buggy # Folosiți-l, în ciuda opțiunii de construire „greșite”.

Cum do I face sigur my producție directoare exista?

Puteți specifica o regulă pentru a construi directorul de ieșire, apoi asigurați-vă că fiecare fișier care
merge în directorul de ieșire depinde de asta. Dar de obicei este mai ușor să faci așa ceva
acest:

# Calea clasică
dummy := $(test shell -d $(OUTPUT_DIRECTORY) || mkdir -p $(OUTPUT_DIRECTORY))
# Acest lucru este de obicei mai ușor decât să depindă toate fișierele
# $(OUTPUT_DIRECTORY) și având o regulă pentru a o face.
# Rețineți că trebuie să utilizați := în loc de = pentru a forța
# executați imediat.
# O abordare alternativă: utilizarea codului Perl, OUTPUT_DIRECTORY local var
perl_begin
-d $OUTPUT_DIRECTORY sau mkdir $OUTPUT_DIRECTORY;
perl_end
# Modul modern, nu face nimic pentru directoarele existente
&mkdir -p $(OUTPUT_DIRECTORY)

Una dintre aceste instrucțiuni ar trebui să fie aproape de partea de sus a fișierului dvs. makefile, astfel încât acestea să fie executate
înainte de orice ar putea avea nevoie de director.

Cum do I putere a comandă la a executa on fiecare construi?

Cea mai ușoară cale este să nu folosești deloc mecanismul regulilor, ci pur și simplu să-l executi, cum ar fi
acest:

dummy := $(data shell > last_build_timestamp)

Sau puneți-l într-un bloc perl, așa:

perl_begin
system("comandă de executat");
perl_end

Această abordare are dezavantajul că va fi executată chiar dacă o țintă fără legătură este
fiind condus.

O a doua abordare este de a declara fișierul ca o țintă falsă, chiar dacă este un fișier real.
Acest lucru va forța makepp să reexecuteze comanda pentru ao construi de fiecare dată, dar numai dacă aceasta
apare în lista de dependențe a unei reguli.

Cum do I scurta il afișat construi comenzi?

Adesea, există atât de multe opțiuni pentru comenzile de compilare încât ceea ce este afișat pe
ecranul este ilizibil. Puteți modifica ceea ce este afișat prin suprimarea afișajului
întreaga comandă și apoi tipăriți în mod explicit partea interesantă a comenzii. este
ușor de imprimat doar partea relevantă a comenzii folosind „$(filter_out )”, cum ar fi
acest:

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

%.o : %.c
@&echo $(notdir $(CC)) ... \
$(filter_out -I* $(ADDL_CXX_FLAGS), $(ALL_CFLAGS)) \
-c $(intrare)
@$(CC) $(ALL_CFLAGS) -c $(intrare) -o $(ieșire)

(„@” din fața comenzii suprimă tipărirea comenzii.)

Acest lucru vă va permite să vedeți majoritatea opțiunilor interesante, dar nu le va afișa pe toate
include directoare (dintre care sunt adesea foarte multe!). Daca partea te intereseaza
in este învecinat cu comanda dvs., puteți utiliza și funcția „printare” (care adaugă un
newline, deci nu vrei mai multe dintre ele):

ţintă:
@... $(printează partea interesantă)...

Cum do I converti a fişier în dependențe?

Pentru unele formate de fișiere obscure, nu merită să implementați un scanner. Într-un singur proiect
avem fișiere xml, să zicem foobar.xml care conţine dependenţele pt foobar.out:


A
b
c


Am decis să aderăm la acest aspect simplu, așa că nu trebuie să analizăm xml. Cu
builtin &sed, iată ce facem cu trei înlocuiri simple pentru cele trei tipuri de
linii:

%.d: %.xml
&sed's! !$(stem).out: \\! || s! (.+) !$$1 \\! || s! !# Gol!' \
$(intrare) -o $(ieșire)

include foobar.d

Încercarea de a include acest lucru, produce mai întâi „foobar.d”:

foobar.out: \
A \
b \
c \
# Gol

Linia goală (doar un comentariu sau chiar goală) evită să vă faceți griji cu privire la final
backslash. O alternativă care produce o listă cu mai multe linii este:

%.d: %.xml
&sed's! !$(stem).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(intrare) -o $(ieșire)

include foobar.d

Aceasta produce un echivalent:

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

Dacă aveți o rescriere mai complexă de făcut, definiți o funcție în fișierul makefile sau într-un
modul pe care îl includeți. De exemplu, nedefinirea $_ va sări peste liniile de intrare:

sub filtrul meu {
returnează undef $_ dacă /
my $stem = f_stem;
s! !$stem.out: \$((! || s! !))! || s!<.+?>!!g;
}

%.d: %.xml
&sed's! !$(stem).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(intrare) -o $(ieșire)

include foobar.d

Utilizați makepp_cookbook online folosind serviciile onworks.net


Servere și stații de lucru gratuite

Descărcați aplicații Windows și Linux

Comenzi Linux

Ad