LoginSignup
2
2

More than 5 years have passed since last update.

[一発芸] GNU Make で相対パスを動的に生成する [無駄知識]

Last updated at Posted at 2016-02-10

◆ 御注意 ◆
この記事で紹介したスクリプトは、タイトルどおりの受け狙いパフォーマンスです。
おおむねは期待したとおりに動作しますが、少し詳しく検証するとポロポロと穴が見つかります。
重要なビルドシステムに組み込んだりすることは、間違ってもお勧めしません。
◆ 御注意 ◆

一身上の都合により GNU Make の Makefile で、とあるふたつのディレクトリ A から B への相対パスを動的に生成しなければならなくなりました。
Make のスクリプトはチューリング完全なプログラミング言語だと噂に聞いたことはありましたが、これはさすがに難しいでしょう。

……と思っていたら、できてしまいました。

Makefile
comma := ,
empty :=
space := $(empty) $(empty)

# exsample path
FROM_PATH := ../a/../b/c/d/c
#FROM_PATH := ../b/x/y
TO_PATH := ../b/x/y
#TO_PATH := ../a/../b/c/d/c

# function relpath_from
relpath_from = $(subst $(space),/,$(strip \
                 $(foreach dir,$(basename $(foreach dir,$(join $(subst /,$(space),$(abspath $(1))),$(addprefix .,$(subst /,$(space),$(abspath $(2))))),$(filter-out $(basename $(dir)).$(basename $(dir)),$(dir)))),$(patsubst %,..,$(dir))) \
                 $(foreach dir,$(suffix $(foreach dir,$(join $(subst /,$(space),$(abspath $(1))),$(addprefix .,$(subst /,$(space),$(abspath $(2))))),$(filter-out $(basename $(dir)).$(basename $(dir)),$(dir)))),$(subst .,$(empty),$(dir)))))

# step trace
F_ABS := $(abspath $(FROM_PATH))
T_ABS := $(abspath $(TO_PATH))

F_WORD := $(subst /,$(space),$(F_ABS))
T_WORD := $(subst /,$(space),$(T_ABS))

PAIRING := $(join $(F_WORD),$(addprefix .,$(T_WORD)))
CROSSOFF := $(foreach dir,$(PAIRING),$(filter-out $(basename $(dir)).$(basename $(dir)),$(dir)))

UPPER := $(foreach dir,$(basename $(CROSSOFF)),$(patsubst %,..,$(dir)))
DOWNER := $(foreach dir,$(suffix $(CROSSOFF)),$(subst .,$(empty),$(dir)))
CONCAT := $(subst $(space),/,$(strip $(UPPER) $(DOWNER)))

all:
    @echo from_path: $(FROM_PATH)
    @echo to_path: $(TO_PATH)
    @echo $(call relpath_from,$(FROM_PATH),$(TO_PATH))
    @echo ===== step trace =====
    @echo from_path: $(FROM_PATH)
    @echo to_path: $(TO_PATH)
    @echo -----
    @echo from_abs_path: $(F_ABS)
    @echo to_abs_path: $(T_ABS)
    @echo -----
    @echo from_words: $(F_WORD)
    @echo to_words: $(T_WORD)
    @echo -----
    @echo pairing: $(PAIRING)
    @echo cross-off: $(CROSSOFF)
    @echo -----
    @echo upper: $(UPPER)
    @echo downer: $(DOWNER)
    @echo $(CONCAT)

あ、ディレクトリ名自体に .(ピリオド) を含んでいるケースは想定していないです、面倒くさいので。
パスは違うけれどたまたま同じ階層に同じ名前のディレクトリがあったりすると、動作がおかしくなります。
それから、たぶん独自拡張の関数を使っているので GNU Make 以外では動かないんじゃないでしょうか。

$ make 
from_path: ../a/../b/c/d/c
to_path: ../b/x/y
../../../x/y
===== step trace =====
from_path: ../a/../b/c/d/c
to_path: ../b/x/y
-----
from_abs_path: /Users/b/c/d/c
to_abs_path: /Users/b/x/y
-----
from_words: Users b c d c
to_words: Users b x y
-----
pairing: Users.Users b.b c.x d.y c
cross-off: c.x d.y c
-----
upper: .. .. ..
downer: x y
../../../x/y
2
2
2

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
2