日々

link permalink

ビルド高速化ツール ccache

巨大なプロジェクト(Android など)をコンパイルするときに欠かせない ccache というツールがあります。

簡単に説明すると、過去にコンパイルした結果をキャッシュデータとして保存しておき、一致する場合はコンパイルをスキップして、結果をキャッシュデータから引き出してくるツールです。

使い方は大きく分けて 2つあって、1つは環境変数や Makefile などを書き換えてコンパイラの名前を変更する方法です。

例えば今まで gcc hoge.c としていたところを ccache gcc hoge.c と書き換えたり、make としていた部分を CC='ccache gcc' make とします。簡単ですが透過性が無いのが欠点で、そこらじゅうの Makefile を変えて回るのは非常に大変だろうことは、容易に想像できるかと思います。

もう 1つはコンパイラの起動をフックする方法です。ccache はシンボリックリンク経由で起動された場合、シンボリックリンクの名前に該当するコンパイラを探して起動する、という動作をします。やることとしては、

  • ccache へのシンボリックリンクを作成し、名前をキャッシュしたいコンパイラと同じ名前にします
  • gcc を起動したときに ~/bin/gcc が選択されるように、PATH を書き換えます

例えば /usr/bin/gcc のコンパイル結果をキャッシュするなら…、

ccache を使う準備
$ which gcc
/usr/bin/gcc

$ ln -s /usr/bin/ccache ~/bin/gcc

$ export PATH=~/bin:$PATH
$ which gcc
/home/katsuhiro/bin/gcc

このようにします。また ccache -s でどれくらいキャッシュが効いているかを見ることができますので、実際キャッシュ出来ているかどうかを見てみます。

ccache が働いている様子
$ echo 'int main;' > a.c

$ ccache -s
cache directory                     /home/katsuhiro/.ccache
cache hit (direct)                     0
cache hit (preprocessed)               0
cache miss                             0
files in cache                         0
cache size                             0 Kbytes
max cache size                       1.0 Gbytes

$ gcc -Wall a.c -c -o a.o
a.c:1:5: warning: ‘main’ is usually a function [-Wmain]
 int main;
     ^
$ ccache -s
cache directory                     /home/katsuhiro/.ccache
cache hit (direct)                     0
cache hit (preprocessed)               0
cache miss                             1 ★★キャシュから結果を返せなかった★★
files in cache                         3
cache size                            12 Kbytes
max cache size                       1.0 Gbytes

$ gcc -Wall a.c -c -o a.o
a.c:1:5: warning: ‘main’ is usually a function [-Wmain]
 int main;
     ^
$ ccache -s
cache directory                     /home/katsuhiro/.ccache
cache hit (direct)                     1 ★★キャシュから結果を返せた★★
cache hit (preprocessed)               0
cache miss                             1
files in cache                         3
cache size                            12 Kbytes
max cache size                       1.0 Gbytes

きちんと働いてくれていそうです。

ccache と PATH 環境変数

で、今日の本題なんですが、会社で ccache が動かないというので相談を受けて見に行ったら、確かに PATH をどう設定しても「コンパイラが見つからない」というエラーが出ていました。

散々悩んで辿り着いた答えは CCACHE_PATH 環境変数でした。man ccache とすると、しっかり説明が載っています。

この名前だけ聞いて、ああ、あれね?とわかる方は、かなり ccache を使い慣れている方だと思います。恥ずかしながら、わたくし全く知りませんでした…。

先の節で説明した 2つ目の方法で ccache を起動すると、ccache は PATH に列挙されたディレクトリからコンパイラを探そうとします。

しかし実はこの挙動は CCACHE_PATH という環境変数により変えることができて、もし CCACHE_PATH という環境変数が定義されていた場合、ccache は PATH の代わりに CCACHE_PATH に列挙されたディレクトリからコンパイラを探そうとします。

相談されたエラーは間違って CCACHE_PATH が定義してしまい、さらに CCACHE_PATH で何もないディレクトリを指していたため、ccache が「コンパイラが無いですねー?」とエラーを出していたのでした。

