Makefile 当订单重要时,如何让一个目标呼叫另一个目标?
我有两个几乎相同的目标:Makefile 当订单重要时,如何让一个目标呼叫另一个目标?,makefile,gnu-make,Makefile,Gnu Make,我有两个几乎相同的目标: # 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 --
# 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)
有没有办法让他们都调用另一个目标来消除一些重复?我不能只向它们添加一个带有公共部分的先决条件,因为先决条件是在命令之前运行的,必须先运行安装/更新位(在删除/重复数据消除/收缩包装之前)。我对此有一些想法。第一种方法是只在
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
,那么gnumake只执行一次共享公共规则。当代表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更新
和npm安装
,则可以使用此方法
以下是文本替换解决方案的完整示例:
.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
运行:
关于这一点,我有几个想法。第一种方法是只在
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
,那么gnumake只执行一次共享公共规则。当代表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更新
和npm安装
,则可以使用此方法
以下是文本替换解决方案的完整示例:
.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
运行:
假设我读得正确,并且两者之间的区别只是
npm install
中的install
和npm update
中的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)
npm-install: command := install
npm-install: arg := --no-shrinkwrap
npm-update: command := update
npm-update: arg :=--save-dev
npm-install npm-update:
....
$(NPM_BIN) $(command) $(arg) --loglevel=error --no-optional
....
您也可以使用$(word 2,$(subst-,$@))
或$(patsubst npm-%,%,$@)
,或者由于上面没有切换--对于,没有收缩包装--save dev
,您可以使用类似的方法(或者将上面的$
用法与arg
变量组合使用,如下所示):
假设我读得正确,并且两者之间的区别只是npm install
中的install
和npm update
中的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)
npm-install: command := install
npm-install: arg := --no-shrinkwrap
npm-update: command := update
npm-update: arg :=--save-dev
npm-install npm-update:
....
$(NPM_BIN) $(command) $(arg) --loglevel=error --no-optional
....
您也可以使用$(word 2,$(subst-,$@))
或$(patsubst npm-%,%,$@)
,或者由于上面没有切换--对于,没有收缩包装--save dev
,您可以使用类似的方法(或者将上面的$
用法与arg
变量组合使用,如下所示):
所以这里的区别是install
与第二个shell行中的update
相比?@EtanReisnerinstall——无收缩包装
vsupdate——保存开发
。所以这里的区别是install
与第二个shell行中的update
相比?@EtanReisnerinstall——无收缩包装
vsupdate--保存dev
。好主意,但有点过于复杂。定义是一个好主意,但我会用最后一个示例那样的参数处理第一个版本,并将整个主体放在定义中。@EtanReisner是的,这基本上是在FOSS中中等复杂的GNU makefiles中看到的标准做法。好主意,但有点过于复杂。定义是一个好主意,但我会用最后一个示例那样的参数处理它的第一个版本,并将整个主体放在定义中。@EtanReisner是的,这基本上是您在FOSS中适度复杂的GNU makefiles中看到的标准做法。