2015-01-31

Animes in the 4th quarter of 2014

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

どう見てもタツミが主人公な件。途中から敵側キャストも拡充されるが、余り盛り上がらずに終息。まあ「どちらかが死ぬ」という設定上、最後が尻すぼみになるのは必然とも言える。

前作は未見。頑張り屋さんのイイ話系かと思ったら、ヒロインが結構ヘタレなのでそうでもない。萌えられるキャラも居なかったが、短尺(15 分)なので見るのは楽だった。

前作からの視聴。相変わらず脚本は聞いている側が恥ずかしくなるレベル。最早ネタのために見てるといっても過言ではない。もちろん次回も見るつもり。

初見で期待してなかったが、やたらと女子キャラのリアクションが可愛い。特にひおたんが秀逸で、監督が苛めたくなる気持ちも分かる。これは是非続きをやってもらいたい。

これこそ私が見たかった正当な Fate な感。しかし主人公が理想を口にするだけのヘタレなことが分かって幻滅。Fate ルートは未視聴だが、見なくても大体予想がついた。多分シリーズでは Fate/Zero が一番ではあるまいか。

ゲーム原作なので全く期待してなかったが、見事に裏切ってくれた。話はもう一押し欲しいが、これだけ丁寧に作り込んでいることに好感が持てる。他のゲーム原作アニメも少しは見習え。

チョロいヒロインにツンデレ彼氏、少女マンガの典型パターン。男が見ても面白味はないが、イケメンなんかに負けないっ→イケメンには勝てなかったよ、に置き換えれば少しは楽しめるか。

何でも脚本が有名人とのことで注意深く視聴していたが、特に何も感じなかった。と言いながら最終話はちょっと涙出してたけど、年寄りは涙腺が弱いので当てにならない。

タイムループ物にしては最後を綺麗に纏めてきたような気もする。が、実は余り良く理解できていない。かといって復習する気もなし。元ネタがエロゲだとは後日知った。

これが噂の進撃(集英社版)ですか。最初は面白く見ていたのだが、火星に降りたくらいから飽きてきた。ゴキがバカっぽいのが原因だと思う。昆虫の勉強には良いのではないかと。

話の内容に言うことはないが、前期に続いて後期もいきなり終わり過ぎ。せめて区切りのある終わり方にしてくれ。劇場版が控えていることは理由にならない。

往年の SEGA 作品が見れて楽しいのだが、ゲーム紹介に留まっていて、当時を生きた老人としては全然足りない。ハードとしてのサターンは好きではなかったが、本サターン(SD キャラ)は可愛くて仕方がない。

主人公強い・ハーレム・ラッキースケベという定番パターンの詰め合わせ。取りあえず、「アーカイブに接続、テーマを実行する」とさえ言っておけば大体合ってる。

清々しい程の中二病に格好良ささえ感じる。各キャラも個性があり絵も綺麗、話も基本コミカルで安心して見れる。この雰囲気を崩さずに、是非第二期をやって欲しい。

魔法少女が不幸を背負うようになったのは、まどマギの罪だと思う。どう終わらせるのか注目していたら、理不尽なハッピーエンドになった。この評価は分かれそうだが、不幸になるよりはマシ。

最初は絵が綺麗なだけと思っていたが、話が進むにつれて各キャラがジワジワ来る。これが原作によるものか、京アニによるものかは不明。フルメタの作者だとは後日知った。

前作からの視聴。パッとしないキャラ揃えからヴィヴィがブレイクするかと思いきや、不発。話も安定の予定調和で終了。萌えられるキャラが居ればまた評価は違ったかも。

前作からの視聴。カムイの言うことがさっぱり分からない上、オカルト現象の種明かしも無し。同じ制作陣が作ったとは思えない。前作が良かったので期待が大きかった分、失望も大きい。

2014-10-31

Animes in the 3rd quarter of 2014

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

主人公が全能過ぎる。ブラコン妹も含めて、各所でネタにされるのも仕方がない。完全に好みが分かれる作品だが、中二病作品の完成形の一つとして、見ておいて損はない。

前作のアップテンポが落ち着いて、ようやく丁度よい感じになった。しかし原作未読で思い入れがないせいか、話が単調に感じる。勉強にはなるので続く限りは見る予定。

無印版を未見でも無問題そうだったので視聴したが、途中からひどい早送り状態。完全にファン向け追加ディスク的な内容だった。寧ろ、これを見たことで原作をやる気が失せた。

