Makefiles

Makefiles tutorial

ref: https://makefiletutorial.com/

Getting started

Makefiles 是用来帮助我们决定在一个项目里那些文件需要被 compiled 的。以如下的 cpp 项目为例,如果任何一个文件的依赖发生了变化,文件都会被重新编译。

除了 Makefiles 之外,其替代品还有还有流行的编译工具比如说 Bazel

Syntax

一个 Makefile 会有一些规则,通常看起来是如下的样子。

1
2
3
4
targets: prerequisites
command
command
command
  • targets : 文件名,使用空格进行分隔。通常,每个 rule 只有一个。
  • commands:commands 是通常用来 make targets 的一系列操作。需要以 tab 开头,而不是 spaces
  • prerequisites:是为了 make targets 所需要的文件。

Beginner Example

some_file 运行的时候会先 check dependencies。clean 常被用来清除 make 的文件,但是并不是是 Make 的保留字。

1
2
3
4
5
6
7
8
9
10
some_file: other_file
echo "This will run second, because it depends on other_file"
touch some_file

other_file:
echo "This will run first"
touch other_file

clean:
rm -f other_file some_file

多 targets 的 make

1
2
3
4
5
6
7
8
9
10
11
all: one two three

one:
touch one
two:
touch two
three:
touch three

clean:
rm -f one two three

Variables

Makefile 的变量只能是 string,可以使用 $(variable) 或者 ${variable} 引用

1
2
3
4
5
6
7
8
9
10
11
12
iles = file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file

file1:
touch file1
file2:
touch file2

clean:
rm -f file1 file2 some_file

Automatic variables and Wildcards

    • wildcard。* 可以在用来搜索所有匹配的文件名。建议永远将 * 放在 wildcards function 中,否则很容易出问题

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      thing_wrong := *.o # Don't do this! '*' will not get expanded
      thing_right := $(wildcard *.o)

      all: one two three four

      # * 不要用在变量定义里
      # Fails, because $(thing_wrong) is the string "*.o"
      one: $(thing_wrong)

      # 如果没有匹配到,就会是 *.o 的状态
      # Stays as *.o if there are no files that match this pattern :(
      two: *.o

      # Works as you would expect! In this case, it does nothing.
      three: $(thing_right)

      # Same as rule three
      four: $(wildcard *.o)
  • % wildcard。% 有很多丰富的用法,之后碰到了可以看:

  • Automatic Variables

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    hey: one two
    # Outputs "hey", since this is the first target
    echo $@

    # Outputs all prerequisites newer than the target
    echo $?

    # Outputs all prerequisites
    echo $^

    touch hey

    one:
    touch one

    two:
    touch two

Commands and execution

Commands Echoing/Sliencing

在命令之前加 “@”,使得当前的命令不 print。也可以用 -s 在每一行之前加 @

1
2
3
all: 
@echo "This make line will not be printed"
echo "But this will"

Go Makefile ref

Examples

ref: https://github.com/protocolbuffers/protobuf/tree/master/examples

Protobuf example 里的 Go Makefile 可以作为参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
go:     add_person_go     list_people_go
gotest: add_person_gotest list_people_gotest

clean:
rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python
rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/*.class
rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/AddressBookProtos.java
rm -f *.pyc
rm -f protoc_middleman_go tutorial/*.pb.go add_person_go list_people_go go.mod go.sum
rm -f protoc_middleman_dart dart_tutorial/*.pb*.dart
rmdir dart_tutorial 2>/dev/null || true
rmdir tutorial 2>/dev/null || true
rmdir com/example/tutorial 2>/dev/null || true
rmdir com/example 2>/dev/null || true
rmdir com 2>/dev/null || true

protoc_middleman: addressbook.proto
protoc $$PROTO_PATH --cpp_out=. --java_out=. --python_out=. addressbook.proto
@touch protoc_middleman

protoc_middleman_go: addressbook.proto
mkdir -p tutorial # make directory for go package
protoc $$PROTO_PATH --go_out=tutorial addressbook.proto
@touch protoc_middleman_go

go_mod:
go mod init github.com/protocolbuffers/protobuf/examples
go mod tidy

add_person_go: add_person.go protoc_middleman_go go_mod
go build -o add_person_go add_person.go

add_person_gotest: add_person_test.go add_person_go go_mod
go test add_person.go add_person_test.go

list_people_go: list_people.go protoc_middleman_go go_mod
go build -o list_people_go list_people.go

list_people_gotest: list_people.go list_people_go go_mod
go test list_people.go list_people_test.go
1
2
3
4
5
6
7
8
# 清理文件并 build proto,生成 go 的定义
make clean
make go
# 运行添加 addressbook
./add_person_go addressbook.data
# 运行读取 addressbook
./list_people_go addressbook.data