CLOVER🍀

That was when it all began.

makeを孊んでみる

これは、なにをしたくお曞いたもの

Goでのビルドツヌルは、makeを䜿うこずが倚いず聞いたので。

いく぀か、Goで曞かれた有名なOSSを芋おみるず、確かにMakefileが眮かれおいたす。

https://github.com/prometheus/prometheus/blob/v2.24.1/Makefile

https://github.com/hashicorp/terraform/blob/v0.14.5/Makefile

https://github.com/moby/moby/blob/v20.10.2/Makefile

https://github.com/kubernetes/kubernetes/blob/v1.20.2/build/root/Makefile

Goの勉匷を始めおいるこずですし、これを機に少し芚えおみようかな、ず。

make自䜓は䜿ったこずがありたす。ただ、すでに甚意されおいるMakefileず手順に埓い、make、make installずかを
実行しおきただけですね。

環境

今回の環境は、こちらです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:    20.04
Codename:   focal


$ uname -srvmpio
Linux 5.4.0-65-generic #73-Ubuntu SMP Mon Jan 18 17:25:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

make自䜓は、むンストヌル枈みです。

$ make --version
GNU Make 4.2.1
このプログラムは x86_64-pc-linux-gnu 甚にビルドされたした
Copyright (C) 1988-2016 Free Software Foundation, Inc.
ラむセンス GPLv3+: GNU GPL バヌゞョン 3 以降 <http://gnu.org/licenses/gpl.html>
これはフリヌ゜フトりェアです: 自由に倉曎および配垃できたす.
法埋の蚱す限り、 無保蚌 です.

make

makeに぀いお芋おいくずいうこずで、たずは䞀次リ゜ヌスをあたりたしょう。

GNUプロゞェクトを芋に行きたす。

Software - GNU Project - Free Software Foundation

こちらにmakeの説明がありたす。ちょっず抂芁を芋おみたしょう。正確には、GNU Makeですね。

Make - GNU Project - Free Software Foundation

makeは、プログラムの゜ヌスファむルから、実行可胜ファむルやその他の非゜ヌスファむルの生成をコントロヌルするツヌルです。
プログラムをビルドするための方法は、makefileず呌ばれるファむルに蚘述したす。

makeの機胜は、以䞋になりたす。

  • パッケヌゞのビルド方法やむンストヌル方法がmakefileに蚘述されおいるため、ナヌザヌは詳现を知らなくおもこれらのタスクを実行できる
  • makeはファむルの曎新を把握し、前回のビルドから倉曎があったファむルに基づいお凊理を行い、必芁なファむルを曎新する
  • 蚀語非䟝存で、makefile内で指定されたシェルコマンドを実行する
  • makeはパッケヌゞの䜜成以倖にも、むンストヌルやアンむンストヌルなど、様々な凊理を行わせるこずができる

makefileには、゜ヌスコヌドからビルド結果を生成するための、䞀連のルヌルを曞きたす。この時、ルヌルには䟝存関係を持たせる
こずができたす。

以䞋は、簡単なルヌルの構文です。

target:   dependencies ...
          commands
          ...

makefile内にはルヌルtargetを曞き、makeでどのルヌルを実行するかを指定するこずができたす。

 ずいうのが、基本的な話のようですね。

makeに぀いおの詳现は、マニュアルを芋おいくこずになりたす。

GNU Make Manual - GNU Project - Free Software Foundation

https://www.gnu.org/software/make/manual/make.html

では、これらを芋ながら簡単に詊しおいっおみたしょう。

How to Read This Manual

If you are new to make, or are looking for a general introduction, read the first few sections of each chapter, skipping the later sections.

はい。

The exception is the second chapter, An Introduction to Makefiles, all of which is introductory.

぀たり、初めおの人は2章を読みなさいずいうこずのようです。

An Introduction to Makefiles

はじめおのMakefile

ずいうわけで、こちらを芋ながら始めおいきたす。

An Introduction to Makefiles

単玔なmakefileは、以䞋のようなシンプルなルヌルで構成されたす。

target 
 : prerequisites 

        recipe
        

        


What a Rule Looks Like

