[C 言語] GNU make と Makefile: 構築ツールと構築記述ファイルの威力

この記事では、ソフトウェア開発でよく使われるビルドツールやビルド記述ファイルであるmakeとMakefileについて詳しく紹介します。この記事では、make の機能、原理、使用法、さらに Makefile の構造、構文、一般的な使用法について説明します。これらのツールを理解することで、開発者は複雑なソフトウェア プロジェクトをより効率的に管理および構築できるようになります。


序章

ソフトウェア開発プロセスにおいて、ビルド(Build)はソースコードのコンパイル、ライブラリファイルのリンク、実行ファイルの生成などの作業を行う重要なリンクです。ビルド プロセスを自動化および簡素化するには、ビルド ツールの登場が重要になっています。make は、Makefile をビルド記述ファイルとして使用する一般的なビルド ツールです。この記事では、読者が make と Makefile についてより包括的に理解できるように、make と Makefile について詳しく紹介します。

Makefile には、複数のターゲットと対応するルール、さらにいくつかの変数、関数、および条件判断を含めることができます


1つ、作る

1.1 基本的な作り方の紹介

make は、一連のルールと依存関係に従ってどのファイルを再構築する必要があるかを自動的に判断できるビルド ツールであり、それによって繰り返しコンパイルの作業負荷を軽減します。ソース ファイルとターゲット ファイルのタイムスタンプを比較してビルドの必要性を判断し、対応するビルド コマンドを実行します。

make の原理は「依存関係」に基づいています。Makefile 内のルールと依存関係を分析することで、make はタスク実行グラフを構築し、ビルド タスクが正しい順序で実行されることを確認できます。これにより、make は複雑なプロジェクトを管理するための強力なツールになります。 。


GUN make公式サイト:https://www.gnu.org/software/make/manual/make.html

公式 PDF マニュアル (最終更新日 2023 年 2 月 26 日): https://www.gnu.org/software/make/manual/make.pdf

Make にはいくつかのバージョンがありますが、最も一般的なのはGNU MakeGNU プロジェクトの一部であることです。GNU Make の最新バージョンは 4.4.1 です。GNU Make に加えて、BSD Make、Microsoft NMAKE、CMake など、異なる構文と関数を持つ Make の他の亜種も存在します。

ここに画像の説明を挿入


1.2 makeの実行処理

make実行プロセスには、Makefile の解析、ターゲットと依存関係の決定、ファイル変更時間の確認、ビルド コマンドの実行、ソフトウェア ビルド プロセスを自動化するためのビルド結果の確認などの手順が含まれます。

makeの基本的な実行プロセス:

  1. makeコマンドは、ユーザーがターミナルに通常 の形式で入力しますmake [target]。ここで、[target]は Makefile で定義されたターゲットです。
  2. makeMakefile を解析し、ビルドするターゲットを決定します。ターゲットが指定されていない場合は、デフォルトで最初のターゲットがビルドされます。
  3. ターゲットごとに、makeその依存関係を確認します。依存関係は、ターゲットを構築するために必要な他のファイルまたはターゲットです。
  4. makeオブジェクト ファイルの存在を確認し、オブジェクト ファイルの変更時刻をそのすべての依存関係の変更時刻と比較します。ターゲット ファイルが存在しない場合、または依存関係の 1 つが更新されている場合は、ターゲットを再構築する必要があります。
  5. ターゲットを再構築する必要がある場合は、makeMakefile で定義されたルールに従って対応するコマンドが実行されます。これらのコマンドは、ソース コードのコンパイル、オブジェクト ファイルのリンク、ファイルのコピーなどの任意のビルド ステップに使用できます。
  6. ビルドコマンドの実行が終了したら、makeオブジェクトファイルが正常に生成されたことを確認します。ビルドが成功した場合は、他のターゲットのビルドを続行するか、ビルド プロセスを完了します。そうでない場合は、ビルドの失敗を報告し、ビルド プロセスを停止します。

make重要なのは、目標と依存関係に基づいて、ビルドの順序と実行する必要があるコマンドを決定することです。ファイルの変更時刻を比較してファイルを再構築する必要があるかどうかを判断し、増分構築の効果を実現し、不要なファイルの繰り返し構築を回避します。

二、Makefile

2.1 Makefile ファイルの命名

Makefile の命名に関する厳密なルールはありませんが、コードの読みやすさと保守性を向上させるために従うべき一般的な命名規則がいくつかあります。

