Hatena::ブログ(Diary)

Flast?なにそれ、おいしいの? このページをアンテナに追加 RSSフィード Twitter

2012-04-23

Blog移行のお知らせ

相当前から移行してたけどアナウンスしてないこと完全に忘れてたので今更ながら...

多分twitterとかで知ってると思いますが、 http://flast.hateblo.jp/ に移ってます。
ネットワーク関係は http://flast-net.hateblo.jp/ こちらに

こっちは消さないですが戻ってくることは無いです

2011-12-29

libstdc++.7のversioned namespaceに起因する問題

GCC4.6.3/4.7.0のlibstdc++から修正されたversioned namespaceを有効にするとboost/detail/container_fwd.hppのforward declとぶつかって曖昧性が生じるためコンパイルエラーになる部分が発生しています。
Interprocessでも同様の問題があったようですが、こちらは修正済みです。

既に報告しましたがこれがどのサブライブラリ下にあるのか私はちょっとよくわかってないので(Boost.Containersかと思ったけど違うよう)対応までには時間がかかるかもしれないです。

#6323 (Conflicting forward declarations with versioned namespace in libstdc++.) ? Boost C++ Libraries
#6287 (hardcoded fwd declarations don’t work for gcc configured withor --enable-symvers=gnu-versioned-namespace) ? Boost C++ Libraries

あと私は4.6.3でしか試してないので4.7.0の方はもう少し違うかもしれないです。

現在のwaはboost/detail/container_fwd.hppで以下のようにします。

Index: container_fwd.hpp
===================================================================
--- container_fwd.hpp	(revision 76217)
+++ container_fwd.hpp	(working copy)
@@ -110,6 +110,8 @@
 
 namespace std
 {
+namespace __7
+{
     template <class T> class allocator;
     template <class charT, class traits, class Allocator> class basic_string;
 
@@ -124,6 +126,7 @@
 #else
     template <class T> class complex;
 #endif
+}
 
 #if !defined(BOOST_CONTAINER_FWD_BAD_DEQUE)
     template <class T, class Allocator> class deque;
@@ -140,8 +143,11 @@
 #if !defined(BOOST_CONTAINER_FWD_BAD_BITSET)
     template <size_t N> class bitset;
 #endif
+namespace __7
+{
     template <class T1, class T2> struct pair;
 }
+}
 
 #if defined(BOOST_MSVC)
 #pragma warning(pop)

いくつかの定義がstd::__7に移っているのでnamespaceを修正すれば問題ないです。
次のBoost 1.49.0で直ってるといいのですが。

2011-12-25

bjam AdC jp 2011 25日目

らすとです。

jamではなくPython

Python知らんし別にJamで困ってないので問題ないのではということに気づきました。

まとめ

結局私を助けてくれる人は一人もいませんでした。

bjam AdC jp 2011 23日目

はー

Boost.Build PythonPortとは

ラスト二回はPythonPortについてにします。と言っても使ったこと無いのでドキュメント見ながら書いてます。
Boost.Build PythonPortに関するドキュメントはこちら

※注 Boost.Build PythonPortは現状でexperimental扱いです。www.boost.orgではアンドキュメント扱いなので場合によっては正しくビルドされないかも知れません。

とりあえずこのPythonPortとはなんぞやということから。本家ではBoost.Build(bjam)には以下問題があると言っています。

  • どマイナーすぎてだれも使ってないし開発者がいないのはやばい
  • bjamいみわからん
  • 標準ライブラリないし使えない
  • データ構造が文字列のリストしか無くて使えないし遅い

肯定しかできないのが素晴らしいですね。
ということで、メジャーだしBoost.PythonあるしPython使おうということになったのです。

現状ですべて実装されているようです。また、1:1で対応がとれているらしいので使ってみるといいかも知れないです。
と書かれてますが一部エラーになったりしました...

Boost.Build PythonPortのビルド

実はtools/build/v2/bootstrap.shを実行するだけではPythonPortは有効になりません。
1.48.0現在は以下のようにする必要があります。

