Angielskifrancuskihiszpański

Ad


Ulubiona usługa OnWorks

makepp_cookbook - Online w chmurze

Uruchom makepp_cookbook w bezpłatnym dostawcy hostingu OnWorks w systemie Ubuntu Online, Fedora Online, emulatorze online systemu Windows lub emulatorze online systemu MAC OS

Jest to polecenie makepp_cookbook, które można uruchomić u dostawcy bezpłatnego hostingu OnWorks przy użyciu jednej z naszych wielu bezpłatnych stacji roboczych online, takich jak Ubuntu Online, Fedora Online, emulator online systemu Windows lub emulator online systemu MAC OS

PROGRAM:

IMIĘ


makepp_cookbook — Najlepszy sposób na skonfigurowanie plików Makefile dla różnych sytuacji

OPIS


Odkryłem, że praktycznie nikt nigdy nie czyta instrukcji obsługi narzędzia do tworzenia, bo szczerze mówiąc
nikogo tak naprawdę nie interesuje sam proces tworzenia — interesują nas jedynie wyniki.
Dlatego też powstała ta książka kucharska, mając nadzieję, że ludzie będą mogli zdobyć to, czego potrzebują
szybko z przykładów, bez przedzierania się przez instrukcję. To pokazuje, jak pisać
pytania, natomiast instrukcje instalacji i przeszkody znajdziesz w pliku
Często Zadawane Pytania.

Budowanie biblioteki
Do ty naprawdę potrzeba a biblioteka?

Widziałem wiele dużych programów, które składały się z dużej liczby modułów, każdy z nich
który żyje w swoim własnym katalogu. Zwykle każdy katalog jest umieszczany we własnej bibliotece,
a następnie końcowy program łączy się ze wszystkimi bibliotekami.

Myślę, że w wielu przypadkach zamiast korzystać z biblioteki, istnieje lepsze podejście. Biblioteki
nie są tak naprawdę właściwym rozwiązaniem, jeśli każdy moduł nie może zostać lub nie zostanie ponownie wykorzystany w żadnym innym
program, ponieważ wtedy uzyskasz wszystkie wady bibliotek i żadnej z nich
zalety. Biblioteki są przydatne w następujących przypadkach:

1. Kiedy masz kilka podprogramów, które muszą być powiązane z kilkoma różnymi
programów i żaden program tak naprawdę nie wykorzystuje 100% podprogramów - każdy program wykorzystuje a
inny podzbiór. W takim przypadku prawdopodobnie dobrym pomysłem jest użycie biblioteki statycznej (a
.a plik lub plik archiwum).

2. Gdy masz moduł, który powinien być powiązany z kilkoma różnymi programami i Ty
chcę ładować go dynamicznie, aby każdy program nie musiał mieć osobnej kopii
Biblioteka. Biblioteki dynamiczne mogą zaoszczędzić miejsce na plikach wykonywalnych, a czasem ulepszyć
wydajność systemu, ponieważ dla wszystkich załadowana jest tylko jedna kopia biblioteki
różne programy, które go używają.

3. Gdy czas łączenia jest zbyt długi, korzystanie z bibliotek współdzielonych w przypadku dużych fragmentów
program może znacznie przyspieszyć łącze.

Korzystanie z bibliotek statycznych ma jedną zasadniczą wadę: w niektórych systemach (np. Linux) występuje kolejność
sposób, w jaki łączysz biblioteki, jest niezwykle ważny. Linker przetwarza biblioteki
w kolejności określonej w wierszu poleceń. Chwyta wszystko, czego uważa, że ​​potrzebuje
każdej biblioteki, a następnie przechodzi do następnej biblioteki. Jeśli jakaś późniejsza biblioteka odwołuje się do a
symbol, który nie został jeszcze włączony z poprzedniej biblioteki, linker tego nie robi
wiem, żeby wrócić i pobrać ją z poprzedniej biblioteki. W rezultacie może to być konieczne
aby wyświetlić listę biblioteki wiele razy w wierszu poleceń konsolidatora. (Pracowałem nad projektem
gdzie musieliśmy trzykrotnie powtarzać całą listę bibliotek. Ten projekt jest tym, co powstało
wolę alternatywne podejście zasugerowane poniżej, czyli łączenie przyrostowe.)

Korzystanie z bibliotek dynamicznych ma kilka wad. Po pierwsze, twój program może być nieco
wolniejsze uruchamianie, jeśli biblioteka nie jest już używana przez jakiś inny program, ponieważ
trzeba go znaleźć i załadować. Po drugie, uzyskanie całej dynamiki może być naprawdę kłopotliwe
biblioteki zainstalowane we właściwych lokalizacjach; nie można po prostu skopiować pliku wykonywalnego programu,
musisz także upewnić się, że skopiowałeś wszystkie jego biblioteki. Po trzecie, w niektórych systemach
trudno jest debugować kod w bibliotekach współdzielonych, ponieważ debugery tego nie obsługują
im dobrze.

Jeśli Twój moduł nigdy nie będzie używany w żadnym innym programie, nie ma powodu, aby go używać
biblioteka: zyskujesz wszystkie wady korzystania z bibliotek i żadnych korzyści.
Techniką, którą preferuję, jest użycie łączenia przyrostowego, jeśli jest ono dostępne.

Oto jak możesz to zrobić w systemie Linux:

mój_moduł.o : $(filter_out mój_moduł.o, $(wildcard *.o))
ld -r -o $(wyjście) $(wejścia)

To spowoduje utworzenie kolejnego .o plik o nazwie mój_moduł.o, który będzie się składał
wszystkie .o pliki w tym podkatalogu. Linker rozwiąże tyle problemów
odniesienia, jak tylko może, a pozostałe odniesienia pozostawi do rozwiązania w a
kolejny etap łączenia. Na najwyższym poziomie, kiedy w końcu zbudujesz swój program,
zamiast łączyć się z libmy_module.a or libmy_module.so, po prostu powiązałbyś z
mój_moduł.o. Kiedy linkujesz .o plików, nie masz problemów z zależnością kolejności w pliku
wiersz poleceń linkera.

Letting makijaż rysunek na zewnątrz który biblioteka Moduły jest potrzebne