CCACHE_PATH の働き
$ gcc
gcc: fatal error: no input files
compilation terminated.

$ export CCACHE_PATH=/usr
$ gcc
ccache: FATAL: Could not find compiler "gcc" in PATH ★★コンパイラが見つからないと言っている★★

$ unset CCACHE_PATH
$ gcc
gcc: fatal error: no input files
compilation terminated.

わかっていれば、何だ、そんなこと…というレベルの話ですが、意外とハマって苦戦したので、思い出として書き残しておきます。

[編集者: すずき]
[更新: 2015年 6月 5日 00:57]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

バカが見る

鼻と耳は繋がっているから、鼻にイヤホン挿したら聞こえるというからやってみたけども、何も聞こえませんでした。

ウォークマンを最大音量にすれば聞こえますが、鼻に刺しても挿さなくても聞こえる音量は変わりません

鼻詰まってるからかな?と思って、鼻かんでからやってみたけどやっぱり聞こえません。

もしかして「うわ、あいつ本当にやってるよ、バーカバーカwww」的な冗談だったのかなあ??

メモ: 技術系?の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2015年 11月 29日 04:50]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

バージョン管理システムと make

前々から感じていたのですが、この 2者は非常に相性が悪いと思います…。

  • タイムスタンプに興味が無く、ファイルの中身しか気にしないバージョン管理システム
  • タイムスタンプを気にして、ファイルの中身には興味が無い make, autoconf/automake

例えば git clone してきたリポジトリを ./configure && make とすると、

  • なぜかまた configure が走り始める
  • 挙句の果てに autoconf/automake が無いぞボケェ!と怒られる

という訳の分からない挙動をすることがあります。これは git clone 時に Makefile.am などのタイムスタンプが変化してしまい、make が勘違いして、ファイルが更新されたよ!依存するファイルを再作成しなければ!というアクションを起こしてしまうせいです。

これがもし tarball で展開したコードであれば taball 作成時のタイムスタンプが復元されますので、この現象は起きません(tarball の作成者がヘマしていなければ、ですが)。

どうしたら良いか?

スマートな解決方法は「make がファイルの変化を検知する方法を変える」ことです。恐らく make がファイルの変化を検知したい理由はたった 1つで、

  • A が B に依存しているとして、A は B より古い(=再ビルド必要)か、新しい(=再ビルド不要)か?

ただこれだけです。タイムスタンプを使うのは手段の一つに過ぎず、2ファイル間の新旧を判別できれば、タイムスタンプでなくても構わないはずです。

この日記のもとになった Facebook のエントリでは「タイムスタンプではなく、ファイルシステムが持っているブロックのハッシュ値が良いんじゃないか?」というコメントをいただきました。

前回の make 起動時と、今回の make 起動時の全ファイルのハッシュ値を記録しておけば、前回と変化したかどうか?はわかるし、ハッシュ再計算のコストがやや心配ですが、ファイルシステムが持っている値などを使えば抑えられる気がします。後は 2ファイル間の順序関係を知る方法があれば、タイムスタンプの代わりになり得ると思います。

しかし、こんなの誰でも考え付きそうな話ですが、既に作られていたりしませんかねー…?

力ずくで解決する

とはいえ、現状では make 以外の選択肢がありません。その場しのぎではありますが、力ずくで解決してみようと思います。

お題は git clone した後などタイムスタンプがメチャクチャになった状態でも、autotools が再実行されないようにするには、どうすれば良いか?です。

まずは autotools ってそもそも何なのか?を調べてみます。適当に autotools を使っているプロジェクトを持ってきて、autoreconf を実行したときの動きを見ます。環境は Debian 8.0 (Jessie, i386) です。

