MakeFile 入門 (学習記録)


# 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