2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【make/Makefile】「なにそれ?おいしいの?」状態からの脱却

Last updated at Posted at 2021-01-05

この記事では,超基本的なMakefileの作り方を解説していきます.

make/Makefileとは?##

makeは主にコンパイラ型言語のプログラムをビルドするために使用されるコマンドです.

bash
$ make

とするだけで勝手にソースコードから実行ファイルが作成されます.
もちろん,ソースコードだけ作成してmakeコマンドを実行すれば勝手に実行ファイルが作成されるような魔法のコマンドではありません.
どのようにソースファイルをビルドするのかを記述したファイルが必要です.
それがMakefileと呼ばれるファイルです.

makeを使うメリット##

Makefileを記述する必要があるのなら,makeコマンドを使用する利点はどこにあるのでしょうか?
利点を説明する前に少しc言語のビルドについて考えてみましょう.
ソースファイルから実行可能なファイルを作るまでにはいくつかの手順があります.
簡単に説明すると,以下のようになります.

手順①プリプロセス
#defineで宣言されたマクロの展開など,プロプロセッサ命令を処理します.機械語に変換するための前処理ですね.

手順②コンパイル
ソースファイルをアセンブリ言語に変換します.gccでは,-Sオプションを指定すると,アセンブリで書かれたファイルが生成されます.

手順③アセンブル
アセンブリで書かれたファイルをコンピュータが理解できる機械語に変換します.gccでは,-cオプションを指定すると,機械語で書かれたオブジェクトファイルが生成されます.

手順④リンク
複数のオブジェクトファイルを連結し,実行可能ファイルを作成します.

これでmakeを使う利点を説明する準備ができました.
複数のソースファイルを扱うプロジェクトを想像してください.
一部のファイルに変更を加えただけで,変更していないファイルについても再度オブジェクトファイルを生成してからリンクしていたのではリソースの無駄となります.かと言って,変更を加えるたびにそのファイルを控えておいて手動でビルドするのは手間ですし,ミスが起こる原因ともなります.
そこで,Makefileに一度ビルドのルールを記述しておけばその後,makeを実行するだけで変更を加えたファイルのみオブジェクトファイルを作り直し,リンクして,実行可能ファイルを自動で作成してくれます.
大きいプロジェクトになればなるほど,makeのメリットは大きくなるでしょう.

Makefileを書いてみよう##

まずは,一番基本的なMakefileを作ってみましょう.
今回は以下のプログラムをビルドします.

hello_world.c
# include <stdio.h>

int main(void){
    printf("Hello, World!\n");
    return 0;
}

これをビルドして,hello_worldという名前の実行ファイルを作成するMakefileは以下のようになります.

Makefile
hello_world: hello_world.c
	gcc hello_world.c -o hello_world

ソースファイルとMakefileを同じディレクトに置いて,そのディレクトリでmakeコマンドを使って見ましょう.

bash
$ make
gcc hello_world.c -o hello_world

上のようになり,実行ファイルhello_worldが作成されていると思います.
ファイルの中身を変更せずにもう一度makeコマンドを実行するとどうなるでしょうか?

bash
$ make
make: 'hello_world' is up to date.

ファイルに変更がないため,hello_worldという実行ファイルがもう一度作られることはありません.
次に,ソースファイルの中身を書き換えてから,makeコマンドを実行してみましょう.

bash
$ make
gcc hello_world.c -o hello_world

すると,今度はgccコマンドが実行され,hello_worldが作り直されています.
このようにmakeコマンドは,実行ファイルが作成された後にソースファイルが変更されたかを確認してビルドするかどうかを決めていることがわかります.

次に,Makefileの書き方を解説します.

Makefile
ターゲット名: 依存関係
[ Tab ]コマンド

ターゲット名にはこれから作成するファイル名を書きます.
依存関係にはターゲットを作成するために必要なファイルを書きます.
コマンドにはターゲットを作成するためのコマンドを記述します.
注意したいのは,コマンド行の先頭にはタブを入れなければならないことです.
そんなに難しくはないですね.

複数ファイルのMakefile##

次は,ソースファイルが複数ある場合のMakefileの書き方です.
ディレクトリ構成は以下のようになっているとします.

ディレクトリ構成
______________ test1.c
       |______ test2.c
       |______ test3.c
       |______ Makefile

この場合のMakefileは以下のようになります.

Makefile
test: test1.o test2.o test3.o
    gcc test1.o test2.o test3.o -o test

test1.o: test1.c
    gcc -c test1.c

test2.o: test2.c
    gcc -c test2.c

test3.o: test3.c
    gcc -c test3.c

testという実行ファイルを作成するルールの他に,testを作成するための依存ファイルになっているオブジェクトファイルを作成するルールも記述されています.
makeコマンドを実行すると,makeはまず,testを作成するためにtest1.o, test2.o, test3.oを探します.
見つからない場合,それらを作成するルールがMakefile内に記述されていないかを探します.
今回の場合,オブジェクトファイルを作成するルールが存在するので,そのルールに従って,オブジェクトファイルが作成されます.
その後,オブジェクトファイルを用いて,目的であったtestを作成します.

実際にmakeコマンドを実行してみましょう.

bash
$ make
gcc -c test1.c
gcc -c test2.c
gcc -c test3.c
gcc test1.o test2.o test3.o -o test

testが作成されました.
test2.cだけを編集して,もう一度makeを実行すると以下のようになります.

bash
$ make
gcc -c test2.c
gcc test1.o test2.o test3.o -o test

test2.cに関数オブジェクトファイルだけが作り直されているがわかったと思います.

おわりに##

このように,複数ファイルがある場合でもルールを追加していけばMakefileを作ることが出来ます.
しかし,もっとファイルが多くなった場合にMakefileを作成するのが面倒に思えてくるでしょう.
実は,Makefileには変数やマクロを使用したもっと効率的な書き方があります.
しかし,それを解説すると長くなるので,追々書いていきたいと思います.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?