2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Makefileで動的な変数をevalで作成する場合の注意点

Posted at

概要

Makefile で eval で変数が空になる現象に悩まされていたのだが、原因が分かったのでまとめておく。

環境

  • Ubuntu 20.04 LTS
    • GNU Make 4.2.1

NG例

KubernatesでPodが起動するのを待ってPostgresのコンテナに対してSQL実行する例。

# NG: make create
create:
    kubectl apply -f postgres.pod.yaml
    kubectl wait --for condition=Ready --timeout=90s pod postgres-pod
    # NG:   ↓empty result
    $(eval TARGET_HOST := $(shell kubectl get services postgres-service -o jsonpath='{.status.loadBalancer.ingress[*].ip}'))
    psql -h $(TARGET_HOST) -U $(DB_USER_NAME) -d fruits -f test.sql

# OK: make exec-sql
exec-sql:
    # OK: -> TARGET_HOST=xxx.xxx.xxx.xxx
    $(eval TARGET_HOST := $(shell kubectl get services postgres-service -o jsonpath='{.status.loadBalancer.ingress[*].ip}'))
    psql -h $(TARGET_HOST) -U $(DB_USER_NAME) -d fruits -f test.sql    

ポイント

  • Makefileの変数の評価は Makefile が読み込まれたときに行われる

解説

  • make create の実行前では postgres-service が存在していないため、 TARGET_HOST の中身は空文字になった
  • make exec-sql は、make createpostgres-service が作成し終わった後に実行するものであり、どのタイミングでも期待通り動作する

eval/shellの実行例

起動時評価の例1: shell

上記の動作が分かる例

test1:
        @date '+%T'  # <1>
        sleep 3
        @echo $(shell date '+%T')

実行結果

2つ目の $(shell date '+%T') が<1>と同時刻になっている。

$ make test1
20:09:43
sleep 3
20:09:43

起動時評価の例2: eval + shell

上記の shell の動作に準じる動きをします。

test2:
        @date '+%T'  # <1>
        sleep 3
        $(eval T1=$(shell date '+%T'))
        @echo $(T1)

実行結果

2つ目の $(eval T1=$(shell date '+%T')) が<1>と同時刻になっている。

$ make test2
20:12:46
sleep 3
20:12:46

遅延評価の例1: eval + date '+%T'

test3:
    @date '+%T'              # <1>
    sleep 3
    $(eval T1=`date '+%T'`)  # <2>
    @echo $(T1)

実行結果

<2>にはsleepした後の時刻が入る。

$ make test3
20:18:57
sleep 3
20:19:00

最初のNG例の修正例

  • eval で変数を作るまでもなかったので、eval は使わなかった
  • $(shell ...) を使うのをやめて、shell実行時の評価に変更した。psql -h $$(...) の行が評価され $$$ となり、psql -h $(...) のコマンドが実行される。
create:
    kubectl apply -f postgres.pod.yaml
    kubectl wait --for condition=Ready --timeout=90s pod postgres-pod
    psql -h $$(kubectl get services postgres-service -o jsonpath='{.status.loadBalancer.ingress[*].ip}') -U $(DB_USER_NAME) -d fruits -f test.sql

参考

あとがき

普通は遅延評価が必要になることはないが、必要になるときは Makefile ではなく、Shellの世界で考えることになる。

遅延評価が必要なケースでは、待ち合わせ処理をシェルで実装することになる。今回は Kubernetes で kubectl wait を使ったが shell の世界で頑張る方法もある。

以下は、while ~ sleep 1 で、空文字でなくなるか 30秒のタイムアウト超過するまで待ち合わせを行う例となる。遅延評価をしたいので $$ を使っていることに留意する。

create2:
    kubectl apply -f postgres.pod.yaml
    timeout 30 bash -c "while [ -z $$(kubectl get services postgres-service -o jsonpath='{.status.loadBalancer.ingress[*].ip}') ]; do sleep 1; done"
    psql -h $$(kubectl get services postgres-service -o jsonpath='{.status.loadBalancer.ingress[*].ip}') -U $(DB_USER_NAME) -d fruits -f test.sql
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?