2009/01/04

Mecabをscalaで

ポスト @ 1:48:52 | , ,     

ということで、cmecabはインストール出来なかったので、mecab-javaバインディングを使う。
下記のページに書かれている通りにやるとうまく良くと思う

via - [mecab][java] MeCabをLeopard (Mac) にインストールしJavaバインディングつかってみる - With skill and creativeness

んで、Mecabをjavaから呼ぶのはできたので、せっかくなのでscalaから呼べるようにする

こんなコードを用意

import org.chasen.mecab.{MeCab, Tagger, Node}

object Test {
    def main(args: Array[String]){
        System.loadLibrary("MeCab");

        var tagger = new Tagger;
        println (tagger.parse ("すもももももももものうち"));

        var node:Node = tagger.parseToNode("隣の客はよく柿食う客だ");
        while(node != null){
            println(node.getSurface() + "\t" + node.getFeature());
            node = node.getNext();
        }
    }
}

んで、scalac -cp で Mecab.jar と -Dava.library.path に libMeCab.dylib を通してあげればコンパイルできる。
でも、毎回コンパイルするたびにこのオプションを指定するのは面倒なので、antに設定しておく

<?xml version="1.0" encoding="utf-8" ?>
<project name="mecab.scala" default="run.demo" basedir=".">
    
    <property name="lib" location="lib"/>
    <property name="jni" location="jni"/>

    <property environment="env" />
    <property name="scala.home" value="${basedir}" />

    <path id="scala.classpath">
        <pathelement path="${basedir}"/>
        <fileset dir="${lib}">
            <include name="*.jar" />
        </fileset>
        <pathelement path="${java.class.path}"/>
    </path>

    <taskdef name="scalac" classname="scala.tools.ant.Scalac">
        <classpath>
            <path refid="scala.classpath" />
        </classpath>
    </taskdef>

    <taskdef name="scaladoc" classname="scala.tools.ant.Scaladoc">
        <classpath>
            <path refid="scala.classpath" />
        </classpath>
    </taskdef>

    <macrodef name="scala">
        <attribute name="args"/>
        <sequential>
            <java classname="scala.tools.nsc.MainGenericRunner" fork="true" dir=".">
                <classpath>
                    <path refid="scala.classpath" />
                </classpath>
                <jvmarg value="-Xbootclasspath/a:${scala.home}/scala-library.jar}" />
                <jvmarg value="-Xmx512M" />
                <jvmarg value="-Xms64M" />
                <jvmarg value="-Dscala.home=${scala.home}" />
                <jvmarg value="-Djava.library.path=${jni}/osx_x64" />
                <arg line="-Djava.library.path=${jni}/osx_x64/libMeCab.dylib @{args}" />
            </java>
        </sequential>
    </macrodef>

   <target name="doc">
        <mkdir dir="${doc.dir}"/>
        <scaladoc
            srcdir="${src.dir}"
            destdir="${doc.dir}"
            deprecation="yes"
            unchecked="yes"
            windowtitle="${doc.windowtitle}"
            doctitle="${doc.title}"
            classpathref="scala.classpath">
            <include name="**/*.scala"/>
        </scaladoc>
    </target>

    <target name="run.demo">
        <scalac srcdir="${basedir}" destdir="${basedir}" classpathref="scala.classpath" target="jvm-1.5" force="changed" />
        <scala args="Test" />
    </target>
</project>

このbuild.xmlはscala-squib - Google Codeをパクってきた。
ここはいろいろと参考になるので好き

また、ディレクトリ構成としてはこんな感じ

|- Hoge.scala
|- Test$.class
|- Test.class
|- Test.scala
|- build.xml
|- build.xml.mf
|- lib   
   |- libMeCab.dylib  
   |- MeCab.jar
   |- scala-compiler.jar
   |- scala-dbc.jar
   |- scala-decoder.jar
   |- scala-library.jar

ちなみに、コンパイルされた scala コードは java から呼び出せるので、

java -cp .:lib/scala-decoder.jar scala.tools.nsc.MainGenericRunner Hoge

とかにしておけば、javaから呼び出せれる(もしかしたらscala-library.jarもいるかも)けど、問題はclasspathに指定したファイルのパスもちゃんと含めてあげないといけない(今回は ./ 大抵の場合は ./src )(当たり前と言えば当たり前だけど、ハマった。。。)

さてさて、肝心の実行結果はというと、こんな感じ

Buildfile: build.xml

run.demo:
     [java] すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
     [java] も	助詞,係助詞,*,*,*,*,も,モ,モ
     [java] もも	名詞,一般,*,*,*,*,もも,モモ,モモ
     [java] も	助詞,係助詞,*,*,*,*,も,モ,モ
     [java] もも	名詞,一般,*,*,*,*,もも,モモ,モモ
     [java] の	助詞,連体化,*,*,*,*,の,ノ,ノ
     [java] うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
     [java] EOS
     [java] 
     [java] 	BOS/EOS,*,*,*,*,*,*,*,*
     [java] 隣	名詞,一般,*,*,*,*,隣,トナリ,トナリ
     [java] の	助詞,連体化,*,*,*,*,の,ノ,ノ
     [java] 客	名詞,一般,*,*,*,*,客,キャク,キャク
     [java] は	助詞,係助詞,*,*,*,*,は,ハ,ワ
     [java] よく	副詞,一般,*,*,*,*,よく,ヨク,ヨク
     [java] 柿	名詞,一般,*,*,*,*,柿,カキ,カキ
     [java] 食う	動詞,自立,*,*,五段・ワ行促音便,基本形,食う,クウ,クウ
     [java] 客	名詞,一般,*,*,*,*,客,キャク,キャク
     [java] だ	助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
     [java] 	BOS/EOS,*,*,*,*,*,*,*,*

BUILD SUCCESSFUL
Total time: 0 seconds

後は、scala のコードがもう少し綺麗にできれば、良さそう

cmecab-java を OSX で...

ポスト @ 0:49:29 | , ,     

cmecab-java - Google CodeをOSXで使ってみるテスト

cmecab-java-1.0.tar.gzを展開して、jniディレクトリに含まれるMakeFile.unixを次のようにする

nowel@macbook: ~/tmp/cmecab-java> cat jni/Makefile.osx 
CMECAB_INCLUDE=-I/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/
CMECAB_SRC=mecab.cpp
CMECAB_TARGET=libCMeCab.jnilib

CXX=g++
CXXFLAGS=-Wall -g -O2 -fPIC $(CMECAB_INCLUDE)
LDFLAGS=-dynamiclib -lmecab

all:
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(CMECAB_TARGET) $(CMECAB_SRC)

install:
	cp $(CMECAB_TARGET) /usr/local/lib

javah:
	javah -classpath ../bin/classes \
	  net.moraleboost.mecab.Tagger \
	  net.moraleboost.mecab.Node

clean:
	rm -f $(CMECAB_TARGET)

もし、以下のようにでてしまっている場合は、コンパイルオプションが-sharedとなっている場合があるので、-dynamiclibとする

g++ -Wall -g -O2 -fPIC -I/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/ -shared -lmecab -o libCMeCab.jnilib mecab.cpp
Undefined symbols:
  "_main", referenced from:
      start in crt1.10.5.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make: *** [all] Error 1

んで、make -f Makefile.osx

nowel@macbook: ~/tmp/cmecab-java/jni> make -f Makefile.osx 
g++ -Wall -g -O2 -fPIC -I/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/ -dynamiclib -lmecab -o libCMeCab.jnilib mecab.cpp

