Як я можу налаштувати свій makefile для налагодження та випуску збірок?


175

У мене є такий makefile для мого проекту, і я хотів би налаштувати його для випуску та побудови налагодження. У своєму коді у мене багато #ifdef DEBUGмакросів, тому просто питання встановити цей макрос і додати -g3 -gdwarf2прапорці до компіляторів. Як я можу це зробити?

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

Просто для уточнення, коли я кажу, що випуск / налагодження будує, я хочу мати змогу просто набрати makeта отримати збірку версії або make debugотримати збірку налагодження, не коментуючи вручну речі в makefile.


12
Увага! $ (CC) = щось інше, ніж CC = щось
levif

4
Виконана ціль порушує золоте правило makefiles: кожна мета повинна оновлювати файл, який називає ціль, у вашому випадку "виконуваним".
JesperE

3
^ І якщо це не так, його слід оголосити.PHONY
underscore_d

Відповіді:


192

Ви можете використовувати цільові змінні значення . Приклад:

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

Не забудьте використовувати $ (CXX) або $ (CC) у всіх своїх командах компіляції.

Тоді "make debug" матиме додаткові прапори на зразок -DDEBUG та -g, де "make" не буде.

З іншого боку, ви можете зробити свій Makefile набагато більш стислим, як пропонували інші публікації.


42
Ніколи не слід змінювати CXX або CC в Makefile або BadThingsMayHappen (TM), вони містять шлях та / або ім'я виконуваних файлів, які потрібно запустити. ЦІЙ ЦІПІ служать CPPFLAGS, CXXFLAGS і CFLAGS.

11
Ця порада є поганою, тому що вона змішує файли об’єктів налагодження та не-налагодження, так що в кінцевому підсумку виходить зіпсована збірка.
Максим Єгорушкін

@MaximEgorushkin як це виправити? Я нещодавно зіткнувся з цією проблемою. У мене є виконувана збірка налагодження, яка була пов’язана з об’єктними файлами випуску. Єдиним рішенням поки що було оголосити налагодження та випустити найпопулярніший фальшивий
MauriceRandomNumber

3
@MauriceRandomNumber Збірка налагодження / випуск у власні папки. Приклад: stackoverflow.com/a/48793058/412080
Максим Єгорушкін

43

Це запитання виникає часто під час пошуку подібної проблеми, тому я вважаю, що повністю реалізоване рішення є гарантованим. Тим більше, що я (і я би припустив, що інші) намагався зібрати всі різні відповіді разом.

Нижче представлений зразок Makefile, який підтримує кілька типів збірки в окремих каталогах. Ілюстрований приклад показує налагодження та версії версій.

Підтримується ...

  • окремі каталоги проектів для конкретних збірок
  • простий вибір збірки цілей за замовчуванням
  • тиха підготовча мета для створення каталогів, необхідних для побудови проекту
  • прапори конфігурації компілятора для побудови
  • Природний метод GNU Make визначити, чи потрібен проект перебудови
  • правила шаблону, а не застарілі суфіксні правила

#
# 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)

Як ви модифікуєте це, щоб дозволити побудову вихідних файлів у каталозі, відмінному від того, в якому перебуває Makefile?
Джефферсон Хадсон

@JeffersonHudson Якщо вихідні файли знаходяться в каталозі з іменем src, змініть рядок SRCS = file1.c file2.c file3.c file4.cдля читання SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
zero2cx

3
Що мені не подобається - це дублювання всіх правил та змінних для налагодження та випуску. У мене схожий Makefile, але при його подовженні мені потрібно ретельно скопіювати вставити кожну нову річ для налагодження та випустити та ретельно перетворити її.
BeeOnRope

Це має бути прийнятою відповіддю. Я б хотів, щоб я це бачив давно.
Майкл Дорст

42

Якщо під час налаштування випуску / складання ви маєте на увазі, що вам потрібен лише один конфігурація для makefile, то це просто питання та роз'єднання CC та CFLAGS:

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

Залежно від того, чи можете ви використовувати gnu makefile, ви можете використовувати умовно, щоб зробити це трохи більш фантазійним та керувати ним з командного рядка:

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

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

а потім скористайтеся:

make DEBUG=0
make DEBUG=1

Якщо вам потрібно керувати обома конфігураціями одночасно, я думаю, що краще мати каталоги збірки та один каталог / конфігурацію збірки.


18
Я не знаю , якщо я роблю що - то дивне, але , щоб отримати налагодження , якщо заяву на роботу ( ifeq (DEBUG, 1)) для мене, DEBUGзмінна потребував , загорнуті в дужках наступним чином: ifeq ($(DEBUG), 1).
Шенет

25

Зауважте, що ви також можете зробити 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

Тепер вам не доведеться повторювати імена файлів повсюдно. Будь-які .l-файли передаватимуться через flex та gcc, будь-які .y-файли передаватимуться через зубр та g ++, а будь-які .cpp-файли через g ++.

Просто перелічіть файли.

для запису:

  • $@ Ім'я цільового файлу (того, що знаходиться перед двокрапкою)

  • $< Назва першого (або єдиного) обов'язкового файлу (першого після двокрапки)

  • $^ Назви всіх необхідних файлів (пробіл розділений)

  • $*Стебло (біт, який відповідає %підстановці у визначенні правила).


У розділі "для запису" є один елемент, визначений двічі з різними описами. Відповідно до gnu.org/software/make/manual/make.html#Automatic-Variables , $^це для всіх необхідних файлів.
Грант Петерс

Дякую за цей грант - виправлено помилку! (Я перевірив Makefile, і, здається, я правильно його використав там, але набрав пояснення.)
Stobor

2
Мені б хотілося, щоб було більше цих коротких посібників щодо написання досить невеликих Makefiles, включаючи автоматичні змінні.
AzP

Приємно мати як налагодження, так і випуск цілі, не змінюючи Makefile, а також можливість вибору за замовчуванням виходячи з власних уподобань.

1
У цьому рішенні є проблема, що налагоджувальні та випускні файли змішуються разом в одному каталозі. Якщо вони не сумісні, це вибухне дивними та чудовими способами, якщо ви не будете обережні, щоб чистити кожен раз, коли ви змінюєте налагодження на ні. Навіть якщо вони сумісні, він не зробить те, що ви очікуєте без чистого: якщо у вас проект побудований як реліз, а потім зробіть DEBUG = 1, він відновить лише ті файли, джерело яких змінилося, тому ви, як правило, не будете отримати таким чином "налагодження".
BeeOnRope

3

ви можете мати змінну

DEBUG = 0

тоді ви можете використовувати умовний вислів

  ifeq ($(DEBUG),1)

  else

  endif

2

Завершення відповідей раніше: Вам потрібно посилатися на змінні, які ви визначаєте інформацію у своїх командах ...

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

1
Є (зараз видалено?) Відповідь (який повинен був бути коментарем до відповіді), який ifeq (DEBUG, 1)слід зазначити ifeq ($(DEBUG), 1). Я здогадуюсь, це, можливо, посилалось на вашу відповідь тут.
Кіт М

0

Ви також можете додати щось просте у свій Makefile, наприклад

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

Потім компілюйте його для налагодження

make DEBUG=1

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.