如何防止纱线安装在Makefile中运行两次?

mpe*_*pen 2 gnu-make

我有这个Makefile:

node_modules: yarn.lock
    yarn install --production=false
    touch node_modules

yarn.lock: package.json
    yarn install --production=false
    touch yarn.lock
Run Code Online (Sandbox Code Playgroud)

基本上,如果node_modules目录丢失(或者有人通过添加/删除文件篡改了它),或者yarn.lock已经更新,那么它应该运行yarn install以重建/完整性检查node_modules目录.

但是,如果yarn.lock缺少,可以重建package.json,或者如果package.json更新,则应安装并重建锁文件.

问题是当两个node_modules yarn.lock丢失,那么相同的命令运行两次.

我怎么能阻止这个?


几乎把它通过包裹在有条件的指令来工作:

ifneq ("$(wildcard yarn.lock)","")
node_modules: yarn.lock
    @yarn install --production=false
    touch node_modules

yarn.lock: package.json
    touch yarn.lock
else # yarn.lock does not exist
node_modules: yarn.lock
    touch node_modules

yarn.lock: package.json
    @yarn install --production=false
endif
Run Code Online (Sandbox Code Playgroud)

现在,如果你touch package.jsonmake node_modulesyarn.lock存在,那么它会随后touch yarn.lock这将导致node_modules重建,就像我想要的.

但是,如果您touch package.json和然后make yarn.lock,从技术上讲它应该尝试yarn install但不会,因为我从该指令中删除了命令:

yarn.lock: package.json
    touch yarn.lock
Run Code Online (Sandbox Code Playgroud)

防止它在前一个场景中运行两次.

Mik*_*han 6

首先,考虑这里说明的方法:

Makefile(1)

.PHONY: all clean

all: yarn.lock

yarn.lock: node_modules package.json
    $(MAKE clean)
    yarn install --production=false

node_modules:
    mkdir -p $@

clean:
    rm -fr node_modules yarn.lock
Run Code Online (Sandbox Code Playgroud)

这将永远不会运行yarn install冗余,这是一个稍微比你正在考虑更强大的解决方案.我会解释一下.

问题中的一个项是package.json.它是其他一切的唯一逻辑先决条件,而不是它本身的构建.

yarn.lock是一个构建人工制品,其生产意味着 yarn install在完成package.json时存在的快照成功完成.yarn install随后将认为安装是最新的,yarn.lock 并且具有与package.json通过算法体现的标准"一致"的内容yarn install.

因此,简单地看一下,这个版本的任务就是在以下方面做到yarn.lock 最新package.json:

yarn.lock: package.json
    yarn install --production=false
Run Code Online (Sandbox Code Playgroud)

但它实际上更复杂.yarn.lock是构建目标,但它不是唯一的构建伪差,这是不连的一个主值的假象.当然,这些都是node-modules因为跑步而填充的人工制品 yarn install.

因此,主要的构建工具看起来像这个构建的副作用,而实际的目标,yarn.lock对我们来说只是作为一个象征,即主要的人工制品,无论它们是什么,都是最新的package.json.

它是一个虚弱的标记.代理商可以搞乱内容 node_modules- 添加不应该存在的文件,删除或修改应该 - 并且yarn install不会做任何事情来纠正它,只要根据自己的标准考虑yarn.lock最新package.json.

你在解释建议的食谱时对这种脆弱性做了广告:

node_modules: yarn.lock
    yarn install --production=false
    touch node_modules
Run Code Online (Sandbox Code Playgroud)

如果缺少node_modules目录(或者有人通过添加/删除文件篡改了它),或者已经更新了yarn.lock,那么它应该运行yarn install来重建/完整性检查node_modules目录.

但是这种规则是由这种篡改引发的错误方式.篡改 - 如果你很幸运 - 将更新修改时间node_modules.但这将使它更年轻,而不是更老yarn.lock,而且不会解雇配方.配方仅适用于node_modules不存在的情况.

配方可以缓解这种弱点:

yarn.lock: node_modules package.json
    $(MAKE) clean
    yarn install --production=false 
Run Code Online (Sandbox Code Playgroud)

如果yarn.lock不存在,或者对于任何一个 node_modules 或者过时package_json,我们将从头开始重新制作所有构建工件.

这是更好的,但是它带来了开机的问题,当既没有文物yarn.locknode_modules存在是node_modules-这是填充作为由产品制造的yarn.lock-也是一个额外补贴yarn.lock.

然而,这是一个微不足道的问题.为先决条件yarn.lock是仅仅存在node_modules,它使得之前是满足的yarn.lock,而 内容node_modules,跟上时代的-只需添加配方:

node_modules:
    mkdir -p $@
Run Code Online (Sandbox Code Playgroud)

有了这个,如果node_modules它不存在,它将被创建为一个先决条件yarn.lock,使其更新yarn.lock,并要求 yarn.lock和主要的构建工件.

但...

这个解决方案基本上表达了依赖关系,因此 - 显示了yarn install永远不需要冗余运行.它纠正了篡改检测逻辑中错误的错误.

但仍然没有强大的篡改检测能力.

我们得到的篡改检测机制是: node_modules 目录中 发生的事情使其修改日期晚于yarn.lock.这将检测到一些篡改,但不是所有的篡改.

作为文件系统对象,当且仅当添加,删除或重命名直接子对象时,才修改目录 - 并更新其修改时间.所以,篡改检测机构是盲目的任何内部的所有事件子目录node_modules和现有的文件或子目录的任何修改 node_modules,除了重新命名它.这留下了足够的空间搞乱了node_modules.