Makefile の命名規則として推奨されるものは次のとおりです。

  1. デフォルト ルール: プロジェクトに Makefile が 1 つだけあり、それがプロジェクト全体のデフォルトのビルド ルールである場合は、それに名前を付けることができます (Makefile最初の文字が大文字であることに注意してください)。これは、makeツールが検索するファイル名です。デフォルト。

  2. プロジェクト名に従って: プロジェクトの名前に従って Makefile ファイルに名前を付けることができます。これにより、ファイルと特定のプロジェクトとの関連性がより明確に表現されます。

    • たとえば、プロジェクトの名前が「myproject」の場合、Makefilemyproject.mkまたはMakefile.myproject.
  3. ビルド ターゲットに応じて: Makefile が、異なるコンパイラ オプションや異なるバイナリ ファイルなど、特定のビルド ターゲットをターゲットにしている場合は、ターゲットの名前に従って Makefile に名前を付けることができます。

    • たとえば、Makefile を使用して 2 つのターゲット「debug」と「release」をビルドする場合、その名前はMakefile.debugと となりますMakefile.release
  4. サフィックスの追加: ファイルが Makefile であることを明確に示すために、.mkや などのサフィックスを追加できます.make

    • たとえば、Makefile.mkまたは という名前を付けることができますproject.make

どのような名前を選択する場合でも、チーム メンバーや他の開発者が必要な Makefile をすぐに理解して見つけられるように、ファイル名が説明的であり、その目的と関連性が明確に伝わるようにしてください。

2.2 基本的な構文

2.21 注意事項

#コメントで始まる行を使用して、ルールまたはディレクティブの動作を説明します。

2.22 変数の定義

変数は値の保存と転送に使用され、その値は変数名によって参照できるため、構築プロセス中のパラメータ化と構成が実現されます。

Makefile は、変数を定義する 2 つの一般的な方法、つまり変数の単純な展開と変数の再帰的展開をサポートしています。

  1. 変数を展開するだけです。

    • =変数の割り当てには演算子を使用します(例: ) VAR = value
    • 変数が使用される場合、その変数は単純に展開されます。つまり、変数が参照されると、その値は参照された位置に直接置き換えられます。
    • 単純な展開された変数の例:
      NAME = John
      GREETING = Hello, $(NAME)!
      
      target:
      	@echo $(GREETING)   # 将展开为 "Hello, John!"
      
  2. 変数を再帰的に展開します。

    • :=変数の割り当てには演算子を使用します(例: ) VAR := value
    • 変数は割り当て時に再帰的に展開されます。つまり、変数参照は割り当て時にその値に展開され、後続の参照では展開された値が維持されます。
    • 変数を再帰的に展開する例:
      A := 1
      B := $(A)
      A := 2
      
      target:
      	@echo $(B)   # 将展开为 "1"
      
    • 上の例では、の値が変更される前に のB代入が行われるため、の値はの初期値から拡張されます。ABA

変数定義には、他の変数への参照、関数呼び出し、コマンド置換などを含めることもできます。Makefile には、変数の値を処理するために使用できる、 $(wildcard)$(patsubst)、などのいくつかの組み込み関数が用意されています。$(shell)これらの機能は、ニーズに応じて柔軟に使用できます。

=変数の代入にand演算子を使用するだけでなく、や などの他の演算子を使用して変数、条件付き代入、および上書きを防止する代入を変更する:=こともできます。+=?=!=

2.23 ターゲットルール

ターゲット ルールは、1 つ以上のターゲット ファイルと、そのターゲットのビルドに必要な依存関係とビルド コマンドを定義します。

ターゲット ルールは Makefile の中核部分であり、プロジェクトの構築プロセスを記述するために使用されます。

ターゲット ルールは次の部分で構成されます。

  1. ターゲット( Target):

    • ターゲットはビルド プロセスからの出力ファイルであり、実行可能ファイル、ライブラリ、中間ファイルなどが考えられます。
    • ターゲットは通常ファイル名ですが、特定のコマンドをトリガーするために使用される疑似ターゲットにすることもできます。
    • 目標ルールでは 1 つ以上の目標を定義できます。
  2. 依存関係( Dependencies):

    • 依存関係は、ターゲットの構築に必要な追加のファイルまたはターゲットを定義します。
    • 依存関係には、ソース ファイル、ヘッダー ファイル、他のターゲットなどが含まれます
    • 依存関係のファイルが変更された場合、ターゲットを再構築する必要があります。
  3. ビルドコマンド ( Commands):

    • ビルド コマンドは、ビルド ターゲットを実行するために必要なコマンド ラインです。
    • ビルド コマンドはタブ文字でインデントされ、ターゲット ルールの次の行に配置されます。
    • ビルド操作は、シェル コマンド、コンパイラ コマンド、リンカー コマンドなどを使用して実行できます。

単純なターゲット ルールの例を次に示します。

# 目标规则
target: dependency1 dependency2
	commands
	commands
	...

上の例では、targetはオブジェクト ファイル、dependency1のビルドに必要な依存関係ファイルdependency2です。依存ファイルが変更された場合、またはオブジェクト ファイルが存在しない場合に実行されるビルド コマンドです。targetcommandstarget

Makefile は依存関係を使用して、ターゲットがビルドされる順序と、リビルドが必要な条件を決定します。依存ファイルの変更時刻がターゲット ファイルよりも新しい場合、またはターゲット ファイルが存在しない場合は、ターゲット ルールのビルド コマンドが実行されます。