autoreconf が起動するツール群
$ autoreconf --force -v 2>&1 | egrep ^autoreconf
autoreconf2.50: Entering directory `.'
autoreconf2.50: configure.ac: not using Gettext
autoreconf2.50: running: aclocal --force ★★こいつ★★
autoreconf2.50: configure.ac: tracing
autoreconf2.50: configure.ac: adding subdirectory component/empty to autoreconf
autoreconf2.50: Entering directory `component/empty'
autoreconf2.50: configure.ac: not running libtoolize: --install not given
autoreconf2.50: running: /usr/bin/autoconf --force ★★こいつ★★
autoreconf2.50: running: /usr/bin/autoheader --force ★★こいつ★★
autoreconf2.50: running: automake --force-missing ★★こいつ★★
autoreconf2.50: Leaving directory `component/empty'
autoreconf2.50: Leaving directory `.'

結果を見た感じでは、実行されるツールは 4つ aclocal, autoconf, autoheader, automake です。

次にこれらのツールが再実行される仕組みを追うため、./configure 実行後に生成される Makefile を見てみます。

まずは aclocal から。

aclocal の再実行ルール(適宜抜粋)

top_srcdir = .
srcdir = .

ACLOCAL_M4 = $(top_srcdir)/aclocal.m4

am__aclocal_m4_deps = $(top_srcdir)/configure.ac


$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
        $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)

$(am__aclocal_m4_deps):

ルールによれば aclocal.m4 > configure.ac であれば、つまり aclocal.m4 の方が新しければ aclocal は再実行されません。

ちなみに m4 ディレクトリに追加の .m4 ファイルを入れている場合は am__aclocal_m4_deps に m4 ディレクトリ内の .m4 ファイルが並びます。従って aclocal.m4 > configure.ac, (追加の .m4 ファイル) という関係になります。

続けて autoconf です。

autoconf の再実行ルール(適宜抜粋)

top_srcdir = .
srcdir = .

ACLOCAL_M4 = $(top_srcdir)/aclocal.m4

am__aclocal_m4_deps = $(top_srcdir)/configure.ac

am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)

$(top_srcdir)/configure:  $(am__configure_deps)
        $(am__cd) $(srcdir) && $(AUTOCONF)

ルールによれば configure > configure.ac, aclocal.m4 であれば、autoconf は再実行されません。

どんどん行きましょう。続けて autoheader です。

autoheader の再実行ルール(適宜抜粋)

top_srcdir = .
srcdir = .

ACLOCAL_M4 = $(top_srcdir)/aclocal.m4

am__aclocal_m4_deps = $(top_srcdir)/configure.ac

am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)

$(srcdir)/config.h.in:  $(am__configure_deps)
        ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
        rm -f stamp-h1
        touch $@

最後に touch $@ しているのが特徴的です。どうも autoheader は生成した内容と、既にあるファイルの内容に差が無ければ config.h.in を一切書き換えない、という妙な作りになっているらしく、この autoheader 再実行ルールが適用されても config.h.in のタイムスタンプが更新されない場合があります。

もしタイムスタンプが更新されないと make は毎回この autoheader 再実行ルールを適用してしまいますので、無駄を避けるために touch して config.h.in のタイムスタンプを強制的に更新し、次回以降の autoheader 再実行を回避していると思われます。

他のツールは強制的に書き換えに行くんですが、なぜ autoheader だけ仕様が違うんだろう…??

ま、それはさておき、ルールによれば config.h.in > configure.ac, aclocal.m4 であれば、autoheader は再実行されません。

最後に automake です。

automake の再実行ルール(適宜抜粋)

top_srcdir = .
srcdir = .

ACLOCAL_M4 = $(top_srcdir)/aclocal.m4

am__aclocal_m4_deps = $(top_srcdir)/configure.ac

am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)

$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
        @for dep in $?; do \
          case '$(am__configure_deps)' in \
            *$$dep*) \
              echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
              $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
                && exit 0; \
              exit 1;; \
          esac; \
        done; \
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign Makefile

ルールによれば Makefile.in > Makefile.am, configure.ac, aclocal.m4 であれば、automake は再実行されません。

まとめ

