乐趣区

关于make:Y-分钟速成-make

源代码下载:Makefile-cn

Makefile 用于定义如何创立指标文件, 比方如何从源码到可执行文件. 创立这一工具的指标是
缩小不必要的编译或者工作. 是传说中的 Stuart Feldman 在 1976 年花了一个周末写进去的,
而今依然应用宽泛, 特地是在 Unix 和 Linux 零碎上.

尽管每个语言可能都有相应的或多或少提供 make 的性能, 比方 ruby 的 rake, node 的 gulp, broccoli
, scala 的 sbt 等等. 然而 make 的简洁与高效, 和只做一件事并做到极致的格调, 使其至今仍是无可替代的,
甚至与其余构建工具一起应用也并无抵触.

只管有许多的分支和变体, 这篇文章针对是规范的 GNU make.

# 这行示意正文

# 文件名肯定要叫 Makefile, 大小写辨别, 应用 `make <target>` 生成 target
# 如果想要取别的名字, 能够用 `make -f "filename" <target>`.

# 重要的事件 - 只意识 TAB, 空格是不认的, 然而在 GNU Make 3.82 之后, 能够通过
# 设置参数 .RECIPEPREFIX 进行批改

#-----------------------------------------------------------------------
# 高级
#-----------------------------------------------------------------------

# 创立一个 target 的规定非常简单
# targets : prerequisites
#     recipe
#      …
# prerequisites(依赖) 是可选的, recipe(做法) 也能够多个或者不给.

# 上面这个工作没有给 prerequisites, 只会在指标文件 file0.txt 文件不存在时执行
file0.txt:
    echo "foo" > file0.txt
    # 试试 `make file0.txt`
    # 或者间接 `make`, 因为第一个工作是默认工作.
    # 留神: 即便是这些正文, 如果后面有 TAB, 也会发送给 shell, 留神看 `make file0.txt` 输入

# 如果提供 prerequisites, 则只有 prerequisites 比 target 新时会执行
# 比方上面这个工作只有当 file0.txt 比 file1.txt 新时才会执行.
file1.txt: file0.txt
    cat file0.txt > file1.txt
    # 这里跟 shell 里的命令式截然不同.
    @cat file0.txt >> file1.txt
    # @ 不会把命令打印到 stdout.
    -@echo 'hello'
    # - 意思是产生谬误了也没关系.
    # 试试 `make file1.txt` 吧.

# targets 和 prerequisites 都能够是多个, 以空格宰割
file2.txt file3.txt: file0.txt file1.txt
    touch file2.txt
    touch file3.txt

# 如果申明反复的 target, make 会给一个 warning, 前面会笼罩后面的
# 比方反复定义 file2.txt 会失去这样的 warning
# Makefile:46: warning: overriding commands for target `file2.txt'# Makefile:40: warning: ignoring old commands for target `file2.txt'
file2.txt: file0.txt
    touch file2.txt

# 然而如果不定义任何 recipe, 就不会抵触, 只是多了依赖关系
file2.txt: file0.txt file3.txt

#-----------------------------------------------------------------------
# Phony(假的) Targets
#-----------------------------------------------------------------------

# phony targets 意思是 tagets 并不是文件, 能够设想成一个工作的名字而已.
# 因为不是文件, 无奈比对是否有更新, 所以每次 make 都会执行.
all: maker process

# 依赖于 phony target 的 target 也会每次 make 都执行, 即便 target 是文件
ex0.txt ex1.txt: maker

# target 的申明程序并不重要, 比方下面的 all 的依赖 maker 当初才申明
maker:
    touch ex0.txt ex1.txt

# 如果定义的 phony target 与文件名重名, 能够用 .PHONY 显式地指明哪些 targets 是 phony
.PHONY: all maker process
# This is a special target. There are several others.

# 罕用的 phony target 有: all clean install ...

#-----------------------------------------------------------------------
# 变量与通配符
#-----------------------------------------------------------------------

process: file*.txt | dir/a.foo.b    # 能够用通配符匹配多个文件作为 prerequisites
    @echo $^    # $^ 是 prerequisites
    @echo $@    # $@ 代表 target, 如果 target 为多个, $@ 代表以后执行的那个
    @echo $<    # $< prerequisite 中的第一个
    @echo $?    # $? 须要更新的 prerequisite 文件列表
    @echo $+    # $+ 所有依赖, 包含反复的
    @echo $|    # $| 竖线前面的 order-only prerequisites

a.%.b:
    @echo $*  # $* match 的 target % 那局部, 包含门路, 比方 `make dir/a.foo.b` 会打出 `dir/foo`

# 即使离开定义依赖, $^ 仍然能拿到
process: ex1.txt file0.txt
# 十分智能的, ex1.txt 会被找到, file0.txt 会被去重.

