Làm cách nào tôi có thể định cấu hình tệp tạo tệp của mình để gỡ lỗi và phát hành bản dựng?


175

Tôi có tệp tạo tệp sau cho dự án của mình và tôi muốn định cấu hình nó để phát hành và gỡ lỗi bản dựng. Trong mã của tôi, tôi có rất nhiều #ifdef DEBUGmacro, vì vậy đơn giản chỉ là đặt macro này và thêm các -g3 -gdwarf2cờ vào trình biên dịch. Tôi có thể làm cái này như thế nào?

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

Chỉ cần làm rõ, khi tôi nói các bản dựng phát hành / gỡ lỗi, tôi muốn có thể chỉ cần gõ makevà nhận bản dựng phát hành hoặc make debugnhận bản dựng gỡ lỗi, mà không cần nhận xét thủ công mọi thứ trong tệp tạo tệp.


12
Chú ý! $ (CC) = một cái gì đó khác với CC = một cái gì đó
levif

4
Mục tiêu thực thi vi phạm quy tắc vàng của tệp thực hiện: mọi mục tiêu nên cập nhật tệp đặt tên mục tiêu, trong trường hợp của bạn là "thực thi".
JesperE

3
^ Và nếu không, nó sẽ được khai báo.PHONY
underscore_d

Câu trả lời:


192

Bạn có thể sử dụng Giá trị biến đổi dành riêng cho mục tiêu . Thí dụ:

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

Hãy nhớ sử dụng $ (CXX) hoặc $ (CC) trong tất cả các lệnh biên dịch của bạn.

Sau đó, 'tạo gỡ lỗi' sẽ có thêm các cờ như -DDEBUG và -g trong đó 'làm' sẽ không.

Bên cạnh đó, bạn có thể làm cho Makefile của mình ngắn gọn hơn rất nhiều như các bài đăng khác đã đề xuất.


42
Bạn không bao giờ nên thay đổi CXX hoặc CC trong Makefile hoặc BadThingsMayHappen (TM), những thứ này chứa đường dẫn và / hoặc tên của các tệp thực thi để chạy. CPPFLAGS, CXXFLAGS và CFLAGS phục vụ mục đích này.

11
Lời khuyên này rất kém vì nó trộn lẫn các tệp đối tượng gỡ lỗi và không gỡ lỗi, do đó kết thúc với một bản dựng bị hỏng.
Maxim Egorushkin

@MaximEgorushkin làm thế nào để khắc phục điều đó? Tôi đã gặp vấn đề này gần đây. Tôi có một bản dựng thực thi gỡ lỗi, được liên kết với các tệp đối tượng phát hành. Giải pháp duy nhất cho đến nay là tuyên bố gỡ lỗi và phát hành giả mạo khó hiểu nhất
MauriceRandomNumber

3
@MauriceRandomNumber Xây dựng gỡ lỗi / phát hành vào các thư mục riêng của mình. Ví dụ: stackoverflow.com/a/48793058/412080
Maxim Egorushkin

43

Câu hỏi này đã xuất hiện thường xuyên khi tìm kiếm một vấn đề tương tự, vì vậy tôi cảm thấy một giải pháp được thực hiện đầy đủ được bảo hành. Đặc biệt là vì tôi (và tôi sẽ cho rằng những người khác) đã đấu tranh chắp nối tất cả các câu trả lời khác nhau lại với nhau.

Dưới đây là một Makefile mẫu hỗ trợ nhiều kiểu xây dựng trong các thư mục riêng biệt. Ví dụ minh họa cho thấy gỡ lỗi và phát hành bản dựng.

Hỗ trợ ...

  • thư mục dự án riêng cho các bản dựng cụ thể
  • dễ dàng lựa chọn xây dựng mục tiêu mặc định
  • mục tiêu chuẩn bị im lặng để tạo các thư mục cần thiết để xây dựng dự án
  • cờ cấu hình trình biên dịch cụ thể
  • Phương pháp tự nhiên của GNU Make để xác định xem dự án có cần xây dựng lại không
  • quy tắc mẫu hơn là quy tắc hậu tố lỗi thời

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

Làm thế nào để bạn sửa đổi điều này để cho phép xây dựng các tệp nguồn trong một thư mục khác với thư mục Makefile cư trú?
Jefferson Hudson

@JeffersonHudson Nếu các tệp nguồn nằm trong một thư mục có tên src, thì hãy sửa đổi dòng SRCS = file1.c file2.c file3.c file4.cđể đọc SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
zero2cx

3
Điều tôi không thích là sự trùng lặp của tất cả các quy tắc và biến để gỡ lỗi và phát hành. Tôi có một Makefile tương tự nhưng khi mở rộng nó, tôi cần sao chép cẩn thận dán từng thứ mới để gỡ lỗi và phát hành và cẩn thận chuyển đổi nó.
BeeOnRope

