autom4te
アップルパイ
リンゴが2個100円とかで手に入る。規格品以外の跳ね出しだから安い。大体は美味しく食べられるんだけど、中には、味がボケてて、食べる気がしないのもある。そういうのは、100%果汁ジュースにしちゃうんだけど、たまにはアップルパイに挑戦すると女房が言い出した。
パイ生地は、パンプキンパイ用に常備してる。かぼちゃもホクホクじゃないと、スープになったり、パイにしたりしてるからね。
で、アップルパイ用に、レモンとブランデーを買いに行かされましたよ。調理用ってかケーキ用に売ってるのね、知らなかった。
初めてにしては上出来。また、作ってね。
C語のマクロ
rubyのソースrandom.cを見ていたら
#if ! defined HAVE_GETRANDOM && defined __linux__ && defined __NR_getrandom # ifndef GRND_NONBLOCK # define GRND_NONBLOCK 0x0001 /* not defined in musl libc */ # endif
こんな書きかたをしたマクロが出てきた。何が目新しいかと言うと、# ifndefとか# defineみたいに、間に、空白を入れてる事。今迄は、ぴったりくっつけるものとばかり思ってた。 ネストを表現する為に、字下げしてるのね。
それもこれも、プリプロセッサーが良きに図らってくれるから。そもそも、プリプロセッサーが働くと#な行は、取り除かれてしまうからね。また、#が存在する行だけに注目すればいいので、解析が楽になるって事もあるな。
そもそも、# を、コメント記号に決めたのは、世界最古のインタープリタshell scriptに違いない。だから、OSにも取入れられている(#! /usr/bin/ruby みたいな奴)。 この流れを汲んでいるんだな。設計はあの人だしね。
で、こういう風になってるやつの、#if … #elif … #endif みたいなのの構造を追跡しようとすると、ちと面倒。
sakae@deb:/tmp$ grep -n '#' random.c | sed -E -e 's/# +/#/' | egrep '(#if|#el|#end)' : 478:#if 0 479:#elif defined MAC_OS_X_VERSION_10_7 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 488:#if 0 494:#endif 499:#elif defined(HAVE_ARC4RANDOM_BUF) 503:#if (defined(__OpenBSD__) && OpenBSD >= 201411) || \ 508:#else 510:#endif 512:#elif defined(_WIN32) 550:#elif defined HAVE_GETRANDOM 573:#else 575:#endif
emacsで簡単に追跡出来無いものかしら。
autoconf
autoconfした時に、どのように動くか追ってみる
vbox$ sh -x /usr/local/bin/autoconf-2.69 : + test -f configure.ac + test -f configure.in + infile=configure.ac + test -z + test -z + outfile=configure + test -z configure + eval set x --language=autoconf --output="$outfile" "$infile" + set x --language=autoconf --output=configure configure.ac + shift + false + exec /usr/local/bin/autom4te-2.69 --language=autoconf --output=configure configure.ac
省略した部分では、随分と色々なチェックをしてたぞ。
vbox$ sh -x /usr/local/bin/autom4te-2.69 --language=autoconf --output=configure configure.ac + eval case $# in 0) exec /usr/bin/perl -S "$0";; *) exec /usr/bin/perl -S "$0" "$@";; esac + exec /usr/bin/perl -S /usr/local/bin/autom4te-2.69 --language=autoconf --output=configure configure.ac
最後はperlさんの登場だ。もうすっかり忘れているぞ。 取り敢えず、下記でも動く事を確認。
vbox$ autom4te-2.69 -l autoconf -o zzz configure.ac
トレースしたいな。 pkg_add p5-Devel-Trace で、トレーサーを導入。
vbox$ perl -d:Trace -S autom4te-2.69 -l autoconf -o zzz configure.ac
トレースログは3万5千行にもなった。少しソース読んで、perldebug - Perl のデバッグの世話になった方がいいかな?
perl 一夜漬
perlなんて、オイラー的には20年以上も前の言語だ。今更思い出せってか? そんなの一夜漬でいいだろう。
とほほのperl入門 さんはいまだに健在。何かの折に必要になるからね。
ちょいとマニュアルを引くなら
sakae@deb:~$ perldoc -f push
かな。それから、ドットで文字列が結合される。 $_ が暗黙的に使われる。ifは後置出来るぐらいかなあ。
FreeBSDは長らくperl依存症にかかって苦しんでいたけど、努力のかいがあって抜け出せた。OpenBSDは、 pkg_add が利用してて、ずっとperlに依存。動いているならそれでいいじゃんって態度。自分たちでメンテしてるから、どうにでもなるって思ってる節がある。
腰が座らないのはリナ。流行のpythonがディストリの中心になってるけど、最新に追従するのは辛いだろうよ。FreeBSDはそれがいやでperlをOSから排除したんだった。ユーザーは自衛策で、どんどんOS添付のpythonを見切っているね。
まあ、どうでもいいけど。
本棚をのぞいたら、ほこりを被ったperlポケット・リファレンスなんてのが出てきた。んで、パラパラしてたら、デバッガーの説明が載ってたよ。
sakae@pen:/tmp/t$ perl -d autom4te -l autoconf -o zz configure.ac Loading DB routines from perl5db.pl version 1.57 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. main::(autom4te:5): eval 'case $# in 0) exec /usr/bin/perl -S "$0";; *) exec /usr/bin/perl -S "$0" "$@";; esac' main::(autom4te:6): if 0; DB<1> S main main::BEGIN main::__ANON__[autom4te:362] main::files_to_options main::freeze main::handle_m4 main::handle_output main::handle_traces main::load_configuration main::parse_args main::trace_format_to_m4 main::up_to_date main::warn_forbidden
こんな風に定義されてるサブルーチンを確認出来るとな。これイカシてるな。
DB<2> b main::parse_args
DB<3> L
autom4te:
308: my @language;
break if (1)
ブレークポイントを折角名前で指定したのに、リストをみると消えてる。複数設定したら、ワケワカメ必定だ。
DB<3> c
main::parse_args(autom4te:308): my @language;
DB<3> n
main::parse_args(autom4te:309): do {
DB<3> v
306 # definitions. Beware that there can be several --language
307 # invocations.
308:b my @language;
309==> do {
310: @language = ();
311: use Getopt::Long;
312: Getopt::Long::Configure ("pass_through", "permute");
313: GetOptions ("l|language=s" => \@language);
314
315: foreach (@language)
DB<3> n
main::parse_args(autom4te:310): @language = ();
DB<3>
main::parse_args(autom4te:312): Getopt::Long::Configure ("pass_through", "permute");
DB<3>
main::parse_args(autom4te:313): GetOptions ("l|language=s" => \@language);
DB<3>
main::parse_args(autom4te:315): foreach (@language)
main::parse_args(autom4te:316): {
main::parse_args(autom4te:317): error "unknown language: $_"
DB<3> x @language
0 'autoconf'
なんか、日が暮れてしまいそう。
サブルーチンから戻るには、 r すれば良いのは、見付けた。じゃ、下記のようにmapでグダグダやってる場合とか、foreachのループを素早く抜け出すには?
main::(te:95): my @m4_builtin = `echo dumpdef | $m4 2>&1 >/dev/null`;
DB<1>
main::(te:96): map { s/:.*//;s/\W// } @m4_builtin;
DB<1>
main::(te:96): map { s/:.*//;s/\W// } @m4_builtin;
DB<1> c 120
main::(te:120): my %m4_builtin_alternate_name;
main::(te:121): @m4_builtin_alternate_name{"$_", "m4_$_"} = ("m4_$_", "$_")
裏技っぽいけど、c n行 してやればいい。これで、抜けてくれる。なお、抜けて止るべき行は、実行可能な行のみが許されている。v を叩いて、実行文を探そう。長いコメントがあったりすると辟易する事間違いなし。
autom4te -v
ってな事で、マニュアルを眺めていたら、おしゃべりモードを発見。
vbox$ ./te -v -l autoconf -o zz configure.ac >LOG 2>&1
おしゃべりが過ぎるので、取り敢えず、録音した。ああteてやつは、/usr/local/bin/autom4te を手元に持ってきたものだ。
te: the trace request object is:
te: $VAR1 = bless( [
te: '0',
te: 0,
te: [
te: '/usr/local/share/autoconf-2.69'
te: ],
te: [
te: '/usr/local/share/autoconf-2.69/m4sugar/m4sugar.m4',
te: '/usr/local/share/autoconf-2.69/m4sugar/m4sh.m4',
te: '/usr/local/share/autoconf-2.69/autoconf/autoconf.m4',
te: 'configure.ac'
te: ],
te: {
te: 'AC_CANONICAL_TARGET' => 1,
te: '_m4_warn' => 1,
:
te: 'AC_CANONICAL_HOST' => 1
te: }
te: ], 'Autom4te::Request' );
LOGの中身の冒頭部分。なにか、これらのファイルから必要そうなデータを取り出してきましたよって風だ。そして次にあったのは、m4を起動してるやつだ。長いので、分かり易いように整理してみた。(一行野郎なんだけど、分かり易いように行に分解)
/usr/bin/m4 -g -D__gnu__ -I/usr/local/share/autoconf-2.69 -daflq -oautom4te.cache/traces.0t -tAC_CANONICAL_BUILD -tAC_CANONICAL_HOST : -tm4_sinclude -tsinclude /usr/local/share/autoconf-2.69/m4sugar/m4sugar.m4 /usr/local/share/autoconf-2.69/m4sugar/m4sh.m4 /usr/local/share/autoconf-2.69/autoconf/autoconf.m4 configure.ac > autom4te.cache/output.0t
m4sugar.m4やらconfigure.acを材料に変換してね。結果はキャッシュの中のoutput.0に。 指定した複数のマクロを追跡しながら、trace.0へ入れろとな。
traces.0
m4trace:/usr/local/share/autoconf-2.69/m4sugar/m4sugar.m4:2878: -1- m4_sinclude([m4sugar/version.m4]) m4trace:/usr/local/share/autoconf-2.69/m4sugar/m4sugar.m4:2878: -1- sinclude([m4sugar/version.m4]) : m4trace:configure.ac:4: -1- AC_SUBST_TRACE([PACKAGE_VERSION]) m4trace:configure.ac:4: -1- m4_pattern_allow([^PACKAGE_VERSION$]) m4trace:configure.ac:4: -1- AC_SUBST([PACKAGE_STRING], [m4_ifdef([AC_PACKAGE_STRING], ['AC_PACKAGE_STRING'])])
output.0には、変換結果が格納されているんだけど、まだこれは中間結果みたい。
- case `(set -o) 2>/dev/null` in @%:@( + case `(set -o) 2>/dev/null` in #( *posix*) : - set -o posix ;; @%:@( + set -o posix ;; #(
マイナス記号がついてるのは、outputのデータ、プラス記号の方は最終的なconfigureだ。
-v オプションを付けた時、どういう経路でデータが表示されるかは追求出来ていないけど、表示用のそれを見付けた。BPを貼って追跡。
DB<3> b Autom4te::ChannelDefs::verb
DB<4> c
Autom4te::ChannelDefs::verb(/usr/local/share/autoconf-2.69/Autom4te/ChannelDefs.pm:235):
235: my ($msg, %opts) = @_;
DB<4> T
@ = DB::DB called from file '/usr/local/share/autoconf-2.69/Autom4te/ChannelDefs.pm' line 235
. = Autom4te::ChannelDefs::verb('formatting traces for `/tmp/am4tUOT3dC/dependencies\': include, m4_include') called from file 'te' line 710
. = main::handle_traces(ref(Autom4te::Request), '/tmp/am4tUOT3dC/dependencies','include', '$1', 'm4_include', '$1') called from file 'te' line 889
$ = main::up_to_date(ref(Autom4te::Request)) called from file 'te' line 992
:
DB<4> c
te: formatting traces for `/tmp/am4tUOT3dC/dependencies': include, m4_includeAutom4te::ChannelDefs::verb(/usr/local/share/autoconf-2.69/Autom4te/ChannelDefs.pm:235):
235: my ($msg, %opts) = @_;
DB<4> T
@ = DB::DB called from file '/usr/local/share/autoconf-2.69/Autom4te/ChannelDefs.pm' line 235
. = Autom4te::ChannelDefs::verb('up_to_date (autom4te.cache/traces.0): up to date') called from file '/usr/local/share/autoconf-2.69/Autom4te/FileUtils.pm' line 232
$ = Autom4te::FileUtils::up_to_date_p('autom4te.cache/traces.0', '/usr/local/sha
re/autoconf-2.69/m4sugar/m4sugar.m4', '/usr/local/share/autoconf-2.69/m4sugar/m4
sh.m4', '/usr/local/share/autoconf-2.69/autoconf/autoconf.m4', 'configure.ac', '
/usr/local/share/autoconf-2.69/autoconf/autoupdate.m4', '/usr/local/share/autoco
nf-2.69/autoconf/autoupdate.m4', '/usr/local/share/autoconf-2.69/autoconf/autosc
an.m4', '/usr/local/share/autoconf... called from file 'te' line 907
$ = main::up_to_date(ref(Autom4te::Request)) called from file 'te' line 992
:
DB<4> c
te: formatting traces for `/tmp/am4tUOT3dC/warnings': _m4_warn
Autom4te::ChannelDefs::verb(/usr/local/share/autoconf-2.69/Autom4te/ChannelDefs.pm:235):
235: my ($msg, %opts) = @_;
DB<4> T
@ = DB::DB called from file '/usr/local/share/autoconf-2.69/Autom4te/ChannelDefs.pm' line 235
. = Autom4te::ChannelDefs::verb('reading /tmp/am4tUOT3dC/warnings') called fromfile '/usr/local/share/autoconf-2.69/Autom4te/FileUtils.pm' line 361
$ = Autom4te::FileUtils::contents('/tmp/am4tUOT3dC/warnings') called from file 'te' line 1008
スタックトレースは T ってコマンドなんだけど、慣れないと表示がみにくいな。余計な英語の説明は必要無いと思うぞ。
vbox$ grep -n verb te
147: -v, --verbose verbosely report processing
517: verb "creating $output";
530: verb "forbidden tokens: $forbidden";
531: verb "forbidden token : $_ => $forbidden{$_}"
533: verb "allowed tokens: $allowed";
710: verb "formatting traces for `$output': " . join (', ', sort keys %trace);
996:verb "the trace request object is:\n" . $req->marshall;
perlは変に括弧を省略出来るんで、サブルーチン呼出がきわだたないな。rubyもこの流儀だったんだね。
autom4te -d
debugオプションが用意されてて、それをつけると一時ファイルが削除されなくなるそうだ。/tmp/am4XXXXXX/ の中に作成されてる。
patterns
forbid:^dnl$: forbid:^_?AS_: allow:^SHELL$ allow:^PATH_SEPARATOR$ allow:^PACKAGE_NAME$
traces.m4
divert(-1)
changequote([, ])
# _at_MODE(SEPARATOR, ELT1, ELT2...)
# ----------------------------------
# List the elements, separating then with SEPARATOR.
# MODE can be:
# `at' -- the elements are enclosed in brackets.
:
define([_at_at],
[at_ifelse([$#], [1], [],
[$#], [2], [[[$2]]],
[[[$2]][$1]$0([$1], at_shift(at_shift($@)))])])
:
define([at_flatten],
[at_patsubst(at_patsubst([[[$1]]], [\\
]), [[
]+], [ ])])
define([at_args], [at_shift(at_shift(at_shift(at_shift(at_shift($@)))))])
define([at_at], [_$0([$1], at_args($@))])
:
# Copy the builtins.
define([at_undivert], defn([undivert]))
define([at_ifelse], defn([ifelse]))
:
これらが、どのように使われるか調べると、autoconfとm4の橋渡しが理解出来る(かな)。
perldoc and Pod
autom4teの下請けスクリプトとして、下記のようなものが用意されてる。
[sakae@fb /usr/local/share/autoconf-2.69/Autom4te]$ ls C4che.pm Configure_ac.pm Getopt.pm ChannelDefs.pm FileUtils.pm Request.pm Channels.pm General.pm XFile.pm
それぞれを開いてみると Perl Pod形式で、マニュアルが埋め込まれている事がわかる。折角なのでそれらのマニュアルを読んでみる。
[sakae@fb /usr/local/share/autoconf-2.69/Autom4te]$ perldoc ChannelDefs.pm
NAME
Autom4te::ChannelDefs - channel definitions for Automake and helper
functions
SYNOPSIS
use Autom4te::ChannelDefs;
print Autom4te::ChannelDefs::usage (), "\n";
prog_error ($MESSAGE, [%OPTIONS]);
error ($WHERE, $MESSAGE, [%OPTIONS]);
error ($MESSAGE);
fatal ($WHERE, $MESSAGE, [%OPTIONS]);
fatal ($MESSAGE);
verb ($MESSAGE, [%OPTIONS]);
DESCRIPTION
This package defines channels that can be used in Automake to output
diagnostics and other messages (via "msg()"). It also defines some
:
FUNCTIONS
"usage ()"
Return the warning category descriptions.
:
"verb ($MESSAGE, [%OPTIONS])"
"--verbose" messages.
ワケワカメなコードを読むより、こちらの方が生産的と思うぞ。General.pmを参照したら、
DESCRIPTION
This perl module provides various general purpose support functions
used in several executables of the Autoconf package.
Global Variables
$debug
Set this variable to 1 if debug messages should be enabled. Debug
messages are meant for developers only, or when tracking down an
incorrect execution.
$verbose
Enable verbosity messages. These messages are meant for ordinary
users, and typically make explicit the steps being performed.
こんな説明が出てた。これで、automete -v した時のカラクリが分った事にしておこうな。 これ以上の深入りは無駄ってもんです。