디버그 및 릴리스 빌드를 위해 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

명확히하기 위해 릴리스 / 디버그 빌드를 말할 때 makefile의 내용을 수동으로 주석 처리하지 않고 make릴리스 빌드를 입력 하고 make debug디버그 빌드를 얻을 수 있기를 원합니다 .


12
주의! $ (CC) = CC와는 다른 것 = 무언가
Levif

4
실행 파일 대상은 makefile의 황금률을 위반합니다. 모든 대상은 "실행 파일"인 경우 대상 이름을 지정하는 파일을 업데이트해야합니다.
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'에는 'make'가 아닌 -DDEBUG 및 -g와 같은 추가 플래그가 있습니다.

참고로, 다른 게시물에서 제안한 것처럼 Makefile을 훨씬 간결하게 만들 수 있습니다.


42
Makefile 또는 BadThingsMayHappen (TM) 내에서 CXX 또는 CC를 변경해서는 안되며 여기에는 실행할 실행 파일의 경로 및 / 또는 이름이 포함됩니다. CPPFLAGS, CXXFLAGS 및 CFLAGS가이 목적에 사용됩니다.

11
이 조언은 디버그 및 비디 버그 오브젝트 파일을 혼합하여 손상된 빌드로 끝나기 때문에 좋지 않습니다.
Maxim Egorushkin 2016 년

@MaximEgorushkin은 어떻게 고치나요? 최근 에이 문제가 발생했습니다. 릴리스 객체 파일과 연결된 디버그 실행 파일 빌드가 있습니다. 지금까지 유일한 해결책은 디버그를 선언하고
targest

3
@MauriceRandomNumber 자체 폴더에 디버그 / 릴리스를 빌드합니다. 예 : stackoverflow.com/a/48793058/412080
Maxim Egorushkin

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이있는 디렉토리가 아닌 다른 디렉토리에 소스 파일을 빌드 할 수 있도록이를 어떻게 수정합니까?
Jefferson Hudson

@JeffersonHudson 소스 파일이라는 디렉토리에 있으면 읽을 srcSRCS = 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

이것이 정답입니다. 나는 오래 전에 이것을 보았기를 바랍니다.
Michael Dorst

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
이상한 일을하고 있는지 모르겠지만 디버그 if 문이 작동하도록하려면 ( ifeq (DEBUG, 1)) DEBUG변수를 괄호로 묶어야 ifeq ($(DEBUG), 1)합니다.
shanet

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가 어떤 규칙이 요구를 충족시킬 수 있는지 알아내는 작업을 수행합니다 ...

기록을 위해 :

  • $@ 대상 파일의 이름 (콜론 앞의 이름)

  • $< 첫 번째 (또는 유일한) 전제 조건 파일의 이름 (콜론 다음의 첫 번째 파일)

  • $^ 모든 전제 조건 파일 이름 (공백으로 구분)

  • $*스템 ( %규칙 정의에서 와일드 카드 와 일치하는 비트)


"레코드 용"섹션에는 설명이 다른 두 개의 정의 된 항목이 있습니다. 에 따르면 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

1
언급 된 (현재 삭제 되었습니까?) 답변이 있습니다 (응답에 대한 주석 ifeq (DEBUG, 1)이어야 함) ifeq ($(DEBUG), 1). 나는 그것이 당신의 답변을 여기에서 언급했을 것으로 추측합니다.
Keith M

0

Makefile에 간단한 것과 같은 것을 추가 할 수도 있습니다.

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

그런 다음 디버깅을 위해 컴파일하십시오.

make DEBUG=1

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.