今までのルールをまとめると、下記のようになります。

  • aclocal: aclocal.m4 > configure.ac
  • autoconf: configure > configure.ac, aclocal.m4
  • autoheader: config.h.in > configure.ac, aclocal.m4
  • automake: Makefile.in > Makefile.am, configure.ac, aclocal.m4

全部まとめると Makefile.in > Makefile.am, configure, config.h.in > aclocal.m4 > configure.ac であれば autotool は一切、再実行されない、と思われます。

要するに?

だからどうしたら良いんだ!俺は忙しいんだぞ!!という超短気な人のため、autotools の怒りを避けるためのビルド用のシェルスクリプトも付けておきます。

autotools の怒りを避けるビルドスクリプト

#!/bin/sh

# Prevent the autotools running...
touch aclocal.m4
touch config.h.in
touch configure

touch Makefile.am
touch src/Makefile.am
### もし他のサブディレクトリに Makefile.am があればそれも
### find -name Makefile.am | xargs touch でも良いかもしれない

touch Makefile.in
touch src/Makefile.in
### もし他のサブディレクトリに Makefile.in があればそれも
### find -name Makefile.in | xargs touch でも良いかもしれない

# Build
./configure
make

本当にこの節しか読まない人に注意しておくと、このスクリプトは現在の autotools の実装に依存していますので、将来 autotools の実装が変わると、動かなくなる可能性が非常に高いです。動かなくなっても泣かないでください。

感想

こういうダーティーハックは個人的には面白いから好きですが、苦労の割には利益が無いと思いました。

数年もすればこの手のハックは動かなくなるので周りに迷惑ですし、後進の人がメンテしようにも意味不明で「シバくぞゴラァ!書いた奴出てこいやー!!」ってキレること請け合いです。

[編集者: すずき]
[更新: 2015年 6月 7日 03:20]
link 編集する

コメント一覧

  • hdk 
    なんでタイムスタンプを覚えてくれないんだーって、某電子掲示板の Git スレ等で時々湧いてきた (くる?) 話題です。本来これは make の仕組みと相性がよいもので、必ず現在時刻に置き換わるおかげで、git blame 等過去のバージョンを引っ張り出した時に、差分があるファイルを確実に再コンパイルできます。

    Makefile.am のタイムスタンプが変化して、無駄な configure が走る、というのは、そもそもバージョン管理の対象にすべきでないファイルを入れている感じがします。Makefile の : の左側に来るファイルを入れても、: の右側とのタイムスタンプの差は管理できないので、例えばソースコードから生成されるオブジェクトファイルを入れといたからコンパイル時間短縮できますーとなるとは限りません。マージ後 make せずにコミットすれば、正しくないオブジェクトファイルがコミットに残ってしまうかも知れません。Makefile.am で言えば、Makefile.am がマージで変更されたのに autoreconf せずに commit してしまうと、それを checkout した人が首を傾げることになります。

    プロジェクトによっては、configure がリリース版 tarball のみにあって、バージョン管理下には入ってなくて latest を試す時には自分で autogen.sh を走らせるタイプのものがありますが、そういうのが正しい解なのかなと思います。 
    (2015年06月07日 09:54:03)
  • すずき 
    >hdk さん

    なるほど Git で差分更新したときのことはあまり考えていませんでした。今回の話は git clone のときだけの問題ですね。

    おっしゃるように、一般の利用者はリリースバージョンの tarball を使って、開発者は git clone を使おう。開発者なら autoreconf なり autogen.sh を使って configure を生成するくらいはやろう、という割り切り方は有りだと思います。実際それで回っているプロジェクトもあるわけですし。

    ただ、それで万事解決か?というと、そんなこともないよなーと思うわけで…。

    利用者にとっては tarball を使えと言われても、公開されていないバージョンだとお手上げですし、開発者にとっては「チェックアウトした状態でビルド&テストが通る」ってのは一番とっつきやすくてありがたいように思います。
     
    (2015年06月07日 11:51:31)
open/close この記事にコメントする



link permalink

都市ガスからプロパンガスへ

