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

时间:2017-05-18 01:03:39

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

基本上,如果缺少node_modules目录(或者有人通过添加/删除文件篡改了它),或者yarn.lock已更新,那么它应该运行yarn install来重建/ integrity检查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

现在,如果您touch package.json然后make node_modulesyarn.lock存在,那么随后touch yarn.lock会导致node_modules重建,就像我一样想。

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

yarn.lock: package.json
    touch yarn.lock

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

2 个答案:

答案 0 :(得分: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

这绝不会冗余地运行yarn install,而且有些更多 比您考虑的强大解决方案。我会解释一下。

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

yarn.lock是一个构建人工制品,其生产意味着这一点 yarn install已成功完成快照 完成时存在的package.jsonyarn install会 随后认为只要yarn.lock安装是最新的 存在并且具有"同意"按标准package.jsonyarn install中以算法方式体现。

因此,简单地看一下,这个构建的任务就是使yarn.lock 关于package.json

的最新信息
yarn.lock: package.json
    yarn install --production=false

但它实际上更复杂。 yarn.lock是构建目标但是 它不是唯一的构建工艺品,它甚至不是其中一个的人工制品 主要价值。当然,这些都是人工制品的填充物 运行node-modules后的yarn install

因此主要的构建工件显示为此构建的副作用, 而实际目标yarn.lock仅对我们来说只是作为令牌 无论它们是什么,主要的文物都是最新的 使用package.json

这是一个虚弱的标记。机构可以搞乱内容 node_modules - 添加不应该存在的文件,删除或 修改那些应该 - 而yarn install不会做任何事情的人 只要 认为yarn.lockpackage.json保持同步,就可以纠正它, 按照自己的标准。

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

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

如果缺少node_modules目录(或者有人篡改过它   添加/删除文件),或已更新yarn.lock,然后它应该运行yarn   安装以重建/完整性检查node_modules目录。

但是 规则是错误的方式被这种篡改触发。该 篡改 - 如果你很幸运 - 将更新node_modules的修改时间。 但这会使它更年轻,而不是yarn.lock,并且不会发生火灾 食谱。该配方仅适用于node_modules不存在的情况。

配方减轻了这种弱点:

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

如果yarn.lock不存在,或者已经过时 node_modules 或者package_json,我们将从头开始重新制作所有的构建文物。

那更好,但带来了一个引导带问题,当时两者都没有 人工制品yarn.locknode_modules存在,但node_modules - 就是 被填充为按产品制作yarn.lock - 也是yarn.lock必需

然而,这是一个微不足道的问题。 yarn.lock的先决条件仅仅是存在node_modulesyarn.lock之前,node_modules是令人满意的 node_modules: mkdir -p $@ 内容,最新 - 只需添加食谱:

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

从这个角度来看,你可能决定: -

<强>棒

脆弱的篡改检测优于无。我不想使用任何一个 比这更贵。

但你可能不会。更可能的替代方案:

<强>折叠

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

make clean

我认为不正确的篡改是我构建的超出范围。如果它发生,某些东西会破裂, 我会注意到它, yarn install 然后重试。

提高赌注

我想要强大的篡改检测。

强大的篡改检测使得提升相当重 - 但不会更重。 您需要强制清除node_modules,否则取决于结果 node_modules内容完整清单之间的旧与新比较 - 足够清楚地表明任何重大差异都会显现出来。

详细说明每个文件的路径名和修改时间的清单 make是最佳人选。此清单将包含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) 的信息 如果难以捉摸的主要文物,我们需要知道,并且会从文件系统中获取 这个版本可以拼写出来,并且相关的信息会发生变化 它最后记录的状态是重制一切的可靠触发器。因此:

Makefile(2)

node_modules

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

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

这相当于将更新修改的构建前导码 篡改检测测试的yarn.lock时间。然后 构建与以前一样,除了它的目标不再存在 $(LAST_MANIFEST),但是新的持久化清单yarn-install,始终是一个 立即发布 - yarn.lock快照,因此依赖于 { "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" }

锻炼Makefile(2)

lab-rat 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.17s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last

从头开始

$ make
make: Nothing to be done for 'all'.

不做任何改动并重拍

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

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

node_modules

触摸package.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

yarn.lock

触摸$ touch yarn.lock $ make find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last

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

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

中的相关性
$ 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

撤消更改

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

子目录中的现有文件
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

的子目录中
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

的子目录中删除文件
{{1}}

答案 1 :(得分:0)

借鉴Mike,我认为这足以满足我的需求:

BIN := node_modules/.bin
SRC_FILES := $(shell find src -name '*.js')
DIST_FILES := $(patsubst src/%,dist/%,$(SRC_FILES))
DIST_DIRS := $(sort $(dir $(DIST_FILES)))

# these are not files
.PHONY: all clean nuke

# disable default suffixes
.SUFFIXES:

all: $(DIST_FILES)

dist/%.js: src/%.js yarn.lock .babelrc | $(DIST_DIRS)
    $(BIN)/babel $< -o $@

$(DIST_DIRS):
    mkdir -p $@

yarn.lock: node_modules package.json
    @yarn install --production=false --check-files
    @touch -mr $(shell ls -Atd $? | head -1) $@

node_modules:
    mkdir -p $@

clean:
    rm -rf node_modules dist

nuke: clean
    rm -rf yarn.lock

我添加了

@touch -mr $(shell ls -Atd $? | head -1) $@

yarn.lock的修改时间更新为较新的node_modulespackage.json。当触摸这些内容但不会导致锁定文件更改时,这是必要的 - yarn install无法更新文件,这会导致yarn.lock永久过时。< / p>

--check-files会强制yarn installnode_modules/内实际重新安装任何丢失的软件包,但它不会进行深度搜索。因此,如果篡改包内的一些文件(而不是仅删除整个包),这种改变就不会被捕获。

巴贝尔的东西只是我如何使用它的一个例子。它将仅重新编译已更改的源文件。现在我只需要在一个刚刚克隆的项目中运行make,它将使用一个命令使所有内容保持最新。