1. ホーム
  2. c

[解決済み] SRC、OBJ、BIN サブディレクトリを持つ C プロジェクト用の Makefile を作成するにはどうしたらいいですか?

2023-01-07 12:54:25

質問

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

# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# 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!"

これは基本的にすべての .c.h を生成するためのファイル .o ファイルを生成し、実行ファイルである projectname をすべて同じフォルダーに置きます。

さて、これを少し推し進めたいと思います。 次のようなディレクトリ構造のCプロジェクトをコンパイルするためのMakefileはどのように書けばよいでしょうか。

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

つまり、C言語のソースをコンパイルするMakefileを ./src/ から ./obj/ で、すべてをリンクして、実行ファイルを ./bin/ .

私はさまざまな Makefile を読んでみましたが、上記のプロジェクト構造に対してそれらを動作させることができません。代わりに、プロジェクトはあらゆる種類のエラーを伴ってコンパイルに失敗します。もちろん、私は完全な IDE (Monodevelop, Anjuta など) を使用できましたが、正直なところ、私は gEdit と古き良きターミナルに固執することを好みます。

作業ソリューション、またはこれがどのように行われるかについての明確な情報を与えてくれるグルはいますか?ありがとうございます。

** アップデイト(v4) **

最終的な解決策:

# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# 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!"

どのように解決するのですか?

まず、あなたの $(OBJECTS) のルールは問題があります、なぜなら。

  1. は無差別であり、そのため をすべて ソースをすべてのオブジェクトの前提条件とします。
  2. は、しばしば 間違ったソース を使うことがあります (あなたが file1.ofile2.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!"

もし、ヘッダファイルの一つを変更した場合、この makefile はどのオブジェクト/実行可能ファイルを再構築しなければならないかを知ることができません。しかし、それは別の日に待つことができます。

EDIT:

すみません、一部省略しました。 $(OBJECTS) のルールの一部を省略していましたので、修正しました。(コードサンプル内で"strike"が使えればいいのですが...)