2009-07-03 11 views
147

Mam następujący plik Makefile dla mojego projektu i chciałbym go skonfigurować do wydania i debugowania kompilacji. W moim kodzie mam wiele makr #ifdef DEBUG w miejscu, więc jest to po prostu kwestia ustawienia tego makra i dodania flag do -g3 -gdwarf2 kompilatorów. Jak mogę to zrobić?Jak mogę skonfigurować plik Makefile do kompilacji debugowania i wydania?

$(CC) = g++ -g3 -gdwarf2 
$(cc) = gcc -g3 -gdwarf2 

all: executable 

executable: CommandParser.tab.o CommandParser.yy.o Command.o 
    g++ -g -o output CommandParser.yy.o CommandParser.tab.o Command.o -lfl 

CommandParser.yy.o: CommandParser.l 
    flex -o CommandParser.yy.c CommandParser.l 
    gcc -g -c CommandParser.yy.c 

CommandParser.tab.o: CommandParser.y 
    bison -d CommandParser.y 
    g++ -g -c CommandParser.tab.c 

Command.o: Command.cpp 
    g++ -g -c Command.cpp 

clean: 
    rm -f CommandParser.tab.* CommandParser.yy.* output *.o 

Właśnie w celu wyjaśnienia, kiedy mówię, uwolnienie/debug buduje, Chcę móc wpisz make i uzyskać kompilacji uwolnienia lub make debug i uzyskać build debugowania, bez ręcznego zakomentowanie rzeczy w makefile.

+10

Uwaga! $ (CC) = coś jest inne niż CC = coś – levif

+3

Wykonywany cel narusza złotą regułę plików Makefile: każdy cel powinien zaktualizować plik nazywający cel, w twoim przypadku "wykonywalny". – JesperE

+2

^A jeśli nie, to powinno być zadeklarowane '.PHONY' –

Odpowiedz

151

Można użyć Target-specific Variable Values. Przykład:

CXXFLAGS = -g3 -gdwarf2 
CCFLAGS = -g3 -gdwarf2 

all: executable 

debug: CXXFLAGS += -DDEBUG -g 
debug: CCFLAGS += -DDEBUG -g 
debug: executable 

executable: CommandParser.tab.o CommandParser.yy.o Command.o 
    $(CXX) -o output CommandParser.yy.o CommandParser.tab.o Command.o -lfl 

CommandParser.yy.o: CommandParser.l 
    flex -o CommandParser.yy.c CommandParser.l 
    $(CC) -c CommandParser.yy.c 

Pamiętaj o używaniu $ (CXX) lub $ (CC) we wszystkich komendach kompilacji.

Następnie "make debug" będzie zawierał dodatkowe flagi, takie jak -DDEBUG i -g, gdzie "make" nie będzie.

Na marginesie, możesz zrobić Makefile o wiele bardziej zwięzły, jak sugerowały inne posty.

+40

Nie należy nigdy zmieniać CXX lub CC w Makefile lub BadThingsMayHappen (TM), zawierają one ścieżkę i/lub nazwę plików wykonywalnych do uruchomienia. CPPFLAGS, CXXFLAGS i CFLAGS służą do tego celu. – Barry

+1

@JesperE: skopiuj i wklej komentarz do oryginalnego wpisu i posprzątaj po sobie. –

+0

Jak zmienić nazwę pliku wykonywalnego w pliku debugowania, na przykład, aby dołączyć _d do tej nazwy pliku? – manatttta

35

Jeśli przez configure wydania/build, to znaczy, że wystarczy jeden config za makefile, to jest po prostu kwestia i oddzielenie CC i CFLAGS:

CFLAGS=-DDEBUG 
#CFLAGS=-O2 -DNDEBUG 
CC=g++ -g3 -gdwarf2 $(CFLAGS) 

W zależności od tego, czy można użyć gnu makefile, ty można użyć warunkowy, aby ta nieco bardziej wyszukane i kontrolować je z wiersza poleceń:

DEBUG ?= 1 
ifeq ($(DEBUG), 1) 
    CFLAGS =-DDEBUG 
else 
    CFLAGS=-DNDEBUG 
endif 

.o: .c 
    $(CC) -c $< -o [email protected] $(CFLAGS) 

a następnie użyć:

make DEBUG=0 
make DEBUG=1 