Nawet jeśli masz prawdziwą bibliotekę, gdzie dany program potrzebuje tylko kilku plików z niej
(a nie każdego pojedynczego modułu), makepp może być w stanie dowiedzieć się, które moduły są
potrzebne z biblioteki i uwzględniaj tylko te z pliku build. Może to zaoszczędzić kompilację
czas, jeśli rozwijasz bibliotekę wraz z programem, ponieważ nie zawracasz sobie tym głowy
skompiluj moduły biblioteczne, które nie są potrzebne dla konkretnego programu, nad którym pracujesz.

Jeśli Twoja biblioteka ściśle przestrzega konwencji, w której zadeklarowano wszystkie funkcje lub klasy
plik xyz.h są całkowicie zaimplementowane w pliku źródłowym, który kompiluje się do xyz.o (tzn. ty
nie dziel implementacji na xyz1.o i xyz2.o), wtedy możesz użyć
Funkcja „$(infer_objects)” nakazująca makeppowi wyciągnięcie tylko odpowiednich modułów z pliku
biblioteka. Może to działać zaskakująco dobrze w przypadku bibliotek zawierających nawet dziesiątki plików dołączanych.
Zasadniczo „$(infer_objects)” sprawdza listę .h dołączone pliki i wygląd
dla odpowiedniego .o akta. Jeśli szybko rozwijasz bibliotekę i program
razem może to zaoszczędzić czas kompilacji, ponieważ nigdy nie zawracasz sobie głowy kompilowaniem modułów
biblioteka, z której program nie korzysta.

Oto przykład sposobu, w jaki go używam:

mój_program: $(infer_objects *.o, $(LIB1)/*.o $(LIB2)/*.o)
$(CXX) $(wejścia) -o $(wyjścia) $(SYSTEM_LIBRARIES)

Funkcja „$(infer_objects )” zwraca swój pierwszy argument (po wykonaniu symbolu wieloznacznego
jego rozwinięcie), a także przegląda listę plików w drugim argumencie, for
pliki, których nazwa jest taka sama jak nazwa dowolnego innego .h pliki zawarte w dowolnym pliku w jego pierwszym
argument. Jeśli zostaną znalezione takie pliki, zostaną one dodane do listy.

Budowanie a statyczny biblioteka

Jeśli jesteś pewien, że rzeczywiście potrzebujesz biblioteki, a łączenie przyrostowe nie jest dostępne lub
nie jest tym, co chcesz zrobić, możesz to zrobić na kilka sposobów. Po pierwsze, oto przykład
gdzie wszystkie pliki są wyraźnie wymienione:

LIBRARY_FILES = abcde

libmine.a: $(LIBRARY_FILES).o
&rm -f $(wyjście)
$(AR) cr $(wyjście) $(wejścia)
ranlib $(wyjście) # Może nie być konieczne, w zależności od systemu operacyjnego.

&rm to wbudowane polecenie „rm” programu Makepp. Jeśli jesteś przyzwyczajony do pisania plików makefile, być może tak jest
trochę zaskoczony tym poleceniem; możesz być przyzwyczajony do czegoś bardziej takiego:

libmine.a: $(LIBRARY_FILES).o
$(AR) ru $@ $? # Niepolecane!!!!!!!
ranlib $ (wyjście)

gdzie $? (znany również jako „$(changed_inputs)”) to zmienna automatyczna, która oznacza dowolne pliki
które uległy zmianie od czasu ostatniej kompilacji biblioteki, a $@ jest mniej więcej takie samo
jako „$ (wyjście)”.

Takie podejście nie jest zalecane z kilku powodów:

· Załóżmy, że usuwasz plik źródłowy z bieżącego katalogu. Nadal jest w
biblioteka, ponieważ nie przebudowałeś biblioteki od zera. W rezultacie cokolwiek
że linki do tej biblioteki będą nieaktualne .o plik, a to może zepsuć twój plik
buduje. (Kiedyś byłem przez to całkowicie zdezorientowany, gdy próbowałem usunąć martwy kod
z projektu: ciągle usuwałem pliki i nadal były one powiązane, więc pomyślałem, że kod jest
martwy. Jednak gdy ktoś inny przebudował projekt od zera, nie połączył żadnego
więcej! Problem w tym, że stary .o pliki nadal znajdowały się w archiwum.)

Ponadto, w zależności od opcji „ar” i implementacji „ar” (np
użyj opcji „q” zamiast „r”), możesz mieć kilka wersji pliku
taki sam .o wewnątrz .a plik. Jeśli różne wersje definiują różne globale, plik
linker może spróbować wciągnąć oba. To prawdopodobnie coś złego.

Dlatego najpierw usuwamy plik biblioteki i tworzymy go od zera. To będzie
zająć nieco dłużej niż samo aktualizowanie modułów w bibliotece, ale niewiele dłużej; NA
współczesnego komputera, ilość czasu zużywanego przez ar program jest niewielki w porównaniu
do tego, co kompilator C zajmuje w typowej kompilacji, więc po prostu nie warto się tym martwić
o.

· Jednym ze sposobów, w jakie makepp stara się zagwarantować poprawne kompilacje, jest to, że tak się stanie
automatycznie odbudowuje, jeśli linia poleceń do zbudowania danego celu uległa zmianie. Ale
używając $? zmienna może powodować problemy, ponieważ przy każdej aktualizacji biblioteki,
polecenie budowania jest inne. (Możesz to stłumić za pomocą
":build_check ignorowanie_akcji"; szczegóły znajdziesz w makepp_build_check.)

· Aktualizacja archiwum zamiast jego odbudowy uniemożliwi makeppowi wykonanie tej operacji
umieść plik poprawnie w pamięci podręcznej kompilacji (więcej szczegółów znajdziesz w makepp_build_cache).

Czasami może się okazać, że wyświetlenie wszystkich plików jest dość uciążliwe, szczególnie jeśli:
projekt podlega szybkiemu rozwojowi, a lista plików stale się zmienia. To
może łatwiej będzie zbudować bibliotekę przy użyciu symboli wieloznacznych, takich jak ten:

libmine.a: $(tylko_cele *.o)
&rm $(wyjście)
$(AR) cr $(wyjście) $(wejścia)

To stawia wszystko .o pliki z bieżącego katalogu do biblioteki. Dzika karta
pasuje do każdego .o plik, który istnieje lub można go zbudować, więc będzie działać, nawet jeśli pliki nie
jeszcze istnieć.

Do wykluczania używana jest funkcja „only_targets”. .o pliki, które nie mają odpowiednich plików
plików źródłowych. Załóżmy, że masz plik o nazwie xyz.c które zwykle wkładałeś do swojego
biblioteka. Oznacza to, że istnieje xyz.o leżący plik. Teraz usuwasz xyz.c
ponieważ jest przestarzały, ale zapomniałeś usunąć xyz.o. Bez „only_targets”
funkcjonować, xyz.o nadal byłby uwzględniony na liście .o pliki zawarte w bibliotece.

Budowanie a dynamiczny biblioteka

Proces budowania bibliotek dynamicznych jest całkowicie zależny od systemu. Bardzo bym chciał
zalecamy użycie libtool do zbudowania biblioteki dynamicznej (patrz
<http://www.gnu.org/software/libtool/>), więc nie musisz wymyślać, jak to zrobić
platformę i aby plik makefile nadal działał nawet po przejściu na wersję
inny system operacyjny. Szczegóły znajdziesz w dokumentacji libtool. Oto przykładowy plik Makefile:

LIBTOOL := libtool

libflick.la : $(tylko_cele *.lo)
$(LIBTOOL) --mode=link $(CC) $(wejścia) -o $(wyjście)

%.lo: %.c
$(LIBTOOL) --mode=kompiluj $(CC) $(CFLAGS) $(ZAWIERA) -c $(dane wejściowe) -o $(wyjście)

Budowanie on kilka różne maszyny or sieci
Jednym z najbardziej irytujących problemów z plikami makefile jest to, że prawie nigdy nie działają one, gdy ty
przełączyć się na inną maszynę lub inną sieć. Jeśli Twoje pliki makefile muszą pracować
każdą możliwą maszynę na świecie, prawdopodobnie będziesz potrzebować jakiejś konfiguracji
scenariusz. Jeśli jednak musisz pracować tylko na kilku różnych maszynach, istnieje kilka sposobów
możesz podejść do tego problemu:

Zastosowanie a różne zawierać filet in cała kolekcja dotychczasowy środowiska

Na początku każdego pliku makefile możesz umieścić taką linię:

dołącz plik system_defs.mk

Plik system_defs.mk zwykle znajdowałyby się w innym miejscu dla każdego
środowisko. Jeśli chcesz, aby Twoje katalogi kompilacji były identyczne na wszystkich komputerach, umieść
system_defs.mk w katalogu powyżej katalogów kompilacji lub podaj ścieżkę dołączania
do makepp przy użyciu opcji wiersza poleceń „-I”.

Zwykle jest to dość bolesne, ale działa dobrze, jeśli jest ich ogromna liczba
różnice.

Zastosowanie if oświadczenia

To najbrzydszy sposób, ale zazwyczaj działa.

ifsys i386
CC := gcc
inaczej ifsys sun4u
CC := CC
inaczej ifsys hpux11
CC = c89
endif

Jeśli wszystko, co musisz zrobić, to znaleźć kilka programów lub bibliotek lub dołączyć pliki w różnych
miejsc, mogą istnieć lepsze sposoby (patrz poniżej).

znajdź_program, pierwszy_dostępny, Znajdź plik

Funkcje te umożliwiają przeszukiwanie różnych katalogów w systemie w celu znalezienia pliku
odpowiednie pliki. Oczywiście nie jest to tak potężne narzędzie jak skrypt konfiguracyjny, ale wydaje mi się, że tak
użyteczne. Na przykład wykonuję następujące czynności:

CXX ;= $(znajdź_program g++ c++ pg++ cxx CC aCC)
# Wybierz pierwszy kompilator C++ dostępny w PATH.
# (Nawiasem mówiąc, jeśli w ogóle nie zdefiniujesz CXX, this
# jest sposobem, w jaki jest zdefiniowany.)
TCL_INCLUDE ;= -I$(katalog_noslash $(znajdźplik 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 ) szuka tcl.h w każdym ze wskazanych
# katalogów i zwraca pełną ścieżkę. To jest wtedy
# przekonwertowany na opcję kompilacji poprzez usunięcie pliku
# nazwa pliku (opuszczenie katalogu) i poprzedzenie -I.
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(TCL_INCLUDE) $(wejście) -o $(wyjście)

TCL_LIB ;= $((pierwszy_dostępny
/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))
# Znajdź, gdzie znajduje się biblioteka Tcl. To jest wtedy wyraźnie
# wymienione w poleceniu link:
mój_program: *.o
$(CXX) $(CXXFLAGS) $(wejścia) -o $(wyjście) $(TCL_LIB)

Brać korzyść of Perla config Informacja

Powyższe techniki mogą nie być wystarczające, jeśli potrzebujesz dodatkowych informacji na temat
w systemie, na przykład, czy istnieje długi znak podwójny lub jaka jest kolejność bajtów. Jednakże,
Perl już to obliczył, więc możesz po prostu skorzystać z jego odpowiedzi.

Skrypt autokonfiguracji Perla udostępnia wszystkie informacje konfiguracyjne za pośrednictwem
skrót %Config. Nie ma składni umożliwiającej dostęp do skrótu Perla bezpośrednio w makepp, ale możesz
przejdź do Perla i ustaw zmienne skalarne, które są bezpośrednio dostępne z makepp:

perl_begin
# Pobiera wartości ze skrótu konfiguracyjnego.
użyj konfiguracji;
$CC = $Konfiguracja{'cc'}; # Kompilator C, którego użył Perl;
$byteorder_flags = "-DBYTEORDER=$Config{'kolejność bajtów'}";
$longdouble_definiowane = $Config{'d_longdbl'} eq 'zdefiniuj';
$CFLAGS_for_shared_libs = $Config{'cccdlflags'};
$LDFLAGS_for_shared_libs = $Config{'ccdlflags'};
perl_end

Ponadto, gdy już wykonasz polecenie „use Config”, możesz użyć instrukcji „$(perl )”, np.
to:

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

Wpisz „perldoc Config”, aby zobaczyć, jakie informacje są dostępne poprzez skrót %Config.

Konfiguracja Perla to dobre miejsce, aby uzyskać informacje o typach całkowitych i bajtach
kolejność i inne rzeczy, których zlokalizowanie zwykle wymaga osobnego skryptu konfiguracyjnego. Niektóre z
jego informacje dotyczące obecności rzeczy w systemie plików mogą nie być
ważny. Na przykład $Config{'cc'} odnosi się do kompilatora C, za pomocą którego zbudowano Perl,
który może nie być tym samym kompilatorem C, którego chcesz użyć. W rzeczywistości może nawet nie istnieć
w twoim systemie, ponieważ prawdopodobnie zainstalowałeś Perla poprzez pakiet binarny.

Tips dla za pomocą symbole wieloznaczne
Dopasowywanie cała kolekcja pliki z wyjątkiem a pewien podzbiór

Symbole wieloznaczne Makeppa nie umożliwiają obecnie dopasowania wszystkich plików z wyjątkiem pewien
ustawione, ale można to zrobić za pomocą kombinacji funkcji.

Załóżmy na przykład, że masz program testowy dla każdego modułu w bibliotece, ale tak nie jest
chcesz dołączyć programy testowe do biblioteki. Jeśli wszystkie programy testowe zaczynają się od
test, możesz je wykluczyć w ten sposób:

libproduction.a: $(test_filtru*, $(znak wieloznaczny *.o))

Funkcje „$(filter )” i „$(filter_out )” to bardzo potężny zestaw filtrów do wykonania
wszelkiego rodzaju operacje na przecięciach i różnicach na zbiorach. Na przykład,

PODDIRS ;= $(filter_out *test* *$(ARCH)*, $(znalezienie powłoki . -type d -print))
# Zwraca wszystkie podkatalogi, które nie mają
# „test” lub $(ARCH) w nich.

$(filtr $(patsubst katalog_testowy/test_%.o, %.o, $(wieloznaczny katalog_testowy/*.o)), \
$(znak wieloznaczny *.o))
# Zwraca listę plików .o w bieżącym pliku
# katalog, dla którego istnieje odpowiedni plik
# plik test_*.o w podkatalogu test_dir.
$(filter_out $(patsubst man/man3/%.3, %.o, $(wildcard man/man3/*.3)), \
$(znak wieloznaczny *.o))
# Zwraca listę plików .o w bieżącym pliku
# katalog, dla którego nie ma strony podręcznika
# o tej samej nazwie pliku w podkatalogu man/man3.

Korzystanie z dotychczasowy „$(tylko_cele )" funkcjonować do wyeliminować czerstwy .o pliki

Załóżmy, że budujesz program lub bibliotekę za pomocą polecenia kompilacji w następujący sposób:

program: *.o
$(CC) $(wejścia) -o $(wyjścia)

Załóżmy, że teraz usuwasz plik źródłowy. Jeśli zapomnisz usunąć odpowiedni plik .o file,
nadal będzie połączony, mimo że nie ma już możliwości jego zbudowania. w
przyszłości, makepp prawdopodobnie automatycznie rozpozna tę sytuację i wykluczy ją
listę symboli wieloznacznych, ale obecnie musisz jej powiedzieć, aby wykluczyła ją ręcznie:

program: $(tylko_cele *.o)
$(CC) $(wejścia) -o $(wyjścia)

Makepp nie zna żadnego sposobu na zbudowanie nieaktualnego .o nie ma już pliku, ponieważ jego plikiem źródłowym jest
zniknął, więc funkcja „$(only_targets )” wykluczy go z listy zależności.

Tips dla wielokrotność katalogi
Jednym z głównych powodów napisania makepp było uproszczenie obsługi wielu plików
katalogi. Makepp może łączyć polecenia kompilacji z wielu plików makepp, więc jest to możliwe
poprawnie poradzić sobie z regułą w jednym pliku makefile, który zależy od pliku zbudowanego przez a
inny plik makefile.

Co do do in miejsce of rekurencyjne robić

Makepp obsługuje funkcję rekursywną w celu zapewnienia kompatybilności wstecznej, ale jest to wysoce zalecane
Że Ty nie Użyj tego. Jeśli nie wiesz, co to jest, dobrze.

Zobacz „Lepszy system dla kompilacji hierarchicznych” w makepp, aby uzyskać szczegółowe informacje o tym, dlaczego nie chcesz
użyj marki rekurencyjnej lub wyszukaj w Internecie hasło „marka rekurencyjna uważana za szkodliwą”.

Zamiast wykonywać rekurencyjne tworzenie, aby ustawić cel „wszystkich” w każdym pliku make, tak jest
zwykle łatwiej jest pozwolić Makeppowi zorientować się, które cele będą faktycznie musiały zostać zbudowane.
Co więcej, jeśli umieścisz wszystkie swoje .o i pliki bibliotek w tym samym katalogu co plik
makefile, wówczas makepp automatycznie ustali, które pliki makefile również są potrzebne — plik
jedyne, co jest potrzebne, to sporządzenie listy potrzebnych plików na najwyższym poziomie
w ostatnim etapie łączenia. Zobacz przykłady poniżej.

jeden makefile dla każdy katalog: w domniemany załadunek

Najbardziej powszechnym sposobem obsługi wielu katalogów jest umieszczenie pliku makefile w każdym katalogu
który opisuje, jak zbudować wszystko w tym katalogu lub z niego. Jeśli umieścisz .o plików w
w tym samym katalogu co pliki źródłowe, a następnie niejawne ładowanie (patrz „Niejawne ładowanie” w
makepp_build_algorithm) automatycznie znajdzie wszystkie pliki makefile. Jeśli umieścisz swoje .o
pliki w innym katalogu (np. w podkatalogu zależnym od architektury), niż ty
prawdopodobnie będzie musiał załadować wszystkie odpowiednie pliki makefile za pomocą instrukcji „load_makefile”.

Oto przykładowy plik makefile najwyższego poziomu dla hierarchii katalogów korzystającej z niejawnego ładowania
aby zbudować program składający się z wielu bibliotek współdzielonych (ale zobacz „Czy naprawdę potrzebujesz pliku
biblioteka?” w makepp_cookbook, ponieważ tworzenie programu z kilku współdzielonych bibliotek
niekoniecznie jest dobrym pomysłem):

# Makefile najwyższego poziomu:
program : main.o **/*.la # Linki w bibliotekach współdzielonych ze wszystkich podkatalogów.
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(wejścia) -o $(wyjście) $(LIBS)

To prawie wszystko, czego potrzebujesz w pliku makefile najwyższego poziomu. W każdym podkatalogu ty
prawdopodobnie zrobiłby coś takiego:

# Makefile w każdym podkatalogu:
obejmują standard_defs.mk # Wyszukiwania ., .., ../..itd. aż do tego
# znajduje wskazany plik dołączany.
# zastąp tutaj niektóre definicje zmiennych
SPECIAL_FLAGS := -zrób_coś_różnego

Każdy plik makefile może prawdopodobnie być prawie taki sam, jeśli polecenia służące do budowania obiektów docelowych
są dość podobne.

Na koniec umieściłbyś następujące elementy w pliku standard_defs.mk plik (który prawdopodobnie powinien
znajdować się w katalogu najwyższego poziomu):

# Wspólne ustawienia zmiennych i zasady budowania dla wszystkich katalogów.
CFLAGI := -g -O2
INCLUDE_DIR := $(find_upwards zawiera)
# Wyszukiwania ., .., ../..itp. dla pliku lub
# katalog o nazwie zawiera, więc jeśli umieścisz
# wszystkie zawarte tam pliki dołączane, to będzie
# znajdź je.
ZAWIERA := -I$(INCLUDE_DIR)

%.lo: %.c
$(LIBTOOL) --mode=kompiluj $(CC) $(CFLAGS) $(ZAWIERA) -c $(dane wejściowe) -o $(wyjście)

lib$(relative_to ., ..).la: $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(wyjście) $(wejścia)
# $(relative_to ., ..) zwraca nazwę bieżącego
# podkatalog względem wyższego poziomu
# podkatalog. Więc jeśli ten plik makefile to xyz/Makefile,
# ta reguła zbuduje plik xyz/libxyz.la.

# Publikuj publiczne pliki dołączane do głównego katalogu dołączanego:
$(INCLUDE_DIR)/publiczny_%.h : publiczny_%.h
:build_check symbol
&ln -fr $(wejście) $(wyjście)

jeden makefile dla każdy katalog: wyraźny załadunek

Jeśli chcesz umieścić wszystkie swoje .o następnie pliki do podkatalogu zależnego od architektury
powyższy przykład należy zmodyfikować tak, aby wyglądał mniej więcej tak:

# Makefile najwyższego poziomu:
MAKEFILES := $(wildcard **/Makeppfile) # Lista wszystkich podkatalogów do
# pobierz pliki makefile z.

loading_makefile $(MAKEFILES) # Załaduj je wszystkie.

include standard_defs.mk # Pobierz polecenie kompilacji dla main.o.

program : $(ARCH)/main.o */**/$(ARCH)/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(wejścia) -o $(wyjście) $(LIBS)
# */**/$(ARCH) wyklucza podkatalog
# $(ARCH), gdzie nie chcemy budować
# biblioteka współdzielona.

Każdy plik makefile byłby dokładnie taki sam jak poprzednio:

# Makefile w każdym podkatalogu:
dołącz standard_defs.mk
# ... tutaj zastępuje zmienne

I w końcu, standard_defs.mk zawierałoby coś takiego:

# Wspólne ustawienia zmiennych i zasady budowania dla wszystkich katalogów.
ARCH ;= $(unazwa powłoki -s)-$(unazwa powłoki -m)-$(unazwa powłoki -r)
# Czasami ludzie używają tylko $(shell uname -m), ale
# będzie tak samo dla FreeBSD i Linuksa
#x86. Opcja -r nie jest zbyt przydatna w systemie Linux,
#, ale jest ważne dla innych systemów operacyjnych: pliki binarne dla
# SunOS 5.8 zazwyczaj nie będzie działać na SunOS 5.7.
&mkdir -p $(ARCH) # Upewnij się, że katalog wyjściowy istnieje.
CFLAGI := -g -O2
INCLUDE_DIR := $(find_upwards zawiera)
# Wyszukiwania ., .., ../..itp. dla pliku lub
# katalog o nazwie zawiera, więc jeśli umieścisz
# wszystkie zawarte tam pliki dołączane, to będzie
# znajdź je.
ZAWIERA := -I$(INCLUDE_DIR)

$(ARCH)/%.lo: %.c
$(LIBTOOL) --mode=kompiluj $(CC) $(CFLAGS) $(ZAWIERA) -c $(dane wejściowe) -o $(wyjście)

$(ŁUK)/ lib$(relative_to ., ..).la: $(only_targets *.lo)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(wyjście) $(wejścia)
# $(relative_to ., ..) zwraca nazwę bieżącego
# podkatalog względem wyższego poziomu
# podkatalog. Więc jeśli ten plik makefile to xyz/Makefile,
# ta reguła zbuduje plik xyz/$(ARCH)/libxyz.la.

# Skopiuj publiczne pliki dołączane do katalogu najwyższego poziomu:
$(INCLUDE_DIR)/publiczny_%.h : publiczny_%.h
&cp $(wejście) $(wyjście)

Automatycznie zrobienie dotychczasowy makefile

Jeśli wszystkie Twoje pliki makefile są bardzo podobne (jak w powyższym przykładzie), możesz powiedzieć Makeppowi
aby je automatycznie zbudować, jeśli nie istnieją. Po prostu dodaj następujące elementy do swojego najwyższego poziomu
plik makefile:

PODKATALOGI := $(filter_out niechciany_katalog1 niechciany_katalog2, $(symbol wieloznaczny */**))
$(foreach)/Makeppfile: : foreach $(SUBDIRS)
&echo "włącz standard_defs.mk" -o $(wyjście)
&echo "_include dodatkowy_defs.mk" -o >>$(wyjście)
# Jeśli plik extra_defs.mk istnieje, to
# zostanie uwzględniony, ale jeśli nie istnieje,
# instrukcja _include zostanie zignorowana.

Teraz same pliki makefile zostaną zbudowane automatycznie.

jeden makefile tylko at dotychczasowy Top poziom

Jeśli wszystkie twoje pliki makefile są identyczne, możesz zapytać: dlaczego powinienem mieć plik makefile w każdym z nich
poziom? Dlaczego nie umieścić tego wszystkiego w pliku makefile najwyższego poziomu?

Tak, można to zrobić. Główną wadą jest to, że trudniej jest określić
różne opcje kompilacji dla każdego podkatalogu. Drugą wadą jest to, że Twój
makefile prawdopodobnie stanie się nieco trudniejszy do odczytania.

Oto przykład takiego działania:

# Plik makefile najwyższego poziomu dla hierarchii katalogów. Tworzy program
# z zestawu bibliotek współdzielonych jako przykład. (Patrz zastrzeżenia powyżej
# dlaczego możesz chcieć użyć łączenia przyrostowego lub innego
# podejście zamiast bibliotek współdzielonych.)
makepp_percent_subdirs := 1 # Zezwalaj % na dopasowanie wielu katalogów.
PODKATALOGI := $(filter_out *CVS* inne-niechciane_katalogi $(znak wieloznaczny **))
CFLAGI := -g -O2
ZAWIERA := -Zawiera

%.lo: %.c
$(LIBTOOL) --mode=kompiluj $(CC) $(ZAWIERA) $(CFLAGS) -c $(wejście) -o $(wyjście)

$(foreach)/ lib$(notdir $(foreach)).la: $(foreach)/*.lo : foreach $(SUBDIRS)
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(wyjście) $(wejścia)
# Reguła tworzenia wszystkich bibliotek.

program: main.o **/*.la
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o $(wyjście) $(wejścia)

zawiera/$(notdir $(foreach)): $(foreach): foreach **/public_*.h
&cp $(wejście) $(wyjście)
# Przykładowa reguła kopiowania pliku public
# dostępne pliki .h we właściwym miejscu.

A kleń cel

Tradycyjne pliki makefile zawierają czysty cel, który umożliwia usunięcie wszystkiego, co było
wybudowany. Są trzy powody, dla których nie powinieneś tego robić z makepp:

1. Makepp dokłada wszelkich starań, aby zapewnić prawidłową kompilację. Zatem desperackie „Nie
wiedzieć, co jest nie tak”, zmuszanie do zaczynania od zera należy już do przeszłości.

2. Czasami ludzie próbują zaoszczędzić czas, robiąc dwie sprzeczne rzeczy na raz:
„oczyśćcie wszystko”. Może to zmylić inteligentny system symboli wieloznacznych Makepp, ponieważ tak się stanie
najpierw zapoznaj się z faktami, zanim cokolwiek zrobisz. Potem przychodzi czysta akcja, która działa
nie mów Makeppowi, co robi (w rzeczywistości nie może, ponieważ coś cofa - plik
w przeciwieństwie do tego, do czego służy narzędzie do kompilacji). Potem przychodzi „wszystko”, ale aktualne pliki,
które tam, gdzie tam, w tajemniczy sposób zniknęły.

3. Istnieje polecenie „makeppclean”, które robi to samo, ale wydajniej.

Niemniej jednak zachowujemy tę sekcję historyczną, ponieważ mówi ona coś o
sposób działania makepp: fałszywy cel o nazwie „clean” to tylko nazwa zestawu poleceń
usuń wszystkie pliki powstałe w procesie tworzenia. Zwykle wygląda na czysty cel
coś takiego:

$(fałszywie czysty):
&rm -fm $(znak wieloznaczny *.o .makepp_log)
# -m i .makepp_log usuwają wszystkie śmieci związane z makeppem.

Zamiast jawnie wymieniać pliki, które chcesz usunąć, możesz także powiedzieć makeppowi
usuń wszystko, co umie zbudować, na przykład:

$(fałszywie czysty):
&rm -fm .makepp_log $(only_targets *)

Ma to tę zaletę, że jeśli którykolwiek z plików źródłowych można zbudować z innych plików,
one również zostaną usunięte; z drugiej strony nieaktualne .o pliki (pliki, które kiedyś były
do zbudowania, ale którego plik źródłowy został już usunięty) nie zostaną usunięte.

Jeśli masz kompilację zawierającą pliki makefile w kilku różnych katalogach, Twój top-
plik makefile na poziomie może odwoływać się do „czystego” celu (lub dowolnego innego fałszywego celu) w innym
plik makefile:

# Plik makefile najwyższego poziomu
PODKATALOGI := sub1 sub2

# reguły kompilacji tutaj

# Posprzątaj po kompilacji:
$(fałszywe czyste): $(SUBDIRS)/czyste
&rm -fm .makepp_log $(only_targets *)

Alternatywnie możesz umieścić swój „czysty” cel tylko w pliku makefile najwyższego poziomu i mieć go
przetwarzaj wszystkie katalogi w następujący sposób:

$(fałszywie czysty):
&rm -fm $(only_targets **/*)

Korzystanie z Qt Moc preprocesor
Ten przykład pokazuje plik makefile dla narzędzia korzystającego z biblioteki Qt GUI firmy Nokia (patrz
<http://qt.nokia.com>). Jedyną rzeczą, która jest w tym nieco niezwykła, jest to, że ty
musi uruchomić preprocesor o nazwie „moc” na większości plików „.h”, które zawierają definicje widżetów,
ale nie chcesz uruchamiać „moc” na plikach „.h”, które nie używają makra „Q_OBJECT”.

Automatycznie określaniu który pliki potrzeba Moc pliki

Można oczywiście po prostu wypisać wszystkie pliki „.h”, na których musi działać „moc”.
Jeśli jednak szybko tworzysz nowe widżety, może to być dość uciążliwe
aktualizuj listę w pliku makefile. Można obejść potrzebę wystawienia listy moc
moduły jawnie za pomocą czegoś takiego:

MOC := $(QTDIR)/bin/moc
MODUŁY := dowolne moduły, które masz w swoim programie
MOC_MODULES := $(patsubst %.h, moc_%, $(&grep -l /Q_OBJECT/ *.h))
# Skanuje wszystkie pliki .h w poszukiwaniu makra Q_OBJECT.

mój_program: $(MODULES).o $(MOC_MODULES).o
$(CXX) $(wejścia) -o $(wyjścia)

moc_%.cxx: %.h # Tworzy pliki moc z plików .h.
$(MOC) $(wejście) -o $(wyjście)

%.o: %.cxx
$(CXX) $(CXXFLAGS) -c $(wejście) -o $(wyjście)

To podejście skanuje każdy plik .h pliki za każdym razem, gdy uruchamiany jest makepp, szukając pliku
Makro „Q_OBJECT”. Brzmi to drogo, ale prawdopodobnie nie zajmie dużo czasu. (T .h
wszystkie pliki i tak będą musiały zostać załadowane z dysku w procesie kompilacji, więc tak się stanie
być buforowane.)

#zawierać dotychczasowy .moc filet

Innym podejściem jest „#include” danych wyjściowych preprocesora „moc” w widżecie
plik implementacyjny. Oznacza to, że musisz pamiętać o napisaniu „#include”, ale tak się stało
zaletą jest to, że jest mniej modułów do skompilowania, dzięki czemu kompilacja przebiega szybciej.
(W przypadku większości kompilacji C++ większość czasu spędza się na czytaniu plików nagłówkowych i
dane wyjściowe preprocesora muszą zawierać prawie tyle plików, ile ma Twój widget
w każdym razie.) Na przykład:

// mój_widget.h
klasa MójWidget: publiczny QWidget {
Q_OBJECT
//...
}

// mój_widget.cpp

#include „my_widget.h”
#include "my_widget.moc" // my_widget.moc to wynik pliku
// moc preprocesora.
// Tutaj inne rzeczy związane z implementacją.
MyWidget::MyWidget(QWidget * element nadrzędny, stały znak * nazwa):
QWidget(rodzic, nazwa)
{
//...
}

Teraz musisz mieć regułę w swoim pliku makefile, aby utworzyć wszystkie pliki „.moc”, tak jak poniżej:

MOC := $(QTDIR)/bin/moc
# Reguła tworzenia plików .moc:
%.moc: %.h
$(MOC) $(wejście) -o $(wyjście)

Makepp jest na tyle mądry, że zdaje sobie sprawę, że jeśli tak się nie stanie, musi utworzyć „my_widget.moc”.
już istnieje lub jest nieaktualny.

To drugie podejście jest tym, którego zwykle używam, ponieważ przyspiesza kompilację.

Części zamienne dla przestarzałe robić frazeologia
MAKECMDGOALS

Czasami ludzie mają w swoich plikach makefile reguły zależne od tego, jaki cel budują,
przy użyciu specjalnej zmiennej „MAKECMDGOALS”. Na przykład czasami widzi się takie rzeczy jak
to:

ifneq ($(produkcja filtra, $(MAKECMDGOALS)),)
CFLAGI := -O2
więcej
CFLAGI := -g
endif

To będzie działać dobrze z makepp. Zalecam jednak nie używać w tym przypadku „MAKECMDGOALS”.
przypadkach (podobnie jak GNU tworzy podręcznik). Lepiej jest umieścić zoptymalizowane i
skompilowany debugowo .o pliki w oddzielnych katalogach lub nadając im różne przedrostki lub
przyrostków lub korzystania z repozytoriów, aby zachować je oddzielnie.

Prawdopodobnie jedyny moment, w którym możesz chcieć odwołać się do „MAKECMDGOALS”, ma miejsce wtedy, gdy jest to możliwe
Ładowanie plików makefile zajmuje dużo czasu i nie jest to potrzebne w przypadku „czystego” celu
(ale nie potrzebujesz czystego celu). Na przykład,

ifneq ($(MAKECMDGOALS),wyczyść)
plik_załaduj_make $(znak wieloznaczny **/plikMakepp)
więcej
no_implicit_load . # Zapobiegaj automatycznemu ładowaniu innych plików makefile.
endif

$(fałszywie czysty):
&rm -f $(znak wieloznaczny **/*.o)

Rekurencyjne robić do budować in różne katalogi

Zobacz „Wskazówki dotyczące wielu katalogów” w makepp_cookbook.

Rekurencyjne robić do zmiana wartość of a zmienna

Niektóre pliki makefile wywołują się ponownie z inną wartością zmiennej, np. debug
target w następującym fragmencie pliku makefile

.PHONY: wszystkie debugowania

zoptymalizowany:
Program $(MAKE) CFLAGS=-O2

odpluskwić:
$(MAKE) program CFLAGS=-g

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

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

Jeśli użytkownik wpisze „make debug”, program zbuduje program w trybie domyślnym z włączoną opcją debugowania
zamiast z optymalizacją.

Lepszym sposobem na osiągnięcie tego jest zbudowanie dwóch różnych programów z dwoma różnymi zestawami
pliki obiektowe, takie jak ten:

CFLAGI := -O2
DEBUG_FLAGS := -g
MODUŁY := ok

program: $(MODUŁY).o
$(CC) $(CFLAGS) $(wejścia) -o $(wyjście)

debug/program: debug/$(MODULES).o
$(CC) $(DEBUG_FLAGS) $(wejścia) -o $(wyjście)

%.o: %.c
$(CC) $(CFLAGS) -c $(wejście) -o $(wyjście)

debug/%.o: %.c
$(CC) $(DEBUG_FLAGS) -c $(wejście) -o $(wyjście)

$(fałszywe debugowanie): debugowanie/program

Zaletą takiego działania jest to, że (a) nie musisz odbudowywać wszystkiego, kiedy to zrobisz
przełącz się z debugowania na zoptymalizowane i z powrotem; (B)

Powyższe można zapisać nieco bardziej zwięźle, korzystając z repozytoriów. Następujące
makefile jest dokładnie równoważny:

debugowanie repozytorium=. # Sprawia, że ​​podkatalog debugowania wygląda jak kopia
# bieżący podkatalog.
debugowanie pliku_makefile CFLAGS=-g
# Zastąp CFLAGS po wywołaniu w podkatalogu debugowania
CFLAGS := -O2 # Wartość CFLAGS wywołana w tym podkatalogu

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

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

$(fałszywe debugowanie): debugowanie/program
# Jeśli użytkownik wpisze „debug makepp”, builds
# debug/program zamiast programu.

Różne wskazówki
W jaki sposób do I budować pierwszej część różnie właśnie raz?

Makepp utrudnia to, ponieważ wynik jest niezgodny z zasadami.
Są jednak sytuacje, w których możesz tego potrzebować, np. do skompilowania tylko jednego modułu
ciężkie informacje dotyczące debugowania. Można to osiągnąć w dwóch krokach, najpierw budując plik
zależność osobno, a następnie wykluczenie jej z fazy łączenia:

makepp DEBUG=3 buggy.o # Zbuduj go z inną opcją.
makepp --dont-build=buggy.o buggy # Użyj go pomimo „złej” opcji kompilacji.

W jaki sposób do I robić pewnie my wydajność katalogi istnieć?

Możesz określić regułę tworzenia katalogu wyjściowego, a następnie upewnić się, że każdy plik
idzie do katalogu wyjściowego, zależy od tego. Ale zazwyczaj łatwiej jest zrobić coś takiego
to:

# Klasyczny sposób
manekin := $(test powłoki -d $(KATALOG WYJŚCIOWY) || mkdir -p $(KATALOG WYJŚCIOWY))
# Zwykle jest to łatwiejsze niż uzależnianie wszystkich plików od
# $(OUTPUT_DIRECTORY) i posiadanie reguły, aby to zrobić.
# Pamiętaj, że musisz użyć := zamiast =, aby to wymusić
# wykonaj natychmiast.
# Alternatywne podejście: użycie kodu Perla, lokalnej zmiennej OUTPUT_DIRECTORY
perl_begin
-d $KATALOG_WYJŚCIOWY lub mkdir $KATALOG_WYJŚCIOWY;
perl_end
# Nowoczesny sposób nie robi nic dla istniejących katalogów
&mkdir -p $(KATALOG_WYJŚCIOWY)

Jedna z tych instrukcji powinna znajdować się na górze pliku makefile, aby została wykonana
przed wszystkim, co mogłoby potrzebować katalogu.

W jaki sposób do I siła a komenda do wykonać on każdy zbudować?

Najłatwiej jest w ogóle nie używać mechanizmu reguł, ale po prostu go wykonać, np
to:

manekin := $(data powłoki > last_build_timestamp)

Lub umieść go w bloku Perla, w ten sposób:

perl_begin
system("polecenie do wykonania");
perl_end

To podejście ma tę wadę, że zostanie wykonane, nawet jeśli zostanie wykonany niepowiązany cel
jest prowadzony.

Drugie podejście polega na zadeklarowaniu pliku jako fałszywego celu, nawet jeśli jest to prawdziwy plik.
Zmusi to makepp do ponownego wykonania polecenia kompilacji za każdym razem, ale tylko wtedy, gdy tak się stanie
pojawia się na liście zależności jakiejś reguły.

W jaki sposób do I skracać dotychczasowy wystawiany budować polecenia?

Często opcji poleceń kompilacji jest tak wiele, że to, co jest wyświetlane na ekranie
ekran jest nieczytelny. Możesz zmienić to, co jest wyświetlane, wyłączając wyświetlanie
całe polecenie, a następnie jawnie wydrukuj interesującą część polecenia. Jego
łatwo wydrukować tylko odpowiednią część polecenia, używając „$(filter_out )”, np
to:

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

%.o: %.c
@&echo $(notdir $(CC)) ... \
$(filter_out -I* $(ADDL_CXX_FLAGS), $(ALL_CFLAGS)) \
-c $(wejście)
@$(CC) $(ALL_CFLAGS) -c $(wejście) -o $(wyjście)

(Znak „@” przed poleceniem zapobiega drukowaniu polecenia.)

Umożliwi to wyświetlenie większości interesujących opcji, ale nie wyświetli wszystkich
zawierać katalogi (których często jest bardzo wiele!). Jeśli część Cię interesuje
in jest ciągły w twoim poleceniu, możesz także użyć funkcji „drukuj” (która dodaje a
newline, więc nie chcesz ich kilku):

cel:
@... $(wydrukuj interesującą część) ...

W jaki sposób do I konwertować a filet najnowszych zależności?

W przypadku niektórych mało znanych formatów plików nie warto instalować skanera. W jednym projekcie
mamy, powiedzmy, pliki XML foobar.xml który zawiera zależności dla foobar.out:


A
B
C


Zdecydowaliśmy się zastosować ten prosty układ, więc nie musimy analizować pliku XML. Z
wbudowany &sed, oto co robimy z trzema prostymi podstawieniami dla trzech rodzajów
kwestia:

%.d: %.xml
&sed! !$(rdzeń).out: \\! || S! (.+) !$$1 \\! || S! !# Pusty!' \
$(wejście) -o $(wyjście)

dołącz foobar.d

Próba uwzględnienia tego powoduje wygenerowanie najpierw pliku „foobar.d”:

foobar.out: \
A \
B \
C \
# Pusty

Pusta (tylko komentarz lub naprawdę pusta) linia pozwala uniknąć martwienia się o koniec
ukośnik wsteczny. Alternatywą tworzenia listy wielowierszowej jest:

%.d: %.xml
&sed! !$(rdzeń).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(wejście) -o $(wyjście)

dołącz foobar.d

Daje to odpowiednik:

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

Jeśli masz bardziej złożone przepisanie, zdefiniuj funkcję w pliku makefile lub w pliku a
moduł, który dołączysz. Np. undefiniing $_ spowoduje pominięcie linii wejściowych:

sub mój filtr {
zwróć undef $_ jeśli /
mój $rdzeń = f_stem;
S! !$stem.out: \$((! || s! !))! || s!<.+?>!!g;
}

%.d: %.xml
&sed! !$(rdzeń).out: \$$((! || s! !))! || s!<.+?>!!g' \
$(wejście) -o $(wyjście)

dołącz foobar.d

Korzystaj z makepp_cookbook online, korzystając z usług onworks.net


Darmowe serwery i stacje robocze

Pobierz aplikacje Windows i Linux

Komendy systemu Linux

Ad