cd $BOOST_ROOT/tools/build/v2
./bootstrap.sh
cd engine
./build.sh --with-python=/usr
TOOLSET=`./build.sh --guess-toolset`
ARCH=`./bootstrap/jam0 -d0 -f build.jam --toolset=$TOOLSET --toolset-root= --show-locate-target`
cp -f engine/$ARCH/b2 engine/$ARCH/bjam ..
cd ..
sudo ./b2 install --prefix=$PREFIX
sudo cp -f *.py *.pyc $PREFIX/share/boost_build/


bootstrap.shに特設渡すことができないので面倒です。Windows環境では--with-pythonPythonがインストールされてるパスを渡した上で、Boost.Buildがインストールされたディレクトリにも必要なファイルをコピーする必要があります。

Boost.Build PythonPortでビルドする

以上のことをしてPythonPortが有効なbjamを用意したらあとは手元のプロジェクトをビルドするだけです。
いつもの通りbjamを起動するだけではPythonPortは使用されません。bjamに--pythonを渡します。

tools/build/v2/example/helloをPythonPortでビルドしてみると以下のようになります。

$ bjam --python
Boost.Build V2 Python port (experimental)
...found 10 targets...
...updating 4 targets...
common.MkDir1-quick-fix-for-unix ./bin/gcc-4.6.3
common.MkDir1-quick-fix-for-unix ./bin/gcc-4.6.3/debug
gcc.compile.c++ ./bin/gcc-4.6.3/debug/hello.o
gcc.link ./bin/gcc-4.6.3/debug/hello
...updated 4 targets...


ちなみにJamでビルドするとこんな感じ。微妙に違います。
$ bjam
...found 9 targets...
...updating 5 targets...
common.mkdir bin
common.mkdir bin/gcc-4.6.3
common.mkdir bin/gcc-4.6.3/debug
gcc.compile.c++ bin/gcc-4.6.3/debug/hello.o
gcc.link bin/gcc-4.6.3/debug/hello
...updated 5 targets...

つまり

次回はPythonで書きましょう。

2011-12-21

bjam AdC jp 2011 21日目

つらいれす...

フラグとか共通化

feature.extend toolset : nvcc ;
toolset.inherit-generators nvcc : unix : unix.link unix.link.dll ;
toolset.inherit nvcc : unix ;

generators.override nvcc.prebuilt : builtin.lib-generator ;
generators.override nvcc.prebuilt : builtin.prebuilt ;
generators.override nvcc.searched-lib-generator : searched-lib-generator ;

ここらへんは既存のものを使い回すための設定です。最初の1行だけはnvccというtoolsetがあるという登録をしてる感じです。

rule init ( version ? : command * : options * )
{
	local condition = [ common.check-init-parameters nvcc : version $(version) ] ;
	local command = [ common.get-invocation-command nvcc : nvcc : $(command) ] ;
	handle-options nvcc : $(condition) : $(command) : $(options) ;
}

local rule handle-options ( toolset : condition * : command * : options * )
{
	common.handle-options $(toolset) : $(condition) : $(command) : $(options) ;

	flags nvcc.compile.cu OPTIONS $(condition)
	  : [ feature.get-values <cuflags> : $(options) ]
	  : unchecked ;
}

using nvcc ; で呼ばれるinit ruleです。これはtoolsetの初期化を行ないます。バージョン番号を取得したり色々します。
今回は特に何もしてないです。大体こんなことかいてあればいい感じらしいので適当です。

で、generatorがくるのですが、これは前々回やってるので飛ばします。

コンパイラオプション周りの定義です。

flags nvcc.compile OPTIONS <address-model>32 : -m32 ;
flags nvcc.compile OPTIONS <address-model>64 : -m64 ;

flags nvcc.compile OPTIONS <debug-symbols>on : -g ;

feature device-debug-symbols : off on : propagated ;
flags nvcc.compile OPTIONS <device-debug-symbols>on : -G ;

flags nvcc.compile DEFINES <define> ;
flags nvcc.compile INCLUDES <include> ;

feature cuflags : : free optional ;
flags nvcc.compile.cu OPTIONS <cuflags> ;

flags nvcc.link LINKPATH <library-path> ;
flags nvcc.link LIBRARIES <library-file> ;
flags nvcc.link OPTIONS <linkflags> ;