#-----------------------------------------------------------------------
# 模式匹配
#-----------------------------------------------------------------------

# 能够让 make 晓得如何转换某些文件到其余格局
# 比方 从 svg 到 png
%.png: %.svg
    inkscape --export-png $^

# 一旦有须要 foo.png 这个工作就会运行

# 门路会被疏忽, 所以下面的 target 能匹配所有 png
# 然而如果加了门路, make 会找到最靠近的匹配, 如果
# make small/foo.png (在这之前要先有 small/foo.svg 这个文件)
# 则会匹配上面这个规定
small/%.png: %.svg
    inkscape --export-png --export-dpi 30 $^

%.png: %.svg
    @echo 反复定义会笼罩后面的, 当初 inkscape 没用了

# make 曾经有一些内置的规定, 比方从 *.c 到 *.o

#-----------------------------------------------------------------------
# 变量
#-----------------------------------------------------------------------
# 其实是宏 macro

# 变量都是字符串类型, 上面这俩是一样一样的

name = Ted
name2="Sarah"

echo:
    @echo $(name)
    @echo ${name2}
    @echo $name    # 这个会被蠢蠢的解析成 $(n)ame.
    @echo \"$(name3)\" # 未声明的变量会被解决成空字符串.
    @echo $(name4)
    @echo $(name5)
# 你能够通过 4 种形式设置变量.
# 按以下程序由高到低:
# 1: 命令行参数. 比方试试 `make echo name3=JICHAO`
# 2: Makefile 外面的
# 3: shell 中的环境变量
# 4: make 预设的一些变量

name4 ?= Jean
# 问号意思是如果 name4 被设置过了, 就不设置了.

override name5 = David
# 用 override 能够避免命令行参数设置的笼罩

name4 +=grey
# 用加号能够连贯 (两头用空格宰割).

# 在依赖的中央设置变量
echo: name2 = Sara2

# 还有一些内置的变量
echo_inbuilt:
    echo $(CC)
    echo ${CXX)}
    echo $(FC)
    echo ${CFLAGS)}
    echo $(CPPFLAGS)
    echo ${CXXFLAGS}
    echo $(LDFLAGS)
    echo ${LDLIBS}

#-----------------------------------------------------------------------
# 变量 2
#-----------------------------------------------------------------------

# 加个冒号能够申明 Simply expanded variables 即时扩大变量, 即只在申明时扩大一次
# 之前的等号申明时 recursively expanded 递归扩大

var := hello
var2 :=  $(var) hello

# 这些变量会在其援用的程序求值
# 比方 var3 申明时找不到 var4, var3 会扩大成 `and good luck`
var3 := $(var4) and good luck
# 然而个别的变量会在调用时递归扩大, 先扩大 var5, 再扩大 var4, 所以是失常的
var5 = $(var4) and good luck
var4 := good night

echoSEV:
    @echo $(var)
    @echo $(var2)
    @echo $(var3)
    @echo $(var4)
    @echo $(var5)

#-----------------------------------------------------------------------
# 函数
#-----------------------------------------------------------------------

# make 自带了一些函数.
# wildcard 会将前面的通配符变成一串文件门路
all_markdown:
    @echo $(wildcard *.markdown)
# patsubst 能够做替换, 比方上面会把所有 markdown
# 后缀的文件重命名为 md 后缀
substitue: *
    @echo $(patsubst %.markdown,%.md,$* $^)

# 函数调用格局是 $(func arg0,arg1,arg2...)

# 试试
ls:    *
    @echo $(filter %.txt, $^)
    @echo $(notdir $^)
    @echo $(join $(dir $^),$(notdir $^))

#-----------------------------------------------------------------------
# Directives
#-----------------------------------------------------------------------

# 能够用 include 引入别的 Makefile 文件
# include foo.mk

sport = tennis
# 流程管制语句 (如 if else 等等) 顶格写
report:
ifeq ($(sport),tennis)
    @echo 'game, set, match'
else
    @echo "They think it's all over; it is now"
endif

# 还有 ifneq, ifdef, ifndef

foo = true

# 不只是 recipe, 还能够写在里面哟
ifdef $(foo)
bar = 'bar'
endif

hellobar:
    @echo bar

资源

  • GNU Make 官网文档 HTML PDF
  • software carpentry tutorial
  • learn C the hard way ex2 ex28

有倡议?或者发现什么谬误?在 Github 上开一个 issue,或者发动 pull request!

原著 Robert Steed,并由 0 个好心人批改。
© 2022 Robert Steed, Jichao Ouyang
Translated by: Jichao Ouyang
本作品采纳 CC BY-SA 3.0 协定进行许可。

退出移动版