Đây phải là câu trả lời được chấp nhận. Tôi ước tôi đã nhìn thấy điều này từ lâu.
Michael Dorst

42

Nếu bằng cách định cấu hình phát hành / xây dựng, bạn có nghĩa là bạn chỉ cần một cấu hình cho mỗi tệp thực hiện, thì đó chỉ đơn giản là vấn đề và tách riêng CC và CFLAGS:

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

Tùy thuộc vào việc bạn có thể sử dụng gnu makefile hay không, bạn có thể sử dụng điều kiện để làm cho điều này trở nên lạ hơn một chút và điều khiển nó từ dòng lệnh:

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

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

và sau đó sử dụng:

make DEBUG=0
make DEBUG=1

Nếu bạn cần kiểm soát cả hai cấu hình cùng một lúc, tôi nghĩ sẽ tốt hơn nếu có các thư mục xây dựng và một thư mục xây dựng / cấu hình.


18
Tôi không biết nếu tôi đang làm điều gì đó lạ, nhưng để gỡ lỗi nếu câu lệnh hoạt động ( ifeq (DEBUG, 1)) đối với tôi, DEBUGbiến cần được gói trong ngoặc đơn như vậy : ifeq ($(DEBUG), 1).
shanet

25

Lưu ý rằng bạn cũng có thể làm cho Makefile của mình đơn giản hơn cùng một lúc:

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

Bây giờ bạn không phải lặp lại tên tập tin ở khắp mọi nơi. Mọi tệp .l sẽ được truyền qua flex và gcc, mọi tệp .y sẽ được chuyển qua bison và g ++ và mọi tệp .cpp thông qua g ++.

Chỉ cần liệt kê các tệp .o mà bạn mong muốn kết thúc và Make sẽ thực hiện công việc tìm ra quy tắc nào có thể đáp ứng nhu cầu ...

cho hồ sơ:

  • $@ Tên của tệp đích (tên trước dấu hai chấm)

  • $< Tên của tệp tiên quyết đầu tiên (hoặc duy nhất) (tệp đầu tiên sau dấu hai chấm)

  • $^ Tên của tất cả các tệp điều kiện tiên quyết (phân tách không gian)

  • $*Thân cây (bit khớp với %ký tự đại diện trong định nghĩa quy tắc.


Phần "cho bản ghi" của bạn có một mục được xác định hai lần với các mô tả khác nhau. Theo gnu.org/software/make/manual/make.html#Automatic-Variables , $^dành cho tất cả các tệp tiên quyết.
Grant Peters

Cảm ơn vì Grant - đã sửa lỗi chính tả! (Tôi đã kiểm tra Makefile và có vẻ như tôi đã sử dụng nó chính xác ở đó, nhưng đánh máy giải thích.)
Stobor

2
Tôi ước có nhiều hướng dẫn ngắn hơn để viết một Makefiles nhỏ hợp lý, bao gồm các biến tự động.
AzP

Thật tuyệt khi có cả mục tiêu gỡ lỗi và phát hành mà không phải thay đổi Makefile và khả năng chọn mặc định dựa trên sở thích của riêng bạn.

1
Giải pháp này có vấn đề là các tệp đầu ra gỡ lỗi và giải phóng được trộn lẫn với nhau trong cùng một thư mục. Nếu chúng không tương thích, điều này sẽ nổ tung theo những cách kỳ lạ và tuyệt vời trừ khi bạn cẩn thận làm sạch mỗi khi bạn thay đổi giữa gỡ lỗi và không. Ngay cả khi chúng tương thích, nó sẽ không làm những gì bạn mong đợi mà không sạch: nếu bạn có dự án được xây dựng dưới dạng phát hành, và sau đó tạo DEBUG = 1, nó sẽ chỉ xây dựng lại các tệp có nguồn đã thay đổi, vì vậy bạn sẽ không nói chung có được một "gỡ lỗi" xây dựng theo cách đó.
BeeOnRope

3

bạn có thể có một biến

DEBUG = 0

sau đó bạn có thể sử dụng một tuyên bố có điều kiện

  ifeq ($(DEBUG),1)

  else

  endif

2

Hoàn thành các câu trả lời từ trước đó ... Bạn cần tham khảo các biến bạn xác định thông tin trong các lệnh của mình ...

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
Có câu trả lời (hiện đã bị xóa?) (Đáng lẽ phải là Nhận xét về Câu trả lời) ifeq (DEBUG, 1)cần lưu ý ifeq ($(DEBUG), 1). Tôi đoán nó có thể đã được đề cập đến câu trả lời của bạn ở đây.
Keith M

0

Bạn cũng có thể thêm một cái gì đó đơn giản vào Makefile của bạn, chẳng hạn như

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

Sau đó biên dịch nó để gỡ lỗi

make DEBUG=1

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.