導入事例

Cプログラミングでは、プログラムは複数のソースファイルとヘッダファイルを分割して組織、再利用可能な、コンパイル速度を改善します。しかし、このモジュール性は、ヘッダファイルの変更時に、それを含むすべてのソースファイルが再コンパイルされなければならないという課題を紹介します。これを行うと、手動でエラーが発生し、時間がかかります。 Makefilesは、のビルド自動化ツールによって駆動され、この問題を解決し、エンコーディングによって自動的に問題を解決し、それらを作成する方法は、それらを作成します。 マスターファイルの作成とプロセスは、どのようにして、どのようにして、それらを作成するか、Cfileは、どのようにして、どのようにして、どのようにして、どのようにして、どのようにして、どのようにして、どのようにして、それらを作成するか、Cfileファイルを作成するか、 。

Makefilesとは?

フォームファイルとは、プロジェクトをビルドするためのルールのセットを定義する、 プレーンテキストファイルです。 [make] ユーティリティは、これらのルールを読み取り、ファイルタイムスタンプを確認し、プロジェクトを最新の状態に保つために必要なコマンドのみを実行します。 コアのアイデアは簡単です。各ルールは] target] (生成するファイル)、 [[Fシェル:4:4] と をセットして、 リストに 変更したファイルのみ [FLT] を 設定します。 [FLTF] 対象は、 対象の対象の対象は、 [FLTF] と [F] のみ [F] は、 [FLTF] のみ を構成します。 [FLTF] は、 [F] は、 [[FLTF] は、 [F] は、 [[F] は、 [[F] は、 [FLTF] は、 [[F] は、 [[F] は、 は、 [[F

Makefiles は 1970 年代以降 Unix の一部で、GNU Make は Linux と macOS のデファクトスタンダードです。構文は簡潔ですが、微妙にすることができます。依存関係を正しく取得することは、C 開発者のニーズを第一に考えるものです。

依存関係の理解

Cプロジェクトでは、依存関係は]ファイルに限定されません。各ソースファイルには、1つ以上のヘッダーファイル(例、)が含まれます。ヘッダーが変更された場合、そのヘッダーを含むすべての[]]]が再コンパイルされなければならない。同様に、オブジェクトファイルは、対応するファイルに依存し、最終実行可能はすべてのオブジェクトファイルに依存します。

明示的対インプリシットの依存関係

Makefiles の初期では、プログラマは手動ですべての前提条件をリストしました。このアプローチは壊れやすいです。ヘッダを忘れると、余りに多くのトリガーをリストしている間に、ビルドが固定されます。プロジェクトが成長するにつれて、手動リストは維持できません。現代のソリューションは、コンパイラが依存関係を自動的に生成し、インプリシット、機械チェックされた前提条件にそれらをオンにすることです。

よく作られた Makefile は、依存関係を一流の懸念として扱います。この目標は、再構築を必要としないものを再構築し、常にそのすべてを再構築するものではありません。これは、増分ビルドにおける の本質です]] と [ の効率] の本質です。

Makefileの基本構造

典型的な Makefile には、変数定義、ルール、およびフォニーのターゲットが含まれています。 と のプロジェクトでは、最小限の機能例を示します。

CC = gcc
CFLAGS = -Wall -Wextra -O2

main: main.o utils.o
 $(CC) $(CFLAGS) -o main main.o utils.o

main.o: main.c
 $(CC) $(CFLAGS) -c main.c

utils.o: utils.c
 $(CC) $(CFLAGS) -c utils.c

clean:
 rm -f main main.o utils.o

この Makefile には 4 つのターゲットがあります。 (実行可能), 2 つのオブジェクト ファイル, フォニー ターゲット ]. コロンの後に依存線は ] を指示します] 。 対象を再構築する前にチェックするファイル.

ポニーターゲット

や のようなターゲットは、ファイルを表すものではありません。 ] をファイルの名前でそれらを混同するから] を防ぐには、 ]] フォニー[ として宣言する必要があります。

.PHONY: clean all

これを使わなければ、[というファイルが存在していた場合は、]]make]は、それをアップ〜〜デートに考え、レシピをスキップします。

依存関係を効果的に管理

上記のマニュアルの Makefile は、深刻な欠陥があります。[の依存性は正しいですが、ヘッダはどうですか?[]が変更された場合、(これを含む)再構築する必要がありますが、ルールはに依存するだけと言います。 修正は、コンパイラが実際の依存リストを生成できるようにすることです。

GCCによる自動依存生成

GCC(およびClang)は、フラグの []家族を使用して依存情報を生成することができます。ほとんどのプロジェクトのための最も実用的な組み合わせは、です。

  • ] – コンパイル時に依存ファイル()を記述し、ユーザ定義ヘッダーのみ(システムヘッダではなく)をリストします。
  • ] – 依存関係ファイルの名称を指定します。
  • [] - ヘッダーが削除されるとエラーを防ぐ、各ヘッダーのフォニーターゲットを追加します。

