自己写 Makefile
作为一个 Emacs 用户,得用 Org Mode 做标记语言,其实通过配置 org-publish 应该也能实现的。但我不大会 Elisp,也不熟悉 Org Mode,所以要找其他工具来做发布的任务。我发现了一个叫做 blorgit 的博客系统,跟着教程一步步做,默认页面不怎么样。而且它是 Ruby 写的,我配置不来,于是这个也放弃了。最后选择了用 Makefile 自己写。基本思路
.org 存放实际写的文章,用于生成 .html,首页 index.html 是文章列表。还要使用第三方的评论系统。设计
目录设计如下: - 创建目录 2011/07/12/ 来表示当天写的文章(当前还有其他日期,这里使用 2011/07/12/ 只是为了方便阐述) - 2011/07/12/ 下创建 blogging-with-makefile.org ,用于生成 blogging-with-makefile.html - 2011/ 下创建 titles-07 缓存2011年7月所有文章的标题文章部分就用如上形式储存。所有文章都有共用的页头、页脚(我不知道准确的术语),比如本页上方的的链接和下方 disqus 的评论系统,它们分别储存为 header.org 和 footer.org 。当要把某篇文章的 .org 导出为 .html 时,在前面包含 header.org ,后面包含 footer.org 。 disqus 要求每个网页要包含几个特定的变量,所以 footer.org 需要做下修改再包含。
首页是所有文章的列表,需要用脚本生成出来,还要注意的是它不需要评论系统,所以不包含 footer.org ,
Makefile
Makefile 的主要规则如下:- 2011/titles-07 依赖 2011/07/ ,如果该目标过期(需要重建), 那么尝试重建 2011/07/12/ 下所有日期的所有 .html 。然后把这些 .html 的标题抽取出来, 写入 2011/titles-07
- %.html 依赖 %.org 以及页头页脚,需要用 Org Mode 根据 .org 生成 .html。 这个规则也对首页 index.org 有效。
注意到这种设计把目录作为了依赖,我们要知道目录的修改时间是不会因为被它包含的文件的修改而改变的。所以当 2011/07/12/blogging-with-makefile.org 修改之后, 2011/07/12/ 和 年/月/ 的修改时间都是不会变化的。这样就会导致 2011/07/12/blogging-with-makefile.org 修改后,执行 make 时 2011/07/12/blogging-with-makefile.org 不会得到更新。
为了处理这种情况,我们需要让目录随着被它包含的文件的修改而更新修改时间。我想到的办法是用 inotify,当 2011/07/12/blogging-with-makefile.org 修改之后,自动 touch 年/月/ 。
from http://maskray.me/blog/2011-07-12-blogging-with-makefile
----------
前一篇用Makefile搭建博客说到我用
inotifywait
监控目录下文件的写操作来更新目录的修改时间,以后只要把make inotify
放到启动脚本中就行了。但当时就发现了一个问题,一直拖到今天才解决掉。GoboLinux Scripts包中的ColorMake
先从/ColorMake/说起。/GNU Make/的颜色是很单调的,我一般用GoboLinux的包Scripts里的ColorMake
来给make
上色。而这个ColorMake
实际上是写了一个mtail
的配置文件ColorMake.mtailrc
,把make
的输出管道给mtail
来上色。可以参看。我之前则是用的http://bre.klaki.net/programs/colormake/里的colormake.pl
脚本来上色的。我把
ColorMake.mtailrc
保存为 ~/bin/ColorMake.mtailrc ,另外写了个 wrapper,保存到 ~/bin/mk ,内容如下:#!/bin/sh
/usr/bin/make "$@" 2>&1 | mtail -q --config ~/bin/ColorMake.mtailrc
意思就是把mk
的参数全部传递给make
,make
的 stdout stderr 全部管道给mtail
来上色。另外, ~/bin 在我的环境变量
PATH
中。产生问题的命令
先来看我的 [[/Makefile][Makefile]],只要看 inotify 伪目标,其他的可以忽略掉。之前 inotify 的规则是 inotifywait -e modify -m -r . –format %w | xargs -I % sh -c “touch `dirname %`” &make inotify 运行得非常正常,
inotify
和xargs
在后台执行;但如果执行mk inotify
,问题就来了,终端会被占着,无法再执行其他命令了。分析
make inotify
先来看执行make inotify
会发生什么,不妨假设交互用的shell是Zsh,这里用Bash效果也是一样的。-
zsh
进程产生一个make
进程 -make
进程执行重建 inotify 的规则,即产生一个进程执行inotifywait -e modify -m -r . --format %w | xargs -I % sh -c "touch \`dirname %\`" &
不妨用 /Lisp/ 来表示进程树,那么这些进程的关系如下: (zsh (make (inotifywait) (xargs))) 。 一对圆括号代表进程,圆括号第一个元素是进程名,其余元素代表子进程。
接着,
-
make
退出,因为规则执行完了 -zsh
检测到它的子进程make
退出,又可以执行其他命令了
mk inotify
-zsh
进程产生mk
,其实是用/bin/sh
解释mk
,这里就简写成mk
-mk
产生make
和mtail
,其中管道的一端是make
的fd1、fd2,另一端是mtail
的fd0 -make
产生inotifywait
和xargs
第三步中,
make
的文件描述符被inotifywait
和xargs
继承, 由于inotifywait
和xargs
用另一根管道而把 fd1 关闭了, 所以现在原管道的两端分别是:-
make
的fd1、fd2;inotifywait
的fd2;xargs
的fd1、fd2 -mtail
的fd0现在的进程树是: (zsh (mk (make (inotifywait) (xargs)) (mtail)))
接着,
make
退出,因为规则执行完了- 因为管道的写端描述符没有全部关闭,
mtail
不会读到EOF退出,而是等待管道读端的数据 mk
也不会退出,因为它的某个子进程mtail
没有退出zsh
未检测到mk
的退出,所以终端被占用了,没法执行其他命令
解决方案
由前面的分析可以看出,只要让mtail
退出,那么mk
会跟着退出,终端就不会被占用了。 而要让mtail
退出,就要让它读到 eof 退出,我们只要让make
产生进程时不要把管道的写端描述符 传递给inotifywait
和xargs
,但是这个据我所知是做不到的。 但我们可以让inotifywait
和xargs
立刻把相应写端描述符关闭, 这个很简单,用1
| inotifywait -e modify -m -r . --format %w 2>&- | xargs -I % sh -c "touch \`dirname %\`" >&- 2>&- &
|
from http://maskray.me/blog/2011-07-19-blogging-with-makefile-2