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. 簡単に言ってしまうと、暗号よ。 だから、計算料が高いのよ。

Salsa20 -> ChaCha

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する時の素材ってのは感じられるんだけど、本当の所はどうなの? 首を突っ込んでみる事にする。

configureの作り方(autotoolsの使い方)

Makefile.am

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

即席GNU (autoconf & automake)

Autotoolsについてのメモ

4. 出力ファイルの初期化

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

good job of Makefile

make dist-gzip   el-1.0.tar.gz の様なリリース用TGZの作成
make tags        emacs用のTAGSの作成

This year's Index

Home