ビルドするが、うまくコンパイルできない...

nowel@macbook: ~/tmp/cmecab-java> java -version
java version "1.5.0_16"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_16-b06-284)
Java HotSpot(TM) Client VM (build 1.5.0_16-133, mixed mode, sharing)
nowel@macbook: ~/tmp/cmecab-java> ant
Buildfile: build.xml

init:
    [mkdir] Created dir: /Users/nowel/tmp/cmecab-java/bin/classes

compile:
    [javac] Compiling 15 source files to /Users/nowel/tmp/cmecab-java/bin/classes
    [javac] /Users/nowel/tmp/cmecab-java/src/net/moraleboost/mecab/MeCabException.java:35: シンボルを見つけられません。
    [javac] シンボル: コンストラクタ IOException(java.lang.Throwable)
    [javac] 場所    : java.io.IOException の クラス
    [javac]         super(e);
    [javac]         ^
    [javac] /Users/nowel/tmp/cmecab-java/src/net/moraleboost/mecab/MeCabException.java:40: シンボルを見つけられません。
    [javac] シンボル: コンストラクタ IOException(java.lang.String,java.lang.Throwable)
    [javac] 場所    : java.io.IOException の クラス
    [javac]         super(msg, e);
    [javac]         ^
    [javac] /Users/nowel/tmp/cmecab-java/src/net/moraleboost/solr/FeatureRegexFilterFactory.java:64: シンボルを見つけられません。
    [javac] シンボル: メソッド isEmpty()
    [javac] 場所    : java.lang.String の クラス
    [javac]                 if (!regex.isEmpty()) {
    [javac]                           ^
    [javac] 注:/Users/nowel/tmp/cmecab-java/src/net/moraleboost/solr/FeatureRegexFilterFactoryTest.java は推奨されない API を使用またはオーバーライドしています。
    [javac] 注:詳細については、-Xlint:deprecation オプションを指定して再コンパイルしてください。
    [javac] エラー 3 個

BUILD FAILED
/Users/nowel/tmp/cmecab-java/build.xml:43: Compile failed; see the compiler error output for details.

Total time: 4 seconds

見てみると、IOException関係のは、J2SE1.6系統からっぽい。
ref - http://java.sun.com/javase/6/docs/api/java/io/IOException.html

OSXのjavaを1.6にする。
Java Preferences.app が /Applications/Utilities/Java/Java Preferences.app にあるので、実行


概要から、JavaSE6を一番上にする

が、まだ、ビルドはできないので、JAVA_HOMEを1.6にする

export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/

これで、ビルドはできるようになった

nowel@macbook: ~/tmp/cmecab-java> ant
Buildfile: build.xml

init:

compile:
    [javac] Compiling 8 source files to /Users/nowel/tmp/cmecab-java/bin/classes
    [javac] ??:/Users/nowel/tmp/cmecab-java/src/net/moraleboost/solr/FeatureRegexFilterFactoryTest.java ??????????? API ???g?p?????I?[?o?[???C?h???Ă?????B
    [javac] ??:????????A-Xlint:deprecation ?I?v?V???????w???čăR???p?C?????Ă????????B

dist:
      [jar] Building jar: /Users/nowel/tmp/cmecab-java/bin/cmecab-1.0.jar

BUILD SUCCESSFUL
Total time: 1 second

文字化けしてる...

とりあえず、jniをパスにコピーしておく

nowel@macbook: ~/tmp/cmecab-java> sudo cp jni/libCMeCab.jnilib /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Libraries/

実行してみると

nowel@macbook: ~/tmp/cmecab-java> java -cp bin/cmecab-1.0.jar net.moraleboost.mecab.Tagger UTF-8 すもももももももものうち
Exception in thread "main" java.lang.UnsatisfiedLinkError: /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Libraries/libCMeCab.jnilib: 
	at java.lang.ClassLoader$NativeLibrary.load(Native Method)
	at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1822)
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1723)
	at java.lang.Runtime.loadLibrary0(Runtime.java:823)
	at java.lang.System.loadLibrary(System.java:1030)
	at net.moraleboost.mecab.Tagger.<clinit>(Unknown Source)

うーん。もう一度jniをコンパイルしてみても。だめ

結局Makefileはこんな感じに。

CMECAB_INCLUDE=-I/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers \
			   -I/opt/local/include
CMECAB_SRC=mecab.cpp
CMECAB_TARGET=libCMeCab.jnilib
CMECAB_LIBS=-L/opt/local/lib -lmecab -lstdc++

CXX=g++
CXXFLAGS=-Wall -c -O3 -fPIC -dynamic $(CMECAB_INCLUDE)
LDFLAGS=-lmecab -framework JavaVM

all:
	$(CXX) $(CXXFLAGS) $(CMECAB_SRC)
	$(CXX) -dynamiclib mecab.o -o $(CMECAB_TARGET) $(CMECAB_LIBS)

install:
	cp $(CMECAB_TARGET) /usr/local/lib

javah:
	javah -classpath ../bin/classes \
	  net.moraleboost.mecab.Tagger \
	  net.moraleboost.mecab.Node

clean:
	rm -f $(CMECAB_TARGET)

このMakefileだと、dSYMみたいなファイルができずに、jnilib作れるけど、結局読み込まれないみたいなのでダメっぽい。もう少し調べてみる。

2008/12/14

PHP で float と int に触れる

ポスト @ 3:31:07 | ,     

PHPでは数値をどうやって持っているのか。気になったので試してみる

「-1」はどうやっているのか。ということで、decbinで2進数表記にしてみた

$a = decbin(-1);
var_dump($a);
==> string(32) "11111111111111111111111111111111"

32ビットで扱っているみたいだ。
逆にこの値を数値に戻してみる。

$b = bindec('11111111111111111111111111111111');                          
var_dump($b);
==> float(4294967295)

なるほど、int の最大値(?) を越えて float になった上で、それを整数値で持つのね。
ということは、これを int にすると、「-1」の値になる。と

$c = (float) 4294967295;
var_dump((int) $c);
==> int(-1)

ふむふむ。じゃあ、intな「-1」をfloatにすると「4294967295」になる...

$d = -1;    
$e = (float) $d;
var_dump($e);
==> float(-1)

だよね。PHPだとこうなるよね。つまり、「-1」なfloatの「4294967295」はintで作ったものからは作り出せそうに無い。と。
このあたりはソースを追ってみる必要がありそう。

んじゃ、調子に乗って浮動小数は...

var_dump(decbin(0.1));
==> string(1) "0"

ここはまたの機会に調べる。浮動小数と負の浮動小数とか。

ちなみに、すごく手抜きだけど、ビットでの試行錯誤はbindecを使って試した。こんな感じ

$a = bindec(join(explode(' ', '1111 1111 1111 1111 1111 1111 1111 1110')));
var_dump($a);
var_dump((int) $a);

ビット周辺は面白い。

PHP でバイナリから文字列を作る

ポスト @ 2:53:11 , 修正 @ 2008/12/14 2:54:40 | , ,     

よく「PHPはバイナリで文字列を持っている」とか言われているので、やってみた。
参考にしたのは、文字コードについて より、 Unicode対応 文字コード表

試しに、上記の表から「あ」の文字列をUTF-8なバイナリから作成してみる

// utf8 E38182
echo pack('C*', 0xE3, 0x81, 0x82), PHP_EOL;
==> あ

これは、コンソールで使っている端末もUTF-8なので、そのまま出力できた。EUCな端末なら、こんな感じ

// utf8 E38182
echo mb_convert_encoding(pack('C*', 0xE3, 0x81, 0x82), 'EUC-JP', 'UTF-8'), PHP_EOL;
==> あ

ということで、他の文字エンコードでのバイナリをいくつか作ってみた。(やっていることは、文字列バイナリをmb_convert_encodingで端末の文字エンコードであるUTF-8に変換して表示しているだけ)

// sjis 82A0
echo mb_convert_encoding(pack('C*', 0x82, 0xA0), 'UTF-8', 'Shift_JIS'), PHP_EOL;
==> あ
// utf8 E38182
echo mb_convert_encoding(pack('C*', 0xE3, 0x81, 0x82), 'UTF-8'), PHP_EOL;
==> あ
// eucjp A4A2
echo mb_convert_encoding(pack('C*', 0xA4, 0xA2), 'UTF-8', 'EUC-JP'), PHP_EOL;
==> あ
// utf16 3042
echo mb_convert_encoding(pack('C*', 0x30, 0x42), 'UTF-8', 'UTF-16'), PHP_EOL;
==> あ

ということで、packを使えばバイナリから文字列を作れそう。ということはやっぱりバイナリで文字列を持っているというわけで。さらには、そのバイナリはmb_convert_encodingで他の値にもシフトできそう。と。
勉強になります。

2008/12/08

フラワーロック 2.0 と CruiseControl を連携させてみた の作成編を更新

ポスト @ 0:18:41 , 修正 @ 2008/12/08 0:20:07 | , ,     

と言うわけで(?)、書きかけだったあいつを更新
ref - フラワーロック 2.0 と CruiseControl を連携させてみた

俺、これでフラワーロックの売上が上がったら、もっと別の、プロジェクト以外のフラワーロックの利用方法考えるんだ...
(流石にフラワーロックが踊る「ビルド失敗の舞」は使い道が違うような気がする...)

2008/12/07

Q4MをPHP(PDO)で

ポスト @ 17:57:37 | , ,     

Q4Mが無事インストールできたので、PHPから触ってみる。詳細なことは今度書く。とりあえず触りだけでも

テーブル定義

drop table if exists hoge_queue;
create table hoge_queue(
    id int not null,
    name varchar(25) not null
) engine=queue;

こんなスクリプトをガンガン回して、監視

$conn = new PDO('mysql:host=localhost; dbname=hogetest', 'user', 'password');
$wait = $conn->prepare('SELECT queue_wait("hoge_queue", 10)');
$abort = $conn->prepare('SELECT queue_abort()');
$e = $conn->prepare('SELECT queue_end()');
$select = $conn->prepare('SELECT * FROM hoge_queue');

while(true){
    $start = time(); // for epch tstmp

    // wait call query
    $wait->execute();

    $end = time();
    echo 'wait for: ', $end - $start, ' sec.', PHP_EOL;
    echo 'is_queue: ', $wait->fetch(PDO::FETCH_COLUMN), PHP_EOL;

    $select->execute();
    while($row = $select->fetch(PDO::FETCH_OBJ)){
        var_dump($row);
        //
        // delete しなくとも queue_end で消える
        //
        $e->execute();
        continue 2;
    }

    // abort call
    $abort->execute();
    echo 'next...', PHP_EOL, PHP_EOL;
}

別の端末からmysqlの動いてるサーバに対して、insert

insert into hoge_queue values(1, 'hello world');

こんな感じになるよ

int(10)
array(1) {
  ["queue_wait("hoge_queue", 10)"]=>
  string(1) "0" 
}
next...

int(10)
array(1) {
  ["queue_wait("hoge_queue", 10)"]=>
  string(1) "0" 
}
next...

int(10)
array(1) {
  ["queue_wait("hoge_queue", 10)"]=>
  string(1) "0" 
}
next...

int(2)
array(1) {
  ["queue_wait("hoge_queue", 10)"]=>
  string(1) "1" 
}
object(stdClass)#6 (2) {
  ["id"]=>
  string(2) "1" 
  ["name"]=>
  string(11) "hello world" 
}
next...

