ฉันจะกำหนดคอนฟิก 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

เพื่อชี้แจงเมื่อฉันพูดว่า build / debug builds ฉันต้องการพิมพ์makeและรับ build build หรือmake debugรับ debug build โดยไม่ต้องคอมเม้นต์สิ่งต่าง ๆ ใน 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
คำแนะนำนี้ไม่ดีเนื่องจากมันผสมแฟ้มวัตถุที่ตรวจแก้จุดบกพร่องและไม่ใช่แก้จุดบกพร่องเพื่อให้หนึ่งจบลงด้วยการสร้างที่เสียหาย
Maxim Egorushkin

@ MaximEgorushkin วิธีการแก้ไขที่? ฉันเจอปัญหานี้เมื่อเร็ว ๆ นี้ ฉันมีบิลด์ที่ปฏิบัติการได้ดีบั๊กซึ่งเชื่อมโยงกับไฟล์ออบเจ็กต์ปล่อย ทางออกเดียวคือการประกาศการแก้ปัญหาและปล่อยปลอมเป้าหมาย
MauriceRandomNumber

3
@MauriceRandomNumber บิลด์ debug / release ในโฟลเดอร์ของตัวเอง ตัวอย่าง: 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 หากไฟล์ที่มาอยู่ในไดเรกทอรีชื่อ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

นี่ควรเป็นคำตอบที่ยอมรับได้ ฉันหวังว่าฉันจะได้เห็นสิ่งนี้มานานแล้ว
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
ผมไม่ทราบว่าผมกำลังทำอะไรบางอย่างที่แปลก แต่ที่จะได้รับการแก้ปัญหาถ้ามีคำสั่งในการทำงาน ( 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 , $^สำหรับไฟล์ทั้งหมดที่จำเป็น
Grant Peters

ขอบคุณสำหรับการให้สิทธิ์ - พิมพ์ผิด! (ฉันตรวจสอบไปที่ Makefile และดูเหมือนว่าฉันใช้อย่างถูกต้อง แต่พิมพ์คำอธิบาย)
Stobor

2
ฉันหวังว่าจะมีคำแนะนำสั้น ๆ เหล่านี้มากขึ้นในการเขียน Makefiles ขนาดเล็กที่สมเหตุสมผลรวมถึงตัวแปรอัตโนมัติ
AzP

เป็นเรื่องดีที่มีทั้ง debug และ release target โดยไม่ต้องเปลี่ยน Makefile และความสามารถในการเลือก default ตามความต้องการของคุณ

1
วิธีการแก้ปัญหานี้มีปัญหาว่าไฟล์ debug และ release output ต่าง ๆ กันในไดเรกทอรีเดียวกัน หากพวกเขาเข้ากันไม่ได้สิ่งนี้จะระเบิดในรูปแบบที่แปลกและมหัศจรรย์เว้นแต่คุณจะระมัดระวังในการทำความสะอาดทุกครั้งที่คุณเปลี่ยนระหว่างการดีบั๊กและไม่ แม้ว่ามันจะใช้งานร่วมกันได้ แต่ก็จะไม่ทำตามที่คุณคาดหวังหากไม่มีการล้างข้อมูล: ถ้าคุณมีโครงการที่สร้างเป็นรีลีสแล้วให้ DEBUG = 1 มันจะสร้างเฉพาะไฟล์ที่แหล่งที่มีการเปลี่ยนแปลงเท่านั้น รับ "debug" สร้างทางนั้น
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.