以下は、Makefile に自動依存関係をトラッキングする仕組みです。

CC = gcc
CFLAGS = -Wall -Wextra -O2 -MMD -MP
SRCDIR = src
OBJDIR = obj
SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))
DEPS = $(OBJS:.o=.d)

all: myprogram

myprogram: $(OBJS)
 $(CC) $(CFLAGS) -o $@ $^

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

-include $(DEPS)

.PHONY: all clean
clean:
 rm -rf $(OBJDIR) myprogram

説明:

  • [ は、ソースディレクトリ内の [] ファイルを全て収集します。
  • は、オブジェクトファイルパスに変換します。
  • [] は依存ファイル (例えば、]) をリストします。
  • パターンルールは、それぞれ[]]ファイルをコンパイルし、]のせいで、 をサイドエフェクトとして生成します。
  • 行 [] は生成された ファイルを読み込み、実際の Makefile 前提条件に変えます。 ダッシュ () は、 ] ファイルがまだ存在しないときにエラーを抑制します(例えば、最初のビルドで)。

が変更された場合、の次の呼び出しは、]が自動的に再コンパイルされます。 [の []]のファイルには、前提条件として []が含まれています。

高度な技術

発生依存関係を安全に処理

ヘッダが削除されたとき、 [] ファイルがまだ参照して、 ] を] を行ない、 未処理のターゲットエラーで失敗させる。 [] フラグは、各依存ヘッダーの空のフォニールールを追加することによってこれに対処します。 ヘッダがなくなった場合は、]] は、単に偽のルールを実行し、続行します。

ソース変更後の依存ファイルを含む

一つの微妙: ソースファイルが を追加または削除した場合、対応する ファイルが再生されなければならない。 [ ファイルがオブジェクトファイルの前提条件であるので、]]make[[]]は、変更されたタイムスタンプと再コンパイルに気づくので、 [] ファイルを再生します。 この再帰は、最初にビルドが完了したら自動的に行われます。

オーダーのみの前提条件を使用する

建物の前に存在するディレクトリが必要であるが、そのタイムスタンプが再構築をトリガーしたくない。これは、 のロールだ。 注文のみの前提条件 (で区切られた)。 上記のパターンルールでは、レシピの中のを、代替手段として使用した。

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

$(OBJDIR):
 mkdir -p $@

コンパイルの前にディレクトリが作成されるようにしますが、ディレクトリ自体(例えば、内部の新しいファイル)に変更は、recompilationをトリガーしません。

依存関係の管理のためのベストプラクティス

  1. は、一日から自動依存生成を使用します。[ - 単一ファイルプロジェクトでも、それは良い習慣です。 それは何も費用もかかり、将来の間違いを防ぐ。
  2. [] 依存関係ファイルをソースファイルから分離します。[]] または ] ディレクトリにそれらを置きます。これにより、清掃が容易になり、ソースツリーの乱雑を避けます。
  3. [] 生成された を 生成した規則の後に 含める。[] ディレクティブは うまく なりますが、パターン規則の後に配置すると、 を 最初に読み込む前にオブジェクトファイルをビルドする方法が わかります。 ファイルを読み込む。
  4. [] フォニーの対象を清掃に使用します。[[ ]]、]、、]]は共通です。 で常に宣言します。
  5. [コンパイラフラグ、ソースディレクトリ、ファイルのリストの変数をレバレッジします。]] これにより、プロジェクト全体でMakefile再利用可能なようになり、カスタマイズが容易になります。
  6. [ は、 意図したヘッダの変更で Makefile をテストします。[] はヘッダを修正し、]を実行し、影響を受けたオブジェクトファイルだけが再コンパイルされます。 完全な再ビルドが行われると、依存性トラッキングが間違っています。
  7. [] Makefileをシンプルにし、必要なよりもシンプルにしない。[[]]のような高度な機能でオーバーエンジニアリングすることで、痛みを伴うデバッグを行うことができます。この記事で示されているパターンから始めてください。それはファイルの数十にうまくスケールします。

外部リソース

理解を深めるためには、これらの権威ある参照に相談してください。

  • [GNU は、マニュアル]をします。 - 構文、関数、および高度な機能を作るための決定的なガイド。
  • []GCC プリプロセッサーオプション – ]、 ]、 []、および関連するフラグのドキュメンテーション。
  • Chase LambertによるMakefileチュートリアル – 実際のユースケースを多くカバーする、実用的な、よく構成されたチュートリアル。

コンテンツ

Makefiles で C プログラムの依存関係を管理することは、豪華ではなく、単一のファイルを上回るプロジェクトに必要な必要性ではありません。 パターンルールを組み合わせることにより、 で自動依存生成され、生成された []] のファイルへの慎重なインクルードは、迅速かつ正しいビルドシステムを作成できます。 ここで示した技術は、手動の追跡をなくし、ビルド時間を削減し、オブジェクトファイルによって引き起こされた微妙なバグを防ぎます。 これらのパターンを内部化したら、手動で記述するだけで、手動で実行されることはありません。