Jak mogę skonfigurować mój plik makefile do debugowania i kompilacji wydania?

175

Mam następujący plik makefile dla mojego projektu i chciałbym go skonfigurować do wydania i debugowania kompilacji. W moim kodzie mam wiele #ifdef DEBUGmakr, więc wystarczy ustawić to makro i dodać -g3 -gdwarf2flagi do kompilatorów. W jaki sposób mogę to zrobić?

$(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

Dla wyjaśnienia, kiedy mówię o kompilacjach do wydania / debugowania, chcę mieć możliwość wpisania makei pobrania kompilacji wydania lub kompilacji make debugdo debugowania, bez ręcznego komentowania rzeczy w pliku makefile.

samoz
źródło
12
Uwaga! $ (CC) = coś jest inne niż CC = coś
levif
4
Cel wykonywalny narusza złotą zasadę makefile: każdy cel powinien zaktualizować plik, nazywając go docelowym, w twoim przypadku „wykonywalny”.
JesperE
3
^ A jeśli tak się nie stanie, należy to zadeklarować.PHONY
podkreślenie_d

Odpowiedzi:

192

Możesz użyć wartości zmiennych specyficznych dla celu . 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, aby używać $ (CXX) lub $ (CC) we wszystkich poleceniach kompilacji.

Wtedy 'make debug' będzie miał dodatkowe flagi, takie jak -DDEBUG i -g, gdzie jako 'make' nie będzie.

Na marginesie, możesz uczynić Makefile dużo bardziej zwięzłym, tak jak sugerowały to inne posty.

David Lin
źródło
42
Nigdy nie należy zmieniać CXX lub CC w Makefile lub BadThingsMayHappen (TM), które zawierają ścieżkę i / lub nazwę plików wykonywalnych do uruchomienia. Służą temu CPPFLAGS, CXXFLAGS i CFLAGS.
11
Ta rada jest słaba, ponieważ łączy pliki debugowania i nie-debugowania, tak że jeden kończy się uszkodzoną kompilacją.
Maxim Egorushkin
@MaximEgorushkin jak to naprawić? Niedawno natknąłem się na ten problem. Mam plik wykonywalny do debugowania, który został powiązany z plikami obiektów wydania. Jedynym jak dotąd rozwiązaniem było zadeklarowanie debugowania i wypuszczenie najciekawszego fałszywego
MauriceRandomNumber
3
@MauriceRandomNumber Zbuduj debugowanie / wydanie do własnych folderów. Przykład: stackoverflow.com/a/48793058/412080
Maxim Egorushkin
43

To pytanie często pojawiało się podczas poszukiwania podobnego problemu, więc czuję, że w pełni wdrożone rozwiązanie jest uzasadnione. Zwłaszcza, że ​​ja (i przypuszczam, że inni) zmagaliśmy się ze złożeniem wszystkich różnych odpowiedzi.

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

Obsługuje ...

  • oddzielne katalogi projektów dla określonych kompilacji
  • łatwy wybór domyślnej kompilacji docelowej
  • cichy cel przygotowania do tworzenia katalogów potrzebnych do zbudowania projektu
  • flagi konfiguracji kompilatora specyficzne dla kompilacji
  • Naturalna metoda GNU Make do określania, czy projekt wymaga przebudowy
  • reguły wzorców zamiast przestarzałych reguł przyrostków

#
# 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 $@ $<

#
# Release rules
#
release: $(RELEXE)

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

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

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

remake: clean all

clean:
    rm -f $(RELEXE) $(RELOBJS) $(DBGEXE) $(DBGOBJS)
ffhaddad
źródło
Jak to zmodyfikować, aby umożliwić budowanie plików źródłowych w katalogu innym niż ten, w którym znajduje się Makefile?
Jefferson Hudson
@JeffersonHudson Jeśli pliki źródłowe znajdują się w katalogu o nazwie src, zmodyfikuj wiersz SRCS = file1.c file2.c file3.c file4.cdo odczytu SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
zero2cx
3
To, co mi się nie podoba, to powielanie wszystkich reguł i zmiennych dotyczących debugowania i wydawania. Mam podobny plik Makefile, ale kiedy go rozszerzam, muszę ostrożnie kopiować, wklejać każdą nową rzecz do debugowania i wydania oraz ostrożnie konwertować.
BeeOnRope
To powinna być akceptowana odpowiedź. Żałuję, że nie widziałem tego dawno temu.
Michael Dorst
42

Jeśli przez konfigurację wydania / kompilacji masz na myśli tylko jedną konfigurację na plik makefile, to jest to po prostu kwestia oddzielenia CC i CFLAGS:

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

W zależności od tego, czy możesz użyć pliku makefile GNU, możesz użyć warunkowego, aby uczynić to nieco bardziej wyszukanym i sterować nim z wiersza poleceń:

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

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

a następnie użyj:

make DEBUG=0
make DEBUG=1

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

David Cournapeau
źródło
18
Nie wiem, czy robię coś dziwnego, ale aby uzyskać debug if do pracy ( ifeq (DEBUG, 1)) dla mnie, DEBUGzmienna potrzebne owinięte w nawiasach tak: ifeq ($(DEBUG), 1).
shanet
25

Pamiętaj, że jednocześnie możesz uprościć swój plik Makefile:

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 $@ $^ $(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 musisz wszędzie powtarzać nazw plików. Wszystkie pliki .l przejdą przez flex i gcc, wszystkie pliki .y przejdą przez bison i g ++, a wszystkie pliki .cpp tylko przez g ++.

Po prostu wymień pliki .o, które spodziewasz się na końcu, a Make wykona pracę polegającą na ustaleniu, które reguły mogą zaspokoić potrzeby ...

dla przypomnienia:

  • $@ Nazwa pliku docelowego (przed dwukropkiem)

  • $< Nazwa pierwszego (lub jedynego) pliku wymagań wstępnych (pierwszego po dwukropku)

  • $^ Nazwy wszystkich plików wymagań wstępnych (oddzielone spacjami)

  • $*Temat (bit, który pasuje do %symbolu wieloznacznego w definicji reguły.

Stobor
źródło
Sekcja „Do rekordu” zawiera jeden element zdefiniowany dwukrotnie z różnymi opisami. Według gnu.org/software/make/manual/make.html#Automatic-Variables , $^dotyczy wszystkich wstępnie wymaganych plików.
Grant Peters
Dzięki za to Grant - poprawiono literówkę! (Sprawdziłem plik Makefile i wygląda na to, że użyłem go poprawnie, ale wpisałem wyjaśnienie.)
Stobor
2
Chciałbym, żeby było więcej tych krótkich przewodników, jak pisać rozsądnie małe pliki Makefile, w tym zmienne automatyczne.
AzP
Dobrze jest mieć zarówno cel debugowania, jak i wydania bez konieczności zmiany pliku Makefile, a także możliwość wybrania domyślnego na podstawie własnych preferencji.
1
W tym rozwiązaniu występuje problem polegający na tym, że pliki wyjściowe debugowania i wydania są mieszane razem w tym samym katalogu. Jeśli nie są kompatybilne, wybuchnie to w dziwny i cudowny sposób, chyba że będziesz ostrożnie czyścił za każdym razem, gdy zmieniasz debugowanie lub nie. Nawet jeśli są kompatybilne, nie zrobią tego, czego oczekujesz bez czystego: jeśli masz projekt skompilowany jako wydanie, a następnie ustawisz DEBUG = 1, przebuduje tylko pliki, których źródło się zmieniło, więc generalnie nie będziesz uzyskaj kompilację "debugowania" w ten sposób.
BeeOnRope
3

możesz mieć zmienną

DEBUG = 0

wtedy możesz użyć instrukcji warunkowej

  ifeq ($(DEBUG),1)

  else

  endif
Tiberiu
źródło
2

Uzupełniając odpowiedzi z wcześniejszych ... Musisz odwołać się do zmiennych, które definiujesz w swoich poleceniach ...

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
Stobor
źródło
1
Istnieje (teraz usunięta?) Odpowiedź (która powinna być komentarzem do odpowiedzi), która ifeq (DEBUG, 1)powinna być ifeq ($(DEBUG), 1). Domyślam się, że mogło to odnosić się do twojej odpowiedzi tutaj.
Keith M,
0

Możesz także dodać coś prostego do swojego Makefile, na przykład

ifeq ($(DEBUG),1)
   OPTS = -g
endif

Następnie skompiluj go do debugowania

make DEBUG=1

Manolete
źródło