Jeśli potrzebujesz kontrolować obie konfiguracje w tym samym czasie, myślę, że lepiej jest mieć katalogi kompilacji i jeden katalog kompilacji/config.

+18

Nie wiem, czy robię coś dziwnego, ale żeby uzyskać polecenie debugowania, jeśli instrukcja ma działać (' ifeq (DEBUG, 1) ') dla mnie zmienna 'DEBUG' potrzebna jest owinięta w nawiasach takich jak:' ifeq ($ (DEBUG), 1) '. – shanet

2

można mieć zmienną

DEBUG = 0 

następnie można użyć instrukcji warunkowej

ifeq ($(DEBUG),1) 

    else 

    endif 
1

ukończeniu odpowiedzi od wcześniej ... Trzeba odwoływać się do zmiennych można zdefiniować informacje w twoich poleceń ...

DEBUG ?= 1 
ifeq (DEBUG, 1) 
    CFLAGS =-g3 -gdwarf2 -DDEBUG 
else 
    CFLAGS=-DNDEBUG 
endif 

CXX = g++ $(CFLAGS) 
CC = gcc $(CFLAGS) 

all: executable 

executable: CommandParser.tab.o CommandParser.yy.o Command.o 
    $(CXX) -o output CommandParser.yy.o CommandParser.tab.o Command.o -lfl 

CommandParser.yy.o: CommandParser.l 
    flex -o CommandParser.yy.c CommandParser.l 
    $(CC) -c CommandParser.yy.c 

CommandParser.tab.o: CommandParser.y 
    bison -d CommandParser.y 
    $(CXX) -c CommandParser.tab.c 

Command.o: Command.cpp 
    $(CXX) -c Command.cpp 

clean: 
    rm -f CommandParser.tab.* CommandParser.yy.* output *.o 
21

Pamiętaj, że możesz również uprościć Makefile, w tym samym czasie:

DEBUG ?= 1 
ifeq (DEBUG, 1) 
    CFLAGS =-g3 -gdwarf2 -DDEBUG 
else 
    CFLAGS=-DNDEBUG 
endif 

CXX = g++ $(CFLAGS) 
CC = gcc $(CFLAGS) 

EXECUTABLE = output 
OBJECTS = CommandParser.tab.o CommandParser.yy.o Command.o 
LIBRARIES = -lfl 

all: $(EXECUTABLE) 

$(EXECUTABLE): $(OBJECTS) 
    $(CXX) -o [email protected] $^ $(LIBRARIES) 

%.yy.o: %.l 
    flex -o $*.yy.c $< 
    $(CC) -c $*.yy.c 

%.tab.o: %.y 
    bison -d $< 
    $(CXX) -c $*.tab.c 

%.o: %.cpp 
    $(CXX) -c $< 

clean: 
    rm -f $(EXECUTABLE) $(OBJECTS) *.yy.c *.tab.c 

Teraz nie trzeba powtarzać nazw plików w każdym miejscu. Wszystkie pliki .l zostaną przekazane za pomocą flex i gcc, wszelkie pliki .y zostaną przekazane przez bison i g ++, a także pliki .cpp za pośrednictwem g ++.

Wystarczy wymienić .o pliki można oczekiwać, aby skończyć z, i Make będzie wykonywać pracę na zastanawianie się, jakie przepisy mogą zaspokoić potrzeby ...

