Linux の Makefile とは何ですか? どのように機能しますか?

Linux の Makefile とは何ですか? どのように機能しますか?

この便利なツールでプログラムをより効率的に実行およびコンパイルします
Makefile は自動コンパイルとリンクに使用されます。プロジェクトは多数のファイルで構成されます。ファイルに変更を加えると、プロジェクトが再リンクされます。ただし、すべてのファイルを再コンパイルする必要はありません。Makefile はファイル情報を記録し、リンク時に再コンパイルする必要があるファイルを決定できます。

make ツールは通常、一部のソース ファイルが変更された後にタスクを実行または更新する必要がある場合に使用されます。 make ツールは、実行される一連のタスクを定義する Makefile (または makefile) ファイルを読み取る必要があります。 make を使用してソース コードを実行可能プログラムにコンパイルできます。ほとんどのオープンソース プロジェクトでは、make を使用して最終的なバイナリ ファイルをコンパイルし、make install コマンドを使用してインストールを実行します。
この記事では、いくつかの基本的な例と高度な例を通して、make と Makefile の使用方法を説明します。始める前に、システムに make がインストールされていることを確認してください。

基本的な例は、依然として「Hello World」を印刷することから始まります。まず、myproject という名前のディレクトリを作成し、そのディレクトリ内に次の内容の Makefile ファイルを作成します。

こんにちは:
「こんにちは世界」をエコーし​​ます

myproject ディレクトリで make を実行すると、次の出力が表示されます。

$ 作る
「こんにちは世界」をエコーし​​ます
こんにちは世界

上記の例では、「say_hello」は他のプログラミング言語の関数名に似ています。これをターゲットといいます。ターゲットの後に、前提条件または依存関係が続きます。簡潔にするために、この例では前提条件を定義しませんでした。 echo 'Hello World' コマンドはレシピと呼ばれます。これらのステップは、目標を達成するための前提条件に基づいています。目標、前提条件、および手順が組み合わさってルールが構成されます。

要約すると、典型的なルールの構文は次のようになります。

目的: 前提条件
<TAB> 手順

たとえば、ターゲットは、前提条件 (ソース コード) に基づいたバイナリ ファイルになる場合があります。一方、前提条件は、他の前提条件に依存するターゲットになることもできます。

final_target: サブターゲット final_target.c
最終ターゲットを作成するためのレシピ
サブターゲット: sub_target.c
サブターゲットを作成するレシピ

ターゲットはファイルである必要はなく、この例のようにステップの名前だけにすることもできます。これを「疑似ターゲット」と呼びます

上記の例に戻ると、make が実行されると、コマンド全体 echo "Hello World" が表示され、その後に実際の実行結果が表示されます。コマンド自体を印刷したくない場合は、echo の前に @ を追加する必要があります。

こんにちは:
@echo "こんにちは世界"

make を再度実行すると、次の出力が表示されます。

$ 作る
こんにちは世界

次に、Makefile に次の疑似ターゲットを追加します: generate および clean:

こんにちは:
@echo "こんにちは世界"
生成する:
@echo "空のテキスト ファイルを作成しています..."
タッチファイル-{1..10}.txt
クリーン:
@echo "クリーンアップ中..."
rm *.txt

後で make を実行すると、 say_hello ターゲットのみが実行されます。これは、Makefile の最初のターゲットがデフォルトのターゲットであるためです。通常、デフォルトのターゲットが呼び出されます。これは、ほとんどのプロジェクトの最初のターゲットとして表示されるものです: all。 all はターゲットを呼び出す責任があります。特別な疑似目標.DEFAULT_GOAL を使用して、デフォルトの動作をオーバーライドできます。

Makefile の先頭に .DEFAULT_GOAL を追加します。

.DEFAULT_GOAL := generate

Make は、generate をデフォルトのターゲットとして使用します。

$ 作る
空のテキスト ファイルを作成しています...
タッチファイル-{1..10}.txt

名前が示すように、.DEFAULT_GOAL 疑似ターゲットは 1 つの目標のみを定義できます。このため、多くの Makefile には、複数のターゲットを呼び出すことができるターゲット all が含まれています。
次に、.DEFAULT_GOAL を削除し、all 目標を追加します。

すべて: say_hello 生成
こんにちは:
@echo "こんにちは世界"
生成する:
@echo "空のテキスト ファイルを作成しています..."
タッチファイル-{1..10}.txt
クリーン:
@echo "クリーンアップ中..."
rm *.txt