Makefile は、ルールに対応する複数のターゲットもサポートします。この場合、ビルド コマンドを 1 回作成するだけで、複数のターゲットに適用できます。

ターゲット ルールには、特定のビルド ニーズを満たすために複数の依存関係とビルド コマンドを含めることができます。適切なターゲット ルールを定義することで、プロジェクト全体またはプロジェクトの特定の部分を構築できます。

2.24 偽りの標的

偽のターゲット ( Phony Targets) は、実際のファイルに対応しない特殊なタイプのターゲットですが、特定のコマンドまたはアクションをトリガーするために使用されます。Makefile における疑似ターゲットの主な役割は、一般的に使用される操作や、make コマンドによって実行されるコマンドを定義することです。

偽ターゲットの特徴は次のとおりです。

  1. 本物のファイルに対応していない: 偽のターゲットには実際のファイルが関連付けられておらず、その名前はターゲットを識別するためにのみ使用されます。
  2. 常に実行される: 疑似ターゲットは実際のファイルに対応していないため、疑似ターゲットによって定義されたコマンドは、make コマンドが実行されるたびにトリガーされます。
  3. ビルド条件は通常満たされない: 擬似ターゲットには実際のファイルが関連付けられていないため、そのビルド条件は通常 false になります。

偽のターゲットは次のように定義されます。

.PHONY: target1 target2 ...

target1:
	commands

target2:
	commands

上の例では、.PHONY特殊なターゲットを使用して擬似ターゲットを宣言しています。擬似ターゲットの名前target1とそれに対応するビルド コマンドが続きます。target2必要なだけ疑似ターゲットを定義し、その後に対応するビルド コマンドを定義できます。

偽のターゲットの一般的な用途は次のとおりです。

  • clean: クリーン操作を実行し、ビルドによって生成されたファイルを削除します。
  • all: すべてのオブジェクト ファイルをビルドします。
  • install: ビルドによって生成されたファイルを指定された場所にインストールします。
  • test:テスト動作を実行します。
  • dist: ソフトウェア配布パッケージを生成します。

疑似ターゲットを定義すると、複雑なコマンドを手動で入力することなく、これらの一般的な操作を簡単に実行できます。

make コマンドを実行するときに、ビルドする疑似ターゲットを指定できます。たとえば、ターゲットmake cleanをトリガーするビルド コマンドを実行します。clean

疑似ターゲットの名前は実際のファイルの名前と競合してはいけないことに注意することが重要です。競合しないと、ビルド エラーや予期しない動作が発生する可能性があります。したがって、競合を避けるために、疑似ターゲット名の前に.PHONYプレフィックスやその他のプロジェクト関連のプレフィックスを追加することをお勧めします。

2.25 条件文

条件ステートメントは、特定の条件に基づいてさまざまなアクションを実行するために使用されます。条件ステートメントを使用すると、変数の値またはその他の条件に基づいて、特定のコマンドまたはアクションを選択的に実行できます。

Makefile には次の条件ステートメントが用意されています。

  1. ifeqそしてifneq

    • ifeq変数の値が指定された値と等しいかどうかを確認するために使用されます。
    • ifneq変数の値が指定された値と等しくないかどうかをチェックするために使用されます。
    • 構文は次のとおりです。
      ifeq ($(variable), value)
      	# 条件为真时的操作
      else
      	# 条件为假时的操作
      endif
      
    • 条件文内でifeqor を複数使用することでifneq、複雑な条件判定を行うことができます。
  2. ifdefそしてifndef

    • ifdef変数が定義されているかどうかを確認するために使用されます。
    • ifndef変数が未定義かどうかを確認するために使用されます。
    • 構文は次のとおりです。
      ifdef variable
      	# 变量已定义时的操作
      else
      	# 变量未定义时的操作
      endif
      
  3. ifeqとの入れ子ifdef:

    • 条件文を互いに入れ子にして、より複雑な条件判断を実現できます。
    • 例えば:
      ifeq ($(variable1), value1)
      	ifdef variable2
      		# 条件为真时的操作
      	else
      		# 条件为假时的操作
      	endif
      endif
      
  4. 条件ステートメント内のコマンド:

    • 条件ステートメントには、条件が満たされた場合に特定のコマンドを実行するコマンドを含めることができます。
    • コマンドはタブ文字でインデントし、条件ステートメント内の各分岐に指定する必要があります。
    • 例えば:
      ifeq ($(variable), value)
      	@echo "Variable is equal to value."
      else
      	@echo "Variable is not equal to value."
      endif
      

条件付きステートメントは Makefile で非常に役立ち、さまざまな条件に従ってさまざまなコマンドや操作を実行できるため、より柔軟なビルド プロセスを実現できます。変数の値、変数の定義の有無、条件文の入れ子などに基づいて複雑な条件判定を行うことができます。条件ステートメントをターゲット ルール、変数定義などの他の要素と組み合わせると、より構成可能で拡張可能な Makefile を構築できます。

2.26 機能

