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 した時のカラクリが分った事にしておこうな。 これ以上の深入りは無駄ってもんです。


This year's Index

Home