makefile - 双向编解码器的 Makefile
问题描述
曾几何时,我编写了一个小编解码器来将 IPython Notebooks ( .ipynb
) 转换为“装饰”的 Python 代码,反之亦然。事实证明,这对于使用与笔记本对应的源代码非常有用。在我的团队中,我们使用它来将修饰过的 Python 源代码存储在我们的 git 存储库中(而不是有时巨大的.ipynb
文件)。我们可以区分笔记本(与其他笔记本或版本图),在我们最喜欢的 IDE 中重构它们等。我们通常在我们的代码库中有一些示例笔记本作为示例和文档的一部分(它们在我们构建时被水合并运行文档)。
语法是:
nb2py example.ipynb -o example.py
和
py2nb example.py -o example.ipynb
现在,我想设置一个Makefile
以更新所有文件的方式,.py
以使最后更改的文件形式占上风。.ipynb
例如,当我修改foo.py
, thenfoo.ipynb
将被更新以反映该更改。但是,如果我改为编辑foo.ipynb
(可能是通过 Jupyter),那么foo.py
将更新以反映该更改。
我有这个简单Makefile
的,它几乎可以满足我的要求:
.PHONY: clean update nb py
# py --> nb
PY_SRC = $(wildcard *.py)
NB_DST = $(PY_SRC:.py=.ipynb)
# nb --> py
NB_SRC = $(wildcard *.ipynb)
PY_DST = $(NB_SRC:.ipynb=.py)
%.ipynb: %.py
py2nb $< -o $@ && touch -r $< $@
%.py: %.ipynb
nb2py $< -o $@ && touch -r $< $@
clean:
tbd
update: nb py
nb: $(NB_DST)
py: $(PY_DST)
该touch -r
命令将目标文件设置为与源文件具有完全相同的时间戳,这样它就不会在一秒钟内再次更新make
,也不会更新回源文件。
我可以做到:make nb
它会更新所有.ipynb
比相应.py
. 我可以make py
走另一条路。然而,有很多喘不过气来make
,警告我整个设置的明显循环性。但它做正确的事。
update
目标不完全起作用。py --> nb
方向有效,但反之则不行。
最后,我不知道如何实现clean
。比如说,在我的上下文中,我想clean
删除任何.ipynb
不会更新相应内容的内容(因为不存在或因为它较旧)。.py
.py
解决方案
Make不是为这种事情而设计的。它不喜欢循环依赖。尝试使用纯 make 解决它可能会创建一个难以维护的 makefile。我会尝试创建一个工件文件,并使用 shell 逻辑进行复制,如下所示:
.%.updated: %.py %.ipynb
@if [ $*.py -nt $*.ipynb ]; then \
py2nb $*.py -o $*.ipynb; \
else \
nb2py $*.ipynb-o $*.py;
touch $@
当规则完成运行时,它将创建.foo.updated
比.py
or.ipynb
文件更新的 ,只要没有人修改这两个文件中的任何一个。一旦有人进行修改,它就会向正确的方向复制。
这假设.py
和.ipynb
文件都存在。如果情况并非如此,您可以添加一些仅订单依赖项来创建它们:
%.py : | %.ipynb
touch -d "1 second ago" $@; touch $^
%.ipynb: | %.py
touch -d "1 second ago" $@; touch $^
这些将使依赖项比目标更新,然后通过.%.updated
规则,这将在正确的方向上运行副本。