私は常にミニマリズムに惹かれてきました。 1つのことで1つの機能を果たすべきであると同時に、可能な限り最高の機能を果たすという考えから、UNIXが誕生しました。 そして、UNIXはもはや単純なシステムと呼ぶことはできず、ミニマリズムはそれほど簡単には見えませんが、多くの単純で理解可能なものを非常に複雑で透明ではないものに量-質変換する良い例と考えることができます。 その開発では、makeはほぼ同じ方法で行われました。スケールの成長に伴う単純さと明快さは、恐ろしいモンスターに変わりました(メイクファイルを最初に開いたときの気持ちを思い出してください)。
私が長い間makeをしつこく無視していたのは、使用されているIDEの利便性と、この「過去の遺物」(本質的に怠iness)を理解したがらないためです。 ただし、これらの迷惑なボタン、メニューなどはすべて さまざまなスタジオの属性により、私はこれまで練習してきた仕事の方法の代替手段を探しました。 いいえ、私はメイクの達人になりませんでしたが、得た知識は私の小さなプロジェクトにとっては十分です。 この記事は、最近の私のように、居心地の良い窓の奴隷制から禁欲的な、しかし自由なシェルの世界に侵入したい人を対象としています。
Make-基本情報
makeは、ある形式から別の形式へのファイルの変換を自動化するように設計されたユーティリティです。 変換規則は、Makefileと呼ばれるスクリプトで設定されます。これは、プロジェクトの作業ディレクトリのルートに配置する必要があります。 スクリプト自体は一連のルールで構成されており、これらのルールについて順に説明します。
1)目標(このルールの機能);
2)必要条件(ルールを満たし目標を達成するために必要なもの);
3)チーム(コンバージョンデータの実行)。
一般に、メイクファイルの構文は次のように表すことができます。
つまり、makeルールは3つの質問に対する答えです。
{ ? ()} ---> [ ? ()] ---> { ? ()}
翻訳とコンパイルのプロセスがこのスキームに非常にうまく適合していることは簡単にわかります。
{ } ---> [] ---> { }
{ } ---> [] ---> { }
最も単純なメイクファイル
1つのファイルのみで構成されるプログラムがあるとします。
#include <stdio.h> int main() { printf("Hello World!\n"); return 0; }
コンパイルするには、非常に単純なmakefileで十分です。
hello: main.c gcc -o hello main.c
このMakefileは1つのルールで構成され、ルールはターゲット-「hello」、小道具-「main.c」、およびコマンド-「gcc -o hello main.c」で構成されます。 さて、コンパイルには作業ディレクトリでmakeコマンドを与えるだけで十分です。 デフォルトでは、実行ターゲットが呼び出しで明示的に指定されていない場合、makeは最初のルールを実行します。
$ make <>
複数のソースからのコンパイル
2つのファイルで構成されるプログラムがあるとします。
main.c
int main() { hello(); return 0; }
およびhello.c
#include <stdio.h> void hello() { printf("Hello World!\n"); }
このプログラムをコンパイルするメイクファイルは次のようになります。
hello: main.c hello.c gcc -o hello main.c hello.c
これは完全に機能しますが、重大な欠点が1つあります。それはどちらかです-さらにオープンします。
インクリメンタルコンパイル
私たちのプログラムが12個または2個のソースファイルで構成されていると想像してください。 そのうちの1つに変更を加えており、それを再構築したいと考えています。 前の例で説明したアプローチを使用すると、例外なくすべてのソースファイルが再度コンパイルされ、再コンパイルの時間に悪影響を与えるという事実につながります。 解決策は、コンパイルを2つの段階(翻訳段階とリンク段階)に分割することです。
これで、ソースファイルの1つを変更した後、それを翻訳してすべてのオブジェクトファイルをリンクするだけで十分です。 同時に、変更の影響を受けない詳細の翻訳段階をスキップし、全体としてコンパイル時間を短縮します。 このアプローチは、インクリメンタルコンパイルと呼ばれます。 それをサポートするために、makeは目標とその詳細を変更する時間を比較します(ファイルシステムのデータを使用)
main.o: main.c gcc -c -o main.o main.c hello.o: hello.c gcc -c -o hello.o hello.c hello: main.o hello.o gcc -o hello main.o hello.o
このプロジェクトをビルドしてみてください。 ビルドするには、ターゲットを明示的に指定する必要があります。 コマンドにmake helloを指定します。
ソースファイルのいずれかを変更し、再度コンパイルします。 2回目のコンパイルでは、変更されたファイルのみがブロードキャストされることに注意してください。
makeを開始すると、すぐにhelloターゲットを取得しようとしますが、作成するには、まだ利用できないmain.oおよびhello.oファイルが必要です。 したがって、ルールの実行は遅延し、makeは欠落している詳細の受信を記述するルールを探します。 すべての詳細が受信されると、makeは遅延ゴールの実行に戻ります。 makeは規則を再帰的に実行します。
架空の目標
実際、実際のファイルだけがmakeターゲットとして機能できるわけではありません。 ソースコードからプログラムをコンパイルしなければならなかった人は誰でも、UNIXの世界の2つの標準コマンドに精通している必要があります。
$ make $ make install
makeコマンドはプログラムをコンパイルし、make installコマンドがインストールされます。 このアプローチは非常に便利です。ターゲットシステムでアプリケーションをビルドおよびデプロイするために必要なものはすべて1つのファイルに含まれているからです(しばらくの間、configureスクリプトは忘れてください)。 最初の場合は目的を指定せず、2番目の目的はインストールファイルを作成することではなく、システムにアプリケーションをインストールするプロセスです。 このようなトリックにより、いわゆる偽の目標を達成することができます。 標準的な目標の短いリストは次のとおりです。
- all-これはデフォルトのデフォルトターゲットです。 makeが呼び出されると、明示的に省略できます。
- clean-コンパイルの結果として取得されたすべてのファイルからディレクトリを削除します。
- インストール-インストール
- アンインストール-それに応じてアンインストールします。
makeがそのような名前のファイルを検索しないようにするには、.PHONYディレクティブを使用してMakefileで定義する必要があります。 以下は、all、clean、install、およびuninstallを目的としたMakefileの例です。
.PHONY: all clean install uninstall all: hello clean: rm -rf hello *.o main.o: main.c gcc -c -o main.o main.c hello.o: hello.c gcc -c -o hello.o hello.c hello: main.o hello.o gcc -o hello main.o hello.o install: install ./hello /usr/local/bin uninstall: rm -rf /usr/local/bin/hello
これで、プログラムをビルドし、インストール/アンインストールし、標準のmake目標を使用して作業ディレクトリをクリアできます。
すべてのターゲットに目標が指定されていないことに注意してください。 彼女が必要なのはこんにちは小道具を取得することです。 makeの再帰的な性質を考えると、このスクリプトがどのように機能するかは簡単に推測できます。 また、helloファイルが既に存在し(以前のコンパイル後に残っている)、その詳細が変更されていない場合、makeコマンド
は何も再コンパイルしないという事実にも特別な注意を払う必要があります。 これは古典的なmakeレーキです。 たとえば、誤って詳細リストに含まれていないヘッダーファイルを変更すると、長時間の頭痛の種になります。 したがって、プロジェクトの完全な再構築を保証するには、最初に作業ディレクトリをクリアする必要があります。
$ make clean $ make
インストール/アンインストールの目標を達成するには、sudoを使用する必要があります。
変数
DRY(自分自身を繰り返さないでください)ルールに精通しているすべての人は、おそらく何かが間違っていることに気づいているでしょう。 命令型言語では、これらの目的のために、変数と定数があります。 makeにも同様のツールがあります。 makeの変数は名前付き文字列であり、非常に簡単に定義されています。
<VAR_NAME> = <value string>
大文字の変数にどの名前を付けるべきかによって、書かれていない規則があります。例えば:
SRC = main.c hello.c
そこで、ソースファイルのリストを定義しました。 変数の値を使用するには、$(<VAR_NAME>)構造を使用して名前を変更する必要があります。 たとえば次のように:
gcc -o hello $(SRC)
以下は、2つの変数を使用するメイクファイルです。TARGET-ターゲットプログラムの名前を決定し、PREFIX-プログラムをシステムにインストールするパスを決定します。
TARGET = hello PREFIX = /usr/local/bin .PHONY: all clean install uninstall all: $(TARGET) clean: rm -rf $(TARGET) *.o main.o: main.c gcc -c -o main.o main.c hello.o: hello.c gcc -c -o hello.o hello.c $(TARGET): main.o hello.o gcc -o $(TARGET) main.o hello.o install: install $(TARGET) $(PREFIX) uninstall: rm -rf $(PREFIX)/$(TARGET)
これはすでにきれいです。 上記の例では、特別なコメントは不要だと思います。
自動変数
自動変数はメイクファイルを単純化するように設計されていますが、私の意見では、それらは可読性に悪影響を及ぼします。 それはそうかもしれませんが、ここでは最も頻繁に使用される変数のいくつかを紹介します。あなたがすべきこと(そしてやるべきかどうか)はあなた次第です:
- $ @処理中のルールのターゲット名
- $ <処理中のルールの最初の依存関係の名前
- $ ^処理中のルールのすべての依存関係のリスト
スクリプトを完全に難読化する場合は、ここからインスピレーションを得てください。
自動変数おわりに
この記事では、メイクファイルの作成と操作の基本について詳しく説明しようとしました。 makeの本質を理解し、この実績のあるツールをすぐにマスターできることを願っています。
githubのすべての例味わった人のために:
OpenNET Makefile mini HOWTOGNU Makeリチャード・M・ストールマンとローランド・マクグラス、翻訳©Vladimir Ignatov、2000GNU Makeの効果的な使用