petitviolet blog

    2016年だけどMakefileを使ってみる

    2016-10-19

    QiitaMakefile

    この資料はなに?

    Make の良さを伝えるものです

    • C とか C++の持ち物じゃない
    • 普段使うコマンドのショートカットみたいな
    • 2016 年でも Makefile は便利

     2016-10-19 19.11.07.png


    Make ってなに?Makefile???

    make(メイク)は、プログラムのビルド作業を自動化するツール。コンパイル、リンク、インストール等のルールを記述したテキストファイル (makefile) に従って、これらの作業を自動的に行う。 出典:https://ja.wikipedia.org/wiki/Make

    つまり、自動化のためのツール。


    make が生成するのはふつう C のプログラムだが、べつに C のプログラムに限らず、Makefile に書く生成コマンドの書きように よっては、TeX のファイルだろうが、Java のプログラムだろうが 何でも make を利用して作業を軽減することができる。 出典:http://www.unixuser.org/~euske/doc/makefile/

    なので、C だろうが C++だろうが Scala だろうが関係ない。


    ぐぐるとこんなの出てきて難しそう...

    PROGRAM = main
    OBJS = main.o
    CC = gcc
    CFLAGS = -Wall -O2
    .SUFFIXES: .c .o
    
    .PHONY: all
    all: depend $(PROGRAM)
    
    $(PROGRAM): $(OBJS)
    	$(CC) -o $(PROGRAM) $^
    
    .c.o:
    	$(CC) $(CFLAGS) -c $<
    
    .PHONY: clean
    clean:
    	$(RM) $(PROGRAM) $(OBJS) depend.inc
    
    .PHONY: depend
    depend: $(OBJS:.o=.c)
    	-@ $(RM) depend.inc
    	-@ for i in $^; do cpp -MM $$i | sed "s/\ [_a-zA-Z0-9][_a-zA-Z0-9]*\.c//g" >> depend.inc; done
    
    -include depend.inc
    

    http://www.ie.u-ryukyu.ac.jp/~e085739/c.makefile.tuts.html


    何が良いの?

    ぼくがおもう Make のいいところ

    • わざわざインストールしなくていい(はず)
      • デフォルトで入ってる(/usr/bin/make)
    • タブ補完が効く
    • よく使うコマンドを保存出来る
      • .bashrc にaliasはるのとは違う
        • ディレクトリ(プロジェクト)毎にコマンドを用意できるので環境は汚れない
    • よく使うコマンドを共有できる
      • 「配信サーバに対するリクエストってどうやって送ればいいですか?」に応える

    サンプル

    Makefile はこんな感じ。

    show-ip:
            ipconfig getifaddr en0
    

    これを使ってみる。

    $ make show-ip
    ipconfig getifaddr en0
    192.168.0.27
    

    これはshow-ipというタスクでipconfig getifaddr en0というコマンドを実行している。 難しいコマンドをおぼえなくていいし、Makefile の文法も簡単なので学習コストは低い。


    書き方

    ざっくりとはこれだけ。

    <変数宣言>
    
    <タスク名>:
      <実行したいshell>
    
    <タスク名>:<必要ファイル名>
      <実行したいshell>
    

    インデントはタブ文字なので注意。 ほんとは変数じゃなくてマクロと呼ばれる。


    実行するシェルを標準出力に表示しない方法

    実行すると実行するシェルが出力されてしまうので、鬱陶しい場合は@を先頭につける。

    show-files:
            ls -lh
    
    show-files-suppress:
            @ls -lh
    

    実行するときは何も変わらない

    $ make show-files
    ls -lh
    total 8
    -rw-r--r--  1 petitviolet  staff    51B Oct 15 15:10 Makefile
    $ make show-files-suppress
    total 8
    -rw-r--r--  1 petitviolet  staff    51B Oct 15 15:10 Makefile
    

    変数宣言

    Makefile の中では変数を使用することが出来る。

    $ cat Makefile
    MESSAGE := "HELLO"
    
    hello:
            @echo $(MESSAGE)
    $ make hello
    HELLO
    

    タスクで使用する際に上書き出来る。

    $ make hello MESSAGE=こんにちは
    こんにちは
    

    タスク実行に必要なファイルを指定できる

    activatorというファイルが存在していれば...みたいなタスクを定義する。

    compile: activator
      @echo "Yey!"
    

    ファイルが無いと失敗し、ファイルがあれば成功する。

    $ ls
    Makefile
    $ make compile
    make: *** No rule to make target 'activator', needed by 'compile'.  Stop.
    $ touch activator
    $ make compile
    Yey!
    

    順番にタスクを実行するような場合、ファイルの作成を持って成否判定を出来たりして便利!


    タスクからタスクを呼ぶ

    あるタスクの中から別のタスクを実行することも出来る。

    task-a:
    	@echo "A"
    	@touch a.txt
    	@make task-b
    
    task-b: a.txt
    	@echo "B"
    	@touch b.txt
    	@make task-c
    
    task-c: b.txt
    	@echo "C"
    	@touch c.txt
    
    clean: a.txt b.txt
    	@rm a.txt b.txt c.txt
    	@echo 'complete!!!'
    
    doit:
    	@make task-a
    	@make clean
    

    実行するとこんな感じになる。

    $ make doit
    A
    B
    C
    complete!!!
    

    複数行にわたるコマンドを実行したい時

    一行ごとに別の shell として実行される点に注意。

    $ ls
    Makefile
    $ cat Makefile
    make-tmp-file-home:
            @cd ~
            @touch hogehoge
    $ make make-tmp-file-home
    $ ls
    Makefile  hogehoge
    

    これはサブシェルの中でcdして、別のサブシェルの中でtouchしているので、カレントディレクトリでの操作となる。


    &&\でつないで 1 つのシェルとして実行すれば良い。

    $ ll $HOME | grep 'hogehoge' | wc -l
           0
    $ cat Makefile
    make-tmp-file-home:
            @cd ~ && \
            touch hogehoge
    $ make make-tmp-file-home
    $ ll $HOME | grep 'hogehoge' | wc -l
           1
    

    シェル変数と制御文

    上記の点さえ気をつければ for 文も書ける。

    LENGTH := 3
    
    show-seq:
    	@for i in {1..$(LENGTH)}; do \
    		echo $$i; \
    	done
    

    ここでシェル変数が$$iとなっている点に注意。

    from: https://qiita.com/petitviolet/items/a1da23221968ee86193b