ソフトウェア開発において、Makeはコマンドラインインターフェースを備えたソフトウェアツールであり、 Makefileと呼ばれる設定ファイルで定義された依存関係に基づいて、設定された順序に従ってアクションを実行します。一般的には、ソースコードから実行可能コード(プログラムやライブラリなど)をビルドするためのビルド自動化に使用されます。Makeはビルドだけでなく、オペレーティングシステムのシェルで利用可能なあらゆる操作を実行できます。
Makeは、特にUnixやUnixライクなオペレーティングシステムで広く使用されていますが、依存関係に基づいてアクションを実行する類似のツール、一部のコンパイラ、統合開発環境を介した対話型ツールなど、多くの競合する技術やツールが利用可能です。
Makeは、オリジナルのUnixツールを指すだけでなく、複数のツールがほぼ同じ機能(類似したmakefile構文や意味論を含む)で実装されているため、技術としても捉えることができます。
スチュアート・フェルドマンはベル研究所に在籍中にMakeを作成した。初期バージョンは1976年4月に完成した。[ 1 ] [ 2 ] [ 3 ]フェルドマンはMakeの作者として2003年のACMソフトウェアシステム賞を受賞した。[ 4 ]
フェルドマンは、Makeを執筆するきっかけとなったのは、当時の利用可能なツールに対する同僚の不満だったと述べている。
Make の始まりは、 Steve Johnson氏 (yacc などの作者) が私のオフィスに飛び込んできて、正しいプログラムのデバッグに午前中を無駄にした運命を呪ったことでした (バグは修正済みで、ファイルはコンパイルされていなかった
cc *.oため、影響を受けていませんでした)。私も前日の夜、自分が取り組んでいたプロジェクトで同じような問題に対処していたため、それを解決するためのツールのアイデアが浮かびました。最初は複雑な依存関係アナライザーのアイデアでしたが、よりシンプルなものに絞り込まれ、その週末に Make が完成しました。まだ開発途中のツールを使うことは、当時の文化の一部でした。Makefile は、魔法のようにエンコードされたバイナリではなく、テキストファイルでした。なぜなら、印刷可能で、デバッグ可能で、理解しやすいもの、それがUnix の精神だったからです。
—スチュアート・フェルドマン著『Unixプログラミングの芸術』、エリック・S・レイモンド社、 2003年
Makeが登場する以前は、Unix上でのビルドは主に各プログラムのコードベースごとに書かれたシェルスクリプトで構成されていました。Makeの依存関係の順序付けと最新状態のチェック機能により、ビルドプロセスはより堅牢で効率的になりました。Makefileのおかげで、ビルドロジックをより適切に整理でき、ビルドファイルの数も少なくて済むようになりました。
Makeは、さまざまなソフトウェア開発ツールを備えたPWB/UNIX 1.0から始まるUnixに早期に組み込まれたこともあり、広く使用されています。 [ 3 ]
Makeはこれまで何度も実装されており、一般的には同じmakefile形式を使用し、同じ機能を提供していますが、中にはオリジナル版から機能強化されているものもあります。例:
POSIX には Make ユーティリティの基本機能と動作の標準化が含まれており、Unix ベースの Make バージョンとの互換性は様々です。一般的に、シンプルな makefile はさまざまなバージョンの Make 間で問題なく使用できます。GNU Make、Makepp、および一部の BSD Make バージョンは、デフォルトでそれぞれ "GNUmakefile" [ 35 ] "Makeppfile" [ 36 ]、および "BSDmakefile" [ 37 ]という名前のファイルを最初に検索します。これにより、実装定義の動作を使用する makefile を別の場所に配置することができます。
一般的に、Make は makefile に基づいて、ソースファイルのタイムスタンプがターゲットファイルよりも新しい場合、またはターゲットファイルが存在しない場合、ソースファイルからターゲットファイルを更新します。たとえば、これにはCファイル ( ) をオブジェクトファイル*.cにコンパイルし、そのオブジェクトファイルを実行可能プログラムにリンクすることが含まれます。また、TypeScriptファイル ( ) をJavaScriptにコンパイルしてブラウザで使用することも含まれます。その他の例としては、ソース画像ファイルを別の形式に変換したり、ファイルをコンテンツ管理システムにコピーしたり、ビルドステータスに関するメールを送信したりすることが挙げられます。*.ts
メイクファイルはターゲットを定義します。各ターゲットは、生成するファイルであるか、または擬似ターゲットと呼ばれるユーザー定義の概念のいずれかです。
引数として渡されたターゲットを更新します。
make [ -f makefile ] [オプション] [ターゲット]ターゲットが指定されていない場合、Make は makefile の最初のターゲットを更新します。これは多くの場合、最もよく使用されるアクションを実行するためのダミーターゲットです。
Make は、ターゲット ファイルのタイムスタンプがソース ファイルのタイムスタンプより後の場合、ビルド アクションをスキップします。[ 38 ]これにより、ターゲット ファイルが最新の場合にアクションをスキップすることでビルド プロセスが最適化されますが、ソース ファイルの古いバージョンを復元する場合や、ネットワーク ファイルシステムがファイルのソースであり、そのクロックまたはタイム ゾーンが Make を実行しているマシンと同期していない場合など、ファイルのタイムスタンプの問題により、更新が誤ってスキップされることがあります。また、ソース ファイルのタイムスタンプが未来の場合、make は不要なアクションを繰り返しトリガーし、ビルド 時間が長くなります。
Make が起動すると、コマンドラインで指定された makefile が使用されます。指定されていない場合は、特定の検索ルールによって見つかった makefile が使用されます。通常、Make はデフォルトで作業ディレクトリにあるMakefileという名前のファイルを使用します。GNU Make は、 GNUmakefile、makefile、またはMakefileに一致する最初のファイルを検索します。
Makeは、読み込まれたMakefileに基づいてコマンドラインのオプションを処理します。
Makefile言語は部分的に宣言型プログラミングであり、終了条件は記述されるが、アクションを実行する順序は記述されない。[ 40 ] [ 41 ] [ 42 ] [ 43 ]
Makefileには以下の構成要素を含めることができます。[ 44 ]
#各ルールは、ルールのターゲット名の後にコロン(:)が続く依存関係行で始まり、オプションでルールのターゲットが依存するターゲット(前提条件とも呼ばれる)のリストが含まれます。 [ 45 ]
ターゲット [ターゲット ...]: [コンポーネント ...] Tab ↹[コマンド 1] 。 。 。 Tab ↹[コマンド n]
通常、ルールは複数の対象ではなく、単一の対象を持つ。
依存関係行の後には、レシピが続く場合があります。レシピとは、タブでインデントされた一連のコマンドラインで、コンポーネント(ソースファイル)からターゲットを生成する方法を定義します。前提条件のいずれかのタイムスタンプがターゲットファイルよりも新しい場合、またはターゲットがファイルとして存在しない場合は、レシピが実行されます。
最初のコマンドは、前提条件の後にセミコロンで区切って同じ行に記述できます。
対象:前提条件;コマンド例えば、
こんにちは: ; @ echo "こんにちは" 各コマンドラインはタブ文字で始めなければなりません。スペースも空白文字ですが、Make ではタブが必要です。これはしばしば混乱や間違いにつながるため、makefile 構文のこの側面は批判の対象となっています。Eric S. Raymond はこれを「Unix の歴史上最悪の設計ミスの 1 つ」[ 46 ]と評し、 The Unix-Haters Handbookでは「構文の一部としてタブを使用することは、グリーンベレーのパンジー [sic] スティックトラップのようなものだ」と述べています。Feldman は、この選択は初期実装の困難に対する回避策であり、最初のユーザーとの後方互換性を維持したいという要望によって維持されていると説明しています。
なぜ1列目にタブを入れたのか?Yaccは新しく、Lexは全く新しいツールだった。どちらも使ったことがなかったので、これを機に学ぼうと思ったのだ。Lexを初めて使ってみて苦戦した後、改行とタブを組み合わせたシンプルなパターンを使ってみた。うまくいったので、そのまま使い続けた。それから数週間後、ユーザー数は10人ほどになり、そのほとんどが友人だった。せっかく築き上げた基盤を台無しにしたくなかった。あとは残念ながら、歴史が物語る通りだ。
—スチュアート・フェルドマン[ 46 ]
GNU Make バージョン 3.82 以降では、特殊変数 .RECIPEPREFIX を使用して、レシピの接頭辞として任意の記号 (1 文字) を選択できます。
.RECIPEPREFIX := : all : :@echo "レシピ接頭辞記号は '$(.RECIPEPREFIX)' に設定されています"各コマンドは個別のシェルで実行されます。オペレーティングシステムによってシェルが異なるため、移植性の低いメイクファイルが発生する可能性があります。たとえば、GNU Make(すべてのPOSIX Make)はデフォルトで/bin/shを使用してコマンドを実行しますが、このシェルでは通常cpなどのUnixコマンドが使用されます。一方、Microsoftのnmakeはcmd.exeを使用してコマンドを実行しますが、このシェルではcopyなどのバッチコマンドは使用できますが、必ずしもcpコマンドが使用できるとは限りません。
レシピはオプションであるため、依存関係の行は、他のターゲットを参照するコンポーネントのみで構成できます。
realclean :クリーンdistclean以下のルール例は、Make がターゲット file.txt を更新する際に評価されますmake file.txt。file.html が file.txt より新しい場合、または file.txt が存在しない場合は、コマンドを実行して file.html から file.txt を生成します。
file.txt : file.html lynx -dump file.html > file.txt コマンドには、タブの後に以下の接頭辞を1つ以上付けることができます。
エラーを無視し、エコーを抑制することは、特別なターゲット.IGNOREとを使用して代替的に実現できます.SILENT。[ 47 ]
Microsoft の NMAKE には、これらの makefile から省略できる事前定義されたルールがあります。c.obj$(CC)$(CFLAGS)
Makefileではマクロを定義して使用できます。マクロは、例えば のような単純な文字列定義を保持する場合、通常は変数と呼ばれます。Makefile内のマクロは、Makeユーティリティに渡されるコマンドライン引数で上書きできます。環境変数もマクロとして使用できます。CC=clang
例えば、このマクロはメイクファイル内でCCCコンパイラの場所を指定するためによく使用されます。メイクファイル全体で一貫して使用すれば、コンパイラを呼び出す各ルールコマンドを変更するのではなく、マクロの値を変更するだけでコンパイラを変更できます。
マクロの名前は通常すべて大文字で表記されます。
マクロ=定義 マクロ値は、他のマクロ値で構成できます。マクロの値は、使用されるたびに遅延的に展開されます。
マクロは、$ NAMEまたは $( NAME ) のいずれかを使用して展開することで使用します。括弧を省略すると Make が の次の文字を$変数名全体として解釈してしまうため、後者の方が安全です。同等の形式では、括弧の代わりに波括弧を使用します。つまり、 BSD${}で使用されるスタイルです。
NEW_MACRO = $( MACRO ) - $( MACRO2 )マクロは、コマンド置換演算子を使用してシェルコマンドで構成できます!=。[ 48 ]
YYYYMMDD ≠日付 マクロをオーバーライドするためのコマンドライン構文は次のとおりです。
make MACRO = "value" [ MACRO = "value" ... ] TARGET [ TARGET ... ]Makefile は、定義済みの内部マクロにアクセスできます。 と?は@よく使用されます。
ターゲット:コンポーネント1コンポーネント2 # ターゲットより若いコンポーネントを表示echo $? # ターゲット名を表示echo $@BSDとGNU Makeで動作するマクロを定義する際の一般的な構文は、等号(= )の代わりに+=、?=、!=を使用することです。[ 49 ]
サフィックスルールには、形式の名前を持つ「ターゲット」があり.FROM.TO、ファイル拡張子に基づいてアクションを実行するために使用されます。サフィックスルールのコマンドラインでは、POSIX [ 50 ]は、内部マクロが$<最初の前提条件を参照し、$@ターゲットを参照することを規定しています。任意の HTML ファイルをテキストに変換するこの例では、シェルリダイレクトトークンは>コマンドラインの一部であり、$<は HTML ファイルを参照するマクロです。
拡張子: .txt .html# .html から .txt へ.html.txt : lynx -dump $< > $@コマンドラインから呼び出された場合、上記の例は次のように展開されます。
$ make -n file.txt lynx -dump file.html > file.txt接尾辞ルールには、独自の前提条件があってはなりません。[ 51 ]もし前提条件がある場合、それらは接尾辞ルールではなく、通常とは異なる名前のファイルとして扱われます。GNU Make は、古い makefile との互換性のために接尾辞ルールをサポートしていますが、それ以外の場合はパターンルールの使用を推奨しています。[ 52 ]
パターンルールは、そのターゲットに %文字列内にちょうど 1 つの文字が含まれている点を除いて、通常のルールのように見えます。ターゲットはファイル名のマッチングのためのパターンと見なされます。 は%0 文字以上の任意の部分文字列にマッチできますが、[ 53 ]他の文字はそれ自身にのみマッチします。前提条件も同様に を使用して%、その名前がターゲット名とどのように関連しているかを示します。
上記の接尾辞ルールの例は、次のパターンルールのようになります。
# %.html から %.txt へ%.txt : % .html lynx -dump $< > $@ディレクティブは、別のメイクファイルを含めるなどの特別な動作を指定します。
行の継続は\、行末にバックスラッシュ文字を付けることで示されます。
対象: コンポーネント \ 成分 Tab ↹指示 ; \ 別のコマンド | \ パイプコマンド
以下のコマンドは、後述のメイクファイルのコンテキスト内で実行されます。
make # 最初のターゲット「all」を更新します make help # ターゲット「help」を更新してターゲットの一覧を表示します make dist # ターゲット「dist」を更新して配布用にビルドしますパッケージ=パッケージ バージョン= ` date "+%Y.%m%d%" `リリースディレクトリ= .. リリースファイル= $(パッケージ) - $(バージョン)# デフォルトターゲット# 注: 変数 LOGNAME は環境変数から取得されますall : echo "こんにちは$( LOGNAME ) 、デフォルトでは何もする必要はありません" echo "'make help' を試してください"# このファイルを検索してターゲットを表示するhelp : egrep "^# target:" [ Mm ] akefile# リリース配布ファイルを作成する: tar -cf $( RELEASE_DIR ) / $( RELEASE_FILE ) && \ gzip -9 $( RELEASE_DIR ) / $( RELEASE_FILE ) .tar 以下は、デフォルトでは(最初に「all」ルールがリストされています)システムのCコンパイラを使用して「helloworld.c」というソースファイルをコンパイルし、ユーザーが最初からやり直したい場合に生成されたファイルを削除する「clean」ターゲットも提供するシンプルなMakefileです。 と は、$@いわゆる$<内部マクロ(自動変数とも呼ばれます)の2つで、それぞれターゲット名と「暗黙の」ソースを表します。以下の例では、 は$^スペースで区切られた前提条件のリストに展開されます。他にも多くの内部マクロがあります。[ 50 ] [ 54 ]
CFLAGS ?= -g LDLIBS += -lmすべて:出力.txtOut.txt : helloworld ./$< > $@helloworld : helloworld . o $( CC ) $( LDFLAGS ) -o $@ $^ $( LDLIBS )helloworld.o :ハローワールド。c $( CC ) $( CFLAGS ) -c -o $@ $<クリーン: $( RM ) Out.txt helloworld helloworld.o 多くのシステムには、ファイル拡張子に基づくコンパイルなどの一般的なタスクを指定するための、定義済みのMakeルールとマクロが付属しています。これにより、ユーザーはソースからターゲットを生成する実際の手順(多くの場合、移植性が低い)を省略できます。このようなシステムでは、上記のMakefileを次のように変更できます。
すべて: helloworldhelloworld : helloworld . o $( CC ) $( CFLAGS ) $( LDFLAGS ) -o $@ $^クリーン: $( RM ) helloworld helloworld.o# 接尾辞ルール.co : $( CC ) $( CFLAGS ) -c $<.接尾辞: .c「helloworld.o」が「helloworld.c」に依存していることは、Makeによって自動的に処理されるようになりました。ここで示したような単純な例ではほとんど問題になりませんが、ソフトウェアプロジェクトのソースファイルの数が増え始めると、サフィックスルールの真の威力が明らかになります。リンクステップ用のルールを記述し、オブジェクトファイルを前提条件として宣言するだけで済みます。すると、Makeはすべてのオブジェクトファイルの作成方法とすべてのソースファイルの変更点を自動的に判断します。
ソースファイルが互いに依存せず、ヘッダー ファイルなどの他のファイルにも依存しない限り、単純なサフィックス ルールはうまく機能します。ビルド プロセスを簡素化するもう 1 つの方法は、コンパイラによる依存関係生成と組み合わせることができる、いわゆるパターン マッチング ルールを使用することです。gcc コンパイラと GNU Make を必要とする最後の例として、フォルダ内のすべての C ファイルを対応するオブジェクト ファイルにコンパイルし、それらを最終的な実行可能ファイルにリンクする汎用メイク ファイルを以下に示します。コンパイルが行われる前に、依存関係はメイク ファイルに適した形式で隠しファイル ".depend" に収集され、その後メイク ファイルに追加されます。移植可能なプログラムでは、以下で使用されている構造を避ける必要があります。
# 汎用GNUMakefile# GNU でない場合は失敗するスニペットifneq (,)このメイクファイルにはGNU Makeが必要です。endifPROGRAM = foo C_FILES := $( wildcard *.c ) OBJS := $( patsubst %.c, %.o, $( C_FILES )) CC = cc CFLAGS = -Wall -pedantic LDFLAGS = LDLIBS = -lmすべて: $(プログラム)$(PROGRAM) : . depend $( OBJS ) $( CC ) $( CFLAGS ) $( OBJS ) $( LDFLAGS ) -o $( PROGRAM ) $( LDLIBS )依存: .依存.depend : cmd = gcc - MM - MF depend $( var ) ; cat depend >> . depend ; .depend : @echo "依存関係を生成しています..." @ $( foreach var, $( C_FILES ) , $( cmd )) @rm -f depend-include .depend# これらはパターンマッチングルールです。ここで使用されている自動変数に加えて、% が表すものに一致する変数 $* は、特殊なケースで役立つ場合があります。%.o : %. c $( CC ) $( CFLAGS ) -c $< -o $@% : %. o $( CC ) $( CFLAGS ) -o $@ $<clean : rm -f .depend $( OBJS ).PHONY :クリーンな依存Makefile は依存関係で構成されており、依存関係が忘れられていたり、余分な依存関係があったりすると、ユーザーにはすぐには気づかれず、生成されたソフトウェアに検出が困難な微妙なバグが発生する可能性があります。この問題を回避し、ソースと Makefile の依存関係を同期させるために、さまざまなアプローチが考えられます。1 つのアプローチは、コンパイラを使用して依存関係の変更を追跡することです。GCC は、スイッチを使用することで、ソースコードを静的に解析し、指定されたファイルに対してルールを自動的に生成できます-MM。もう 1 つのアプローチは、依存関係を含む Makefile を生成する Makefile またはサードパーティ ツール (たとえば、GNU プロジェクトのAutomakeツールチェーンは、これを自動的に実行できます) を使用することです。
別の方法としては、 CMakeやMesonなどのメタビルドツールを使用する方法があります。
{{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク)ソース コード管理システムと make ユーティリティを使用しました。
{{cite web}}: CS1 maint: bot: 元の URL の状態が不明です (リンク)では、C言語を彷彿とさせるMakefileのインクルード、条件構造、forループが提供されています。