如果订单有问题,如何让一个目标调用另一个?

时间:2016-05-13 00:03:50

标签: 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 --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)

有什么方法可以通过让他们同时调用另一个目标来删除一些重复?我不能只为这两个部分添加公共部分的先决条件,因为在命令之前运行先决条件并且必须首先运行安装/更新位(之前剪枝/重复数据删除/拆封)。

2 个答案:

答案 0 :(得分:3)

假设我正确阅读了这一点,两者之间的差异只是install中的npm-installupdate中的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-updatenpm-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