また良くあるハーレム作品か、と思っていたら、回を重ねるにつれて各キャラが可愛くなっていった。特にティアがお気に入り。製作者の思う壺な感じだが、素直に続きが見たい。

初話で「GJ 部」のフィギアを見た気がして、てっきり同じ作者だと勘違いした。見終わった後、やっぱりこれが同じ作者のはずがないよね、という結論に。

鉄オタならではの濃い話を見せてくれると思いきや、単なるラッキースケベのハーレム作品だった。そっちは間に合ってるんで、もっと本作品にしかできないことをやってください。

作品については、ほのぼの癒し系くらいしか言うことがない。「ろこどる」が初耳だったので、調べると命名はつんく♂か。「こ」はいったい何処から来たのか。

同性の立場からは主人公(男)にイライラする。ギャグ担当がヒロインくらいしか居ないので、普通に見るには退屈。ただ私的には村尾ちゃんがストライクだったので問題なし。

絶望的な戦力差を覆す主人公にやり過ぎ感を覚えるが、そこに目を瞑れば面白い。それ故に、最終話の早送りには閉口。こういうのを見せられると気持ちが冷めてしまう。

BL 物は今までも幾つか見てきたが、男の娘は初めてかも知れない。話の内容は特にどうということはないが、「シよっ」のセリフは破壊力あり。BL 物でこのセリフは反則だろ。

絵は綺麗で割と好みなのだが、話は主人公強い・モテるの普通のハーレム作品。女子キャラが気に入るかどうかが全て。私的には、青い風使いの人が良かった。

「喰霊-零-」の作者と聞いて。しかし期待に及ばなかった。恐らく「喰霊-零-」の時は、制作者(監督?)の力量による所が大きかったのではないかと推測。

とにかくやたら千代ちゃんが可愛い。今回は「小澤亜李」を覚えた。少女マンガ風なのにガンガン ONLINE と聞いて意外だったが、作者が女性だと知って納得。

「凪のあすから」にインスパイアされた作品か?と思ったら同じ会社とのこと。正直、話の結末には納得が行かないが、何かが心に残る作品ではあった。

話の内容よりも、三森すずこの唄に惚れた。私の中で彼女の株が急上昇。真面目な話、今まで聞いた声優の中で一番かも知れない。でも演技はもう少し頑張って。

基本この手の腐女子向けはスルーなのだが、パステル絵調が好みだったので視聴してみた。結果、やはり見なくても良かったという結論。

前作からの視聴だが、未だに良さが分からない。但し龍子だけは例外。お前はもっと暴れて私を笑わせてくれ。Fate の勉強として、今後も見るつもりではいる。

原作は未読。意外にも、一番つまらないと予想していたアリ編が一番面白かった。但しここがピークだと思うので、どうぞもう作者様はゆっくりお休みください。

今期の一番。「ハートフル」のキャッチコピーは伊達じゃなかった。基本賑やかコメディーで、見ていて全く退屈しない。是非とも続編を希望。ところで「なる」が男だと思っていたのは私だけではないはず。

話はなかなか面白いが、途中で強敵が現れる辺りから雰囲気が怪しくなる。相手が悪役ではなく良きライバルだったなら、もっと盛り上がったのではないかと思う。

2014-09-30

Apache httpd: /bin/sh: /apr-1/build/libtool: No such file or directory

いつも通り Apache httpd を make すると、エラーが発生。