ここで、タヌゲットtargetには実行可胜ファむルの名前やオブゞェクトファむル、アクションの名前を指定したす。
main.o、clean、install、buildなどですね。

前提条件prerequisitesずしお、別のタヌゲットを指定するこずができたす。これはタヌゲット間の䟝存関係を
衚したす。

レシピrecipeは、このタヌゲットで実際に行う凊理コマンドを蚘述したす。

泚意点ずしお、レシピの先頭にはタブを配眮する必芁がありたす。

実際に、makefileを曞いおみたしょう。

Makefile

step1: step2 step4
  echo 1
  echo one

step2: step3
  echo 2

step3:
  echo 3 \
  three

step4:
  echo 'four!!'

A Simple Makefile

makefileはMakefileずいう名前でmakeコマンドは認識したすが、-fオプションで別の名前を指定するこずもできたす。

step1はstep2、step4を前提にしおいお、step2はstep3を前提にしおいたす。step3には前提条件はありたせん。
step1には耇数のコマンドレシピを曞いおいたす。step3は、レシピを継続行にしおいたす。

この状態で、makeコマンドを実行しおみたす。

$ make
echo 3 \
three
3 three
echo 2
2
echo 'four!!'
four!!
echo 1
1
echo one
one

step3 → step2 → step4 → step1ずいう実行順になりたした。

なんずなく、䟝存関係を解決し぀぀実行しおいるのがわかるず思いたす。

makeコマンドの匕数ずしおタヌゲットを指定するこずで、各タヌゲット単䜍ず䟝存するタヌゲットごずにも実行できたす。

$ make step3
echo 3 \
three
3 three


$ make step4
echo 'four!!'
four!!


$ make step2
echo 3 \
three
3 three
echo 2
2

ずころで、単にmakeず指定した時は、step1のタヌゲットが実行されたように思いたす。

この理由は、以䞋に曞いおありたす。

By default, make starts with the first target (not targets whose names start with ‘.’). This is called the default goal.

How make Processes a Makefile

぀たり、最初のタヌゲットが実行されるようです。これをデフォルトゎヌルず呌びたす、ず。

これをオヌバヌラむドする方法ずしお、コマンドの匕数で指定する方法先ほど䜿った方法ず.DEFAULT_GOALずいう特殊倉数を
䜿う方法があるようです。

You can override this behavior using the command line (see Arguments to Specify the Goals) or with the .DEFAULT_GOAL special variable (see Other Special Variables).

詊しに、.DEFAULT_GOALを指定しおみたしょう。

Makefile

.DEFAULT_GOAL := step4

step1: step2 step4
  echo 1
  echo one

step2: step3
  echo 2

step3:
  echo 3 \
  three

step4:
  echo 'four!!'

確認。

$ make
echo 'four!!'
four!!

明瀺的にタヌゲットを指定しない堎合に、実行されるタヌゲットが倉わりたしたね。

ビルドで䜿うようなmakefileの䟋を芋おみるず、タヌゲットの名前はファむル名になっおいたりしたす。

A Simple Makefile

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

makefileは、このファむル名をタヌゲットにするこずで䟝存関係を衚珟したす。

今回䜜成したstep1のようなタヌゲットは、ファむルではありたせん。䞊蚘の䟋だずcleanがそうですね。

このようなファむルではないタヌゲットをPhonyタヌゲットず呌ぶそうです。

Phony Targets

Phonyタヌゲットは、ファむル名ではないタヌゲットのこずです。これを䜿う理由は、同じ名前のファむルずの競合を回避するためず、
パフォヌマンスの向䞊のためです。

レシピがタヌゲットずなるファむルを䜜成しない堎合、タヌゲットが再䜜成される床にレシピが実行されるようです。
たた、タヌゲットず同じ名前のファむルがあるずうたく機胜しなくなるようです。

ずするず、今回のmakefileはすべおPhonyタヌゲットなので、以䞋のようにするのが正解でしょうね。

Makefile

# .DEFAULT_GOAL := step4

.PHONY: step1
step1: step2 step4
  echo 1
  echo one

.PHONY: step2
step2: step3
  echo 2

.PHONY: step3
step3:
  echo 3 \
  three