実行する前に、いくつかの特別な疑似ターゲットを追加しましょう。 .PHONY は、ファイルではないターゲットを定義するために使用されます。デフォルトでは、make はファイル名や最終変更日の存在を確認せずに、これらの疑似ターゲットの下のステップを呼び出します。完全な Makefile は次のとおりです。

.PHONY: すべて say_hello 生成クリーン
すべて: say_hello 生成
こんにちは:
@echo "こんにちは世界"
生成する:
@echo "空のテキスト ファイルを作成しています..."
タッチファイル-{1..10}.txt
クリーン:
@echo "クリーンアップ中..."
rm *.txt

make コマンドは say_hello を呼び出して次を生成します。

$ 作る
こんにちは世界
空のテキスト ファイルを作成しています...
タッチファイル-{1..10}.txt

clean は all や first ターゲットには入れないでください。 cleanは、クリーニングが必要な場合に手動で呼び出す必要があります。呼び出しメソッドはmake cleanです。

$ クリーンにする
掃除中…
rm *.txt

Makefile の基本を理解できたので、次は高度な例を見てみましょう。

高度な例 変数 前の例では、ターゲットと前提条件のほとんどは固定されていましたが、実際のプロジェクトでは通常、それらは変数とパターンに置き換えられます。

変数を定義する最も簡単な方法は、= 演算子を使用することです。たとえば、コマンド gcc を変数 CC に割り当てるには、次のようにします。

CC = gcc

これは再帰変数拡張と呼ばれ、次のようなルールで使用されます。

こんにちは: hello.c
${CC} hello.c -o hello

ご想像のとおり、これらの手順は次のように拡張されます。

gcc hello.c -o hello

${CC} と $(CC) はどちらも gcc を参照できます。しかし、変数が自分自身に代入しようとすると、無限ループが発生します。これを検証してみましょう:

CC = gcc
CC = ${CC}
全て:
@echo ${CC}

この時点で make を実行すると、次の結果になります。

$ 作る
Makefile:8: *** 再帰変数 'CC' は (最終的に) 自分自身を参照します。停止します。

これを回避するには、:= 演算子を使用します (これは単純な変数展開と呼ばれます)。次のコードでは上記の問題は発生しません。

CC := gcc
CC := ${CC}
全て:
@echo ${CC}

モードと関数 次の Makefile は、変数、モード、および関数を使用してすべての C コードをコンパイルします。行ごとに分析してみましょう。

# 使用法:
# make # すべてのバイナリをコンパイル
# make clean # すべてのバイナリとオブジェクトを削除
.PHONY = すべてクリーン
CC = gcc # 使用するコンパイラ
リンカーフラグ = -lm
SRCS := $(ワイルドカード *.c)
ビン:= $(SRCS:%.c=%)
すべて: ${BINS}
%: %.o
@echo "確認中..."
${CC} ${LINKERFLAG} $< -o $@
%.o: %.c
@echo "オブジェクトを作成しています。"
${CC} -c $<
クリーン:
@echo "クリーンアップ中..."
rm -rvf *.o ${BINS}

#で始まる行はコメントです
.PHONY = all clean という行は、 all と clean という 2 つの疑似ターゲットを定義します。
変数 LINKERFLAG は、gcc コマンドがステップで使用する必要があるパラメータを定義します。
SRCS := $(ワイルドカード *.c): $(ワイルドカード パターン) はファイル名に関連する関数です。この例では、「.c」サフィックスを持つすべてのファイルが SRCS 変数に保存されます。
BINS := $(SRCS:%.c=%): これは置換参照と呼ばれます。この例では、SRCS の値が「foo.c bar.c」の場合、BINS の値は「foo bar」になります。
all: ${BINS}: という行は、疑似ターゲット all は ${BINS} 変数内のすべての値をサブターゲットとして呼び出します。
ルール:

%: %.o
@echo "確認中..."
${CC} ${LINKERFLAG} $< -o $@

このルールを理解するために例を見てみましょう。 foo が変数 ${BINS} の値であると仮定します。 % は foo に一致します (% は任意のターゲットに一致します)。拡張後のルールは次のようになります。

foo: foo.o
@echo "確認中..."
gcc -lm foo.o -o foo

上記のように、% は foo に置き換えられます。 $< は foo.o に置き換えられます。 $< は事前設定された条件に一致させるために使用され、 $@ はターゲットに一致します。このルールは、${BINS} 内の各値に対して 1 回呼び出されます。
ルール:

%.o: %.c
@echo "オブジェクトを作成しています。"
${CC} -c $<

前のルールの各前提条件は、このルールではターゲットとして扱われます。展開後の外観は次のようになります。