# make
make[1]: Entering directory `/usr/local/src/httpd-2.2.24'
Making all in srclib
make[2]: Entering directory `/usr/local/src/httpd-2.2.24/srclib'
Making all in pcre
make[3]: Entering directory `/usr/local/src/httpd-2.2.24/srclib/pcre'
make[4]: Entering directory `/usr/local/src/httpd-2.2.24/srclib/pcre'
/apr-1/build/libtool --silent --mode=compile gcc -pthread        -I/usr/local/src/httpd-2.2.24/srclib/pcre -I. -I/usr/local/src/httpd-2.2.24/os/unix -I/usr/local/src/httpd-2.2.24/server/mpm/prefork -I/usr/local/src/httpd-2.2.24/modules/http -I/usr/local/src/httpd-2.2.24/modules/filters -I/usr/local/src/httpd-2.2.24/modules/proxy -I/usr/local/src/httpd-2.2.24/include -I/usr/local/src/httpd-2.2.24/modules/generators -I/usr/local/src/httpd-2.2.24/modules/mappers -I/usr/local/src/httpd-2.2.24/modules/database -I/usr/include/apr-1 -I/usr/local/src/httpd-2.2.24/modules/proxy/../generators -I/usr/local/src/httpd-2.2.24/modules/ssl -I/usr/local/src/httpd-2.2.24/modules/dav/main   -c maketables.c && touch maketables.lo
/bin/sh: /apr-1/build/libtool: No such file or directory
make[4]: *** [maketables.lo] Error 127
make[4]: Leaving directory `/usr/local/src/httpd-2.2.24/srclib/pcre'
make[3]: *** [all-recursive] Error 1
make[3]: Leaving directory `/usr/local/src/httpd-2.2.24/srclib/pcre'
make[2]: *** [all-recursive] Error 1
make[2]: Leaving directory `/usr/local/src/httpd-2.2.24/srclib'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/usr/local/src/httpd-2.2.24'
make: *** [install-build] Error 2

よく見ると、configure 時にも同様のログが出ていた。

Applying OS-specific hints for httpd ...

./configure: line 5774: /apr-1/build/libtool: No such file or directory
  forcing SINGLE_LISTEN_UNSERIALIZED_ACCEPT to "1"
  forcing AP_NONBLOCK_WHEN_MULTI_LISTEN to "1"

確かにいつもと違ってこのシステムには apr パッケージが入っているが、一体 /apr-1/build/libtool とはどこから来たのか。configure スクリプトを追いかけると、apr-1-config --apr-libtool の出力に辿り着いた。

# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 6.5 (Santiago)

# type apr-1-config
apr-1-config is hashed (/usr/bin/apr-1-config)

# apr-1-config --version
1.3.9

# apr-1-config --apr-libtool
/apr-1/build/libtool

私には apr-1-config と configure スクリプトのどちらが間違っているのかは分からない。しかしどちらであろうと、動かないシステムに価値はない。

結論: --with-included-apr が正義。

今後も余計なトラブルに逢いたくなければ、常にこのオプションを付けておくのが良さそうだ。普通、パッケージ提供バイナリよりも自分でビルドした方がパフォーマンスも良くなるはずだし。

もしかすると最新版の httpd では対処済みかも知れないが、面倒いので未確認。

2014-07-31

Animes in the 2nd quarter of 2014

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

殺し屋が云々って前振りで始まる割に、結局誰も死んで無くね?という。毎回ある謎掛けも一体何だったのか。結局、最後まで何がやりたかったのか良く分からなかった。

原作は途中から既読。話数が足りずに描写不足だし(話が抜けているっぽい)、原画にあったキャラの魅力もアニメ絵で失われている。結論として、原作を呼んだ方が面白い。

正直、前期の方が楽しめた。あと、この作品に限ったことでは無いが、モーションキャプチャー CG をそのまま使うのは、アニメとしては手抜きだと思う。

最近では珍しいエロスの方向性。個人的にはアリ。ただ途中の主人公差し替えが水を差した。リッツは最初は何とも思わなかったが、後からジワジワ可愛くなってくる。

絵的に期待してなかったが、先出二名の女子キャラには妙な魅力がある。主人公が欲望に素直なのも良い。個人的にはベタだと言われようが「みはり」ちゃん推し。

女子キャラ多過ぎ、主人公モテ過ぎ、終盤の展開早過ぎ。最後は全くついていけなかった。何か無理やり終わらせた感じを受ける。ただ一つ、「がをがをー」だけは認める。

記憶消去作品は今までに何度か見てきたが、特定日に消去というのは初体験。基本いい話系だが、途中で少し陰鬱な感じになるのと、主人公(男)がヘタレなのが頂けない。

前作からの視聴だが、前作程のインパクトがない。新キャラ中心で話が進むが、その新キャラ勢に狂三みたいな勢いがない。その狂三も今作ではいい子ちゃんでつまらない。

主人公たち自身がチートなことに目を瞑れば、独特な絵調もあって普通に楽しめた。ただ人によっては、受け付けないことも多そう。最近は日笠陽子の演技に感心することが多い。

話は典型的な少女マンガなので意外性はない。この手の作品を見ると、やっぱり女子もツンデレが好きなんだなと思う。そして、「但し二次元に限る」という所まで同じ。

前作からの視聴。相変わらず独特の雰囲気があり、他作品とは一線を画している。続きがある限り見ていくつもりだが、何故あんな中途半端で終わったし。

地味ながら丁寧に作られている感はある。ただ、私にはヒロインに萌えるのは無理だった。サバター兄妹も活躍が地味だし、全てにおいて何かが足りない気がする。

スポコン作品としては安定。しかし気持ち重視の作品なので、納得できない部分も多い。個人的に「かもめ☆チャンス」と比べてしまうので、どうしてもそういう評価になる。

基本 CG アニメは認めない派だが、本作に限れば CG が原画にマッチしていて全くアリだった。話も独特で面白いし、次回も(あるなら)期待している。

原作のゲームは知らないが、取りあえずメディアミックスでアニメにしました、という感じが半端ない。少なくとも、これを見てゲームをやる気は起きない。

絵を見たときは不安しか無かったが、良く考えればこの絵でアニメ化するのに話が面白くない訳がなかった。アニメを見て原作を読みたくなった作品は久しぶり。

作品の雰囲気に似合わず財宝トラップがエグい。話も終盤は陰鬱になるし、宝探しはもっと楽しくあるべき。「ファイ・ブレイン」と同じ過ちを犯している気がする。

2014-07-01

Emacs 23: Fix font-lock colors for 8 colors terminal

例えば RHEL6 で、SSH + screen + Emacs 23 とかいう組み合わせで使うと、Emacs の色付けが上手くいかない。具体的には、

  1. ソースコードのコメントに色が付かない
  2. Bash スクリプトの関数名に色が付かない

8 色ターミナル + Emacs 23 + 黒背景という組み合わせだとこうなる。最初は Emacs 23 からの仕様かとも思ったが、そんなはずはなかった。正直(2)はどうでも良いが、(1)は冗談抜きで生産性に直結するので見過ごせない。

;;; font-lock settings for 8 colors terminal
(when (require 'font-lock nil t)
  ;; fix comment color in dark background
  (and (>= emacs-major-version 22)
       (eq 'unspecified (face-attribute 'font-lock-comment-face :foreground))
       (set-face-attribute 'font-lock-comment-face nil
			   :foreground (face-attribute
					'font-lock-comment-delimiter-face
					:foreground)))
  ;; fix function symbol color in Bash script
  (when (= emacs-major-version 23)
    (eval-after-load "sh-script"
      '(let ((tail (cddr (assq 'bash sh-font-lock-keywords-var))))
         (and (eq 'shell (car tail))
	      (setcar tail 'sh)))))
  (global-font-lock-mode 1))

どちらも Emacs 24 では直ってるっぽいが、オールド世代への扱いが不憫過ぎて泣ける。8 色ターミナルで Emacs 黒背景とかもう誰も使ってないですか、そーですか。

2014-04-30

Animes in the 1st quarter of 2014

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

ヒーローごっこで通すと思いきや、途中から想定外の展開に。以降、安っぽくなった感がある。彼女さんには最後のどんでん返しを期待したけど不発。ただ瑞希は良い女で惚れそう。ミネミラの歌はどれも良かった。

エロスはともかく、肝心のヒロインと主人公に魅力を感じない。むしろサブキャラの方が良い味を出している。主に紗矢華とか紗矢華とか、王女も結構いける。

陰陽道物としてはオリジナリティーを感じる。しかし全体的にキャラの魅力がイマイチ、話も最後の件でご都合主義に見える。ただ鈴鹿はツボったので次回があれば見ると思う。

初見で「あの夏で待ってる」との既視感が半端ない。突っ込みたい部分も色々と有るが、結果的には名作になったと思う。耐性のない厨房が見たら妙な病気に罹らないか心配になるレベル(褒め言葉)。

典型的な気持ちだけ主人公なので見ててイライラする。メカも話も特に魅力を感じないし、これ以上面白くなりそうにないので後期があっても見ない予感。

キャラも粒ぞろいで嫌いではないが、もう二押しくらい足りない。ちなみに喫煙者は第三話だけでも見るべき。非喫煙者から普段どう思われているか知っておいて損は無い。てか喫煙者に対する扱いが私と同じ過ぎて笑った。

ヒロインに萌えられず、主人公も空気。結果、たんぽぽちゃん一人が際立つ結果に。声もハマってるし、もうこの娘がヒロインでいいわ。むしろ後半のシリアス部は要らない。

絵は好みじゃ無いが、まさかのパロディー・下ネタ満載で良い意味で期待を裏切ってくれた。ただその反面、元アイドルという設定が殆ど活かせてない気はする。

OP が神。後は内容が OP にどれだけ近づけるかだったが、流石にそれは難しかった模様。個人的にはアタリの作品だったが、最終二話の作画だけは許せない。

前作までの面白さが感じられない。今回ばかりは話数に対してキャラが多過ぎたように思う。個人的に好きなシリーズなので、やるなら時間を掛けてゆっくりやって欲しい。

エロが売りのはずだが、何故かエロスが感じられない。胸タプ音に拘りが感じられなくもないが、キャラに萌えられないのが問題。唯一萌えられる姫神にはおっぱい無いし。

元ネタを知らないので特に思い入れも無いが、見るのが苦痛で無かったことは良かった。逆に言うと、見ても見なくても良い消費型作品の典型例。

2014-03-31

Python: Portablize Sphinx

そろそろ、以前から気になっていた Sphinx を触ってみようかと。スタンドアロン版ソフトウェアをこよなく愛す私は、当然のようにスタンドアロン版を選択。(以降、スタンドアロン版インストーラー SphinxInstaller-1.2.20131210-py2.7-win32.zip を使用)

しかし使ってみて、コレジャナイ・・・orz。うん、私が悪かった。「スタンドアロン」ではなくて、「ポータブル」と言うべきだった。上記はスタンドアロンではあるが、ポータブルではない。具体的には、インストールしたディレクトリを別の場所に移動すると動かなくなる。これでは zip で固めてメンバーへ配布、という訳にはいかない。

調べると、bin/sphinx-*-script.py 中に絶対パスが記述されているのが原因のようだ。特に shebang 行がまずい。shebang は bin/sphinx-*.exe が bin/sphinx-*-script.py を実行するために使用するのだと思うが、shebang は絶対パスでないと動かない。

ここで諦めようとも思ったが、何か悔しいので「起動時に毎回 sphinx-*-script.py を書き換える」という力技に出てみた。

$SPHINX_HOME/sphinx.bat:

@echo off
setlocal

set _dp0=%~dp0
set SPHINX_HOME=%_dp0:~0,-1%
set _dp0=
set PYTHON_HOME=%SPHINX_HOME%\python
set PATH=%PATH%;%SPHINX_HOME%\bin;%PYTHON_HOME%;%PYTHON_HOME%\Scripts

python.exe "%SPHINX_HOME%\bin\setup-portable.py" -x >NUL
if errorlevel 1 (
  echo ERROR: failed to setup
  pause
  exit 1
)
title PortableSphinx
echo *** Welcome to PortableSphinx! ***
echo.
cmd.exe /k

$SPHINX_HOME/bin/setup-portable.py:

#!python.exe

__doc__ = """\
Usage:
  python %s -x
Description:
  Setup PortableSphinx.  This should be executed first whenever the
  Sphinx directory is moved on your PC.
""" % __file__

import filecmp
import glob
import os
import re
import shutil
import sys
import tempfile

_sphinx_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

def _create_tempfile(lines=None):
    path = ''
    try:
        with tempfile.NamedTemporaryFile(delete=False) as fh:
            path = fh.name
            if lines:
                fh.writelines(lines)
        return path
    except StandardError:
        _remove_file(path)
        raise

def _remove_file(path):
    try:
        os.remove(path)
    except StandardError:
        pass

def _read_file(path):
    with open(path) as fh:
        return fh.readlines()

def _write_file(path, lines):
    tmp_path = _create_tempfile(lines)
    try:
        if not filecmp.cmp(tmp_path, path):
            shutil.copyfile(tmp_path, path)
    finally:
        _remove_file(tmp_path)

def _convert_script(lines):
    lines = iter(lines)
    for line in lines:
        yield '#!"%s"\n' % os.path.join(_sphinx_home, 'python', 'python.exe')
        break
    sphinx_home = _sphinx_home.replace('\\', '\\\\\\\\')
    re_library = re.compile(r"^(\s*').+?(?=\\\\(eggs|python)\\\\)", re.I)
    for line in lines:
        yield re_library.sub(r'\1' + sphinx_home, line)

def _setup_bin():
    script_glob = os.path.join(_sphinx_home, 'bin', 'sphinx-*-script.py')
    script_paths = glob.glob(script_glob)
    if not script_paths:
        raise RuntimeError('not found: %s' % script_glob)
    for script_path in script_paths:
        lines = _read_file(script_path)
        _write_file(script_path, _convert_script(lines))

def main():
    status = 1
    args = sys.argv[1:]
    if args and args[0] == '-x':
        _setup_bin()
        print 'OK'
        status = 0
    else:
        print __doc__,
    return status

if __name__ == '__main__':
    sys.exit(main())

スタンドアロン版 Sphinx のインストール先に上記を突っ込んで、毎回 sphinx.bat から起動するようにすれば、擬似 PortableSphinx の出来上がり。後はディレクトリを zip で固めればそのままプロジェクトメンバーに配布できる。今のところ、私が使う範囲で不具合は出ていない。(不具合がないとは言っていない)

さて、実際に Sphinx を使ってみると、確かにこれはなかなか良い。懸念していた日本語ファイル名(CP932)も問題ないし、印刷時の改頁も(組み込み機能にはないものの)制御可能。簡単なブロック図であれば Sphinx 上で生成できる。正に、至れり尽くせり。うん、きっとこれはハヤル。(実際にはずっと前から流行っている)

Sphinx の「ソースを編集 → make → 動作確認」という流れは、昨今のプログラマーには受け入れ難いだろうが、古き良き時代のプログラム開発みたいで老年プログラマーには逆に心地良い。私も最初はウキウキしながら make していたが、デザイン調整などで CSS を書き換える度に make するのは流石に面倒になってきた。人間って本当に我がままだと思う。

ということで、(誰もが考え付くであろう)変更を自動で検知して make してみる。

$SPHINX_HOME/bin/makebot.bat:

@echo off
setlocal

:loop
echo [%~n0] watching... press Ctrl-C to abort
python.exe "%~dp0notify-changed.py" .
if errorlevel 1 exit /b 1
echo [%~n0] detected
call make clean && call make html
echo.
goto loop

$SPHINX_HOME/bin/notify-changed.py:

#!python.exe

__doc__ = """\
Usage:
  python %s DIRECTORY
Description:
  Watch and wait for changes in a DIRECTORY and ends with status 0.
""" % __file__

import fnmatch
import os
import sys
import time

_include_patterns = ('*.conf', '*.css', '*.css_t', '*.html', '*.py', '*.rst')
_exclude_patterns = ('_*',)

def _fnmatch_any(name, patterns):
    for pattern in patterns:
        if fnmatch.fnmatch(name, pattern):
            return True
    return False

def _filter_name(names, includes=None, excludes=None):
    if includes:
        names = [x for x in names if _fnmatch_any(x, includes)]
    if excludes:
        names = [x for x in names if not _fnmatch_any(x, excludes)]
    return names

def _filter_dir(names):
    return _filter_name(names, excludes=_exclude_patterns)

def _filter_file(names):
    return _filter_name(names,
                        includes=_include_patterns,
                        excludes=_exclude_patterns)

def _find_files(path):
    for dirpath, dirnames, filenames in os.walk(path):
        dirnames[:] = _filter_dir(dirnames)
        for filename in _filter_file(filenames):
            yield os.path.join(dirpath, filename)

def _iter_filestats(path):
    for filepath in _find_files(path):
        yield filepath, int(os.stat(filepath).st_mtime)

def _diff_filestats(before, after):
    for filepath, mtime in after.iteritems():
        if mtime != before.get(filepath, 0):
            return True
    return False

def _watch_directory(path, interval=1):
    if not os.path.isdir(path):
        return "ERROR: directory not found: `%s'" % path
    before = dict(_iter_filestats(path))
    while True:
        time.sleep(interval)
        after = dict(_iter_filestats(path))
        if _diff_filestats(before, after):
            break
        before = after
    return 0

def main():
    status = 1
    args = sys.argv[1:]
    if args:
        try:
            status = _watch_directory(args[0])
        except KeyboardInterrupt:
            pass
    else:
        print __doc__,
    return status

if __name__ == '__main__':
    sys.exit(main())

pywin32 辺りを使えばファイル変更を検知する API が使えたりするのかも知れないが、生憎 Sphinx には pywin32 が添付されていないので、力技で実装。数千くらいのファイルなら問題ないはず(数万とかは知らない)。後は Ctrl-C で終了すると「バッチ ジョブを終了しますか (Y/N)?」とか聞かれるのがウザいが、これは Windows BAT のクソ仕様の問題だし、基本 makebot しっ放しなので我慢する。

取りあえず、これで私の Sphinx on Windows 環境は幸せになった。