首页 > 解决方案 > 双向编解码器的 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

标签: makefile

解决方案


Make不是为这种事情而设计的。它不喜欢循环依赖。尝试使用纯 make 解决它可能会创建一个难以维护的 makefile。我会尝试创建一个工件文件,并使用 shell 逻辑进行复制,如下所示:

.%.updated: %.py %.ipynb
    @if [ $*.py -nt $*.ipynb ]; then \
       py2nb $*.py -o $*.ipynb; \
    else \
       nb2py $*.ipynb-o $*.py;
    touch $@

当规则完成运行时,它将创建.foo.updated.pyor.ipynb文件更新的 ,只要没有人修改这两个文件中的任何一个。一旦有人进行修改,它就会向正确的方向复制。

这假设.py.ipynb文件都存在。如果情况并非如此,您可以添加一些仅订单依赖项来创建它们:

%.py : | %.ipynb
    touch -d "1 second ago" $@; touch $^

%.ipynb: | %.py
    touch -d "1 second ago" $@; touch $^

这些将使依赖项比目标更新,然后通过.%.updated规则,这将在正确的方向上运行副本。


推荐阅读