int(10)
array(1) {
  ["queue_wait("hoge_queue", 10)"]=>
  string(1) "0" 
}
next...

int(10)
array(1) {
  ["queue_wait("hoge_queue", 10)"]=>
  string(1) "0" 
}
next...

Q4Mの動きで、特徴的なの

  • queue_wait で取れたレコードは、queue_end で消える。
  • queue_wait で止まる時間を指定しても、queueが入ってきたときは、即レコードが取れる
  • queue_wait を連発すると一行前のレコードが消える(?要確認だけど)
  • queue_wait で取れたレコードは他のクライアントから queue_wait しても取れない
  • queue_wait で取得したレコードは、他のクライアントからだとテーブル上に見えなくなってる
  • queue_wait で、レコードが見つからない場合は、0 で、見つかったら 1 が帰ってくる
  • queue_wait で取ったレコードは「ちゃんと処理をする」queue_abort すると消えるよ

Q4Mの動きですこしハマるやつ。その1

全レコード分 wait

mysql> select * from hoge_queue;
+----+------+
| id | name |
+----+------+
|  4 | foo4 | 
|  5 | foo5 | 
|  6 | foo6 | 
|  7 | foo7 | 
|  8 | foo8 | 
|  9 | foo9 | 
+----+------+
6 rows in set (0.00 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           1 | 
+-----------------------------+
1 row in set (0.00 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           1 | 
+-----------------------------+
1 row in set (0.06 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           1 | 
+-----------------------------+
1 row in set (0.06 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           1 | 
+-----------------------------+
1 row in set (0.05 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           1 | 
+-----------------------------+
1 row in set (0.05 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           1 | 
+-----------------------------+
1 row in set (0.05 sec)

mysql> select queue_wait('hoge_queue', 1);
+-----------------------------+
| queue_wait('hoge_queue', 1) |
+-----------------------------+
|                           0 | 
+-----------------------------+
1 row in set (1.05 sec)

最後は、waitで 1sec 止まって、結果は0に

selectすると

mysql> select * from hoge_queue;             
Empty set (0.00 sec)

abort すると...?

mysql> select queue_abort();
+---------------+
| queue_abort() |
+---------------+
|             1 | 
+---------------+
1 row in set (0.00 sec)

その後、select すると...

mysql> select * from hoge_queue;
Empty set (0.00 sec)

全部消えてる...

wait して取り出せたレコードは、レスキューできないのか。残念。

Q4Mの動きですこしハマるやつ。その他

Q4Mは面白いので、別の機会にまとめて書く

Q4Mのビルド(CentOS 5.2 と OSX)

ポスト @ 17:39:07 , 修正 @ 2008/12/07 18:26:47 | , , , ,     

Q4Mのアイディアがとても面白そうなので、使ってみることにしました。
が、ビルドに色々つまづいたので、メモ。かれこれ2週間くらい前のログなので、微妙にバージョンが違ってたら申し訳ないです。

CentOS 5.2 に入れる

CentOS を使っているので、これに入れるログ

既にインストール済みのMySQLをアンインストール

とりあえず、キレイにしておきました。

hata@local ~/local/rpms/mysql5> sudo yum remove mysql
Resolving Dependencies
--> Running transaction check
---> Package mysql.x86_64 0:5.0.45-7.el5 set to be erased
--> Processing Dependency: libmysqlclient.so.15()(64bit) for package: mysql-devel
--> Processing Dependency: libmysqlclient.so.15()(64bit) for package: libdbi-dbd-mysql
--> Processing Dependency: libmysqlclient.so.15()(64bit) for package: mysql-server
--> Processing Dependency: libmysqlclient.so.15()(64bit) for package: perl-DBD-MySQL
--> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: libdbi-dbd-mysql
--> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: mysql-server
--> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: perl-DBD-MySQL
--> Processing Dependency: libmysqlclient_r.so.15()(64bit) for package: mysql-devel
--> Processing Dependency: libmysqlclient_r.so.15()(64bit) for package: mysql-server
--> Processing Dependency: libmysqlclient_r.so.15(libmysqlclient_15)(64bit) for package: mysql-server
--> Processing Dependency: mysql = 5.0.45-7.el5 for package: mysql-devel
--> Processing Dependency: mysql = 5.0.45-7.el5 for package: mysql-server
--> Processing Dependency: mysql for package: libdbi-dbd-mysql
--> Running transaction check
---> Package mysql-devel.x86_64 0:5.0.45-7.el5 set to be erased
---> Package mysql-server.x86_64 0:5.0.45-7.el5 set to be erased
---> Package libdbi-dbd-mysql.x86_64 0:0.8.1a-1.2.2 set to be erased
---> Package perl-DBD-MySQL.x86_64 0:3.0007-1.fc6 set to be erased
--> Finished Dependency Resolution

Dependencies Resolved

=============================================================================
 Package                 Arch       Version          Repository        Size 
=============================================================================
Removing:
 mysql                   x86_64     5.0.45-7.el5     installed         7.5 M
Removing for dependencies:
 libdbi-dbd-mysql        x86_64     0.8.1a-1.2.2     installed          54 k
 mysql-devel             x86_64     5.0.45-7.el5     installed         6.3 M
 mysql-server            x86_64     5.0.45-7.el5     installed          22 M
 perl-DBD-MySQL          x86_64     3.0007-1.fc6     installed         328 k

Transaction Summary
=============================================================================
Install      0 Package(s)         
Update       0 Package(s)         
Remove       5 Package(s)         

Is this ok [y/N]: y
Downloading Packages:
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Erasing   : mysql-devel                  ######################### [1/5] 
  Erasing   : mysql                        ######################### [2/5] 
  Erasing   : mysql-server                 ######################### [3/5] 
warning: /var/log/mysqld.log saved as /var/log/mysqld.log.rpmsave
  Erasing   : libdbi-dbd-mysql             ######################### [4/5] 
  Erasing   : perl-DBD-MySQL               ######################### [5/5] 

Removed: mysql.x86_64 0:5.0.45-7.el5
Dependency Removed: libdbi-dbd-mysql.x86_64 0:0.8.1a-1.2.2 mysql-devel.x86_64 0:5.0.45-7.el5 mysql-server.x86_64 0:5.0.45-7.el5 perl-DBD-MySQL.x86_64 0:3.0007-1.fc6
Complete!

MySQL ABのバイナリをインストールする

MySQL ABはここから落としてきて、とりあえず rpm を使いました。

hata@local: ~/local/rpms/mysql5> sudo rpm -ivh MySQL-*.rpm
準備中...                ########################################### [100%]
   1:MySQL-shared-compat    ########################################### [ 20%]
   2:MySQL-client-community ########################################### [ 40%]
   3:MySQL-devel-community  ########################################### [ 60%]
   4:MySQL-server-community ########################################### [ 80%]
ERROR: 1136  Column count doesn't match value count at row 1
081121 10:59:56 [ERROR] Aborting

081121 10:59:56 [Note] /usr/sbin/mysqld: Shutdown complete

Installation of system tables failed!  Examine the logs in
/var/lib/mysql for more information.

You can try to start the mysqld daemon with:

    shell> /usr/sbin/mysqld --skip-grant &

and use the command line tool /usr/bin/mysql
to connect to the mysql database and look at the grant tables:

    shell> /usr/bin/mysql -u root mysql
    mysql> show tables

Try 'mysqld --help' if you have problems with paths.  Using --log
gives you a log in /var/lib/mysql that may be helpful.

The latest information about MySQL is available on the web at
http://www.mysql.com/.  Please consult the MySQL manual section
'Problems running mysql_install_db', and the manual section that
describes problems on your OS.  Another information source are the
MySQL email archives available at http://lists.mysql.com/.

Please check all of the above before mailing us!  And remember, if
you do mail us, you MUST use the /usr/bin/mysqlbug script!

Starting MySQL.[  OK  ]
Giving mysqld 2 seconds to start
   5:MySQL-shared-community ########################################### [100%]

Q4M-0.8.3を落としてきて make

q4m.31tools.comから無印の q4m-{version}.tar.gz だと、なぜかエラーが沢山(下記)でたので、mysql-{version}-*q4m-{version}.tar.gz を使った。

gcc/x86_64-redhat-linux/4.1.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -lstdc++ -lm -lgcc_s -lc -lgcc_s /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtendS.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crtn.o  -Wl,--hash-style=both -o .libs/libqueue_engine.so.0.0.0
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o: In function `_init':
(.init+0x0): multiple definition of `_init'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o:(.init+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o: In function `_fini':
(.fini+0x0): multiple definition of `_fini'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o:(.fini+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbeginS.o:(.data.rel.ro+0x0): multiple definition of `__dso_handle'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbeginS.o:(.data.rel.ro+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtendS.o:(.dtors+0x0): multiple definition of `__DTOR_END__'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtendS.o:(.dtors+0x0): first defined here
collect2: ld returned 1 exit status
make[2]: *** [libqueue_engine.la] Error 1
make[2]: Leaving directory `/home/vmdev/local/src/q4m-0.8.3/src'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/vmdev/local/src/q4m-0.8.3/src'
make: *** [all-recursive] Error 1

かなりハマった...。

んで、他の人も書いてるけど、configure オプションを注意しつつ、make

僕は、CXXFLAGS に /usr/include/mysql として、AB の rpm が入れたヘッダを指定するようにしました。

--with-mysql=/home/hata/local/src/mysql-5.1.29-rc CXXFLAGS="-I/usr/include/mysql
hata@local: ~/local/src> wget http://q4m.31tools.com/dist/mysql-5.1.28-rc-linux-x86_64-glibc23-with-fast-mutexes-q4m-0.8.3.tar.gz
hata@local: ~/local/src> tar xzvf mysql-5.1.28-rc-linux-x86_64-glibc23-with-fast-mutexes-q4m-0.8.3.tar.gz 
q4m-0.8.3-linux-x86_64/
q4m-0.8.3-linux-x86_64/ChangeLog
q4m-0.8.3-linux-x86_64/config/
q4m-0.8.3-linux-x86_64/config/config.sub
q4m-0.8.3-linux-x86_64/config/depcomp
q4m-0.8.3-linux-x86_64/config/missing
q4m-0.8.3-linux-x86_64/config/compile
q4m-0.8.3-linux-x86_64/config/install-sh
q4m-0.8.3-linux-x86_64/config/config.guess
q4m-0.8.3-linux-x86_64/config/ac_mysql.m4
q4m-0.8.3-linux-x86_64/config/ltmain.sh
q4m-0.8.3-linux-x86_64/examples/
q4m-0.8.3-linux-x86_64/examples/crawler/
q4m-0.8.3-linux-x86_64/examples/crawler/crawler.pl
q4m-0.8.3-linux-x86_64/examples/crawler/initdb.sql
q4m-0.8.3-linux-x86_64/examples/crawler/README
q4m-0.8.3-linux-x86_64/Makefile.in
q4m-0.8.3-linux-x86_64/Makefile.am
q4m-0.8.3-linux-x86_64/t/
q4m-0.8.3-linux-x86_64/t/02-queue.t
q4m-0.8.3-linux-x86_64/t/05-multireader-read.c
q4m-0.8.3-linux-x86_64/t/05-multiwait-core.c
q4m-0.8.3-linux-x86_64/t/Makefile.in
q4m-0.8.3-linux-x86_64/t/04-blob-cond.t
q4m-0.8.3-linux-x86_64/t/Makefile.am
q4m-0.8.3-linux-x86_64/t/08-forward.t
q4m-0.8.3-linux-x86_64/t/02-queue-cond.t
q4m-0.8.3-linux-x86_64/t/05-multireader.t
q4m-0.8.3-linux-x86_64/t/09-pqueue-single-table.t
q4m-0.8.3-linux-x86_64/t/07-trans.t
q4m-0.8.3-linux-x86_64/t/02-queue-owned-delete.t
q4m-0.8.3-linux-x86_64/t/05-multiwait.t
q4m-0.8.3-linux-x86_64/t/06-multi.t
q4m-0.8.3-linux-x86_64/t/03-queue-error-wait.t
q4m-0.8.3-linux-x86_64/t/04-blob.t
q4m-0.8.3-linux-x86_64/t/01-base-rnd_pos.t
q4m-0.8.3-linux-x86_64/t/09-pqueue-single-table-wake-listener.t
q4m-0.8.3-linux-x86_64/t/05-multirw-core.c
q4m-0.8.3-linux-x86_64/t/05-multirw.t
q4m-0.8.3-linux-x86_64/t/01-base.t
q4m-0.8.3-linux-x86_64/t/03-queue-error.t
q4m-0.8.3-linux-x86_64/run_tests.pl
q4m-0.8.3-linux-x86_64/libqueue_engine.so
q4m-0.8.3-linux-x86_64/configure.in
q4m-0.8.3-linux-x86_64/aclocal.m4
q4m-0.8.3-linux-x86_64/configure
q4m-0.8.3-linux-x86_64/INSTALL
q4m-0.8.3-linux-x86_64/COPYING
q4m-0.8.3-linux-x86_64/src/
q4m-0.8.3-linux-x86_64/src/dllist.h
q4m-0.8.3-linux-x86_64/src/Makefile.in
q4m-0.8.3-linux-x86_64/src/Makefile.am
q4m-0.8.3-linux-x86_64/src/queue_cond.h
q4m-0.8.3-linux-x86_64/src/queue_config.h.in
q4m-0.8.3-linux-x86_64/src/ha_queue.h
q4m-0.8.3-linux-x86_64/src/queue_cond.cc
q4m-0.8.3-linux-x86_64/src/adler32.c
q4m-0.8.3-linux-x86_64/src/ha_queue.cc
q4m-0.8.3-linux-x86_64/TODO
q4m-0.8.3-linux-x86_64/AUTHORS
q4m-0.8.3-linux-x86_64/doc/
q4m-0.8.3-linux-x86_64/doc/style.css
q4m-0.8.3-linux-x86_64/doc/top.jpg
q4m-0.8.3-linux-x86_64/doc/install.html
q4m-0.8.3-linux-x86_64/doc/index.html
q4m-0.8.3-linux-x86_64/doc/q4m-modes.gif
q4m-0.8.3-linux-x86_64/doc/tutorial.html
q4m-0.8.3-linux-x86_64/README
q4m-0.8.3-linux-x86_64/support-files/
q4m-0.8.3-linux-x86_64/support-files/install.sql
q4m-0.8.3-linux-x86_64/support-files/install-exec-hook.txt
q4m-0.8.3-linux-x86_64/support-files/q4m-forward
q4m-0.8.3-linux-x86_64/NEWS
hata@local: ~/local/src> cd q4m-0.8.3-linux-x86_64/
hata@local: ~/local/src/q4m-0.8.3-linux-x86_64> ./configure --prefix=/usr --with-mysql=/home/hata/local/src/mysql-5.1.29-rc CXXFLAGS="-I/usr/include/mysql" 
:
:
:
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating t/Makefile
config.status: creating src/queue_config.h
config.status: executing depfiles commands

hata@local: ~/local/src/q4m-0.8.3-linux-x86_64> make
hata@local: ~/local/src/q4m-0.8.3-linux-x86_64> make install

プラグインのインストール

sudo make install後

/usr/lib64/mysql の下に、libqueue_engine.* がおかれているので、プラグインディレクトリにコピーする

もしくは、configure オプションの libdir を指定してみる

./configure --with-mysql=/home/vmdev/local/src/mysql-5.1.29-rc CXXFLAGS="-I/usr/include/mysql" --libdir=/usr/lib64/mysql/plugin

これでやると、なぜか mysql/plugin にコピーされる(libdirの動きとしていいのか...?)

OSX(10.5.5)に入れる

port から MySQL5.1.29を入れる

MySQL AB のものだとハマったので、今回は port を使う

hata@mac.local > sudo port install mysql5-devel

Q4M のインストール

linux と同じように無印は使わず、mysql-5.1.28-rc-osx10.4-i686-without-fast-mutexes-q4m-0.8.3.tar.gzを使う

hata@mac.local ~/local/src/q4m> ./configure --prefix=/opt/local --with-mysql=/Users/yusukehata/tmp/mysql-5.1.29-rc CFLAGS="-I/opt/local/include/mysql5/mysql -I/opt/local/include" CPPFLAGS="-I/opt/local/include/mysql5/mysql -I/opt/local/include"

また、以下のようにシンボリックリンク貼っておく必要がある

> sudo ln -s /opt/local/include/mysql5/mysql/ /opt/local/include/mysql
> sudo ln -s /opt/local/lib/mysql5/mysql/ /opt/local/lib/mysql

MySQL AB のバイナリで、ビルド時に発生するエラーとか

もしかしたら、portのでも出たかも

source='05-multireader-read.c' object='05_multireader_read-05-multireader-read.o' libtool=no \
    depfile='.deps/05_multireader_read-05-multireader-read.Po' tmpdepfile='.deps/05_multireader_read-05-multireader-read.TPo' \
    depmode=gcc3 /bin/sh ../config/depcomp \
    gcc -DHAVE_CONFIG_H -I. -I. -I../src -I/Users/yusukehata/tmp/mysql-5.1.29-rc/sql -I/Users/yusukehata/tmp/mysql-5.1.29-rc/include -I/Users/yusukehata/tmp/mysql-5.1.29-rc/regex -I/Users/yusukehata/tmp/mysql-5.1.29-rc  -I/opt/local/include/mysql5/mysql -I/opt/local/include  -I/opt/local/include/mysql5/mysql -I/opt/local/include -Wall -c -o 05_multireader_read-05-multireader-read.o `test -f '05-multireader-read.c' || echo './'`05-multireader-read.c
/bin/sh ../libtool --preserve-dup-deps --mode=link gcc  -I/opt/local/include/mysql5/mysql -I/opt/local/include -Wall -L/usr/local/mysql/lib  -o 05-multireader-read  05_multireader_read-05-multireader-read.o -lmysqlclient 
mkdir .libs
gcc -I/opt/local/include/mysql5/mysql -I/opt/local/include -Wall -o 05-multireader-read 05_multireader_read-05-multireader-read.o  -L/usr/local/mysql/lib /usr/local/mysql/lib/libmysqlclient.dylib -lm
ld: warning in /usr/local/mysql/lib/libmysqlclient.dylib, file is not of required architecture
Undefined symbols:
  "_mysql_real_connect", referenced from:
      _main in 05_multireader_read-05-multireader-read.o
  "_mysql_store_result", referenced from:
      _do_select in 05_multireader_read-05-multireader-read.o
  "_mysql_init", referenced from:
      _main in 05_multireader_read-05-multireader-read.o
  "_mysql_free_result", referenced from:
      _main in 05_multireader_read-05-multireader-read.o
      _main in 05_multireader_read-05-multireader-read.o
  "_mysql_query", referenced from:
      _do_select in 05_multireader_read-05-multireader-read.o
  "_mysql_close", referenced from:
      _main in 05_multireader_read-05-multireader-read.o
  "_mysql_fetch_row", referenced from:
      _main in 05_multireader_read-05-multireader-read.o
      _main in 05_multireader_read-05-multireader-read.o
  "_mysql_num_rows", referenced from:
      _main in 05_multireader_read-05-multireader-read.o
      _main in 05_multireader_read-05-multireader-read.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make[1]: *** [05-multireader-read] Error 1
make: *** [all-recursive] Error 1

この状態だと、PHPとかビルドするときにエラーになる...

ld: warning in /usr/local/mysql/lib/libmysqlclient.dylib, file is not of required architecture
ld: warning in /usr/local/mysql/lib/libmygcc.a, file is not of required architecture
Undefined symbols:
  "_mysql_get_server_info", referenced from:
      _pdo_mysql_get_attribute in mysql_driver.o
  "_mysql_stmt_close", referenced from:
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
  "_mysql_stmt_fetch", referenced from:
      _pdo_mysql_stmt_fetch in mysql_statement.o
  "_mysql_next_result", referenced from:
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
  "_mysql_stmt_errno", referenced from:
      __pdo_mysql_error in mysql_driver.o
  "_mysql_stmt_store_result", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_real_escape_string", referenced from:
      _mysql_handle_quoter in mysql_driver.o
  "_mysql_stmt_init", referenced from:
      _mysql_handle_preparer in mysql_driver.o
  "_mysql_affected_rows", referenced from:
      _mysql_handle_doer in mysql_driver.o
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
  "_mysql_stmt_prepare", referenced from:
      _mysql_handle_preparer in mysql_driver.o
  "_mysql_stmt_sqlstate", referenced from:
      __pdo_mysql_error in mysql_driver.o
  "_mysql_fetch_fields", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
  "_mysql_get_host_info", referenced from:
      _pdo_mysql_get_attribute in mysql_driver.o
  "_mysql_get_server_version", referenced from:
      _mysql_handle_preparer in mysql_driver.o
  "_mysql_free_result", referenced from:
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
  "_mysql_options", referenced from:
      _pdo_mysql_handle_factory in mysql_driver.o
      _pdo_mysql_handle_factory in mysql_driver.o
      _pdo_mysql_handle_factory in mysql_driver.o
      _pdo_mysql_handle_factory in mysql_driver.o
      _pdo_mysql_handle_factory in mysql_driver.o
      _pdo_mysql_handle_factory in mysql_driver.o
  "_mysql_close", referenced from:
      _mysql_handle_closer in mysql_driver.o
  "_mysql_stmt_affected_rows", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_fetch_row", referenced from:
      _pdo_mysql_stmt_fetch in mysql_statement.o
  "_mysql_num_fields", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
  "_mysql_store_result", referenced from:
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
  "_mysql_get_client_info", referenced from:
      _zm_info_pdo_mysql in pdo_mysql.o
      _pdo_mysql_get_attribute in mysql_driver.o
  "_mysql_real_query", referenced from:
      _mysql_handle_doer in mysql_driver.o
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_errno", referenced from:
      __pdo_mysql_error in mysql_driver.o
      _mysql_handle_preparer in mysql_driver.o
      _pdo_mysql_stmt_fetch in mysql_statement.o
  "_mysql_error", referenced from:
      __pdo_mysql_error in mysql_driver.o
      __pdo_mysql_error in mysql_driver.o
  "_mysql_stmt_bind_result", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_sqlstate", referenced from:
      __pdo_mysql_error in mysql_driver.o
  "_mysql_stmt_result_metadata", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_insert_id", referenced from:
      _pdo_mysql_last_insert_id in mysql_driver.o
  "_mysql_use_result", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
  "_mysql_stmt_bind_param", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_stmt_attr_set", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_num_rows", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_init", referenced from:
      _pdo_mysql_handle_factory in mysql_driver.o
  "_mysql_real_connect", referenced from:
      _pdo_mysql_handle_factory in mysql_driver.o
  "_mysql_more_results", referenced from:
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_dtor in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
  "_mysql_stmt_execute", referenced from:
      _pdo_mysql_stmt_execute in mysql_statement.o
  "_mysql_ping", referenced from:
      _pdo_mysql_check_liveness in mysql_driver.o
  "_mysql_stmt_param_count", referenced from:
      _mysql_handle_preparer in mysql_driver.o
  "_mysql_stmt_free_result", referenced from:
      _pdo_mysql_stmt_next_rowset in mysql_statement.o
      _pdo_mysql_stmt_cursor_closer in mysql_statement.o
  "_mysql_stat", referenced from:
      _pdo_mysql_get_attribute in mysql_driver.o
  "_mysql_fetch_lengths", referenced from:
      _pdo_mysql_stmt_fetch in mysql_statement.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make: *** [libs/libphp5.bundle] Error 1

なぞ

ちなみに、MySQL AB は

Mach-O 64-bit で x86_64 となってるのだが

> file /usr/local/mysql/lib/libmysqlclient.16.0.0.dylib
/usr/local/mysql/lib/libmysqlclient.16.0.0.dylib: Mach-O 64-bit dynamically linked shared library x86_64
> file /usr/local/mysql/lib/libmysqlclient.dylib
/usr/local/mysql/lib/libmysqlclient.dylib: Mach-O 64-bit dynamically linked shared library x86_64
/usr/local/mysql/bin/mysqlbug
>> CFLAGS='-g -Os -arch x86_64 -fno-common'  CXX='gcc -static-libgcc'  CXXFLAGS='-g -Os -arch x86_64 -felide-constructors -fno-common'

/usr/local/mysql/bin/mysql_config 
Usage: /usr/local/mysql/bin/mysql_config [OPTIONS]
Options:
        --cflags         [-I/usr/local/mysql/include  -g -Os -arch x86_64 -fno-common   -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DIGNORE_SIGHUP_SIGQUIT  -DDONT_DECLARE_CXA_PURE_VIRTUAL]
        --include        [-I/usr/local/mysql/include]
        --libs           [-L/usr/local/mysql/lib -lmysqlclient -lz -lm     -lmygcc]
        --libs_r         [-L/usr/local/mysql/lib -lmysqlclient_r -lz -lm     -lmygcc]
        --plugindir      [/usr/local/mysql/lib/plugin]
        --socket         [/tmp/mysql.sock]
        --port           [0]
        --version        [5.1.29-rc]
        --libmysqld-libs [-L/usr/local/mysql/lib -lmysqld -lz -lm       -lmygcc]

まとめ

無事、試行錯誤しながら何とかインストール出来た。もしかしたら wassr でその時の苦悩の様子がライブ中継してるかもしれないけど、何度、試行錯誤したことか...。
環境が違ったときにインストール方法とか微妙に違うのが面倒だねぇ

ま、インストールにハマると長引くので危険。誰かがやってた環境と同じのに合わせるのが一番早いのかもね

2008/12/05

フラワーロック 2.0 と CruiseControl を連携させてみた

ポスト @ 2:36:13 , 修正 @ 2008/12/08 0:13:13 | , ,     

CruiseControlとLEDの組み合わせが海外でも流行っている(?)みたいですが、日本ならフラワーロック2.0だろ!ってことでやってみた。
CruiseControl を使ったもう一つのソリューション


↑アップから。かわいいヤツです。


↑ビルドに失敗して FAILED のときの様子。エラーになってると踊って光っちゃいます。

作り方

まずは、フラワーロックを買ってくる

これが無いと始まりません。
近所のおもちゃ屋さん、電気店、アマゾンなどで購入しましょう

フラワーロックの仕様を確認する

フラワーロック 2.0 の主な仕様を確認します。

フラワーロック 2.0 (コスモスtype)
  • 発売元: タカラトミー
  • レーベル: タカラトミー
  • スタジオ: タカラトミー
  • メーカー: タカラトミー
  • 価格: ¥ 3,999 (34% OFF)
  • 発売日: 2008/10/31
  • 売上ランキング: 548

  • インプットは電源の On/Off と、モードボタン、入力切り替え(Line/マイク)と音声入力のラインしかない
  • その内、フラワーロックを操作できるのは、音声入力の LINE しかない(音に反応するため)
  • フラワーロックは、何も入力が無いと 5分後(5分間経過後) スリープする
  • スリープしたフラワーロックを起こすには、電源の On/Off ボタンを押すしかない
  • フラワーロックのLEDを任意の色で発色させることはできない
  • フラワーロックの踊りを任意のインプットで踊らせることができない

ちなみに、フラワーロックは単3電池 4本、もしくは AC の専用電源が必要です。長時間使うなら電池よりも AC を買うことをおすすめします。

CruiseControl で動かす方法を検討する

CruiseControl(以下、CC) に組み込む方法として、今回は音声入力を主として動かすため、以下のようにしました。

  • CruiseControl でビルドログを、jabber でメッセージを飛ばす

  • jabber クライアントがメッセージの内容を読み取り、ビルドに失敗(BUILD FAILED)となっていた場合は、エラーミュージックを流す。
  • jabber クライアントがメッセージの内容を読み取り、ビルドに成功(Build passed)となっていた場合は、何もしない
  • 5分でフラワーロックは眠ってしまうので、定期的(5分毎)に keepalive (というかwakeon) を ping する。(実際には「ポーン」とか「ピンポン」みたいな音を流す)

CC に組み込む

CC のプラグインjabber があるので、それを使ってみます。

  <project>
    <publishers>
      :
      :
      <jabber
          host="talk.google.com"
          port="5223"
          username="<flower.sender@gmail.com>"
          password="<flower.sender.password>"
          recipient="<flower.receiver@gmail.com>"
          ssl="true"
          buildresultsurl="true"
      />
      :
      :
    </publishers>
    :
    :
  </project>

ここで、注意しなければならないのは、<onsuccess>や<onfailure>で囲ってしまうと、送信されたメッセージをクライアント側で振り分けることができなくなってしまうので、とにかく全部のメッセージをクライアントに送ってしまうようにします。

jabber メッセージを読み込んで音を流す

僕は、ここの部分は手抜きしています。なぜなら最近のメッセンジャー的なものには、メッセージをフィルタリングしておくことが可能で、フィルタリングしたメッセージに応じてアクションを書くことも簡単にできるため。

ということで、Kopeteを使ってみるサンプルを置いておきます。

Kopete の「設定」から「プラグインの設定」へ
その後「強調」より、フィルタリングすぐ文字列を設定(今回は「FAILED」)して、「サウンドの再生」よりBuild Failed な音楽を指定します。


ブラグインの設定へ 行き


failed になったときのサウンドを設定しておく

これで後は、CC が勝手にエラーになったらメッセージを流してくれます。failed になっている間はずっと音楽を流して、「エラーの舞」をフラワーロックに踊らせちゃいましょう。

それ以外の方法

実は、上のCCでメッセージを送信して、Kopete などで受信するという方法は、フラワーロックと少し相性が悪いです。
なぜなら、フラワーロックは「5分間何もしないと、スリープする」という省電力設計。しかも復帰するには、On/Off ボタンを押さなければ復帰できない(音を流すだけでは復帰できない)ので、定期的に「起こして」あげる必要があります。

上記方法だと、ビルドにすごく時間がかかってしまって、CCが300秒に一回メッセージを流していたとしても、失敗してしまうことがあります。
なので、今回は ruby スクリプトで、定期的にフラワーロックを起こしてあげる keepalived なスクリプトを投げやりに書きました。

メッセージを送信するヤツ

#!/usr/bin/env ruby

require 'rubygems'
require 'xmpp4r'
require 'kconv'

class Message
    def initialize(user, password)
        @user = user
        @password = password
        @client = nil
    end

    def connect(server, port)
        @client = Jabber::Client.new(Jabber::JID.new(@user))
        @client.connect(server, port)
        @client.auth(@password)
    end

    def send(to, body)
        @client.send(Jabber::Message.new(to, body))
        puts body._to_s + Time.now.to_s
    end

    attr_accessor :user, :password
end

yaml に色々設定を書いておく

#
# config.yml
#
# flowerrock:
#   jabber:
#     user: <flower.sender@gmail.com>
#     password: <flower.sender.password>
#     server: talk.google.com
#     port: 5222
#   post:
#     members:
#       - <flower.receiver@gmail.com>
#       - <hoge.foo.bar@gmail.com>
#   feed:
#     urls:
#       - http://hoge.foo.bar/rss
#       - http://qwerty/rss
#     interval: 250
#

config = YAML.load_file(File.dirname(File.expand_path(__FILE__)) + '/config.yml')
settings = config['flowerrock']
jabber = settings['jabber']
post = settings['post']
feed = settings['feed']

user = jabber['user']
pass = jabber['password']
server = jabber['server']
port = jabber['port']

to_users = post['members']

feed_urls = feed['urls']
interval = feed['interval']

RSS(cruisecontrol/rss)を読み込んで、メッセージを飛ばすヤツ

#!/usr/bin/env ruby

require 'rubygems'
require 'yaml'
require 'open-uri'
require 'rexml/document'

message = Message.new(user, pass)
message.connect(server, port)

loop do
    to_users.each{|user|
        message.send(user, 'initialize...')
    }

    feed_urls.each{|feed|
        page = open(feed)
        doc = REXML::Document.new(page)
        REXML::XPath.each(doc, '//item') {|item|
            REXML::XPath.each(item, 'description') {|desc|
                if desc.text =? /FAILED/i
                    REXML::XPath.each(item, 'title') {|title|
                        to_users.each{|user|
                            message.send(user, title.text)
                        }
                    }
                end
            }
        }
    }
    to_users.each{|user|
        message.send(user, 'end...')
    }
    sleep interval
end

ruby スクリプトは汚い。でも動いているうちは良しとする。

CC と フラワーロック 2.0 を連携させる

後は、PC のライン出力にコードをぶっ刺して、フラワーロックにつなぐだけ!
ビルドに失敗しているときに、正しく「ビルド失敗の舞」をフラワーロックが踊ってくれれば OK

ちなみに、フラワーロックが持っている省電力機構のおかげで、夜中はCCを動かさない。とか、音を出さないようにするだけで、夜な夜なフラワーが踊ると言うことはないので、警備員さんにも安心設計!

ぜひ、あなたのプロジェクトの継続的インテグレーションの一貫として、フラワーロックを組み込んでみませんか?
花が増えて、プロジェクトの和ませ役にピッタリの一台です!

その他雑感

ここ最近、海外のCC連携ガジェットを何個もみてきたけど、やっぱあっちのはwifiがついていたり、LEDがもう少し簡単に操作できたりと、面白いのが多い。
フラワーロックはそれに比べると、ライン入力だけと言うシンプルなもの。もう少し弄れるようにすると、海外の市場でも売れるんじゃないかなぁ。ガジェット市場では特に。
だって、変なウサギ(あえてリンクは張らないけど)が売れて、インターネットランプなんてものが売り出されるなら、フラワーロックはもっとイケるハズ!インタフェースがもう少し多ければ。だけど。

近いうちに Arduino とかにも挑戦して見ようと思う。年末か年明けにでも。。

2008/11/16

PHP の self と parent に気をつけろ

ポスト @ 21:05:49 , 修正 @ 2008/11/16 21:18:34 |     

これまた、どうでもいいことシリーズ

php の self と parent は予約語ではないため、以下のようなことが行えます。

define('self', 123); 
var_dump(self); // int(123)

define('parent', 456);
var_dump(parent); // int(456)

しかしながら、class 内でこれら self parentを使っても問題なく利用できます。

define('self', 'Foo'); 
                       
class Hoge {           
    const A = 123;     
    static function getA(){
        return self::A;
    }                  
}                      
                       
class Foo {            
    const A = 456;     
}                      
                       
var_dump(Hoge::getA()); //123

ってか、これ正しく動いてるのだろうか...

class Hoge {
    static function foo(){
        return new self;
    }
}

class Foo extends Hoge {
    static function bar(){
        return new parent;
    }
}

define('self', 'Foo');
define('parent', 'Foo');

$a = spl_object_hash(Hoge::foo());
$b = spl_object_hash(Foo::bar());
var_dump($a, $b);

$a = Hoge::foo();
$b = Foo::bar();
var_dump($a, $b);
var_dump(spl_object_hash($a), spl_object_hash($b));

結果としては

string(32) "af5c83e899a2154cfdf3e4ff093419e7"
string(32) "af5c83e899a2154cfdf3e4ff093419e7"
object(Hoge)#1 (0) {
}
object(Hoge)#2 (0) {
}
string(32) "af5c83e899a2154cfdf3e4ff093419e7"
string(32) "2c04186690093c93e846c3dee1064aca"

うーん。。。

追記

ってか、最後のはselfとかそういうの関係なかった。objectのhashを取るには一度ちゃんと変数に入れる必要があるんだね。

class Hoge {                                    
    static function foo(){                      
        return new self;                        
    }                                           
}                                               
                                                
var_dump(spl_object_hash(Hoge::foo()), spl_object_hash(Hoge::foo()));
                                                
$a = Hoge::foo();                               
$b = Hoge::foo();                               
var_dump(spl_object_hash($a), spl_object_hash($b)); 
string(32) "af5c83e899a2154cfdf3e4ff093419e7"
string(32) "af5c83e899a2154cfdf3e4ff093419e7"
string(32) "af5c83e899a2154cfdf3e4ff093419e7"
string(32) "2c04186690093c93e846c3dee1064aca"

勉強になった。

2008/11/06

第2回設計勉強会に参加してきた

ポスト @ 23:31:32 | ,     

第2回設計勉強会に参加してきました。
まず、こういった機会を提供してくれた id:shimookaさん、ありがとうございます。株式会社ディノさんも会場の提供ありがとうございます。LINDさんも毎度ありがとうです。

とりあえず、資料を置いておきます。

Event Php Study Design 2
View SlideShare presentation or Upload your own. (tags: php design)

ちなみに、Teedaのレイヤーは Teeda Extension featuring Goya 〜アーキテクチャ【レイヤー構成】〜 - たかのり日記 さんのを、チョーそのまま使ってます
Hermit は http://svn.coderepos.org/share/lang/php/misc/Hermit/

今回の発表は、すいません。あまりまとめきれていなくて、早足で話しすぎました。ust の方もすいません、ust 忘れてました。今後はustにもわかりやすいように資料を作ったほうがいいっすね。

後、今回は僕が日頃思っていた疑問をそのまま発表してしまって、なんともまぁgdgdですが、色々と意見交換ができてすごーくためになりました。
今回、設計勉強会で発表するために、「設計だし、コードは見えないプレゼンにしよう。実装はいいっしょ」と思ってたんですが、見えたほうが理解早いっすね。反省。

kunitさんのsymfonyの「罪悪感 & 諦め」は僕もそれです。でも諦めないっすよw
shimooka さんの発表はすげー親近感たっぷりで、別の意味で楽しめましたwあと、色々とノウハウを発表していただいたので、ためになります。
他にも、沢山の方と話すことができました。為になります。「納期があるんだし、FWからの離脱はしゃーない。諦めろ」とか「Propelはしゃーない。諦めろ」とみんな諦めすぎっす

ちなみに、Hermit ですが、僕のプロジェクトでしか使っていない。悲しいやつです。
ref - http://svn.coderepos.org/share/lang/php/misc/Hermit/
いつかちゃんとした形にまとめますが、S2Dao.PHP5 をもっと PHP でも使えるようにはモットーです。
叩いてくれる方、開発してくれる方、よろしくです。(僕もどんどん書いていきますが)

あ、最後に。Daoのコネクションどーする?ってやつは、Hermit & Symfony で Master/Slave の構成だとこんな感じに書きます。(shimookaさんのPropel::getConnection() は symfony の DatabaseManager.getConnection(name)ですね)

class DataSourceSelector {
  
  const MASTER_DBMS_NS = 'master';
  const SLAVE_DBMS_NS = 'slave';
  
  protected static $specSource = array(
    'HogeDao' => self::MASTER_DBMS_NS,
    'FooDao' => self::SLAVE_DBMS_NS,
    'BarDao::callProcA' => self::MASTER_DBMS_NS
  );
  
  protected $dbManager;
  public static function bindHermitDataSource(sfDatabaseManager $manager){
    $instance = new self;
    $instance->dbManager = $manager;
    HermitDataSourceManager::setCallback(array(
      $instance, 'getConnectionCallback'
    ));
  }
  public function getConnectionCallback($targetClass, $methodName, $type){
    $database = $this->fetchDatabase($targetClass, $methodName, $type);    
    return $database->getConnection();
  }
  
  protected function fetchDatabase($targetClass, $methodName, $type){
    if(isset(self::$specSource[$targetClass])){
      return $this->dbManager->getDatabase(self::$specSource[$targetClass]);
    }
    return $this->getDefaultDatabase($type);
  }
  
  protected function getDefaultDatabase($type){
    if(HermitEvent::isInit($type)){
      return $this->getSlave();
    }
    if(HermitEvent::isRead($type)){
      return $this->getSlave();
    }
    if(HermitEvent::isWrite($type)){
      return $this->getMaster();
    }
    if(HermitEvent::isProcedure($type)){
      return $this->getMaster();
    }
    return $this->getMaster();
  }
  
  protected function getMaster(){
    return $this->dbManager->getDatabase(self::MASTER_DBMS_NS);
  }
  
  protected function getSlave(){
    return $this->dbManager->getDatabase(self::SLAVE_DBMS_NS);
  }
}

以前のログ