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を目を凝らして眺めるより、よっぽどか楽です。