我有两个几乎相同的目标:
# install node modules from package.json and bring npm-shrinkwrap.json up to date
npm-install:
ifndef SHRINKWRAP_BIN
$(error `npm-shrinkwrap` not found. Please run `sudo npm install -g npm-shrinkwrap`)
endif
$(NPM_BIN) install --no-shrinkwrap --loglevel=error --no-optional
$(NPM_BIN) prune --no-shrinkwrap --loglevel=error
$(NPM_BIN) dedupe --no-shrinkwrap --loglevel=error
npm-shrinkwrap --dev
touch $(NPM_TIMESTAMP)
# update npm dependencies to their latest version given the semver constraints and re-write npm-shrinkwrap file
npm-update:
ifndef SHRINKWRAP_BIN
$(error `npm-shrinkwrap` not found. Please run `sudo npm install -g npm-shrinkwrap`)
endif
$(NPM_BIN) update --save-dev --loglevel=error --no-optional
$(NPM_BIN) prune --no-shrinkwrap --loglevel=error
$(NPM_BIN) dedupe --no-shrinkwrap --loglevel=error
npm-shrinkwrap --dev
touch $(NPM_TIMESTAMP)
有什么方法可以通过让他们同时调用另一个目标来删除一些重复?我不能只为这两个部分添加公共部分的先决条件,因为在命令之前运行先决条件并且必须首先运行安装/更新位(之前剪枝/重复数据删除/拆封)。
答案 0 :(得分:3)
假设我正确阅读了这一点,两者之间的差异只是install
中的npm-install
和update
中的npm-update
,那么这里的解决方案就是使用正在运行的命令中的目标(或部分目标)。
这样的事情:
# install node modules from package.json and bring npm-shrinkwrap.json up to date
npm-install npm-update:
ifndef SHRINKWRAP_BIN
$(error `npm-shrinkwrap` not found. Please run `sudo npm install -g npm-shrinkwrap`)
endif
$(NPM_BIN) $(subst npm-,,$@) --no-shrinkwrap --loglevel=error --no-optional
$(NPM_BIN) prune --no-shrinkwrap --loglevel=error
$(NPM_BIN) dedupe --no-shrinkwrap --loglevel=error
npm-shrinkwrap --dev
touch $(NPM_TIMESTAMP)
你也可以使用$(word 2,$(subst -, ,$@))
或$(patsubst npm-%,%,$@)
,或者上面没有为--no-shrinkwrap
切换--save-dev
你可以使用这样的东西(或者合并{ {1}}上面用$@
变量,如上所述:)
arg
答案 1 :(得分:1)
我对此有几点想法。第一种是在Makefile
中使用文本处理来明确减少重复。定义一个多行宏,然后调用它。
define NPM_COMMON_STEPS
$(NPM_BIN) prune ...
$(NPM_BIN) dedupe ...
...
endef
由于没有参数,我们不必使用$(call ...)
运算符。简单地说,在配方中我们称之为:
$(NPM_COMMON_STEPS)
然后还有其他方法。您可以使虚拟先决条件目标处理所有逻辑,并根据谁“调用”只是切换它的一部分。我们怎么知道呢?为什么,通过特定于目标的变量!
这可以通过完整的Makefile来说明:
.PHONY: all common-target a-target b-target
all: a-target b-target
common-target:
$(if $(CALLED_FOR_A), echo called for a-target)
$(if $(CALLED_FOR_B), echo called for b-target)
echo common recipe
a-target: CALLED_FOR_A := y
a-target: common-target
b-target: CALLED_FOR_B := y
b-target: common-target
试验:
$ make
echo called for a-target
called for a-target
echo common recipe
common recipe
$ make a-target
echo called for a-target
called for a-target
echo common recipe
common recipe
$ make b-target
echo called for b-target
called for b-target
echo common recipe
common recipe
正如您所看到的,这里有一个缺点,即如果我们更新目标all
,那么GNU Make只执行一次共享公共规则。当该规则代表a-target
运行时,它被视为已更新且未针对b-target
运行。
如果我们不在同一次运行中更新两个目标,这无关紧要,但同样,这是一个潜在的障碍:
$ make a-target b-target
echo called for a-target
called for a-target
echo common recipe
common recipe
make: Nothing to be done for `b-target'.
因此在使用这种技巧之前我会三思而后行。如果你永远不会在同一个调用中执行npm-update
和npm-install
,那么可以使用它。
以下是文本替换解决方案的完整示例:
.PHONY: all a-target b-target
all: a-target b-target
define COMMON
echo common recipe
endef
define COMMON_WITH_ARG
echo common recipe with arg 1 == $(1)
endef
a-target:
echo a-target
$(COMMON)
$(call COMMON_WITH_ARG,a)
echo a-done
b-target:
echo b-target
$(COMMON)
$(call COMMON_WITH_ARG,b)
echo b-done
执行命令
$ make
echo a-target
a-target
echo common recipe
common recipe
echo common recipe with arg 1 == a
common recipe with arg 1 == a
echo a-done
a-done
echo b-target
b-target
echo common recipe
common recipe
echo common recipe with arg 1 == b
common recipe with arg 1 == b
echo b-done
b-done