gnu make:即使目标没有改变,先决条件也总是被调用

dot*_*ter 2 makefile build npm

我是新手,来自 npm。我正在尝试将它们一起使用,效果很好。只是make似乎认为每次都需要运行某个npm命令,但是目标文件在那里并且没有变化。

我有一个 git 项目的网站。该网站有一个该项目的 git 子模块,称为 npm。唯一重要的是,我需要在该目录中构建一些文件并将它们压缩到网站的根目录。该网站没有 package.json,因此 npm 将无法在该目录中运行。

下面是 MakeFile,最后是我的问题。

REPO_FOLDER = npm
MINI_FILES = stream.min.js stream.min.js.map
#STREAM_MINI_FILES := $(REPO_FOLDER)/stream.min.js $(REPO_FOLDER)/stream.min.js.map
STREAM_MINI_FILES := $(MINI_FILES:%=REPO_FOLDER/%) #6.3.1 Substitution References

stream.js.zip: $(STREAM_MINI_FILES)
    cd $(REPO_FOLDER); \
    npm run zip ../stream.js.zip $(MINI_FILES)

$(STREAM_MINI_FILES): minify

minify:
    @cd $(REPO_FOLDER); \
    test -d "node_modules" || npm install; \
    npm run minify;

update:
    @git submodule update --remote

all: update stream.js.zip

.PHONY: minify, update, all
Run Code Online (Sandbox Code Playgroud)

1) 即使 npm/stream.min.js npm/stream.min.js.map 存在, minify 目标也始终运行。为什么会这样,我如何才能识别这两个文件?

2)有什么区别@cdcd?在minify 目标中,我可以@cd到 npm 文件夹并运行npm install,但在 stream.js.zip 目标中,我收到来自 npm 的错误,因为它仍在网站目录中(没有 package.json)。在这种情况下,当我执行 @cd 时,为什么我不在 npm 目录中?

3)我使用 npm 压缩两个编译文件。我已经看到armake中有一个命令,但是它可以制作一个 zip 文件吗?如果是,如何制作?

更新 事实证明我的变量替换是错误的,因此第二个目标也是错误的。$(STREAM_MINI_FILES) 将解析为REPO_FOLDER/stream.min.js REPO_FOLDER/stream.min.js.map而不是stream/stream.min.js stream/stream.min.js.map. 由于第一条规则中的先决条件REPO_FOLDER从未存在,因此每次都会运行第二条规则的配方。

这是最终的工作 MakeFile:

REPO_FOLDER = stream
MINI_FILES = stream.min.js stream.min.js.map
ZIP_FILE = stream.js.zip 
#STREAM_MINI_FILES := $(REPO_FOLDER)/stream.min.js $(REPO_FOLDER)/stream.min.js.map
STREAM_MINI_FILES := $(MINI_FILES:%=$(REPO_FOLDER)/%) #6.3.1 Substitution References


$(ZIP_FILE): $(STREAM_MINI_FILES)
    @cd $(REPO_FOLDER); \
    npm run zip ../$@ $(MINI_FILES)

$(STREAM_MINI_FILES):
    @cd $(REPO_FOLDER); \
    test -d "node_modules" || npm install; \
    npm run minify;


update:
    @git submodule update --remote

clean:
    @rm --verbose $(ZIP_FILE) $(STREAM_MINI_FILES)

all: update $(ZIP_FILE)

.PHONY: update, clean, all
Run Code Online (Sandbox Code Playgroud)

Ren*_*let 5

1)minify被声明为.PHONY特殊目标的先决条件。每次必须构建依赖于它的另一个目标或使用此目标调用 make 时,make 都会重建它。如果您使用make(即,不指定目标)调用 make ,它将尝试构建 makefile 中的第一个目标stream.js.zip,在您的情况下。由于这取决于$(STREAM_MINI_FILES),make 将首先检查这些是否是最新的,如果不是,则重建它们。$(STREAM_MINI_FILES)本身取决于minify,这是 的先决条件,.PHONY因此必须在每种情况下重新制作,即使名为的文件minify存在并且是最新的。

此外,我怀疑minify是一个符号目标,并且没有具有此名称的真实文件(我错了吗?)。这是 make 重建它的另一个原因:它不存在,但$(STREAM_MINI_FILES).

一种选择是minify完全删除(包括从.PHONY先决条件中删除)并合并规则:

$(STREAM_MINI_FILES):
    @cd $(REPO_FOLDER); \
    test -d "node_modules" || npm install; \
    npm run minify
Run Code Online (Sandbox Code Playgroud)

仅当 中列出的文件之一$(STREAM_MINI_FILES)丢失时,才应运行该配方。一个缺点是配方将根据丢失的文件运行多少次,这是一种浪费。为避免这种情况,您可以保留minify目标,但将其设置为一个真正的空文件,以跟踪已完成的操作:

$(STREAM_MINI_FILES): minify

minify:
    @cd $(REPO_FOLDER); \
    test -d "node_modules" || npm install; \
    npm run minify
    touch $@
Run Code Online (Sandbox Code Playgroud)

这是一个非常常见的技巧,每次您的构建过程并不完全像“获取一个源文件并构建一个结果文件”时证明非常有用。

2)command在配方中,make 在执行时回显该命令。@command抑制回声。我想这与您的问题无关。

make 配方中的命令列表由 make 执行,每行一个单独的进程。所以,如果你:

cd there
pwd
Run Code Online (Sandbox Code Playgroud)

这两行在两个不同的进程中执行,第一行对第二行没有影响。将您的命令放在同一个 bash 列表中,即通过;force make分隔命令以在单个进程中执行它们(行\中的最后一个字符转义行尾字符):

cd there; pwd
Run Code Online (Sandbox Code Playgroud)

或者:

cd there; \
pwd
Run Code Online (Sandbox Code Playgroud)

是等效的,将pwdthere目录中执行命令。在您的stream.js.zip配方中,您只有一个 bash 列表。因此,它应该按预期工作,并且应该从$(REPO_FOLDER)目录中执行列表的第二个成员。你的问题来自其他方面。您是否尝试手动运行该命令?您是否检查过\确实是该行的最后一个字符(否则它不会转义行尾)。

3)ar不是 make 命令。它是一个 GNU 存档实用程序。不,它不能制作不同格式的 zip 存档。请注意,make 可以ar像解包一样对档案的内部进行操作,这很有用。键入man ar以了解更多信息ar