関数は変数を処理および操作するために使用されます。これらは、パラメーターを介して値を渡し、処理された結果を返すことができる一連の事前定義された操作を提供します。関数を使用して、ファイルリストの生成、文字列操作、パス処理などを行うことができます。

Makefile には多くの組み込み関数が用意されており、一般的に使用されるいくつかの関数とその関数を以下に示します。

  1. $(subst from,to,text)

    • Replace string 関数。すべての文字列を文字列textfrom置き換えますto
    • 例:$(subst .c,.o,$(SRC_FILES))すべてのソース ファイルの拡張子を.cに置き換えます.o
  2. $(patsubst pattern,replacement,text)

    • textパターンに一致する のpattern部分を に置き換えるパターン置換関数ですreplacement
    • パターンには%、任意の文字を表すワイルドカードを含めることができます。
    • 例:で終わる$(patsubst %.c,%.o,$(SRC_FILES))すべての.cファイルを で.o終わるファイルに置き換えます。
  3. $(wildcard pattern)

    • ファイル マッチング関数一致したファイルのリストを返しますpattern
    • ワイルドカード*?パターン マッチングに使用できます。
    • 例:ディレクトリにあるすべてのファイルのリスト$(wildcard src/*.c)を返しますsrc/.c
  4. $(shell command)

    • コマンド実行関数は、指定されたコマンドを実行し、その出力結果を返します。
    • 任意のシェルコマンドを実行できます。
    • 例:$(shell date +%Y-%m-%d)現在の日付を返します。
  5. $(foreach var,list,text)

    • list各要素を変数に代入しvar各反復で展開するループ関数text
    • 例:ソースファイルごとにコマンド$(foreach file,$(SRC_FILES),$(shell wc -l $(file)))を実行しwc -l、行数を取得します。
  6. $(if condition,then-part,else-part)

    • の値に応じて、またはをcondition選択的に返す条件関数then-partelse-part
    • 例:コンパイラ フラグは変数の値$(if $(DEBUG),$(CC_FLAGS_DEBUG),$(CC_FLAGS_RELEASE))に基づいてDEBUG選択されます。
  7. $(error message)

    • エラー関数: エラー メッセージを出力し、make の実行を停止します。
    • 特定の条件下でビルド プロセスを終了するために使用できます。
    • 例:変数が定義されているかどうかを$(if $(VERSION),,$(error VERSION is not set))確認しVERSION、定義されていない場合はエラー メッセージを出力します。

上記は一般的に使用される Makefile 関数の一部にすぎません。文字列操作、ファイル操作、パス操作などに使用できる関数は他にもたくさんあります。組み込み関数の詳細な使用方法については、Makefile のドキュメントまたはマニュアルを参照してください。

Makefile での強力な処理を実現する関数

2.27 include ディレクティブ

includeディレクティブは、現在の Makefile に他の Makefile を導入するために使用されますこれにより、変数、ターゲット ルール、および他のファイルで定義されたその他のコンテンツを現在の Makefile にマージして、コードの再利用とモジュール化を実現できます。

includeディレクティブの構文は次のとおりです。

include file1 file2 ...

このうち、file1file2、 … はインクルードする Makefile のパスです。

includeディレクティブは次のように機能します。

  1. ディレクティブが解析されるとinclude、Make インタープリターは指定されたファイル内のコンテンツを検索して読み取ります。
  2. インタプリタは、読み取ったコンテンツを現在の Makefile にマージします。
  3. 変数定義、ターゲット ルールなどを含む後続のコンテンツの解析を続けます。

includeディレクティブは通常、次の状況で使用されます。

  1. モジュール構成: ターゲット ルールまたは同様の関数を持つ変数定義を別の Makefile に配置し、includeディレクティブを使用してそれらをメインの Makefile に含めます。これにより、メインの Makefile がクリーンになり、保守と読み取りが容易になります。

  2. 共有変数とルール: 共有変数とルールを 1 つのファイルで定義し、includeそれらをディレクティブとともに複数の Makefile にインポートすることで、それらの一貫性と再利用性を確保できます。

  3. 外部構成ファイル: ビルド システムの構成情報を独立した Makefile に保存し、ディレクティブを使用してincludeそれをメインの Makefile にインポートします。これにより、メインの Makefile を変更せずに構成情報を簡単に変更できます。

includeコマンドを使用するときは、次の点に注意する必要があります。

  • 含めるファイルは、相対パスまたは絶対パスを使用して指定できます。相対パスは、現在の Makefile に対する相対パスです。
  • 変数を使用して、動的に含めるためのディレクティブでファイル パスを指定できますinclude
  • 指定されたファイルが存在しない場合、または読み取れない場合、Make は警告を発行しますが、ビルド プロセスは中断しません。

2.3 ヘッダファイルとライブラリ

-Iそれぞれと を使用して-L指定します。

  1. 変数の使用: ヘッダー ファイルおよびライブラリへのパスを保持する変数を定義し、Makefile 全体で使用できます。例えば:

    # 定义头文件和库路径的变量
    INCLUDE_PATH = -I/path/to/includes
    LIBRARY_PATH = -L/path/to/libraries
    
    # 使用变量来编译目标
    target: source.c
        gcc $(INCLUDE_PATH) $(LIBRARY_PATH) -o target source.c
    

    上記の例では、INCLUDE_PATH変数はヘッダー ファイルのパスを指定し、LIBRARY_PATH変数はライブラリのパスを指定します。これらの変数を使用して、ターゲットをコンパイルするときにパスを指定できます。

  2. CFLAGSMakefile の組み込み変数を使用する: Makefile には、コンパイルおよびリンクのオプションを指定するために使用できる、や などのいくつかの組み込み変数が用意されていますLDFLAGSこれらの変数にパス情報を追加できます。例えば:

    # 添加头文件和库路径到 CFLAGS 变量
    CFLAGS += -I/path/to/includes
    LDFLAGS += -L/path/to/libraries
    
    # 使用 CFLAGS 和 LDFLAGS 变量来编译目标
    target: source.c
        gcc $(CFLAGS) $(LDFLAGS) -o target source.c
    

    上記の例では、CFLAGS変数はコンパイル オプションの指定に使用され、LDFLAGS変数はリンク オプションの指定に使用されます。ヘッダー ファイルのパスをCFLAGS変数に追加し、ライブラリのパスをLDFLAGS変数に追加できます。

  3. コマンド ライン パラメーターの使用: コマンド ライン パラメーターを使用して、ヘッダー ファイルとライブラリのパスを指定することもできます。これにより、makeコマンドの実行時にパス情報を柔軟に渡すことができます。例えば:

    # 使用命令行参数来编译目标
    target: source.c
        gcc -I$(INCLUDE_PATH) -L$(LIBRARY_PATH) -o target source.c
    

    上の例では、コマンド ラインの-Dパラメーターを使用してヘッダー ファイル パス ( INCLUDE_PATH) とライブラリ パス ( ) を指定しLIBRARY_PATH、Makefile でこれらの変数を使用できます。

これらのメソッドは、ニーズに応じてヘッダー ファイルとライブラリへのパスを柔軟に指定し、正しいコンパイルとリンクを保証します。プロジェクトの構造やニーズに応じて、適切な方法を選択してください。

2.4 組み込み変数

Makefile には、コンパイルとリンクのプロセスを制御するために使用できる多数の組み込み変数が用意されています。よく使用される組み込み変数をいくつか示します。

  1. CC: C コンパイラの名前を指定します。デフォルトは ですcc
  2. CFLAGS: 最適化レベル、警告レベルなどの C コンパイル オプションを初期値なしで指定します。
  3. CPPFLAGS: マクロ定義、インクルードパスなどのC/C++プリプロセッサオプションを初期値なしで指定します。
  4. LDFLAGS: ライブラリ パス、ライブラリ名などのリンク オプションを初期値なしで指定します。
  5. LDLIBS:-lm数学ライブラリをリンクする手段など、リンクするライブラリファイルを指定します。
  6. AR: 静的ライブラリ パッケージ化ツールの名前を指定します。デフォルトは ですar
  7. ARFLAGS:ライブラリファイル作成時のパラメータなど、静的ライブラリパッケージツールのオプションを指定します。
  8. RM: ファイルを削除するコマンドを指定します。デフォルトは ですrm -f
  9. MAKE: サブ Makefile を呼び出すコマンドを指定します。デフォルトは ですmake
  10. MAKEFLAGS-s:サイレントモードを意味するMake のグローバルオプションを指定します。
  11. SHELL: Makefile を解釈するシェル プログラムを指定します。デフォルトはシステムのデフォルト シェルです。

上記の変数に加えて、コンパイルとリンクのプロセスをより詳細に制御するために使用できる次のような変数があります。

  • CXX: C++ コンパイラの名前を指定します。デフォルトは ですg++
  • CXXFLAGS: C++ コンパイル オプションを指定します。
  • AS: アセンブラ名を指定します。
  • ASFLAGS: アセンブラオプションを指定します。
  • CPP: C/C++ プリプロセッサの名前を指定します。デフォルトは です$(CC) -E
  • CPPFLAGS: C/C++ プリプロセッサ オプションを指定します。
  • LDCONFIG: ダイナミックリンカを設定するコマンドを指定します。

これらの変数の名前は、特定のニーズに合わせて Makefile で再定義できます。Makefile を作成するとき、これらの変数を使用してコンパイラ、オプション、リンカーの動作を制御したり、ビルド プロセスに関連する他のタスクを実行したりできます。

2.5 自動変数

Makefile には、ルールの実行中に対応する値に自動的に置き換えられる特別な自動変数がいくつかあります。一般的に使用される自動変数は次のとおりです。

  • $@対象ファイル(Target)、つまりルールの対象を示します。
  • $<:ルール内の最初の依存関係ファイル(Dependency)を示します。
  • $^:ルール内のすべての依存ファイルのリストをスペースで区切って示します
  • $?: ターゲット ファイルよりも新しいすべての依存ファイルのリストをスペースで区切って示します。
  • $*: ターゲット ファイルのサフィックスを除いた部分を示します (例: foo.ois $*) foo
  • $(@D):対象ファイルが存在するディレクトリをファイル名を除いて示します。
  • $(@F):対象ファイルのディレクトリ部分を除いたファイル名を示します。

これらの自動変数をルールのコマンドで使用すると、対応するファイル名、ディレクトリ、その他の情報を簡単に参照できます。自動変数を使用すると、Makefile ルールをより柔軟かつ拡張可能にすることができます。

ルールコマンドで自動変数を使用する場合、ファイル名のスペースなどの特殊文字によって引き起こされる問題を避けるために、自動変数を二重引用符で囲む必要があることに注意してください。例えば:

$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) -o "$@" "$^"

はい、ファイル名内のスペースなどの特殊文字もコマンドに正しく渡されるように、$@と を二重引用符で囲みます。$^

上記の一般的に使用される自動変数に加えて、Makefile は、ファイル名のディレクトリ部分を取得するために使用される、$(@D)、などの他の自動変数も提供します。$(<D)

2.6 特殊文字

Makefile では、一部の特殊文字には特定の意味と機能があります。これらの特殊文字は、Makefile を作成するときに特別な注意と取り扱いが必要です。一般的な特殊文字とその意味をいくつか示します。

  1. #コメント記号。Makefile 内の で始まる行は#コメントとみなされ、それ以降の内容は無視されます。コメントを追加し、コードの動作を説明するために使用できます。

  2. $: 変数の参照記号。$変数の値を参照する変数名が続きます。たとえば、$(CC)参照変数の値を表す場合などですCC

  3. :=: 代入演算子。変数に値を代入するために使用されます。たとえば、は変数に代入するCFLAGS := -Wallことを意味します-WallCFLAGS

  4. =: 単純な代入演算子。変数に値を代入するために使用されます。と異なり:==で割り当てられた場合、変数の値は参照時に遅延展開されます。たとえば、CFLAGS = -Wall

  5. ?=:条件付き代入演算子。条件付き代入操作に使用され、変数が未定義の場合にのみ代入が実行されます。たとえば、CFLAGS ?= -Wall変数がCFLAGSunknown の場合にのみ変数に代入することを意味します-Wall

  6. +=:代入演算子を追加します。変数の末尾に値を追加するために使用されます。たとえば、は変数の末尾に追加するCFLAGS += -O2ことを意味します。-O2CFLAGS

  7. @:サイレントモードのマーク。コマンドの出力を端末に表示するかどうかを制御するために使用されます。コマンドの前にシンボルを追加すると、@コマンドの実行時に独自のコマンド ラインが表示されなくなります。たとえば、@echo "Hello, world!"

  8. *?および%: ワイルドカード。パターンマッチング操作に使用されます。ルールの依存関係とターゲットでは、*任意の文字シーケンスと一致し、?単一の文字と一致し、%任意の文字シーケンスと一致します。ファイル名やパスなどを一致させるために使用できます。

  9. .および..: 特別なディレクトリ シンボル。パスにおいて、.はカレントディレクトリを意味し、..は親ディレクトリを意味します。

  10. \: 改行記号。長い行を複数の行に分割して、Makefile の行を読みやすくするために使用されます。

これらの特殊文字は、Makefile 内で特定の意味と機能を持ちます。Makefile を作成するときは、対応する文法規則に従い、必要に応じてこれらの特殊文字を正しく処理し、エスケープする必要があります。

2.7 その他の特別な目標

  1. all:target:allは、プロジェクトをビルドするためのルールを定義するために使用される共通のターゲット名です。通常、all:ターゲットは、プロジェクトの生成に必要なすべてのオブジェクト ファイルと、ビルド操作を実行するためのルールに依存します。makeコマンドの実行時にターゲットが指定されていない場合は、makeデフォルトでターゲットが実行されますall:したがって、all:ターゲットのルールには通常、ソース コードを実行可能ファイルにコンパイルする手順が含まれています。

    簡単な例を次に示します。

    all: project
    
    project: main.o foo.o bar.o
        gcc -o project main.o foo.o bar.o
    
    main.o: main.c
        gcc -c main.c
    
    foo.o: foo.c
        gcc -c foo.c
    
    bar.o: bar.c
        gcc -c bar.c
    

    上記の例では、all:ターゲットはオブジェクト ファイルmain.ofoo.oおよびに依存しておりbar.oprojectこれらのオブジェクト ファイルを実行可能ファイルにリンクするためのルールを定義します。

  2. clean:target:cleanは、アイテムのクリーニングのルールを定義するために使用されるもう 1 つの一般的なターゲット名です。clean:通常、ターゲットのルールには、生成されたオブジェクト ファイル、実行可能ファイル、ビルド プロセス中に生成されるその他の一時ファイルを削除するアクションが含まれます。

    簡単な例を次に示します。

    clean:
        rm -f project main.o foo.o bar.o
    
  3. install:ターゲット:installターゲットは、ビルドされた実行可能ファイル、ライブラリ、またはその他のアーティファクトを指定された場所にインストールするために使用されます。通常、install:ターゲットは生成されたファイルをターゲット パスにコピーし、必要な構成と権限の設定を実行します。

    簡単な例を次に示します。

    install: project
        cp project /usr/local/bin
    

    上記の例では、install:ターゲットはproject実行可能ファイルに依存し、/usr/local/binそのファイルをディレクトリにコピーするためのルールを定義します。

  4. uninstall:ターゲット:uninstallターゲットは、インストールされたファイルをインストール場所からアンインストールするために使用されます。通常、これには、インストールされているファイルをターゲット パスから削除することが含まれます。

    簡単な例を次に示します。

    uninstall:
        rm -f /usr/local/bin/project
    

    上記の例では、uninstall:ターゲットは、/usr/local/binディレクトリからproject実行可能ファイルを削除するルールを定義します。

2.8 メイク実行完了の判定

Makefile の実行中、指定されたターゲット ルールと依存関係に従ってビルド操作が実行されます。Make ユーティリティは、オブジェクト ファイルとその依存関係のタイムスタンプをチェックして、オブジェクト ファイルを再構築する必要があるかどうかを判断します。

Makefile の実行は次の状況で完了します。

  1. オブジェクト ファイルがすでに最新である: すべてのオブジェクト ファイルがすでに最新である場合、つまり、オブジェクト ファイルのタイムスタンプがすべての依存関係よりも古くない場合、Make ツールはビルド タスクが完了したものとみなし、アクションは必要ありません。

  2. 指定されたターゲット ルールを実行する: コマンド ラインでターゲット ルールを指定して Makefile が実行され、ターゲット ルールのビルド コマンドが正常に実行され、すべての依存関係が正常にビルドされた場合、Make ツールは Makefile を実行されました。

  3. エラーまたは中断: Makefile の実行中に、ビルド コマンドの失敗や依存関係が見つからないなどのエラーが発生した場合、Make ツールは実行を直ちに停止し、エラー メッセージを出力します。この時点で、Makefile の実行は中断されたとみなされます。

  4. デフォルトのターゲット ルールを実行する: コマンド ラインでターゲット ルールが指定されておらず、指定されたターゲット ルールがデフォルトのターゲット ルールとして Makefile で定義されており、allこのターゲット ルールのビルド コマンドが正常に実行され、すべての依存関係が正常にビルドされた場合、その後、Make ツールは Makefile が実行されたと認識します。

3. 例

make を学ぶには、まず gcc を使用できるようにする必要があります: [Linux C] GCC コンパイル && GDB デバッグ (開始から破棄まで) (GCC デバッグ オプション、GDB デバッグ、条件付きブレークポイント、リモート デバッグ、スクリプト デバッグの詳細な説明)

3.1 プロジェクトの構造

ここに画像の説明を挿入

3.2 gcc の使用

1 つのステップでコンパイルとリンクを行います。

gcc   -Wall -Wextra  -o gcc_out src/main.c src/foo.c src/bar.c -Iinclude

走る:
ここに画像の説明を挿入

3.3 make の使用

メイクファイル:

# 编译器和编译选项
CC = gcc
CFLAGS = -Wall -Wextra

# 源文件和目标文件
SRC_DIR = src
INC_DIR = include
OBJ_DIR = obj

# 获取源文件列表和对应的目标文件列表
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))

# 可执行文件
TARGET = make_learn

# 默认目标
all: $(TARGET)

# 生成目标文件
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
	$(CC) $(CFLAGS) -I$(INC_DIR) -c $^ -o $@

# 生成可执行文件
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

# 清理生成的文件
clean:
	rm -f $(OBJS) $(TARGET)
 

作成して実行します。
ここに画像の説明を挿入

説明: $(CC) $(CFLAGS) -o $@ $^

$(CC) $(CFLAGS) -o $@ $^gccは、コンパイラの動作を指示するリンカ オプションです。

  • $(CC): 使用したコンパイラを示す変数です。Makefile では、CC変数を定義することで、使用するコンパイラを指定できます。デフォルトでは、 ですgcc
  • $(CFLAGS): これはコンパイラのオプションとフラグを表す変数でもあります。Makefile では、CFLAGS変数を定義することでコンパイラに渡すオプションを指定できます。通常、CFLAGS変数は、警告フラグ、最適化レベルなどのコンパイル オプションを設定するために使用されます。
  • -o: このオプションは、出力ファイルの名前を指定するために使用されます。
  • $@: これは、ルール内のターゲット ファイルを表す自動変数です。このコンテキストでは、生成される実行可能ファイルの名前を表します。
  • $^: これは、ルール内のすべての依存ファイルのリストをスペースで区切って表す自動変数でもあります。この文脈では、リンクされるすべてのオブジェクト ファイルを意味します。

したがって、$(CC) $(CFLAGS) -o $@ $^の意味は、すべてのオブジェクト ファイルを実行可能ファイルにリンクし、その実行可能ファイルにルールで指定されている名前を付けることです。

たとえば、次のルールでは次のようになります。

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

オブジェクト ファイルをリンクしobj/foo.o実行可能ファイルを生成するとします。リンク コマンドは次のようになりますobj/main.oobj/bar.oproject

gcc -Wall -Wextra -o project obj/foo.o obj/main.o obj/bar.o

このコマンドは、オブジェクト ファイルをprojectという名前の実行可能ファイルにリンクします。

$@と は$^Makefile 内の自動変数であり、ルールが実行されるたびに特定のターゲットと依存関係に従って置き換えられることに注意してください。これにより、現在のコンテキストに基づいてリンカー コマンドを自動的に生成できます。


4、一般的に使用される make コマンド

  1. make: ビルドのデフォルトのターゲット (通常は最初のターゲット) を実行します。
  2. make target: 指定したターゲットを実行してビルドします。
  3. make -f filename: デフォルトの Makefile の代わりに、指定された Makefile を使用してビルドします。
  4. make -nまたはmake --just-print、実際にコマンドを実行せずに、実行されたコマンドを表示するだけです (ダミー実行)。
  5. make -C dir: 指定したディレクトリでビルドを実行します。
  6. make -j n: 複数のスレッドを使用して並列ビルドします。ここで、n は並列タスクの数です。
  7. make clean: プロジェクトを再構築できるように、生成されたオブジェクト ファイルと中間ファイルをクリーンアップします。
  8. make install: ビルドされたターゲット ファイルをシステムにインストールします。
  9. make uninstall: インストールされているファイルをシステムからアンインストールします。
  10. make help: Makefile で定義されたヘルプ情報を表示します。
  11. make -v:バージョン情報を表示します。

5. その他のビルドツール

一般的なビルド ツール:

  1. Make : Make は、コードのコンパイル、ビルド、デプロイのプロセスを自動化する一般的なビルド ツールです。Make は Makefile に基づいて構成され、Makefile 内のルールと依存関係を通じて、ソース コード ファイル、ヘッダー ファイル、実行可能ファイルなどのさまざまな部分間の関係を定義できます。
  2. CMake : CMake は、Makefile を自動的に生成してビルド プロセスを簡素化できるクロスプラットフォーム ビルド ツールです。CMake は複数のプラットフォームとコンパイラをサポートしており、単純なコマンド ライン パラメータと構成ファイルを通じて構成できます。
  3. Autotools : Autotools は、Autoconf、Automake、Libtool などのコンポーネントを含む、一般的な自動ビルド ツールのセットです。
  4. SCons : SCons は、コードのコンパイル、ビルド、デプロイのプロセスを自動化する Python で書かれたビルド ツールです。
  5. Ninja : Ninja は、コードのコンパイル、ビルド、デプロイメントの速度を向上させる効率的なビルド グラフを生成する高速ビルド ツールです。

私が使用したものをお話しましょうNinja:

OpenHarmonyNinja+GNを使用します。

Ninja と gn は、プロジェクトの構築と管理のための 2 つの別個のツールであり、一緒に使用されることがよくありますが、直接の関連性はありません。

  1. Ninja: Ninja は、ソフトウェア プロジェクトを迅速に構築するように設計された効率的なビルド システムです。簡潔なビルド ルールと速いビルド速度で知られています。Ninja は、ビルド ルール、ターゲット、依存関係などの情報を含むファイルベースのビルド記述ファイル (通常は.ninja拡張子で終わるファイル) を使用します。Ninja の設計目標は、ビルド システム自体のオーバーヘッドを最小限に抑え、不必要なビルドの繰り返しを最小限に抑えることです。

  2. gn: gn は、ビルド ファイル (Ninja ビルド ファイルなど) を生成するために Google によって開発されたメタビルド システムです。GN は、プロジェクトのビルド構成、ターゲット、依存関係などを記述するための宣言型言語と構成ファイル形式を提供します。gn は高度にカスタマイズ可能なビルド プロセスをサポートしており、開発者はプロジェクトのニーズに応じてビルド ルールとオプションを構成できます。gn の主な目標は、複数のビルド システム (Ninja など) および複数のプラットフォームで使用できる統一されたビルド構成言語を提供することです。

Ninja と gn は単独で使用することもできますが、一緒に使用されることもよくあります。この場合、gn はプロジェクトのビルド構成とルールを記述する Ninja ビルド ファイルの生成に使用され、Ninja は実際のビルド プロセスの実行に使用されます。gn と Ninja を組み合わせることで、効率的でカスタマイズ可能なプロジェクトのビルド プロセスを実現できます。

ここに画像の説明を挿入



最後永遠に愛してと書いてください

おすすめ

転載: blog.csdn.net/weixin_43764974/article/details/131466035