はじめに
基本的な Makefile の書き方はこの記事では紹介してません。
Makefile 初学者の方には以下の記事を先に読むことをおすすめします。
(私の記事ではないですが紹介させていただきます。)
便利スクリプト集
色付き出力で結果を変わりやすく
こういった使い回したいスクリプトは Makefile.common
などに記載し、 Makefile
で include Makefile.common
すると便利になります。
文字列 | 色 |
---|---|
\033[0;31m |
赤色 |
\033[0;32m |
緑色 |
\033[0;33m |
黄色 |
\033[0m |
無色 |
Makefile.common
.PHONY: echo_green
TEXT ?= Success
echo_green:
@echo "\n\033[0;32m${TEXT}\033[0m\n"
#-----------------------------------------------------------------------------------------------
.PHONY: echo_red
echo_red:
@echo "\n\033[0;31m${TEXT}\033[0m\n"
#-----------------------------------------------------------------------------------------------
.PHONY: echo_yellow
echo_yellow:
@echo "\n\033[0;33m${TEXT}\033[0m\n"
Makefile
include Makefile.common
#-----------------------------------------------------------------------------------------------
.PHONY: test
test:
@make echo_green
@make echo_red TEXT="Error: something wrongs"
@make echo_yellow TEXT="Warning: something is warning"
$ make test
依存パッケージなどのインストールチェック
「configure
でやることじゃね?」と思われた方もいるのでは、、、
Makefile
include Makefile.common
#-----------------------------------------------------------------------------------------------
.PHONY: check_deps
check_deps:
# Check node
@if ! which node > /dev/null ; then \
make echo_red TEXT="Error: node not installed" ; false ; \
fi
@make echo_green
# Check yarn
@if ! which yarn > /dev/null ; then \
make echo_red TEXT="Error: yarn not installed" ; false ; \
fi
@make echo_green
#-----------------------------------------------------------------------------------------------
.PHONY: install
installl: check_deps
実行例
$ make check_deps
# Check node
Success
# Check yarn
Success
環境変数が指定されているかチェックする
.PHONY: check_env
SAMPLE_ENV ?= NOT_SPECIFIED
check_env:
@if [ ${SAMPLE_ENV} = "NOT_SPECIFIED" ] ; then \
make echo_red TEXT="Error: SAMPLE_ENV not specified"; false ; \
fi
実行例
$ make check_env SAMPLE_ENV=aaa # 正常終了
$ make check_env # エラー
Error: SAMPLE_ENV not specified
アプリケーションを systemd
のユニットサービスとしてインストールする
systemd
初学者向けの記事
バイナリなどの必須ファイルのインストールやユニットファイルの配置をします。
include Makefile.common
#-----------------------------------------------------------------------------------------------
## systemd unit file
define unit_file
[Unit]
Description=Sample
Documentation=Sample
After=network-online.target
[Service]
Type=simple
EnvironmentFile=${ENV_FILE_PATH}
ExecStart=${DIR_BIN}/sample $$ARGS
[Install]
WantedBy=multi-user.target
endef
export unit_file
## application env file
define env_file
ARGS="--port ${PORT}"
endef
export env_file
DIR_BIN ?= /usr/local/bin
UNIT_FILE_PATH ?= /usr/local/lib/systemd/system/sample.service
ENV_FILE_PATH ?= /usr/local/etc/default/sample
PORT ?= 80
.PHONY: install
install:
# Install
#@cp -b something ${DIR_BIN}/
@echo "-> ${DIR_BIN}/something"
@make echo_green
# Install as systemd unit
@echo "$$unit_file" > ${UNIT_FILE_PATH}
@echo "-> ${UNIT_FILE_PATH}"
@echo "$$env_file" > ${ENV_FILE_PATH}
@echo "-> ${ENV_FILE_PATH}"
@make echo_green
$ make install
# Install
#@cp -b something /usr/local/bin/
-> /usr/local/bin/something
Success
# Install as systemd unit
-> /usr/local/lib/systemd/system/sample.service
-> /usr/local/etc/default/sample
Success
$ systemctl daemon-reload # 配置したユニットファイルのロード
$ systemctl start sample.service # ユニットサービスの起動
ヘルプの表示
変数についてまでヘルプに含む場合は可読性が低い記述になってしまうためおすすめはしませんが、
個人で使う分には細かく記載しておきたいことが多いので僕はよくこんな感じで書いてます。
.DEFAULT_GOAL := help
.PHONY: help
TARGET ?= ''
help: ## Display this help screen.\n VARIABLES:\n - TARGET\n Specify make target
@grep '^[a-zA-Z]' Makefile | \
awk -F ':.*?## ' 'NF==2 {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' | \
grep -E ${TARGET} | \
sed 's/^/\n/g' | sed 's/\\n/\n/g'
@echo ""
.PHONY: check_installed
check_installed: ## Check installed (node, yarn)
# ...
.PHONY: Install
install: check_installed ## Install as systemd unit service.\n VARIABLES:\n - DIR_BIN\n Specify bin dir (default: "/usr/local/bin")\n - UNIT_FILE_PATH\n Path to create systemd unit file. (default: "/usr/local/bin")\n - ENV_FILE_PATH\n Path to create env file. (default: "/usr/local/etc/default/sample")
# ...
$ make help
help Display this help screen.
VARIABLES:
- TARGET
Specify make target
check_installed Check installed (node, yarn)
install Install as systemd unit service.
VARIABLES:
- DIR_BIN
Specify bin dir (default: "/usr/local/bin")
- UNIT_FILE_PATH
Path to create systemd unit file. (default: "/usr/local/bin")
- ENV_FILE_PATH
Path to create env file. (default: "/usr/local/etc/default/sample")
Makefile ハマりポイント集
echo -n
の -n
が出力されてしまう
問題のスクリプト
.PHONY: test
test:
@echo -n "改行したくない!"
@echo "改行されてるじゃん!!!"
$ make test
-n 改行したくない!
改行されてるじゃん!!!
修正後
.PHONY: test
test:
@/bin/echo -n "改行したくない!"
@echo "できた!"
or
.PHONY: test
test:
@bash -c 'echo -n "改行したくない!"'
@echo "できた!"
$ make test
改行したくない!できた!
原因
make
には組み込みの echo
が存在し、この echo
は -n
を解読できないため
make 変数とシェル実行時の環境変数の扱い
make 変数ではなくシェル実行時の環境変数を参照したい
make 変数
VAR ?= make変数
.PHONY: test
test:
@echo ${VAR}
環境変数
VAR ?= make変数
.PHONY: test
test:
@VAR=環境変数 ; \
echo $$VAR
for ループの中で環境変数を変えながら処理をしたい
分かり易い例が思いつかなかったので僕が使っているものを抜粋して紹介します。
パイプで渡された出力やファイルの内容の1行ずつを for ループで繰り返し処理する際に解析処理などによって変数の切り出しが複雑になる場合、一度変数保持用のファイルを作成すると記述が簡単になります。
.PHONY: build_cross
build_cross: get
go tool dist list | grep 'aix\|darwin\|freebsd\|illumos\|linux\|netbsd\|openbsd\|windows' | while read line ; \
do \
# .build.env ファイルに環境変数を一時的に保存する
printf GOOS= > ./.build.env ; \
echo $$line | cut -f 1 -d "/" >> ./.build.env ; \
printf GOARCH= >> ./.build.env ; \
echo $$line | cut -f 2 -d "/" >> ./.build.env ; \
# .build.env ファイルの環境変数を読み込んでコマンドを実行する
. ./.build.env ; \
go build GOOS=$$GOOS GOARCH=$$GOARCH -o ./${MODULE}-$$GOOS-$$GOARCH ;
done
rm ./.build.env