.PHONY: step4
step4:
  echo 'four!!'

倉数を䜿う

makefile内では、倉数を䜿うこずもできたす。

Variables Make Makefiles Simpler

こんな感じで、=で定矩したす。参照は$(倉数)です。

Makefile

ECHO_COMMAND = echo
MESSAGE = 'Hello World'

.PHONY: hello
hello:
  $(ECHO_COMMAND) $(MESSAGE)

確認。

$ make
echo 'Hello World'
Hello World

ちなみに、この倉数は倖郚から䞊曞きするこずもできたす。

$ make MESSAGE=hello
echo hello
hello

これは、makeコマンド自身に䞊曞きしおもらっおいたす。

環境倉数で䞊曞きする堎合は、単玔に指定しただけではダメです。

$ MESSAGE=hello make
echo 'Hello World'
Hello World

-eオプションを付ける必芁がありたす。

$ MESSAGE=hello make -e
echo hello
hello

Variables from the Environment

倉数に぀いおは、ドキュメントのこのあたりを芋たしょう。

How to Use Variables

たた、自動で定矩される倉数もありたす。

Automatic Variables

倱敗しおも終了させない

デフォルトでは、タヌゲット内のコマンドが倱敗するずそのタヌゲットは倱敗したす。この挙動を倉曎し、倱敗しおも
継続するようにするには察象のコマンドの先頭に-を付䞎したす。

clean:
        -rm -f *.o

Errors in Recipes

Goプロゞェクトでmakefileを曞いおみる

最埌にGoプロゞェクトで簡単に詊しおみたしょう。

Goのバヌゞョン。

$ go version
go version go1.15.7 linux/amd64

プロゞェクト䜜成。

$ go mod init app
go: creating new go.mod: module app

go.mod

module app

go 1.15

゜ヌスコヌドを䜜成。

main.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println(GetMessage())
}

func GetMessage() string {
    return "Hello World!!"
}

テストコヌドも䜜成。

main_test.go

package main

import (
    "testing"
)

func TestGetMessage(t *testing.T) {
    if GetMessage() != "Hello World!!" {
        t.Error("test failed!")
    }
}

makefileも曞きたす。

Makefile

EXECUTABLE_FILE = app

.PHONY: build
build: format lint test
    go build

.PHONY: run
run: format
    go run main.go

.PHONY: format
format:
    gofmt -w -d .

.PHONY: lint
lint:
    go vet ./...

.PHONY: test
test:
    go test ./...

.PHONY: clean-testcache
clean-testcache:
    go clean -testcache

.PHONY: clean
clean:
    rm $(EXECUTABLE_FILE)

デフォルトでは、buildタヌゲットを実行したす。タヌゲットの䟝存関係から、フォヌマットしおgo vetをかけお、テストを実行しお
実行可胜ファむルの生成たで行いたす。

$ make
gofmt -w -d .
go vet ./...
go test ./...
ok      app 0.002s
go build

できあがったファむルの実行。

$ ./app 
Hello World!!

clean。こちらは単発のタヌゲットです。

$ make clean
rm app

run。フォヌマットずプログラムの実行が含たれたす。

$ make run
gofmt -w -d .
go run main.go
Hello World!!

䟝存するタヌゲットを順次実行しおいる途䞭で、あるタヌゲットが倱敗するずそこで停止したす。たずえば、今回のデフォルトタヌゲットを
実行しお、go testが倱敗した堎合はこんな感じになりたす。

$ make
gofmt -w -d .
go vet ./...
go test ./...
--- FAIL: TestGetMessage (0.00s)
    main_test.go:9: test failed!
FAIL
FAIL    app 0.002s
FAIL
make: *** [Makefile:21: test] ゚ラヌ 1

たた、-nを付けるず実際には凊理を行わずにどのようなタヌゲットが実行されるかを衚瀺しおくれたす。

$ make -n
gofmt -w -d .
go vet ./...
go test ./...
go build


$ make -n test
go test ./...


$ make -n run
gofmt -w -d .
go run main.go

なんずなく、雰囲気はわかった気がしたす。

あずはOSSで䜿われおいるmakefileを眺めたりしお、芚えおいきたしょう。