住んでいる賃貸アパートのガスが、都市ガス(大阪ガス株式会社)から、いわゆるプロパンガス(帝燃産業株式会社)に切り替わりました。

腐っても国道沿いですし、一応は都市部に分類される地域のはずですから、プロパンから都市ガスに切り替わるならまだしも、その逆というのはあまり聞いたことがないのですが…。

筑波に居た時はプロパンでしたが、ガス代が妙に高かった印象しかありません。今は 3,000〜7,000円程度で済んでいるガス代が、来月以降どれほど跳ね上がるやら、恐ろしい限りです。

LNG と LPG

何となく都市ガスとプロパンガスって呼んでますが、そもそも何が違うのか?が気になったので、調べてみました。

都市ガスは液化天然ガス(LNG, Liquefied Nature Gas)といってメタンを主成分(90%以上がメタン)とするガスです。メタン 100%だと火力が弱いので、ブタンやプロパンを後から足して、火力を強くするそうです。

我が家に来ていた都市ガスは 13A という、一番発熱量が高く(13)一番燃焼が遅い(A)タイプです。1m^3 のガスを燃焼させると 45MJ の熱量が得られます。
参考: 東京ガス: ガスご利用ガイド/都市ガスの熱量・圧力・成分

ガス器具に「プロパン用」「13A 用」などと書いてある理由は、この発熱量と燃焼速度にあるそうです。例えば 13A 用のガス器具に想定していない燃焼速度のガス(例えば 6C など)を使うと、燃えるのが速すぎて、本来ガスが燃えるべきではない場所で燃えてしまい、器具の異常加熱や事故に繋がります。

一方のプロパンガスは液化石油ガス(LPG, Liquefied Petroleum Gas)といってプロパンを主成分としたブタンとの混合ガスです。プロパン 100%ではないので、プロパンガスという呼び方は本来正しくないようですが、何を今更…な感があります。

我が家に来ている LPG は家庭用なので恐らく「い号液化石油ガス」という、プロパンの含有量が最も高いタイプです。1m^3 のガスを燃焼させると約 100MJ の熱量が得られますので、単純な火力としては都市ガスよりも上です。でも単価が高いから相殺されます。
参考: 日本 LP ガス協会: LP ガスの概要: LP ガスの規格
参考: 経済産業省 〜LP ガスを安全に使うために、LP ガスの基礎知識〜

[編集者: すずき]
[更新: 2015年 6月 7日 22:43]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

システムも生き残りたい

ライブラリの守備範囲は狭い方がいい - Konifar's WIP を読んで。

リンク先の記事に同意です。可能な限り 1つのモジュールは 1つの課題にフォーカスし、規模もできるだけ小さくすべきだと思います。

システムは人が作るものですから、人と同様「より長く、より広く生きて(=使って)ほしい」という思いが根底にあるはずで「時代に合わせて変われないものから滅びる」という生物の摂理には逆らえないだろう、という考えでいます。

モジュールのフォーカスを狭めたい理由として「開発の参入障壁を下げて、変更のチャンスを増やす」「規模を小さくして、変更コストを下げる」を挙げたいです。

何でそうしなきゃいけないと思うか?は人によるし、正解も間違いも無いと思いますので、他の方の想いも聞いてみたいところですねー。

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2015年 6月 7日 22:47]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

Amazon どうした

今だけなのか昔からなのかわかりませんが、Amazon ポイントを使って購入すると、注文確認メールに</tr>と入ったメールが来ます…。何これ。通常の買い物では特に異常はないです。

商品の小計:                                  ¥ 514
Amazonポイント:                                        -¥ 86
        </tr>
                                              ........................
注文合計:                                    ¥ 428
[編集者: すずき]
[更新: 2015年 6月 13日 20:09]
link 編集する

コメント一覧

  • foo 
    治らないorz 
    (2015年10月29日 13:35:29)
open/close この記事にコメントする



link permalink

批判なのか評論なのか見分ける方法(試行中)