feature ruleはrequirementsなどに書くfeatureを定義します。いくつかはbuiltinで定義されています。ここではnvcc特有のデバイスコードのデバッグシンボルを含めるかのフラグと<cflags>/<cxxflags>とは違うCUDA専用のフラグとして<cuflags>を定義します。
featureにも種類があって、何でも記述できるfreeや無くても問題ないoptionalなどがいくつかあります。公式のドキュメントに書いてあるので探すといいです。

次にflags ruleです。これは定義したfeatureをどうやってコマンドに投げつけるかという簡単な対応を記述できます。

flags nvcc.compile OPTIONS <device-debug-symbols>on : -G ;

という書き方のだと、

nvcc toolsetでコンパイルする場合に<device-debug-symbols>onというrequirementsが記述されていればOPTIONSというシェル変数に-Gを追加する

となります。OPTIONSはjamで使われる変数ではなく、actionsなどで使われるシェル変数であることに注意してください。
同じ要領でインクルードパスやリンカフラグも設定します。

コンパイラに渡す部分

rule compile.cu ( target * : sources * : properties * )
{
	local target = [ feature.get-values target : $(properties) ] ;
	switch $(target)
	{
		case "" : OPTIONS on $(targets) += -c ;
		case * : EXIT "unknown target" ;
	}
}
actions compile.cu
{
	$(CONFIG_COMMAND) $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -o "$(<)" "$(>)"
}

actions link bind LIBRARIES
{
	$(CONFIG_COMMAND) $(OPTIONS) -L"$(LINKPATH)" -o "$(<)" "$(>)" $(LIBRARIES)
}

rule名と同名のactionsがある場合、ruleを処理してからactionsが呼ばれます。また、actions内ではruleの第1引数は$(>)第2引数は$(<)という変数に格納されます。
$(CONFIG_COMMAND)はinit rule内で呼び出したcommon.handle-options内で定義されます。とりあえず適当にやった甲斐があったものです。

また、rule側でactionsで使用するシェル変数を定義したり変更したりしたいことがあります。この時、シェル変数はターゲット毎に空間が用意されているので、どのターゲットに対して操作するかを指定した上で操作する必要があります。
それが OPTIONS on $(targets) という書き方で、この場合 <target> が無いのでデフォルトでオブジェクトを生成するように-cオプションを追加しています。

また、bjamのsequenceについて面白い特性を見ることができます。通常、シェルスクリプトなどで

var = hoge fuga piyo
echo x$(var)

とすると、出力結果は

xhoge fuga piyo

となります。

しかし、bjamではsequenceにスペースで区切られていない隣合ったものがある場合、すべての順列に展開します。つまり

var = hoge fuga piyo ;
ECHO x$(var) ;

の出力結果は

xhoge xfuga xpiyo

となります。すべての順列なので、複数のsequenceが隣り合っている場合、

var1 = hoge fuga piyo ;
var2 = foo bar baz ;
ECHO $(var1)$(var2) ;

の出力結果は

hogefoo hogebar hogebaz fugafoo fugabar fugabaz piyofoo piyobar piyobaz

となります。

これを利用すると、すべてのインクルードパスに-Iを付けることも、マクロに-Dを付けることも容易です。
これでコンパイルに必要な引数がうまく生成されるわけです。

まとめ

あと2日分のネタがありません。

2011-12-19

bjam AdC jp 2011 19日目

やっべーはてな時間で日付計算してたわー

前回大まかな流れを説明したので今回はtype/cuda.jamです。が、これは単純で説明も何も無いのでちょっと掘り進めます。

scanner

前回ものすごくシンプルなcuda-scannerを示しました。というより単純に派生して何もしてないだけですが。
で、このscannerが何をしているかってのを見ていきます。
どうでもいいですが、ちょっと調べてみたらscannerのctorは1 sequence受け取ればいいようなので、cuda-scanner.__init__ ruleみたいに9まで並べる必要は無いです。

cuda-scannerの継承関係は

cuda-scanner -> c-scanner(tools/types/cpp.jam) -> scanner(build/scanner.jam)

となっています。

scannerクラスを見ると

class scanner
{
	rule __init__ ( )
	{
	}

	rule pattern ( )
	{
		error "method must be overriden" ;
	}

