make scripts
如何使用 make 來達到像是 「npm scripts」或是「composer scripts」的功能
基本概念
撰寫「Makefile」
撰寫「Makefile」內容如下
.PHONY: help status deploy
help:
echo make [command]
echo ====================
echo make help
echo make status
echo make deploy
status:
echo show status
deploy:
echo deploying...
「完整範例」
關於「.PHONY」的用法,請參考「GNU make / 4.6 Phony Targets」
執行指令
執行
$ make
顯示
echo make [command]
make [command]
echo ====================
====================
echo make help
make help
echo make status
make status
echo make deploy
make deploy
執行
$ make help
顯示
echo make [command]
make [command]
echo ====================
====================
echo make help
make help
echo make status
make status
echo make deploy
make deploy
這個結果跟上面直接下「
make
」是一樣的,因為若沒給第一個參數,就會找第一個Rule,「help」在這個範例是放在第一個,所以「make
」和「make help
」結果一樣。
執行
$ make status
顯示
echo show status
show status
執行
$ make deploy
顯示
echo deploying...
deploying...
執行
$ make deploy -s
顯示
deploying...
這裡加上「-s」,就不會把指令也印出來,請參考「GNU make / 5.2 Recipe Echoing」。
或是改寫「Makefile」,在指令前面加上「@」。
.PHONY: help status deploy
help:
@echo make [command]
@echo ====================
@echo make help
@echo make status
@echo make deploy
status:
@echo show status
deploy:
@echo deploying...
「完整範例」
執行
$ make
顯示
make [command]
====================
make help
make status
make deploy
執行
$ make deploy
顯示
deploying...
設定預設「Rule」
或是改寫「Makefile」, 最前面加上一個「Rule」名稱是「default」,然後相依「help」這個「Rule」, 這樣就可以指定預設的「Rule」。
注意:「default」可以任意命名,只要放在第一個「Rule」就行了。
.PHONY: default help status deploy
default: help
status:
@echo show status
deploy:
@echo deploying...
help:
@echo make [command]
@echo ====================
@echo make help
@echo make status
@echo make deploy
「完整範例」
分散「.PHONY」
或是改寫「Makefile」, 將「.PHONY」分散,寫到各個「Rule」底下, 這樣的寫法除了可以防止「.PHONY」後面不會接了一長串之外,然後漏寫, 分散到各個「Rule」底下,除了容易增加,也容易刪除。
default: help
.PHONY: default
status:
@echo show status
.PHONY: status
deploy:
@echo deploying...
.PHONY: deploy
help:
@echo make [command]
@echo ====================
@echo make help
@echo make status
@echo make deploy
.PHONY: help
「完整範例」
把指令分散到「Shell Script」
上面的寫法,只需要一個「Makefile」搞定。
而我個人慣用把這些指令,拆解到個別的「Shell Script」去,就會如同下面的結構。
./
├── bin
│ ├── deploy.sh
│ ├── status.sh
│ └── help.sh
└── Makefile
1 directory, 4 files
撰寫「Makefile」,內容如下
THE_MAKEFILE_FILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
THE_BASE_DIR_PATH := $(abspath $(dir $(THE_MAKEFILE_FILE_PATH)))
THE_BIN_DIR_PATH := $(THE_BASE_DIR_PATH)/bin
default: help
.PHONY: default
help:
@$(THE_BIN_DIR_PATH)/help.sh
.PHONY: help
status:
@$(THE_BIN_DIR_PATH)/status.sh
.PHONY: status
deploy:
@$(THE_BIN_DIR_PATH)/deploy.sh
.PHONY: deploy
「完整範例」
撰寫「bin/help.sh」,內容如下
#!/usr/bin/env bash
THE_BASE_DIR_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
usage()
{
echo ""
echo "Usage: make [command]"
echo
cat <<EOF
Ex:
$ make
$ make help
$ make status
$ make deploy
EOF
}
usage
撰寫「bin/status.sh」,內容如下
#!/usr/bin/env bash
echo show status
撰寫「bin/deploy.sh」,內容如下
#!/usr/bin/env bash
echo deploying...
執行指令
執行
$ make
或是執行
$ make help
顯示
Usage: make [command]
Ex:
$ make
$ make help
$ make status
$ make deploy
執行
$ make status
顯示
show status
執行
$ make deploy
顯示
deploying...
設定「PATH」
撰寫「Makefile」,內容如下
THE_MAKEFILE_FILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
THE_BASE_DIR_PATH := $(abspath $(dir $(THE_MAKEFILE_FILE_PATH)))
THE_BIN_DIR_PATH := $(THE_BASE_DIR_PATH)/bin
PATH := $(THE_BIN_DIR_PATH):$(PATH)
default: help
.PHONY: default
help:
@help.sh
.PHONY: help
status:
@status.sh
.PHONY: status
deploy:
@deploy.sh
.PHONY: deploy
test:
@firefox
.PHONY: test
dump-path:
@echo $(PATH)
「完整範例」
上面加上下面這一行,來設定「PATH」這個「環境變數」。
PATH := $(THE_BIN_DIR_PATH):$(PATH)
原本的
@$(THE_BIN_DIR_PATH)/help.sh
就可以改寫成
@help.sh
注意「$(THE_BIN_DIR_PATH)」要寫在「$(PATH)」前面,這樣就會先尋找「$(THE_BIN_DIR_PATH)」這個資料夾裡面的資料。
執行
make test
顯示
firefox starting...
並不會真的啟動「firefox」,請看「bin/firefox」裡面的內容。
若是改寫成下面這一行
PATH := $(PATH):$(THE_BIN_DIR_PATH)
若執行「make test」, 會執行「firefox」這個指令, 不過不是「bin/firefox」裡面這個檔, 而是系統的「firefox」,通常路徑是「/usr/bin/firefox」,所以會啟動「Firfox」。
另外有一個「Rule」是「dump-path」
可以執行
$ make dump-path
就可以看到「PATH」的內容了。
include
然後將剛剛的某部份寫到「mak/debug.mk」這個檔裡。
test:
@firefox
.PHONY: test
dump-path:
@echo $(PATH)
使用「include $(THE_MAK_DIR_PATH)/debug.mk」來「include」, 撰寫「Makefile」,內容如下
THE_MAKEFILE_FILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
THE_BASE_DIR_PATH := $(abspath $(dir $(THE_MAKEFILE_FILE_PATH)))
THE_BIN_DIR_PATH := $(THE_BASE_DIR_PATH)/bin
THE_MAK_DIR_PATH := $(THE_BASE_DIR_PATH)/mak
PATH := $(THE_BIN_DIR_PATH):$(PATH)
default: help
.PHONY: default
help:
@help.sh
.PHONY: help
status:
@status.sh
.PHONY: status
deploy:
@deploy.sh
.PHONY: deploy
include $(THE_MAK_DIR_PATH)/debug.mk
「完整範例」
一樣可以執行
$ make test
顯示
firefox starting...
執行
$ make dump-path
關於「include」的用法,請參考「GNU make / 3.3 Including Other Makefiles」。
傳參數
尚未研究傳參數的方式,因為在工作流程上,就是要把一些常用的動作包在一起,所以就直接把參數寫死在「script」上了。
只需要下「make [command]
」就行了。
更多參考
- GNU make / 4.6 Phony Targets
- GNU make / 5.2 Recipe Echoing
- GNU make / 2.4 Variables Make Makefiles Simpler
- GNU make / 6 How to Use Variables
- GNU make / 6.5 Setting Variables
- GNU make / 3.3 Including Other Makefiles
- 用Open Source工具開發軟體: 新軟體開發關念 / Chapter 5. Makefile撰寫