Bagaimana saya bisa mengkonfigurasi makefile saya untuk debug dan rilis build?

175

Saya memiliki makefile berikut untuk proyek saya, dan saya ingin mengkonfigurasinya untuk rilis dan debug build. Dalam kode saya, saya punya banyak #ifdef DEBUGmakro di tempat, jadi itu hanya masalah pengaturan makro ini dan menambahkan -g3 -gdwarf2bendera ke kompiler. Bagaimana saya bisa melakukan ini?

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

Hanya untuk memperjelas, ketika saya mengatakan rilis / debug builds, saya ingin bisa cukup mengetik makedan mendapatkan rilis build atau make debugdan mendapatkan build debug, tanpa secara manual mengomentari hal-hal di makefile.

samoz
sumber
12
Perhatian! $ (CC) = sesuatu berbeda dari CC = sesuatu
levif
4
Target yang dapat dieksekusi melanggar aturan emas makefile: setiap target harus memperbarui file yang menamai target, dalam kasus Anda "executable".
JesperE
3
^ Dan jika tidak, itu harus dinyatakan.PHONY
underscore_d

Jawaban:

192

Anda dapat menggunakan Nilai Variabel spesifik Target . Contoh:

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

Ingatlah untuk menggunakan $ (CXX) atau $ (CC) di semua perintah kompilasi Anda.

Kemudian, 'make debug' akan memiliki flag tambahan seperti -DDEBUG dan -g sedangkan 'make' tidak.

Sebagai tambahan, Anda dapat membuat Makefile Anda jauh lebih ringkas seperti yang disarankan posting lain.

David Lin
sumber
42
Anda seharusnya tidak pernah mengubah CXX atau CC dalam Makefile atau BadThingsMayHappen (TM), yang mengandung path dan / atau nama yang dapat dieksekusi untuk dijalankan. CPPFLAGS, CXXFLAGS, dan CFLAGS melayani tujuan ini.
11
Saran ini buruk karena mencampur file objek debug dan non-debug, sehingga salah satu berakhir dengan membangun rusak.
Maxim Egorushkin
@ MaximEgorushkin bagaimana cara memperbaikinya? Saya menemukan masalah ini baru-baru ini. Saya memiliki build yang dapat dieksekusi debug, yang dikaitkan dengan rilis file objek. Satu-satunya solusi sejauh ini adalah dengan mendeklarasikan debug dan melepaskan kepalsuan terbesar
MauriceRandomNumber
3
@MauriceRandomNumber Bangun debug / lepaskan ke foldernya sendiri. Contoh: stackoverflow.com/a/48793058/412080
Maxim Egorushkin
43

Pertanyaan ini sering muncul ketika mencari masalah yang sama, jadi saya merasa solusi yang sepenuhnya diimplementasikan dijamin. Terutama karena saya (dan saya akan berasumsi orang lain) telah berjuang mengumpulkan semua jawaban bersama.

Di bawah ini adalah contoh Makefile yang mendukung beberapa tipe build di direktori yang terpisah. Contoh yang diilustrasikan menunjukkan debug dan rilis build.

Mendukung ...

  • pisahkan direktori proyek untuk bangunan tertentu
  • pemilihan yang mudah dari build target default
  • target persiapan diam untuk membuat direktori yang diperlukan untuk membangun proyek
  • flag konfigurasi compiler khusus membangun
  • Metode alami GNU Make untuk menentukan apakah proyek memerlukan pembangunan kembali
  • aturan pola daripada aturan sufiks yang sudah usang

#
# 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)
fhaddadd
sumber
Bagaimana Anda memodifikasi ini untuk memungkinkan pembangunan file sumber dalam direktori selain dari yang ada di Makefile?
Jefferson Hudson
@JeffersonHudson Jika file sumber ada dalam direktori bernama src, maka modifikasi baris SRCS = file1.c file2.c file3.c file4.cuntuk dibaca SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
zero2cx
3
Hal yang saya tidak suka adalah duplikasi semua aturan dan variabel untuk debug dan rilis. Saya memiliki Makefile yang serupa tetapi ketika memperluasnya saya perlu menyalin paste setiap hal baru untuk debug dan melepaskan dan dengan hati-hati mengubahnya.
BeeOnRope
Ini harus menjadi jawaban yang diterima. Saya berharap saya sudah melihat ini sejak lama.
Michael Dorst
42

