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
相比?@EtanReisner
install——无收缩包装
vs
update——保存开发
。所以这里的区别是
install
与第二个shell行中的
update
相比?@EtanReisner
install——无收缩包装
vs
update--保存dev
。好主意,但有点过于复杂。定义是一个好主意,但我会用最后一个示例那样的参数处理第一个版本,并将整个主体放在定义中。@EtanReisner是的,这基本上是在FOSS中中等复杂的GNU makefiles中看到的标准做法。好主意,但有点过于复杂。定义是一个好主意,但我会用最后一个示例那样的参数处理它的第一个版本,并将整个主体放在定义中。@EtanReisner是的,这基本上是您在FOSS中适度复杂的GNU makefiles中看到的标准做法。