linux:make命令与makefile文件

1. 作用

通俗的来讲:
其实和 shell 脚本差不多,只不过有一些自己的规则可以让你使用起来更方便,比如你写 shell 脚本的话,只能写在一个文件里然后执行这个文件,makefile 可以让你执行这个里面的某一条命令。

2. 格式

Makefile文件由一系列规则(rules)构成。每条规则的形式如下。

<target> : <prerequisites> 
[tab]  <commands>

上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。

"目标"是必需的,不可省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。

2.1 目标

当目标是文件名的时候,比如这时的目标是a.txt:

a.txt: b.txt c.txt
    cat b.txt c.txt > a.txt

当目标为一个操作的名字时,比如这个clean,称为‘伪目标’,但是当你执行 make clean 的时候,如果正好有一个文件叫做clean,那么这个命令不会执行。因为Make发现clean文件已经存在,就认为没有必要重新构建了,就不会执行指定的rm命令。

clean:
      rm *.o

所以,一般我们会明确声明clean是"伪目标":

.PHONY: clean
clean:
        rm *.o temp

多个伪目标直接用空格隔开

像.PHONY这样的内置目标名还有不少,可以查看手册

2.2 前置条件

2.3 命令

var-lost:
    export foo=bar
    echo "foo=[$$foo]"

上面代码执行后(make var-lost),取不到foo的值。因为两行命令在两个不同的进程执行。一个解决办法是将两行命令写在一行,中间用分号分隔。

  1. 两行命令写在一行,中间用分号分隔。
  2. 换行符前加反斜杠转义。
  3. 加上.ONESHELL:
var-kept:
    export foo=bar; echo "foo=[$$foo]"
var-kept:
    export foo=bar; \
    echo "foo=[$$foo]"
.ONESHELL:
var-kept:
    export foo=bar; 
    echo "foo=[$$foo]"

3. 语法

3.1 注释

用 #

3.2 回声

make会打印每条命令,然后再执行,这就叫做回声(echoing)。
在命令的前面加上@,就可以关闭回声。
注:通常只在注释和纯显示的echo命令前面加上@。

test:
    @# 这是测试
    @echo TODO

3.3 通配符

*、? 、[…]

3.4 变量与赋值

txt = Hello World
test:
    @echo $(txt)

用Shell变量,需要在美元符号前,再加一个美元符号,这是因为Make命令会对美元符号转义。

test:
    @echo $$HOME
VARIABLE = value
# 在执行时扩展,允许递归扩展。

VARIABLE := value
# 在定义时扩展。

VARIABLE ?= value
# 只有在该变量为空时才设置值。

VARIABLE += value
# 将值追加到变量的尾端。

3.5 自动变量

1、$@
$@指代当前目标,就是Make命令当前构建的那个目标。比如,make foo的 $@ 就指代foo。

2、$<
<t:p1p2< 指代第一个前置条件。比如,规则为 t: p1 p2,那么< 就指代p1。

等等…

3.6 判断与循环

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif
LIST = one two three
all:
    for i in $(LIST); do \
        echo $$i; \
    done

# 等同于

all:
    for i in one two three; do \
        echo $i; \
    done

3.7 函数

$(function arguments)
# 或者
${function arguments}

Makefile提供了许多内置函数,可供调用。下面是几个常用的内置函数。
1、shell 函数:
用来执行 shell 命令

srcfiles := $(shell echo src/{00..99}.txt)

2、call 函数:

$(call variable,param,param,…)
$(1),$(2)等等表示param,param,…

等等…还有好多函数

最后的实践

我项目中的一些实践:

SHELL := /bin/bash

.PHONY: prerequ-program client server prod_server prod_client


install:|prerequ-program

define require_install
	if test "$(shell which $(1))" = ""; \
	then \
		brew install $(2); \
	else \
		echo $(1) is exists. skip install; \
	fi
endef

prerequ-program:
	@$(call require_install,mongod,mongo)
	mkdir -p ./db/
	if [ "${shell pgrep mongod}" = "" ]; then mongod --bind_ip 127.0.0.1 --fork --dbpath ./db/ --logpath ./db/mongod.log; fi
	@echo "start mongod success!"

# 开发模式
server:
	cd server && npm install && npm run dev

client:
	cd client && npm install && npm run dev

# 生产模式
prod_server:
	cd server && npm install && npm start

prod_client:
	cd client && npm install && npm run build

比如我的项目要安装 mongodb,那么调用shell函数判断一下有没有 mongod,没有的话就 brew install mongo,否则在当前目录下新建文件 mkdir -p ./db/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章