仕事でもプライベートでも、自身の行いや意見に「それは○○の理由でだめ、△△すべき。」のように批判もしくは評論されることがあると思います。

昔は意見の扱いに困っていましたが、最近は、なるほど正論だな〜と思ったら、
「ご意見ごもっともです。では一緒にやりましょう。私は(半分くらい)やります、あなたは(半分くらい)をやっていただけませんか?」
と返すようにしています。

というのは、この後「やる」か「やらない」か?を見れば、その意見が「批判+提案」なのか「評論」なのかが、割とキッパリと分けられる気がするからです。

  • 「やる」人は、半分やってくれる、具体的な分担、時期、適切な協力者の紹介など、提案してきた内容を実施します。
  • 「やらない」人は、いつかやると言ってウヤムヤ、全部押し付けてくるなど、提案してきた内容を絶対に実施しません

この法則が合っているかどうか、しばらくこの返しを続けてみようと思います。

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2015年 11月 29日 04:47]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

IntelliJ IDEA 14 の自動インデント

IntelliJ IDEA 14 のエディタにはコードを選択して Ctrl+Alt+I を押すと、自動的にインデントを調整してくれる機能があります。この機能、Java のコードスタイルに合わせてインデントを調整するので、基本的には文句のない結果になります。

しかしながら、個人的に 1点だけ気に入らない点があります。何かと言うと switch 文の内部にある case に余計なインデントが付くことです。例を挙げると、デフォルトでは下記のようにインデントしてくれます。

デフォルトの switch - case 文のインデント

switch (a) {
    case 0:
        doCase0();
        break;
    default:
        doDefault();
}

本当は下記のように、switch と case の位置が揃ってほしいのです。

個人的に欲しい switch - case 文のインデント

switch (a) {
case 0:
    doCase0();
    break;
default:
    doDefault();
}

この程度、設定(※)で何とかなるだろ?と思ったら、意外にも switch 文に関する設定がありませんでした。困った。

(※)IntelliJ IDEA 14 の自動インデントの設定は、メニューの File - Settings を選び、左側のツリー表示から Editor - Code Style - Java にあります。種別としては Indent に相当するはずですが、switch 文について言及されている項目は 1つもありません。

無理やり設定する

インデントの違いは非常に些細なことですが…、個人的に見た目が受け付けないのと、今まで書いてきたコードのインデントがことごとく変わり、バージョン管理システムが差分を大量に表示するので、うっとおしいのです。

前述のように GUI から設定する方法はなさそうなので、ひとまず GUI からの設定は諦めました。代わりに自動インデントの設定ファイルを直接書き換えようと思います。

まず、自動インデントの設定(メニューの File - Settings、左側のツリー表示から Editor - Code Style - Java)を適当に書き換え、適当な名前、例えば Default(1) という名前で保存します。すると C:\Users\username\.IdeaIC14\config\codestyles\Default _1_.xml という設定ファイルができます。

その後、起動している IntelliJ IDEA 14 を全て終了させて、Default _1_.xml の設定を直接書き換えます。下記の★部分を追加してください。

IntelliJ IDE の switch - case 文の自動インデント設定

<code_scheme name="ConfigName">
  ...
★  <codeStyleSettings language="JAVA">
★    <option name="INDENT_CASE_FROM_SWITCH" value="false" />
★  </codeStyleSettings>
</code_scheme>

設定を書き換えたら、IntelliJ を再び起動してください。すると switch - case 文の内部が自動インデントされなくなります。

[編集者: すずき]
[更新: 2015年 6月 18日 08:52]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

USB 3.0 の帯域を活用

週刊アスキー - Surface 3 や薄型ノート PC をわずか 1万円強で超強化する棒型ドック、その名も“ピッコロ”を読んで。

USB 2.0 の帯域だと Full HD の外部ディスプレイだけで一杯一杯でしたが、USB 3.0 の帯域なら WQXGA の外部ディスプレイ+外付けストレージ x 2 でも余裕なんですねー。

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2015年 11月 29日 04:44]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報