configure
旅行
コロナも収まってきたので、久しぶりに兄貴と地方を行幸。2泊3日の行程。JR全線乗り放題(但し、鈍行のみ)切符が8750円だったかな。微妙な値段と思うけど、鉄分豊富な兄貴に全部任せたので、文句は言うまい。
旅行中は、オイラーは一切支払い無し。兄貴が一括で、最後に請求書が回ってきた。4万円。兄弟特別割引が適用されたのかな。昼も夜も結構、美味い物を食べてたからなあ。これで、宿代、交通費を含んでいるんで、おとく。兄貴、ありがとさん。
rand()
前回、電子負荷装置ってのをやった。これをリナで実行すると、爆速だった。桁のオーダーでOpenBSDは遅い。んな事に気が付いてしまったので、根幹を成すrand()を調べてみる。
NAME rand, rand_r, srand, srand_deterministic – bad pseudo-random number generator DESCRIPTION Standards insist that this interface return deterministic results. Unsafe usage is very common, so OpenBSD changed the subsystem to return non-deterministic results by default.
冒頭に太字で注意書きが出てたぞ。これは一大事だ。ソース嫁。
/usr/src/lib/libc/stdlib/rand.c
static u_int next = 1; int rand_r(u_int *seed) { *seed = *seed * 1103515245 + 12345; return (*seed & RAND_MAX); } int rand(void) { if (rand_deterministic == 0) return (arc4random() & RAND_MAX); return (rand_r(&next)); }
明示的にsrand()を呼ぶと rand_deterministic
が0にセットされる(初期値は0)。 srand_deterministic(seed)
を呼ぶと、seedがセットされ、 rand_deterministic = 1
と言う処理も行われる仕組みになってた。
安全側に転ぶような設計、フェイルセーフになってるんだな。だから、実質arc4random()が使われるとな。
安っぽい実装だと、整数のかけ算と足し算それとAND演算だけだ。これなら、SN74181の範疇だな。この石を知らない人向けには、ALUだよって教えてあげる。
arc4random()は何処? libc/crypt/arc4random.c
uint32_t arc4random(void) { uint32_t val; _ARC4_LOCK(); _rs_random_u32(&val); _ARC4_UNLOCK(); return val; }
要のコードは複雑過ぎて手におえず。でも、ちゃんとロックをかけて取り出す、安全策が講じられている。こうしてみるとrand()のいいかげんさが、際立つな。
お茶を濁してしまった部分は、ファイルの冒頭にあったコメントに讓るとしよう。 ChaCha based random number generator for OpenBSD. 簡単に言ってしまうと、暗号よ。 だから、計算料が高いのよ。
install-info
debian(amd64)に、aptから入れたgaucheが入っているんだけど、goshからinfoするとエラーになる。rfc/zlibが無いとか言ってくるんだ。頭に来たので、gaucheを自前で入れた。そして、emacsから使えるようにした。
まずは、install-infoして、dirファイルを生成する。
sakae@pen:~/MINE/share/info$ install-info gauche-refj.info dir sakae@pen:~/MINE/share/info$ install-info gauche-refe.info dir
HOMEの下なので、忌しいsudoは不要だ。
;; info (require 'info) (add-to-list 'Info-additional-directory-list "/home/sakae/MINE/share/info")
次に、emacsから参照出来る様に、INFOPATH相当の設定をinit.elに書き足した。これにて、一件落着。
後で調べたら、aptの場合は、gauche-zlibも追加しないと、goshからのinfoでは使えないっぽい。そんな事死るか。gauche-docと友にインストールされるべき(多分ね)。
configure
前回、ひょんな事から、configure.acなんてのを見てしまった。普通は素通りしてしまう部分だ。なんとなく、confugireする時の素材ってのは感じられるんだけど、本当の所はどうなの? 首を突っ込んでみる事にする。
Autotoolsを使ってプロジェクトのMakefileを生成する
autotoolsを使ってみよう 超入門編
根幹を成すのは、 macro language processor m4 らしい。ピンとこないので、 m4 チュートリアル なんてのを見る。これが、基礎体力になるかな。 m4 マクロ言語プロセッサ こういうのも有ったぞ。
do sample
autoconfやautomakeのinfoを見ているんだけど、膨大過ぎて手におえない。これはもう、サンプルをやってみるしかないな。上の資料の超入門ってのをやってみた。
[sakae@fb /tmp/hoge]$ autoconf -i configure.ac:2: error: possibly undefined macro: AM_INIT_AUTOMAKE If this token and others are legitimate, please use m4_pattern_allow. See the Autoconf documentation.
早速怒られたんで、言われた通りにする。
[sakae@fb /tmp/hoge]$ ./configure checking for gcc... no checking for cc... cc : configure: creating ./config.status config.status: error: cannot find input file: `Makefile.in'
あらあら、Makefileの種が見付からないとな。ここから、どうしろと? そりゃね、カンニングですよ。/usr/local/share/doc/amhello-1.0.tar.gzなんてサンプルがあるんで、参照。
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
問題になった所は、こんな風にすればいいみたい。
そして、一連の流れは、automakeの 2.4 A Small Hello World に解説があった。
[sakae@fb /tmp/hoge]$ autoreconf --install configure.ac:3: installing './compile' configure.ac:2: installing './install-sh' configure.ac:2: installing './missing' Makefile.am: installing './depcomp' [sakae@fb /tmp/hoge]$ ./configure checking for a BSD-compatible install... /usr/bin/install -c : configure: creating ./config.status config.status: creating Makefile config.status: executing depfiles commands
これでコンパイルでける。
[sakae@fb /tmp/hoge]$ make cc -DPACKAGE_NAME=\"prog1\" -DPACKAGE_TARNAME=\"prog1\" -DPACKAGE_VERSION=\"1.0\" -DPACKAGE_STRING=\"prog1\ 1.0\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DPACKAGE=\"prog1\" -DVERSION=\"1.0\" -I. -g -O2 -MT prog1.o -MD -MP -MF .deps/prog1.Tpo -c -o prog1.o prog1.c mv -f .deps/prog1.Tpo .deps/prog1.Po cc -g -O2 -o prog1 prog1.o
in configure.ac
この記事を書くきっかけとなった、configure.acをgaucheのそれで覗いてみる。
dnl ---------------------------------------------------------- dnl enable-thread=TYPE dnl AC_MSG_CHECKING([thread type]) GAUCHE_THREAD_TYPE=default AC_ARG_ENABLE(threads, AS_HELP_STRING([--enable-threads=TYPE], [Choose thread type. Possible values are: 'none' for not usin\ g threads, 'pthreads' to use pthreads, 'win32' to use Windows threads, and 'def\ ault' to choose system's suitable one (if any). ]), [ AS_CASE([$enableval], [pthreads], [GAUCHE_THREAD_TYPE=pthreads], [win32], [GAUCHE_THREAD_TYPE=win32], : [AC_MSG_ERROR([invalid value $enableval for --enable-threads option (mus\ t be either none, pthreads, win32, or default)])]) ], []) AS_CASE([$GAUCHE_THREAD_TYPE], [pthreads|default], [AS_CASE(["$host"], [*-*-linux*], [ AC_DEFINE(GC_LINUX_THREADS,1,[Define to use Linux threads]) AC_DEFINE(_REENTRANT,1,[Define to use reentrant libc]) THREADLIBS="-lpthread" GAUCHE_THREAD_TYPE=pthreads ], : AS_CASE([$GAUCHE_THREAD_TYPE], [pthreads], [ AC_DEFINE(GAUCHE_USE_PTHREADS,1,[Define if we use pthreads]) ], [win32], [ AC_DEFINE(GAUCHE_USE_WTHREADS,1,[Define if we use windows threa\ ds]) ], []) LIBS="$LIBS $THREADLIBS" dnl Save thread model to be inherited by gc/ subdir. AC_SUBST(GAUCHE_THREAD_TYPE) AC_MSG_RESULT([$GAUCHE_THREAD_TYPE])
-D
突然、-D なんてgccのオプションめいたものが出てきたけど、これは前回やった、電子負荷装置をコンパイルする時に使った。
gcc -DLF=50 el.c
これをconfigure時に指定するには、どうするかって調べていたんだ。
パッケージに二,三個以上のCプリプロセッサのシンボルのテストが含まれているとき, コマンドラインでコンパイラに渡す`-D'オプションはかなり長くなります. これは二つの問題があります.一つは,makeの出力のエラー を探すとき, 見て分からなくなることです.更に深刻な問題は,コマンドラインがいくつかの オペレーティングシステムの長さの制限を越えることです. コンパ イラに`-D'オプションを渡す代わりに, configureスクリプトで`#define' ディレクティブを含んでいるCヘッダファイルを作成することが可能です. AC_CONFIG_HEADERマクロで,この出力を選択します.それ は,AC_INITの直後に呼び出します.
こんな事が、出力ファイルの初期化の所で説明されてた。config.hと言う固定な名前のファイルに書出しておけって事だ。gaucheもこの方法を使っている。
sakae@deb:~/src/Gauche/src/gauche$ grep PTHREAD config.h #define GAUCHE_USE_PTHREADS 1 /* #undef GC_PTHREADS */ :
静的に定義されてると、grepで追跡し易いというのもあるな。
sakae@deb:~/src/Gauche/src$ grep GAUCHE_USE_PTHREADS -rIl . ./core.c ./prof.c ./vm.c ./gauche.h ./main.c ./signal.c ./gauche/config.h ./gauche/vm.h ./gauche/config.h.in
へぇ、結構色々な所に分散してるんだ、ってな事が簡単に調べられる。main.cの中では、こんな使いかたがされてた。
#ifdef GAUCHE_USE_PTHREADS #define THREAD_OPT ",pthreads" #elif GAUCHE_USE_WTHREADS #define THREAD_OPT ",wthreads" #else #define THREAD_OPT "" #endif
main.cの中では、直接config.hをインクルードしてなくて、gauche.hを使っている。で、その定義は、
/* Read config.h _before_ other headers, for it may affect the behavior of system header files. Currently the only known instance of it is sigwait() on Solaris---we need to define _POSIX_PTHREAD_SEMANTICS to get pthread-compatible sigwait()---but we may encounter more of such instances. */ #include <gauche/config.h> #include <gauche/config_threads.h>
こんな風になってた。
my configure
大体感覚が掴めたので、自前のコンフィグ発生器を作ってみる。題材は、冒頭でも取り上げたやつ。SPEEDって変数で、偽物(但し高速)なrandを使う。LFは負荷率の指示。これらは、configureすると自動作成される、config.hに格納されてるって事にする。
el.c
#include "config.h" #include <stdlib.h> #include <time.h> #define BT (5 * 1000 * 1000) // unit ns, must be adj void os() { for (int i=0; i<100000; i++){ rand(); } } // 1% time void spend(int n) {for (int i=0; i< n; i++){ os(); } } int main(){ struct timespec req = {0, (100 - LF) * BT}; #if SPEED srand_deterministic(1234); #endif spend(LF); nanosleep(&req, NULL); }
Makefile.am
bin_PROGRAMS = el
configure.ac
# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) AC_INIT([el], [1.0], [foo@java.jp]) AM_INIT_AUTOMAKE([foreign]) dnl load factor AC_MSG_CHECKING([lf val]) AC_ARG_ENABLE(lf, AS_HELP_STRING([--enable-lf=VAL], [load factor 0 to 100, default is 50]), LFV=$enableval, LFV=50) AC_DEFINE_UNQUOTED(LF, [$LFV], [Define LF value]) AC_MSG_RESULT([$LFV]) dnl high speed AC_MSG_CHECKING([high speed rand]) AC_ARG_WITH(rand, AS_HELP_STRING([--with-rand=ARG],[high speed rand]), SPEED=$with_rand, SPEED=no) AS_CASE([$SPEED], [yes], [ AC_DEFINE(SPEED,1,[high speed] )], [no], [ AC_DEFINE(SPEED,0,[use arc4random])], []) AC_MSG_RESULT([$SPEED]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) AC_PROG_CC AC_OUTPUT
見様見まねなんで、おかしい所あるかも。
run configure
vbox$ ./configure --help : Optional Features: --enable-lf=VAL load factor 0 to 100, default is 50 : Optional Packages: --with-rand=ARG high speed rand :
こんな風に組込まれた。
vbox$ ./configure : checking lf val... 50 checking high speed... no
config.hから抜粋
/* Define LF value */ #define LF 50 /* use arc4random */ #define SPEED 0
今度はオプションを与えてみる。
vbox$ ./configure --enable-lf=88 --with-rand=yes checking lf val... 88 checking high speed... yes
勿論、その設定は、config.hに反映されている。
vbox$ make make all-am cc -DHAVE_CONFIG_H -I. -g -O2 -MT el.o -MD -MP -MF .deps/el.Tpo -c -o el.o el.c mv -f .deps/el.Tpo .deps/el.Po cc -g -O2 -o el el.o vbox$ time ./el 0m00.27s real 0m00.19s user 0m00.02s system
開発サイクル
1. configure.ac Makefile.amを用意する 2. autoreconf --install で、必要品を設置 3. configure してエラーないか確認 4. check config.h 最終出来栄え確認 5. edit configure.ac だめなら、再編集 6. autoreconf 再度生成 7. goto 3
こんな流れになるかな。
AC_MSG_CHECKING
と AC_MSG_RESULT
は、configure時の結果をコンソールに表示してくれるので、記述しておいた方が良いと思う。この結果が正しくて、config.hに思ったように反映されない場合、configureを開いて、 AC_MSG_CHECKING
に与えた文字列を頼りに該当箇所を探して、確認。
–with-randの値をどう反映(取り込み)させるか解らなかったけど、開いてみたら分った。
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking high speed rand" >&5 $as_echo_n "checking high speed rand... " >&6; } # Check whether --with-rand was given. if test "${with_rand+set}" = set; then : withval=$with_rand; SPEED=$with_rand else SPEED=no fi
これをみると、 $with_rand
に、値が載ってる事が一目瞭然でわかる。infoを目を凝らして眺めるより、よっぽどか楽です。