	rule process ( target : matches * )
	{
		error "method must be overriden" ;
	}
}

となっていることから、pattern ruleとprocess ruleがあればとりあえず問題ないということがわかります。abstractキーワード的なのはbjamには無いのでとりあえずerrorにしている感じです。

問題のc-scannerは主要な部分だけ抜き出すと以下のようになってます。

    rule pattern ( )
    {
        return "#[ \t]*include[ ]*(<(.*)>|\"(.*)\")" ;
    }
 
    rule process ( target : matches * : binding )
    {
        local angle  = [ regex.transform $(matches) : "<(.*)>"   ] ;
        angle = [ sequence.transform path.native : $(angle) ] ;
        local quoted = [ regex.transform $(matches) : "\"(.*)\"" ] ;
        quoted = [ sequence.transform path.native : $(quoted) ] ;
 
        local g = [ on $(target) return $(HDRGRIST) ] ;
        local b = [ NORMALIZE_PATH $(binding:D) ] ;
 
        local g2 = $(g)"#"$(b) ;
 
        angle = $(angle:G=$(g)) ;
        quoted = $(quoted:G=$(g2)) ;
 
        local all = $(angle) $(quoted) ;
 
        INCLUDES $(target) : $(all) ;
        NOCARE $(all) ;
        SEARCH on $(angle) = $(self.includes:G=) ;
        SEARCH on $(quoted) = $(b) $(self.includes:G=) ;
 
        scanner.propagate $(__name__) : $(angle) $(quoted) : $(target) ;
 
        ISFILE $(angle) $(quoted) ;
    }

pattern ruleでソースを走査する際に使う正規表現を定義します。この正規表現をずっと使うことになるので、意味不明な構文を持つ言語だとやたら変な正規表現を書くことになると思います。includeの次の空白部分に\tが含まれていないのが若干気になりますが後でバグレポ投げときます。多分。
この正規表現でマッチした場合、\1がprocess ruleのmatchesに投げられることになるので、依存しているファイル名やモジュール名を()で囲う必要があります。C系言語の場合<>と""でincludeの挙動が異なるのでこれも含めた部分を囲っています。

process ruleでpatternでマッチした部分から依存しているファイルのリストを作ります。c-scannerの場合ここで<>(angle)と""(quoted)を振り分けています。
で、本来なら再帰的にこいつらを走査すると思うのですが、どうもそうではないのかprocessが呼ばれてる様子が無いのにもかかわらず変更の検出などを行っています。まったく謎です...

register/set-scanner

registerとset-scannerは計3箇所あります。これらは前回軽く説明したので特に要らないかと思いますが、一応。

scanner.register cuda-scanner : include ;
type.register CUDA : cu ;
type.set-scanner CUDA : cuda-scanner ;

scanner.registerはscannerがどのプロパティを要求しているかという情報と共に登録しています。走査する際にはここで指定したプロパティがctorに渡されてscannerが構築されます。
type.registerはターゲットタイプを登録するものです。今回はCUDA Cの拡張子の.cuだけですが、C++の場合は.cpp .cxx .ccなどもすべてC++のソースなので

type.register CPP : cpp cxx cc ;

となっています。
type.set-scannerで、あるターゲットタイプはどのscannerを使うかを設定しています。

一連の流れ

たとえば

exe hoge : hoge.cu ;

というビルドターゲットがあった場合、hoge.cuというソースはtype.registerで設定したとおりCUDAという種類のソースであることがわかります。
またexe ruleの出力は実行形式のファイルなので、EXEという種類の出力を出すことがわかります。
これからCUDA -> EXEという関係が作られます。

そうするとソースであるCUDAを解析しようとします。CUDAというターゲットはtype.set-scannerで設定したとおりcuda-scannerを使うことがわかります。
scanner.registerでcuda-scannerはincludeプロパティが必要であることを設定したので、現在のプロジェクトおよびexe ruleのrequirementsから<include>を引っ張ってきてcuda-scannerに投げてscannerを構築します。

scannerが構築できると実際にソースを走査します。ここでcuda-scanner.patternにマッチしたものをcuda-scanner.processに投げます。
こうすることで依存関係を解決します。

まとめ

よくわからん。