foo.o: foo.c
@echo "オブジェクトを作成しています。"
gcc -c foo.c

最後に、 clean ターゲットでは、すべてのバイナリとコンパイル済みファイルが削除されます。
以下は書き直された Makefile です。これは foo.c ファイルと同じディレクトリに配置する必要があります。

# 使用法:
# make # すべてのバイナリをコンパイル
# make clean # すべてのバイナリとオブジェクトを削除
.PHONY = すべてクリーン
CC = gcc # 使用するコンパイラ
リンカーフラグ = -lm
SRCS := foo.c
ビン:= foo
全員: foo
foo: foo.o
@echo "確認中..."
gcc -lm foo.o -o foo
foo.o: foo.c
@echo "オブジェクトを作成しています。"
gcc -c foo.c
クリーン:
@echo "クリーンアップ中..."
rm -rvf foo.o foo

これらが一緒になって makefile を形成します。もちろん、これらの関数は少なすぎるため、他の多くのプロジェクトを追加することもできます。しかし、目的は、ファイルをコンパイルするためにどの他のファイルに依存する必要があるかをコンパイラに知らせることです。これらの依存ファイルが変更されると、コンパイラは最終的に生成されたファイルが古くなっていることを自動的に検出し、対応するモジュールを再コンパイルします。

要約する

以上がこの記事の全内容です。この記事の内容が皆様の勉強や仕事に何らかの参考学習価値をもたらすことを願います。123WORDPRESS.COM をご愛顧いただき、誠にありがとうございます。これについてもっと知りたい場合は、次のリンクをご覧ください。

以下もご興味があるかもしれません:
  • Linux での makefile コマンド パッケージの定義と使用
  • Linux での makefile の理解
  • Linux での Makefile の書き方と使い方の詳細な説明

<<:  Linux での MySQL 5.7.17 の最新安定バージョンのインストール チュートリアル

>>:  Vue が Ref を使用してレベル間でコンポーネントを取得する手順

推薦する

Docker で Nginx イメージ サーバーを構築する方法

序文一般的な開発では、画像をディレクトリにアップロードし、ディレクトリとファイル名を連結してデータベ...

HTML でスクロールバーを非表示にしたり削除したりする方法

1. 属性付きHTMLタグXML/HTML コードコンテンツをクリップボードにコピー< htm...

Vue+SpringBoot+Shiroのクロスドメイン問題を解決する

目次1. Vueフロントエンドを構成する1. クロスドメイン構成を開発する2. 本番環境のクロスドメ...

Vue でルートをジャンプする方法をご存知ですか?

目次最初の方法: router-link (宣言型ルーティング) 2番目の方法: router.pu...

MySQLのストレージエンジンについてお話しましょう

基礎リレーショナル データベースでは、各データ テーブルはファイルに相当し、異なるストレージ エンジ...

MySQLデータベースのストアドプロシージャとトランザクションの違い

トランザクションは、複数の SQL ステートメントの原子性、つまり、それらが一緒に完了するか、一緒に...

単一選択折りたたみメニュー機能を実現するCSS

前回の「最もシンプルなスイッチを実現するCSS」のように、HTML5とCSS3でほとんどの機能をすで...

ボタンの 4 つのクリック応答方法の概要

ボタンは頻繁に使用されます。ここでは、イベント処理メソッドを整理し、実装方法が多数あることを発見しま...

テーブルパーティションとパーティション分割とは何ですか?MySqlデータベースパーティションとテーブルパーティション分割方法

1. テーブルとパーティションを分割する必要があるのはなぜですか?日常の開発では、大きなテーブルに遭...

Nginx で IP と IP 範囲をブロックする方法

前面に書かれたNginx は単なるリバース プロキシおよび負荷分散サーバーではなく、電流制限、キャッ...

Vue3の一般的なAPIの使用方法の紹介

目次ライフサイクルの変化反応的な参照vue2.x では ref を使用して要素タグを取得します。vu...

Centos7 に DAMO データベースをインストールするチュートリアル

1. 準備Linux オペレーティング システムをインストールした後、ここで Linux 7 を選択...

MySQLデータベースインデックスの欠点と適切な使用

目次インデックスの適切な使用1. 通常のインデックスのデメリット2. 主キーインデックスの落とし穴3...

Centos7 で MySQL マスター スレーブ サーバーを構築する方法 (グラフィック チュートリアル)

この記事では主に CentOS 上で MySQL マスタースレーブサーバーを構築する方法を紹介します...

すべてのウェブ開発者が知っておくべき61のこと

通常、全員のスピーチを最初から最後まで読む必要があります。ただし、Stack Overflow には...