有鉴于此,你可能会决定: -

狡猾的篡改检测比没有好.我不想使用比这更昂贵的东西.

但你可能不会.更可能的替代品:

篡改检测这种脆弱并不比没有好,所以我会回到:

yarn.lock: package.json
    yarn install --production=false
Run Code Online (Sandbox Code Playgroud)

我认为不正确的篡改是我构建的超出范围.如果它发生了,某些东西会破裂,我会注意到它, make clean 然后再试一次.

提前了

我想要强大的篡改检测.

强大的篡改检测,使相当重的起重-但不是太多重.你需要强制一个干净yarn install,或者不强制,这取决于内容的完整清单之间的旧与新比较的结果node_modules- 显示足够的信息,以便显示任何重大差异.

详细说明每个文件的路径名和修改时间的清单 node_modules是最佳候选者.此清单将包含make 需要知道的信息,并且如果可以拼写出该构建的难以捉摸的主要人工制品,则将从文件系统获取该信息,并且该信息相对于其上次记录状态的变化是可靠的触发重拍一切.从而:

Makefile(2)

RM := rm -fr
MANIFEST_DIR := .manifest
LAST_MANIFEST := $(MANIFEST_DIR)/node_modules.last
NEW_MANIFEST := $(MANIFEST_DIR)/node_modules.peek
GEN_MANIFEST := find node_modules/ -exec stat -c '%n %y' {} \;

$(shell mkdir -p $(MANIFEST_DIR) node_modules)
$(if $(wildcard $(LAST_MANIFEST)),,$(shell touch $(LAST_MANIFEST)))
$(shell $(GEN_MANIFEST) > $(NEW_MANIFEST))
$(shell cmp -s $(LAST_MANIFEST) $(NEW_MANIFEST) || touch node_modules)

.PHONY: all clean

all: $(LAST_MANIFEST)

yarn.lock: node_modules package.json
    $(RM) yarn.lock node_modules
    yarn install --production=false

$(LAST_MANIFEST): yarn.lock
    $(GEN_MANIFEST) > $@

clean:
    $(RM) yarn.lock node_modules $(MANIFEST_DIR)
Run Code Online (Sandbox Code Playgroud)

这开发了Makefile(1),主要是在顶部无条件执行的设备,其中: -

  • 确保(像以前一样)我们从一个node_modules目录开始,即使是空的.
  • 确保我们从一个隐藏的目录(.manifest)开始工作,并保持最新的清单node_modules.(类似于.deps在C/C++中经常用于保存自动依赖文件的隐藏目录).
  • 确保我们从持久的清单开始,即使是空的.
  • 使用:生成一个新的,真实的清单快照,为每个项目find node_modules/ -exec stat -c '%n %y' {} \;写入.此快照是真正的,因为它是,但不一定是真实的,因为它应该是.(它应该遵循符号链接吗? - ?否.因为不会遵循目标或先决条件的符号链接).<filename> <modification_time>node_modulesnode_modules node_modulesfind -L ...make
  • 将新的,真实的快照与持久化的清单进行比较,如果有任何差异,则进行触摸node_modules.

这相当于构建前导码,它将node_modules通过强大的篡改检测测试来更新或不更新修改时间.然后构建就像以前一样,除了它的目标不再是 yarn.lock,而是一个新的持久化清单$(LAST_MANIFEST),它始终是一个立即的后yarn-install快照,因此依赖于 yarn.lock.

Makefile的锻炼(2)

对于实验室老鼠,package.json 我将使用:

{
  "name": "node-js-sample",
  "version": "0.2.0",
  "description": "A sample Node.js app using Express 4",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.13.3"
  },
  "engines": {
    "node": "4.0.0"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/heroku/node-js-sample"
  },
  "keywords": [
    "node",
    "heroku",
    "express"
  ],
  "author": "Mark Pundsack",
  "contributors": [
    "Zeke Sikelianos <zeke@sikelianos.com> (http://zeke.sikelianos.com)"
  ],
  "license": "MIT"
}
Run Code Online (Sandbox Code Playgroud)

从头开始制作

$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.17s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

什么都不改变,重拍

$ make
make: Nothing to be done for 'all'.
Run Code Online (Sandbox Code Playgroud)

node_modules仅触摸

$ touch node_modules/
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.01s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

package.json仅触摸

$ touch package.json 
imk@imk-ThinkPad-T420:~/develop/so/make_prob$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.22s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

触摸node_modulespackage.json

$ touch package.json node_modules/
imk@imk-ThinkPad-T420:~/develop/so/make_prob$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.05s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

触摸 yarn.lock

$ touch yarn.lock 
$ make
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

删除 yarn.lock

$ rm yarn.lock 
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.17s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

更改依赖项 package.json

$ sed -i 's/4\.13\.3/4.15.3/' package.json
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.03s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

撤消更改

$ sed -i 's/4\.15\.3/4.15.3/' package.json
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 2.35s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

触摸子目录中的现有文件 node_modules

$ ls node_modules/vary/
HISTORY.md  index.js  LICENSE  package.json  README.md
$ touch node_modules/vary/README.md 
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.02s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

将文件添加到的子目录 node_modules

$ touch node_modules/vary/interloper
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.20s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)

从子目录中删除文件 node_modules

$ rm node_modules/vary/README.md 
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.16s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Run Code Online (Sandbox Code Playgroud)