dla przypomnienia:

  • [email protected] nazwa pliku docelowego (jeden przed grubego)

  • $< Nazwa pierwszej (lub jedyną) warunkiem pliku (pierwszy po okrężnicy)

  • $^ Nazwy wszystkich wstępnych plików (oddzielonych spacjami)

  • $* trzpieniu (bit który odpowiada % wieloznaczny w definicji reguły.

+0

W sekcji "dla rekordu" jest zdefiniowany jeden element o różnych opisach. Według http://www.gnu.org/software/make/manual/make.html#Automatic-Variables, '$ ^' dotyczy wszystkich wymaganych plików. –

+0

Dzięki za to Grant - poprawiono literówkę! (Sprawdziłem plik Makefile i okazało się, że użyłem go tam poprawnie, ale pisałem wyjaśnienie.) – Stobor

+2

Chciałbym, żeby było więcej tych krótkich poradników do pisania rozsądnie małych plików Makefile, w tym zmiennych automatycznych. – AzP

4

Zgadzam się z @davidlin, użyj zmiennych specyficznych dla celu, aby ustawić różne flagi dla każdej konfiguracji. Prawdopodobnie jednak chcesz umieścić pliki wyjściowe w osobnych katalogach, aby można było budować różne konfiguracje bez odbudowywania wszystkiego. Korzystanie @ przykład davidlin jest, można zrobić:

debug: CONFIG=debug 

debug: executable 

executable: output/$(CONFIG)/myprog 

# Always use "[email protected]" when referring to the target file. 
output/$(CONFIG)/myprog: ... 
     $(CXX) ... -o [email protected] 
+1

Podoba mi się to podejście, ale czy nie musisz ustawić wartości domyślnej dla "KONFIG"? W przeciwnym razie wygląda na to, że 'executable' zbuduje' output // myprog' – MestreLion

+0

Prawdopodobnie. Nie mogę tego teraz sprawdzić. – JesperE

+0

Jak uzyskać '$ (CONFIG)' do oceny w innych obiektach docelowych * po ustawieniu * 'debug'? – jozxyqk

29

Kwestia ta pojawiła się często przy poszukiwaniu podobnego problemu, więc czuję się w pełni wdrożone rozwiązanie jest uzasadnione. Zwłaszcza, że ​​ja (i chciałbym założyć, że inni) zmagali się z różnymi odpowiedziami.

Poniżej znajduje się przykładowy plik Makefile, który obsługuje wiele typów kompilacji w osobnych katalogach. Przedstawiony przykład pokazuje kompilacje debugowania i wydania.

Podpory ...

  • oddzielne katalogi projektów dla konkretnych buduje
  • łatwy wybór kompilacji domyślny docelowej
  • milczy docelową prep do tworzenia katalogów potrzebnych do budowania projektu
  • build-specyficzny znaczniki konfiguracji kompilatora
  • GNU Make naturalna metoda określania, czy projekt wymaga przebudowy
  • zasady wzór zamiast przestarzałych zasad przyrostek

# 
# Compiler flags 
# 
CC  = gcc 
CFLAGS = -Wall -Werror -Wextra 

# 
# Project files 
# 
SRCS = file1.c file2.c file3.c file4.c 
OBJS = $(SRCS:.c=.o) 
EXE = exefile 

# 
# Debug build settings 
# 
DBGDIR = debug 
DBGEXE = $(DBGDIR)/$(EXE) 
DBGOBJS = $(addprefix $(DBGDIR)/, $(OBJS)) 
DBGCFLAGS = -g -O0 -DDEBUG 

# 
# Release build settings 
# 
RELDIR = release 
RELEXE = $(RELDIR)/$(EXE) 
RELOBJS = $(addprefix $(RELDIR)/, $(OBJS)) 
RELCFLAGS = -O3 -DNDEBUG 

.PHONY: all clean debug prep release remake 

# Default build 
all: prep release 

# 
# Debug rules 
# 
debug: $(DBGEXE) 

$(DBGEXE): $(DBGOBJS) 
    $(CC) $(CFLAGS) $(DBGCFLAGS) -o $(DBGEXE) $^ 

$(DBGDIR)/%.o: %.c 
    $(CC) -c $(CFLAGS) $(DBGCFLAGS) -o [email protected] $< 

# 
# Release rules 
# 
release: $(RELEXE) 

$(RELEXE): $(RELOBJS) 
    $(CC) $(CFLAGS) $(RELCFLAGS) -o $(RELEXE) $^ 

$(RELDIR)/%.o: %.c 
    $(CC) -c $(CFLAGS) $(RELCFLAGS) -o [email protected] $< 

# 
# Other rules 
# 
prep: 
    @mkdir -p $(DBGDIR) $(RELDIR) 

remake: clean all 

clean: 
    rm -f $(RELEXE) $(RELOBJS) $(DBGEXE) $(DBGOBJS) 
+0

Jak to zmienić, aby umożliwić budowanie źródła plików w katalogu innym niż ten, w którym znajduje się Makefile? –

+0

@JeffersonHudson Jeśli pliki źródłowe znajdują się w katalogu o nazwie 'src', zmodyfikuj wiersz' SRCS = plik1.c plik2.c plik3.c plik4.c', aby odczytać 'SRCS = src/file1.c src/file2.c src/file3.c src/file4.c'. – zero2cx

1

ifeq (DEBUG, 1) należy zastąpić ifeq ($(DEBUG), 1).

Powiązane problemy