Jika dengan mengkonfigurasi rilis / build, Anda berarti Anda hanya perlu satu konfigurasi per makefile, maka itu hanyalah masalah dan decoupling CC dan CFLAGS:

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

Bergantung pada apakah Anda dapat menggunakan gnu makefile, Anda dapat menggunakan kondisional untuk membuatnya lebih menarik, dan mengontrolnya dari baris perintah:

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

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

lalu gunakan:

make DEBUG=0
make DEBUG=1

Jika Anda perlu mengontrol kedua konfigurasi pada saat yang sama, saya pikir lebih baik membangun direktori, dan satu membangun direktori / konfigurasi.

David Cournapeau
sumber
18
Aku tidak tahu apakah aku melakukan sesuatu yang aneh, tetapi untuk mendapatkan debug jika pernyataan untuk bekerja ( ifeq (DEBUG, 1)) untuk saya, DEBUGvariabel diperlukan dibungkus dalam tanda kurung suka jadi: ifeq ($(DEBUG), 1).
shanet
25

Perhatikan bahwa Anda juga dapat membuat Makefile Anda lebih sederhana, pada saat yang sama:

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

Sekarang Anda tidak perlu mengulang nama file di semua tempat. File .l akan dilewatkan melalui flex dan gcc, semua file .y akan dilewatkan melalui bison dan g ++, dan file .cpp hanya melalui g ++.

Cukup daftarkan file .o yang Anda harapkan berakhir, dan Make akan melakukan pekerjaan mencari tahu aturan mana yang dapat memenuhi kebutuhan ...

untuk catatan:

  • $@ Nama file target (yang sebelum titik dua)

  • $< Nama file prasyarat pertama (atau hanya) (yang pertama setelah titik dua)

  • $^ Nama-nama semua file prasyarat (spasi terpisah)

  • $*Batang (bit yang cocok dengan %wildcard dalam definisi aturan.

Stobor
sumber
Bagian "untuk catatan" Anda memiliki satu item yang didefinisikan dua kali dengan deskripsi yang berbeda. Menurut gnu.org/software/make/manual/make.html#Automatic-Variables , $^untuk semua file prasyarat.
Grant Peters
Terima kasih untuk Hibah itu - kesalahan ketik diperbaiki! (Saya memeriksa Makefile, dan tampaknya saya menggunakannya dengan benar di sana, tetapi mengetikkan penjelasannya.)
Stobor
2
Saya berharap ada lebih banyak panduan singkat untuk menulis Makefile yang cukup kecil, termasuk variabel otomatis.
AzP
Sangat menyenangkan memiliki target debug dan rilis tanpa harus mengubah Makefile, dan kemampuan untuk memilih default berdasarkan preferensi Anda sendiri.
1
Solusi ini memiliki masalah yang debug dan lepaskan file output dicampur bersama di direktori yang sama. Jika mereka tidak kompatibel, ini akan meledak dengan cara yang aneh dan luar biasa kecuali Anda berhati-hati untuk melakukan pembersihan setiap kali Anda mengubah antara debug dan tidak. Bahkan jika mereka kompatibel, itu tidak akan melakukan apa yang Anda harapkan tanpa bersih: jika Anda memiliki proyek yang dibangun sebagai rilis, dan kemudian membuat DEBUG = 1, itu hanya akan membangun kembali file yang sumbernya telah berubah, sehingga Anda tidak akan secara umum dapatkan "debug" build seperti itu.
BeeOnRope
3

Anda dapat memiliki variabel

DEBUG = 0

maka Anda dapat menggunakan pernyataan kondisional

  ifeq ($(DEBUG),1)

  else

  endif
Tiberiu
sumber
2

Melengkapi jawaban dari sebelumnya ... Anda perlu referensi variabel yang Anda tentukan info dalam perintah Anda ...

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
sumber
1
Ada Jawaban (sekarang dihapus?) (Yang seharusnya menjadi Komentar pada Jawaban) yang ifeq (DEBUG, 1)seharusnya dicatat ifeq ($(DEBUG), 1). Saya menduga itu mungkin merujuk pada Jawaban Anda di sini.
Keith M
0

Anda juga dapat menambahkan sesuatu yang sederhana ke Makefile Anda seperti

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

Kemudian kompilasi untuk debugging

make DEBUG=1

Manolete
sumber