SRC、OBJ、BINサブディレクトリを持つCプロジェクトのMakefileを作成するにはどうすればよいですか?


94

数か月前、私はMakefile学校の課題のために次のジェネリックを思いつきました。

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2010-11-05
#
# Changelog :
#   0.01 - first version
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I.

LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall

SOURCES  := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS  := $(SOURCES:.c=*.o)
rm       = rm -f

$(TARGET): obj
    @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

obj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) $(SOURCES)
    @echo "Compilation complete!"

clean:
    @$(rm) $(TARGET) $(OBJECTS)
    @echo "Cleanup complete!"

これにより、基本的にすべての.cand .hファイルがコンパイルされ、ファイル.oと実行可能ファイルがprojectnameすべて同じフォルダーに生成されます。

さて、少し押してみたいと思います。Makefileを記述して、次のディレクトリ構造でCプロジェクトをコンパイルするにはどうすればよいですか?

 ./
 ./Makefile
 ./src/*.c;*.h
 ./obj/*.o
 ./bin/<executable>

言い換えると、Cソースをにコンパイル./src/./obj/、すべてをリンクしてで実行可能ファイルを作成するMakefileが欲しいの./bin/です。

さまざまなMakefileを読み取ろうとしましたが、上記のプロジェクト構造で機能させることはできません。代わりに、プロジェクトはあらゆる種類のエラーでコンパイルに失敗します。もちろん、本格的なIDE(Monodevelop、Anjutaなど)を使用することもできますが、正直なところ、gEditと優れたol 'ターミナルを使用することを好みます。

実用的な解決策を教えてくれるグル、またはこれを行う方法についての明確な情報はありますか?ありがとうございました!

** 更新(v4) **

最終的な解決策:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2011-08-10
#
# Changelog :
#   2010-11-05 - first version
#   2011-08-10 - added structure : sources, objects, binaries
#                thanks to http://stackoverflow.com/users/128940/beta
#   2017-04-24 - changed order of linker params
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc
# compiling flags here
CFLAGS   = -std=c99 -Wall -I.

LINKER   = gcc
# linking flags here
LFLAGS   = -Wall -I. -lm

# change these to proper directories where each file should be
SRCDIR   = src
OBJDIR   = obj
BINDIR   = bin

SOURCES  := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm       = rm -f


$(BINDIR)/$(TARGET): $(OBJECTS)
    @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
    @echo "Linking complete!"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"

ここで具体的な質問は何ですか?
Oliver Charlesworth 2011

私はあなたが何をしたいのか理解できません。
トム

を更新しましたMakefile。私は近づいていますが、自動変数に問題があるので、とにかくそうです
Yanick Rochon '10

解決策を見つけました。誰かがより良いものを見つけたいと思っている場合でも、Makefileを改善することができます。
Yanick Rochon、2011

2
@YanickRochon私はあなたの英語力を批判するつもりはありませんでした。しかし、PHONYターゲットが意味をなさないようにするには、BANANAを書くことはできません;)gnu.org/software/make/manual/html_node/Phony-Targets.html
joni

回答:


34

まず、次の$(OBJECTS)理由により、ルールに問題があります。

  1. それは一種の無差別であり、すべてのソースがすべてのオブジェクトの前提条件になっています。
  2. 多くの場合、間違ったソースを使用します(file1.oおよびで発見したようにfile2.o
  3. オブジェクトで停止するのではなく、実行可能ファイルを構築しようとします。
  4. ターゲットの名前(foo.o)は、ルールが実際に生成する名前()ではありませんobj/foo.o

私は以下を提案します:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

$(TARGET)ルールは、ターゲット名が実際にルールビルドが何か記述されていないのと同じ問題を抱えています。そのため、make何回か入力すると、理由がなくてもMakeは毎回ターゲットを再構築します。小さな変更により、次のことが修正されます。

$(BINDIR)/$(TARGET): $(OBJECTS)
    $(LINKER) $@ $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

それがすべて整うと、より洗練された依存関係の処理を検討できます。ヘッダーファイルの1つを変更した場合、このメイクファイルはどのオブジェクト/実行可能ファイルを再構築する必要があるかを認識しません。しかし、それは別の日を待つことができます。

編集:
申し訳ありませんが、私は$(OBJECTS)上記のルールの一部を省略しました。修正しました。(コードサンプル内で「ストライク」を使用できるといいのですが。)


あなたの提案された変更で、私は得ます:obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
Yanick Rochon

@Yanick Rochon:複数のmain機能はありますか?たぶん一つfile1.cと一つでmain.c?その場合、これらのオブジェクトをリンクすることはできません。main実行可能ファイルには1つしか存在できません。
ベータ

いいえ、私はしません。私が質問に投稿した最後のバージョンですべてがうまく機能します。Makefileをあなたが提案したものに変更すると(そして私はあなたが言っていることの利点を理解しています)、それが私が得るものです。貼り付けfile1.cたばかりですが、プロジェクトのすべてのファイルに同じメッセージが表示されます。そしてmain.c、それはmain関数を備えた唯一のものです...そして、main.cインポートfile1.hfile2.h(との間に関係はfile1.cありませんfile2.c)が、問題はそこから来るのではないかと思います。
Yanick Rochon、2011

@ヤニック・ロション:$(OBJECTS)ルールの最初の行を貼り付けて間違えました。編集しました。悪い行でエラーが発生しましたが、エラーは発生しませんでした...
Beta

6

-Iコンパイラー・フラグ(CFLAGS)にフラグを追加して、コンパイラーがソース・ファイルを探す場所を示し、-oフラグを追加して、バイナリーを残す場所を示すことができます。

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

$(TARGET): obj
    @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

オブジェクトファイルをobjディレクトリにドロップするには-o、コンパイル時にオプションを使用します。また、$@および$< 自動変数も確認してください。

たとえば、次の単純なMakefileを考えてみます。

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

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

更新>

makefileを見ると、-oフラグを使用していることがわかります。良い。引き続き使用しますが、出力ファイルを書き込む場所を示すターゲットディレクトリ変数を追加します。


もっと具体的に教えてもらえますか?に追加-l ...することを意味しますか?リンカ()への引数はCFLAGSすでにあり-oますLINKER
Yanick Rochon、2011

はい、CFLAGS、そしてはい、-oを使い続け、TARGETPATH変数を追加するだけです。
トム

ありがとう、私は変更を加えましたが、まだ何かが足りないようです(質問の更新を参照)
Yanick Rochon '10

ちょうどmake、Makefileが置かれている場所から
Yanick Rochon、2011

実行中のコマンドが読めませんか?たとえば、gcc -c yadayada。あなたが期待するものを含まない変数があることを確認してください
Tom

-1

私は最近、makefileの作成をやめました。先に進むことを学ぶつもりなら、Eclipse CDTに付属する優れたmakefileジェネレーターがあります。ビルドツリーでの保守性/複数のプロジェクトのサポートが必要な場合は、以下をご覧ください-

https://github.com/dmoulding/boilermake私はこれがかなり良いと思った..!


3
意見に基づく。OPの質問には答えません。Eclipse環境を想定しています。
ナサニエルジョンソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.