MakeFile とは
コンパイル、依存関係の管理、インストールなどのルールを記述しておくためのファイルです。
(引用: https://linuc.org/study/knowledge/542/)
Makeileでできること
まずMakeFileの基本機能として通常のターミナルと同じコマンドが使える(一部のコマンドの記述方法は異なる)
①コンパイル
gcc cファイル名 -o 実行ファイル名
②自ディレクトリから子ディレクトリのMakeFileを呼び出す
default :
make -C 子ディレクトリ名
などの基本機能があります。
一方でターミナル同様に実行できないコマンドとしてechoやmkdirがあります(環境によって異なります)
@echo "サンプルテキスト"
基本的には「 TAB @コマンド名 」 の形になる
MakeFileには
2つの段階があり、ターゲットの実行前の[準備段階]と実行後の[実行段階]に分かれています。
この[準備段階]に関してですが、主に変数やコマンドライン引数などを管理・関数の定義を上の方にまとめておきます。
[実行段階]に関しては、出力やコンパイルなどの処理でターゲットの
中 or 実行後 に配置する必要があります。
そして一度、[実行段階]に入ると変数の定義or代入が困難になります。不便ですねぇ…
まぁ、ファイル操作で情報やり取りは可能(">"でリダイレクトして"cat"で読み取り)
あとbashで代用可能コマンドもあるらしい
変数
型の宣言等は不要ですが参照時には $ を使用します。
宣言 : Hoge = xxx または Hoge = "xxx"
参照 : $(Hoge) or ${Hoge}
※変数名が一文字の場合は 参照時に()or{}は不要
定数
変数名 := 要素 で宣言可能
(命名指針としては大文字・区切りはアンダーバー )
ただしプログラム言語とは違い、定数の要素へ上書き可能
(再定義の事ね)
※プログラム言語にあるような「define」での宣言は
MakeFileではマクロ(関数のようなモノ)を指している
・デフォルトの変数(自動変数)
$@ : 自ターゲット名を参照
$% : 構成中ライブラリを参照
$< : 自ターゲットの前提条件を 1つ目のみ 参照
$^ : 自ターゲットの前提条件を 全て
参照(スペースあり)
$? :
自ターゲットのタイムスタンプ(更新日時)が新しい前提条件を参照
$* :
自ターゲット名におけるワイルドカード(%)の文字列を参照
などが存在する
参考資料 : https://tex2e.github.io/blog/makefile/automatic-variables
変数の扱い方
変数は参照時の要素を取得するので、プログラム言語と同じ動作はしない(
アドレスのような感じ )
例 ) ※PHONY は割愛します
x = 初期値x
y = $(x) 初期値y # x の要素 と "初期値y"の文字列を格納
x = 転生x # 再定義
test:
@echo $(y) # "転生x 初期値y" になる
@echo %@ # "test"(自ターゲット名を参照)
もし"初期値x 初期値y"の出力をするのであれば『y := $(x) 初期値y』
yを定数として宣言する必要がある
※※※ コマンドライン引数に関して ※※※
make実行時に「$ make 使用するターゲット名 x=hoge」とすることが可能になる。
これを行うことによって固定値の変数となり、MakeFile内での宣言よりも優先度の高い要素を代入する事が可能になる。
※※※ 演算子に関して ※※※
「 a ?= b 」というように ?= の宣言が存在する
これはvoid型の変数を指しており、"?="以外の代入(a =xxxなど)が行わる場合まで要素は b から不動となる
使用用途に関しては「デフォルトの数値は○○ですが、必要ならコマンドライン引数で変更してください」の命名指針にもなる。
マクロ
・基本構文
define マクロ名
処理
endef
ターゲット:
$(マクロ名) # マクロの呼び出し
の流れになる。
関数とは違い、ファイルの操作等も可能のため今後に記述されるターゲットやシェルスクリプトと同等のモノと考えてよい。
ターゲット :
実行時のコマンドにて使用される可能性がある
(誤爆の可能性)
シェルスクリプト:
ファイルへのパスを指定して実行する
(ファイルの分離化が可能)
マクロ :
関数のようなモノだが、MakeFile本体が冗長になりやすい。
ただし、三種とも変数の操作は行えないと思うので、分岐や動的処理が苦手かもしれない(するならターゲット前でフラグ管理を!)
参考資料 (少ないのでつらい)
①https://qiita.com/Ro3jin/items/27170827707e5136ee89
②https://qiita.com/mkataigi/items/b3028d73f9865d45ba1e
ターゲット
・基本構文
ターゲット名 : 前提条件
処理
の流れになる。
ターゲットを使用するには主に"疑似ターゲット"を作成する必要がある
※理論上は作成しなくとも動作可能だが、コマンドなのかファイル名の指定なのかを判別させるために必要
主に【.PHONY】宣言を使用する。使い方) .PHONY
疑似ターゲット名
例)下記、ターゲットは「make aaa」コマンドで実行可
.PHONY: aaa
aaa:
@echo "Hello World"
※宣言なしの場合でも他のターゲットからの呼び出し自体は可能(ただし同ファイル名があると誤作動)
※あと、宣言がないと『make: 'ターゲット名' is up to
date.』とかいう謎の「最新です」エラーが出る場合があるので注意
・ターゲットの実行方法は二つ存在しており、
①ターミナルでの引数として指定する
(2つ以上でも可能 例: make aaa bbb で2種のタスクの実行が可能 )
②MakeFileにて aaa: bbb という形で前提条件を宣言する
(bbbターゲットが存在しない場合はエラー )
パターンルール
同じようなターゲット処理が複数必要な場合はパターンルールのターゲットを作成する事で解消できます。
・基本構文
※ "%"はワイルドカードを指している
※ "%.o"の拡張子は例です
%.o : 前提条件
処理
PHONYは不要で、前提条件の箇所にもワイルドカード(%)を使用する事が可能です。※PHONYしようにもできないやろ
こちらも通常のターゲットと同様の方法で呼び出し(実行)が出来ます。
ただし【エラー「何もする必要はありません」で実行できない場合】
処理で使用するファイル等をtouchコマンドやコンパイル等で更新する事で解消します。
一番簡単なパターンルールの展開だけします。コマンド:
make hoge.echo にて実行
%.echo:
echo $@
以上
関数
単純にターミナルでのコマンドが関数化している事も多いので注意
例) ファイル・ディレクトリの検索
MakeFile: $(wildcard test*)
ターミナル: find test*
文字列やファイル操作など様々な関数がある。※正確には"組み込み関数"と呼ぶらしい
一つ注意点があり、【区切りに余分な半角スペースやTABを挿入しない事】
参考: https://tex2e.github.io/blog/makefile/functions
make -f- <<< 'x:;@echo =$(sort d b s d t)='
出力結果: =b d s t=
if文
・変数が定義されているかの判別 ( ifdef ) ※ターゲット配下の場合でも左寄せ
ifdef 変数名1
処理
else ifdef 変数名2
処理
else
else処理
endif
・変数が定義されていない場合の判別 ( ifndef ) ※not ifdef
ifndef 変数名
処理
else
else処理
endif
・値の比較 ( ifeq ) ※変数の場合は$(変数名),文字列はそのままで "" は不要
ifeq ($(変数名),比較する文字列)
true処理
else
false処理
endif
include
まず、自分以外のMakeFileをインポートする事を想定します。
MakeFileと同じ階層に"inc.mk"を作成して、てきとーな疑似ターゲット"inc"を作成
MakeFileでは[ include inc.mk ]を記述すると"inc"ターゲットが使用可能になる
自前のヘッダファイルをincludeしたい
まず、cファイルでのincludeでは二種類あります。
#include <stdio.h> // デフォルト要素を指定
#include "Hoge.h" // 相対パスにて指定
そしてこのデフォルト要素のincludeファイルを自前で追加してやろうというお話です。
MakeFileにて
# 目的のパス $(CURDIR) == pwdコマンドと同じ(現在までのディレクトリパス)
FPATH := \
$(CURDIR)/includeしたいディレクトリまでのパス
疑似ターゲット : main.c
gcc -I $(FPATH) main.c -o main.exe
これで main.exe というファイルが生成され、実行可能になる
ただし上記のモノでは拡張性に欠けてしまいます。
(1ファイルごとにコマンドを書く必要が出てくる)
ちなみに、分かりやすさ重視のため、$^→ main.c にしています。
FPATH := \
$(CURDIR)/ディレクトリまでのパス
疑似ターゲット : main.c
gcc -c $(FPATH)/*.c $(FPATH)/*.h main.c -I $(FPATH) main.c
gcc main.o -o 実行ファイル名
上記の例でも可能です。
このmain.cの箇所は、複数のファイル名を格納した変数でも問題ありません。
ちなみに、ワイルドカードを使用しているため -c で生成するオブジェクトファイル名を指定することができません。
番外編
・ターミナルにてMakeFileのコマンドを使う( 今回はpwdと同等のモノを出力できる )
make -f- <<< 'x:;@echo $(CURDIR)'
-f オプションはファイルを指定するオプションで
<<< は Here string というエスケープ処理
x という疑似ファイルを実行して、デフォルトの自動変数を echo している
Links:
[1] https://tex2e.github.io/blog/makefile/automatic-variables
[2] https://tex2e.github.io/blog/makefile/functions