デバッグおよびリリースビルド用にメイクファイルを構成するにはどうすればよいですか?


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

明確にするために、私がリリース/デバッグビルドと言ったときは、メイクファイルで手動でコメントアウトせずにmake、リリースビルドを入力して取得するかmake debug、デバッグビルドを取得できるようにしたいと考えています。


12
注意!$(CC)=何かがCC =何かと異なる
levif '19年

4
実行可能なターゲットは、メイクファイルの黄金律に違反しています。すべてのターゲットは、ターゲットに名前を付けたファイル、つまり「実行可能」を更新する必要があります。
JesperE 2015

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
MakefileまたはBadThingsMayHappen(TM)内のCXXまたはCCは決して変更しないでください。これらには、実行する実行可能ファイルのパスや名前が含まれています。CPPFLAGS、CXXFLAGS、およびCFLAGSがこの目的を果たします。

11
デバッグオブジェクトファイルと非デバッグオブジェクトファイルが混在しているため、ビルドが破損するため、このアドバイスは不十分です。
Maxim Egorushkin 2017年

@MaximEgorushkinそれを修正する方法?最近この問題に遭遇しました。デバッグ実行可能ビルドがあり、リリースオブジェクトファイルにリンクされていました。これまでの唯一の解決策は、デバッグを宣言して最大の偽物をリリースすることでした
MauriceRandomNumber 2018

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が存在するディレクトリ以外のディレクトリにソースファイルを構築できるように、これをどのように変更しますか?
ジェファーソンハドソン

@JeffersonH​​udsonソースファイルがという名前のディレクトリにある場合は、読み取るsrcSRCS = file1.c file2.c file3.c file4.cを変更しますSRCS = src/file1.c src/file2.c src/file3.c src/file4.c
zero2cx 2017年

3
私が気に入らないのは、デバッグとリリースのためのすべてのルールと変数の重複です。私は似たMakefileを持っていますが、拡張するときは、デバッグとリリースのために新しいものをそれぞれ慎重にコピーして貼り付け、慎重に変換する必要があります。
BeeOnRope

これは受け入れられる答えになるはずです。久しぶりに見たかったです。
Michael Dorst

42

リリース/ビルドを構成する場合、メイクファイルごとに1つの構成のみが必要であることを意味し、それは単に問題であり、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

両方の構成を同時に制御する必要がある場合は、ビルドディレクトリと1つのビルドディレクトリ/ configを用意することをお勧めします。


18
何か奇妙なことをしているのかどうかはわかりませんが、デバッグのifステートメントを機能させるには(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はニーズを満たすことができるルールを特定する作業を行います...

記録のために:

  • $@ ターゲットファイルの名前(コロンの前のファイル)

  • $< 最初の(または唯一の)前提条件ファイルの名前(コロンの後の最初のファイル)

  • $^ すべての必須ファイルの名前(スペース区切り)

  • $*ステム(%ルール定義のワイルドカードと一致するビット)。


あなたは「記録のために」のセクションで、1つの項目が異なる説明で2回定義されています。gnu.org/software/make/manual/make.html#Automatic-Variablesによる$^と、すべての前提条件ファイル用です。
Grant Peters

その助成金をありがとう-誤植を修正しました!(Makefileを確認したところ、そこで正しく使用されているようですが、説明を入力しました。)
Stobor

2
自動変数を含めて、かなり小さいMakefileを書くためのこれらの短いガイドがもっとあればいいのにと思います。
AzP、2011年

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)。私はここであなたの答えを参照していたのではないかと思います。
キースM

0

Makefileに簡単なものを追加することもできます。

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

次に、デバッグのためにコンパイルします

make DEBUG=1

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.