Шаблонные правила могут иметь только один случай %
, но для вашего случая потребуется два.
Я бы попытался с помощью макроса на лету сгенерировать необходимые правила, а затем $(eval)
их.Если я правильно понимаю ваши требования, тогда этот make-файл должен это сделать:
# first rule in makefile is the default
.PHONY: all
all:
OUTPUT_DIR := .output
APPS_DIR := apps
# set as a fixed list for the solution - can be dynamic
SOURCE_YMLS := \
$(APPS_DIR)/test1/prod/test1.yml \
$(APPS_DIR)/test1/env1/test1.yml \
$(APPS_DIR)/test1/env2/test1.yml \
$(APPS_DIR)/test2/stage/test2.yml \
$(APPS_DIR)/test2/env3/test2.yml \
$(APPS_DIR)/test2/env4/test2.yml \
OUTPUT_YMLS :=
OUTPUT_DIRS :=
# $(1): source file name $(APPS_DIR)/<app_name>/<app_env>/<app_name>.yml
define generate_yml_rule
_input_parts := $(subst /, ,$(patsubst $(APPS_DIR)/%,%,$(1)))
_app_name := $$(word 1,$$(_input_parts))
_app_env := $$(word 2,$$(_input_parts))
_output_dir := $(OUTPUT_DIR)/$$(_app_name)
_output_yml := $$(_output_dir)/$$(_app_env).yml
# target specific variables for recipe evaluation
$$(_output_yml): _app_name := $$(_app_name)
$$(_output_yml): _app_env := $$(_app_env)
# why does "myprocess" not use $$< directly ???
# NOTE: >$$@ commented out for testing only
$$(_output_yml): $(1) | $$(_output_dir)
@echo my_process --app=$$(_app_name) --environment=$$(_app_env) --apps-dir=$(APPS_DIR) # >$$@
OUTPUT_DIRS += $$(_output_dir)
OUTPUT_YMLS += $$(_output_yml)
_input_parts :=
_app_name :=
_app_env :=
_output_dir :=
_output_yml :=
endef
# generate rules for output yml files
$(eval \
$(foreach _f,$(SOURCE_YMLS), \
$(call generate_yml_rule,$(_f)) \
) \
)
# remove duplicate directories
OUTPUT_DIRS := $(sort $(OUTPUT_DIRS))
$(info OUTPUT_DIRS '$(OUTPUT_DIRS)')
$(info OUTPUT_YMLS '$(OUTPUT_YMLS)')
all: $(OUTPUT_YMLS)
@echo DONE
$(OUTPUT_DIRS): | $(OUTPUT_DIR)
$(OUTPUT_DIRS) $(OUTPUT_DIR):
mkdir -p $@
Если мы добавим info
перед eval
, тогда мы сделаем сгенерированный код видимым (только выдержка):
eval _input_parts := test1 prod test1.yml
_app_name := $(word 1,$(_input_parts))
_app_env := $(word 2,$(_input_parts))
_output_dir := .output/$(_app_name)
_output_yml := $(_output_dir)/$(_app_env).yml
# target specific variables for recipe evaluation
$(_output_yml): _app_name := $(_app_name)
$(_output_yml): _app_env := $(_app_env)
# why does "myprocess" not use $< directly ???
# NOTE: >$@ commented out for testing only
$(_output_yml): apps/test1/prod/test1.yml | $(_output_dir)
@echo my_process --app=$(_app_name) --environment=$(_app_env) --apps-dir=apps # >$@
OUTPUT_DIRS += $(_output_dir)
OUTPUT_YMLS += $(_output_yml)
_input_parts :=
_app_name :=
_app_env :=
_output_dir :=
_output_yml :=
_input_parts := test1 env1 test1.yml
_app_name := $(word 1,$(_input_parts))
_app_env := $(word 2,$(_input_parts))
_output_dir := .output/$(_app_name)
_output_yml := $(_output_dir)/$(_app_env).yml
# target specific variables for recipe evaluation
$(_output_yml): _app_name := $(_app_name)
$(_output_yml): _app_env := $(_app_env)
# why does "myprocess" not use $< directly ???
# NOTE: >$@ commented out for testing only
$(_output_yml): apps/test1/env1/test1.yml | $(_output_dir)
@echo my_process --app=$(_app_name) --environment=$(_app_env) --apps-dir=apps # >$@
OUTPUT_DIRS += $(_output_dir)
OUTPUT_YMLS += $(_output_yml)
_input_parts :=
... and so on for the 4 other source files...
Тестовый прогон (myprocess
закомментирован с echo
, чтобы показать, что происходит):
$ make
OUTPUT_DIRS '.output/test1 .output/test2'
OUTPUT_YMLS ' .output/test1/prod.yml .output/test1/env1.yml .output/test1/env2.yml .output/test2/stage.yml .output/test2/env3.yml .output/test2/env4.yml'
mkdir -p .output
mkdir -p .output/test1
my_process --app=test1 --environment=prod --apps-dir=apps
my_process --app=test1 --environment=env1 --apps-dir=apps
my_process --app=test1 --environment=env2 --apps-dir=apps
mkdir -p .output/test2
my_process --app=test2 --environment=stage --apps-dir=apps
my_process --app=test2 --environment=env3 --apps-dir=apps
my_process --app=test2 --environment=env4 --apps-dir=apps
DONE