Как я могу настроить свой make-файл для отладочных и релизных сборок?

175

У меня есть следующий make-файл для моего проекта, и я хотел бы настроить его для выпуска и отладки. В моем коде у меня много #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получить отладочную сборку, не комментируя вручную вещи в make-файле.

samoz
источник
12
Внимание! $ (CC) = что-то отличается от CC = что-то
Левиф
4
Исполняемая цель нарушает золотое правило make-файлов: каждая цель должна обновить файл с именем цели, в вашем случае «исполняемый».
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 как это исправить? Я столкнулся с этой проблемой недавно. У меня есть отладочная исполняемая сборка, связанная с выпуском объектных файлов. До сих пор единственным решением было объявить отладку и выпустить
targest phony
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)
ffhaddad
источник
Как вы измените это, чтобы разрешить создание исходных файлов в каталоге, отличном от того, в котором находится 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

Если при конфигурировании релиза / сборки вы имеете в виду, что вам нужен только один конфиг для каждого make-файла, то это просто вопрос, отделяющий 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

Если вам нужно управлять обеими конфигурациями одновременно, я думаю, что лучше иметь каталоги сборки и один каталог / config сборки.

Дэвид Курнапо
источник
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 будут передаваться через bison и g ++, а любые файлы .cpp - только через g ++.

Просто перечислите .o файлы, которые вы ожидаете получить, и Make выполнит работу, чтобы выяснить, какие правила могут удовлетворить потребности ...

Для записи:

  • $@ Имя целевого файла (перед двоеточием)

  • $< Имя первого (или единственного) файла предварительных требований (первого после двоеточия)

  • $^ Имена всех необходимых файлов (через пробел)

  • $*Ствол (бит, который соответствует %шаблону в определении правила.

Stobor
источник
В разделе «Для записи» один элемент определен дважды с разными описаниями. Согласно gnu.org/software/make/manual/make.html#Automatic-Variables , $^это для всех необходимых файлов.
Грант Питерс
Спасибо за это Грант - опечатка исправлена! (Я проверил Makefile, и, похоже, я использовал его там правильно, но набрал объяснение.)
Stobor
2
Хотелось бы, чтобы было больше таких коротких руководств по написанию достаточно маленьких Makefile, включая автоматические переменные.
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
Stobor
источник
1
Есть (теперь удаленный?) Ответ (который должен был быть комментарием к ответу), который ifeq (DEBUG, 1)должен быть отмечен ifeq ($(DEBUG), 1). Я предполагаю, что это могло относиться к вашему ответу здесь.
Кит М
0

Вы также можете добавить что-то простое в ваш Makefile, например:

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

Затем скомпилируйте его для отладки

make DEBUG=1

Manolete
источник