ブレーカーが落ちて Ubuntu マシンが落ちた後,ネットワークに全くつながらなくなり,メニューバーには network is disabled と表示されるようになった.また /etc/network/interfaces には lo しかなくなっていた.
これを復旧するには以下のようにする.
# service network-manager stop # rm /var/lib/NetworkManager/NetworkManager.state # reboot
ブレーカーが落ちて Ubuntu マシンが落ちた後,ネットワークに全くつながらなくなり,メニューバーには network is disabled と表示されるようになった.また /etc/network/interfaces には lo しかなくなっていた.
これを復旧するには以下のようにする.
# service network-manager stop # rm /var/lib/NetworkManager/NetworkManager.state # reboot
決めること
リポジトリミラーの準備
$ cd ~/proj/macports/mirror $ rsync -az --delete www.example.com:/srv/www/macports/ .
パッケージの準備
$ cd ~/proj/macports/work $ git clone http://www.upstream.com/example-app.git $ cd example-app $ git checkout -b macports $ mkdir macports $ vi macports/Portfile # vim: set ft=tcl et sw=4 ts=4 sts=4 : PortSystem 1.0 name example-app version 0.1.0 categories sysutils maintainers metalglue@example.com description example-app long_description example-app master_sites http://www.example.com/macports/distfiles/ $ git add macports $ git commit
tar アーカイブファイルに固めて,チェックサムを計算し,Portfile に記述する.
$ cd ~/proj/macports/work $ tar zcf ~/proj/macports/mirror/distfiles/example-app-0.1.0.tar.gz example-app $ md5sum ~/proj/macports/mirror/distfiles/example-app-0.1.0.tar.gz 236aaec0cd680a4d254d26fd6820043d $ vi example-app/macports/Portfile checksums md5 236aaec0cd680a4d254d26fd6820043d
それらをミラーディレクトリにコピーして,portindex コマンドでインデックスを作り,アップロードする.
$ mkdir ~/proj/macports/mirror/portfiles/sysutils/example-app $ cp example-app/macports/Portfile ~/proj/macports/mirror/portfiles/sysutils/example-app $ cd ~/proj/macports/mirror/portfiles $ portindex $ cd .. $ rsync -av --delete ./ www.example.com:/srv/www/macports
$ sudo port sync $ port clean --all example-app $ port -v install example-app
Vim で,読み込んだファイルをリストアップする方法をよく忘れるのでメモしておく.
:scriptnames
また,自動的に読まれるスクリプトを置くディレクトリのリストは以下のようにして見ることができる.
:set runtimepath
MacPorts でインストールしたときにどの variants を指定したかを調べるには installed コマンドを用いる.
$ port installed git-core @1.7.1.1_0+bash_completion+doc+gitweb+svn (active)
オブジェクト指向言語におけるメソッドを関数で実装できるだろうか?
最近のオブジェクト指向言語では,関数型言語の特徴であるファーストクラスオブジェクトとしての関数が提供されていることが多い.このような言語の場合、メソッドは関数なのだろうか?インスタンス変数に関数を代入すればそれが即ちメソッドなのだろうか?しかしことはそう単 純ではない.レシーバ (C++, Java, JavaScript における this
) の扱いをどうするかという問題があるからである.
JavaScript ではまさにメソッドが関数として実装されている.
o = { data: "hello", method: function () { alert(this.data); } }; o.method();
とすれば "hello"
が表示される.それでは以下のように一旦関数を単離するとどうなるだろうか?関数がファーストクラスオブジェクトであるからには単離して持ち運ぶことが可能でなければならない.
m = o.method; m()
この場合 undefined
が表示される.いったい method 中の this は何を指しているのであろうか?引き続いて
data = "world"; m()
を実行してみれば分かる通り ("world"
が表示される),この場合の this
はグローバルオブジェクト (window
) を指している.次に全然別のオブジェクトに持って行ってみよう.
oo = { data: "!", method: o.method }; oo.method();
今度は "!"
が表示される.つまり JavaScript では呼び出し方に応じて this
が指すものを変えることでメソッドを関数として実装することを可能にしていると言える.
JavaScript の仕様書では,object.variable
は単純な値へと評価されるのではなく,(object
の値, "variable"
) という組へと評価されると説明されている (8.7 The Reference Type). この組に対して関数呼び出しが適用される (object.variable()
) と,this が「object
の値」を指すように設定されて,object["variable"]
というプロパティ値の関数が呼び出されることになる (11.2.3 Function Call). この仕様を見れば上記の挙動も理解できるだろう(「object
の値」が無いときには this
はグローバルオブジェクトを指す,という記述が仕様書にある).
ちなみにこの Reference Type はカッコ式を素通りするので,
(o.method)()
のときも this
は o
を指し,"hello"
が表示される.ただしコンマ演算子が適用されるとプロパティ値へと変換されるので,
(1, o.method)()
のときの this
はグローバルオブジェクトを指し,"world"
が表示される.かなり高度な JavaScript パズル問題といえるだろう.
Python では,メソッドのレシーバをプログラマが明示的に宣言しなければならない点で JavaScript よりもさらに単純にメソッド=関数となっているように見える.
class Foo(object): def __init__(self): self.data = "hello" def method(self): print self.data foo = Foo() foo.method()
とすれば当然 "hello"
が表示されるが,以下はどうだろうか?
m = foo.method m() class Bar(object): def __init__(self): self.data = "world" method = m bar = Bar() bar.method()
いずれも "hello"
が表示される.Python では JavaScript のように ()
による関数呼び出しの時点でレシーバが決まるのではなく,インスタンス変数の内容を取得する時点でレシーバが決定されるようになっている.
以下ではわかりやすいように,メソッド関数をグローバルで定義して実験してみる.
def m(self): print self.data class Foo(object): def __init__(self): self.data = "hello" method = m class Bar(object): def __init__(self): self.data = "world" method = m foo = Foo() bar = Bar() print foo.method == m print bar.method == m print foo.method == bar.method
この場合いずれも False
が表示される.このように,同一の関数をインスタンス変数に設定したにもかかわらず,インスタンス変数の内容を取得して比較すると異なっていることが分かる.
実は Python ではインスタンス変数へのアクセスの際に,それがユーザ定義関数であれば,ユーザ定義メソッドという別のオブジェクトが返される (Python リファレンス「3.2 標準型の階層」の「ユーザ定義メソッド」).このオブジェクト (o
とする) には o.im_func
に元の関数が,o.im_self
にレシーバがセットされ,メソッド呼び出し時の関数とレシーバとして用いられる.
Python のメソッド=関数かつレシーバ引数の明示という仕様はシンプルできれいかもしれないが,そのしわよせは別の場所,しかも仕様書のどこにあるかもよくわからないような所に隠されている.
関数でメソッドを実現することは一見単純なことのように思えるが,実際はそうでもないということがわかる.
Perl や Ruby でどうなっているかを調べてみるのもおもしろいだろう.
ターミナルでファンクションキーなどの特殊キーを押したときにどのような文字が入力されるかは端末ハードウェアの種類に依存していた.現在では端末はほとんどソフトウェアなので何でも出力できるが,基本的には VT 端末に従うのが通例のようだ.
VisualStudio 2008 の最初の起動画面で「最も従事する開発作業の種類」をリセットする方法が表示されたのだが忘れそうなのでメモ.
「いつでも設定の別のコレクションを使用できるように変更できます.[ツール] メニューから [設定のインポートとエクスポート] を表示して,[すべての設定をリセット] を選択します」
ホスト www.exapmle.com の /srv/www/example/macports (http://www.example.com/macports/, rsync://www.example.com/macports/) にセットアップすると仮定する.ディストリビューションは debian を用いる.まずディレクトリを準備する.
deb$ mkdir /srv/www/example/macports deb$ cd /srv/www/example/macports deb$ mkdir portfiles distfiles
/srv/www/example/macports/portfiles を rsync プロトコルでアクセスできるようにする.
deb# vi /etc/default/rsync RSYNC_ENABLE=inetd deb# vi /etc/inetd.conf rsync stream tcp nowait root /usr/bin/rsync rsyncd --daemon deb# cp /usr/share/doc/rsync/examples/rsyncd.conf /etc deb# vi /etc/rsyncd.conf [macports] path = /srv/www/example/macports deb# /etc/init.d/openbsd-inetd restart deb# /etc/init.d/rsync restart
Portfile を更新する.
osx$ rsync -avzh www.example.com:/srv/www/example/macports . osx$ cd macports/portfiles osx$ mkdir -p sysutils/samplepackage osx$ cp somewhere/Portfile sysutils/samplepackage osx$ portindex osx$ rsync -avzh ./ www.example.com:/srv/www/example/macports/portfiles
パッケージ本体を更新する.
osx$ rsync samplepackage-1.2.3.tar.gz www.example.com:/srv/www/example/macports/distfiles
ports クライアントを設定する.
osx# vi /opt/local/etc/macports/sources.conf rsync://www.example.com/macports/portfiles/ rsync://rsync.macports.org/release/ports/ [default] osx# port -v sync osx# port install samplepackage
osx$ rsync samplepackage-1.2.3.tar.gz www.example.com:/srv/www/example/macports/distfiles osx$ mkdir temp; cd temp osx$ vi Portfile ... master_sites http://www.example.com/macports/distfiles/ ... osx$ port -d checksum ... checksums md5 3b55d5016728cfb4e76b0af952b73fff \ sha1 51c9e64d720732a016f39475f00ca939c6bfd512 \ rmd160 cd74cc78de6adedb3278aa435b374695c70d92d9 ... osx$ vi Portfile ( paste above checksums ) osx$ port -d build
パッケージの上流を開発しているのが自分自身の場合には,git-buildpackage のやり方を援用して自分でやるのもよいような気がしてきた.
最初のパッケージリリース:
$ cd foo # package top directory managed by GIT $ git checkout master $ git checkout -b debian $ git checkout master ... developing version 0.0.1 ... $ git commit $ git tag v00.00.01 $ git checkout debian $ git merge --squash v00.00.01 $ DEBFULLNAME='Your Name' dh_make -e yourname@example.com -p foo_0.0.1 --native $ git add debian $ git commit $ vi control rules changelog # set version to 0.0.1-1 $ git commit $ debuild -i -us -uc -tc # answer 'y' to warning $ git tag debian/00.00.01-01
次のパッケージリリース:
$ git checkout debian $ dch # set version to 0.0.1-2 ... developing version 0.0.1-2 ... $ git commit $ debuild -i -us -uc -tc # answer 'y' to warning $ git tag debian/00.00.01-02
次のバージョンリリース:
$ git checkout master ... developing version 0.0.2 ... $ git tag version/00.00.02 $ git checkout debian $ git merge --squash version/00.00.02 $ dch # set version to 0.0.2-1 ... developing version 0.0.2-1 ... $ git commit $ debuild -i -us -uc -tc # answer 'y' to warning $ git tag debian/00.00.02-01
Debian パッケージを作成するということは,要するに,上流ソースのディレクトリ内に debian/ ディレクトリを作り,その中にいろいろなファイルを用意することである.このようなディレクトリ構成のせいで,debian/ 以下のコードのバージョン管理をどのようにすればよいかが直感的にわかりにくい.
このため,debian/ 以下のコードのバージョン管理を git で簡単に行うためのパッケージ git-buildpackage が用意されている.git-buildpackage では2つのブランチ,すなわち upstream ブランチ(デフォルトでは upstream)と debian ブランチ(デフォルトでは master)を用意し,以下のような手順で管理する.
今回は既に Debian リポジトリにあるパッケージを git-import-dsc でインポートする方法には触れず,最初から git-buildpackage を用いるとどうなるかを説明する.
例としてパッケージ foo を管理することとする.foo の上流は git でバージョン管理されていて,それは ssh://www.example.com/srv/git/foo.git にあるとする.
$ cd /tmp $ git clone -b release0.0.1 ssh://www.example.com/srv/git/foo $ cd foo $ git archive --prefix=foo/ HEAD | gzip > ../foo-0.0.1.tar.gz
git-import-orig コマンドを用いる.
$ mkdir -p ~/debian-foo/foo $ cd ~/debian-foo/foo $ git init $ git-import-orig /tmp/foo-0.0.1.tar.gz Upstream version is 0.0.1 Warning: Can't symlink orig tarball due to missing debian/changelog Initial import of '/tmp/foo-0.0.1.tar.gz' ... Succesfully imported version 0.0.1 of /tmp/foo-0.0.1.tar.gz $ git branch * master upstream $ git tag upstream/0.0.1
debian ブランチである master で debian/ ディレクトリを作成する.
$ DEBFULLNAME='Your Name' dh_make -e yourname@example.com -p foo_0.0.1 -f /tmp/foo-0.0.1.tar.gz $ git add debian $ git commit $ vi debian/control $ vi debian/rules $ vi debian/changelog $ debuild -i -us -uc -tc # test build
うまくいったら git-buildpackage を用いてパッケージを生成する.ここで dh_make が作った .orig. ファイルを消しておかないと後で変なことになるので注意.
$ git commit $ rm ../foo_0.0.1.orig.tar.gz $ git-buildpackage --git-tag -us -uc -tc
debian/ 以下にバグが見つかった場合.
$ vi debian/... $ git commit $ DEBFULLNAME='Your Name' DEBEMAIL='yourname@example.com' git-dch --release $ git commit $ git-buildpackage --git-tag -us -uc -tc
git-import-orig でインポートしたら,dch でバージョンを 0.0.2 に上げておかなければならない点に注意.あいかわらず orig ファイルは消しておいた方が無難.
$ cd /tmp/foo $ git pull $ git archive --prefix=foo/ HEAD | gzip > ../foo-0.0.2.tar.gz $ cd ~/debian-foo/foo $ git-import-orig /tmp/foo-0.0.2.tar.gz $ rm ../foo_0.0.2.orig.tar.gz $ dch -i $ git commit $ git-buildpackage --git-tag -us -uc -tc
オレオレ apt リポジトリにオレオレ deb パッケージを置いて,特定少人数に対して配布する.
mini-dinstall コマンドと,dupload パッケージを用いてオレオレ apt アーカイブを作成する方法を紹介する.
howto: uploading to people.d.o using dputを参考に作成する.以下を仮定する.
mini-dinstall をインストールする.
$ sudo aptitude install mini-dinstall
$ ssh foo@www.example.com $ gpg --gen-key Please select what kind of key you want: DSA and Elgamal What keysize do you want? (2048) Key is valid for? (0) Real name: foo Email address: Comment: Debian Archive Key
パスフレーズは空にしておく.空にしていないと後で dupload でアップロードしたときにパスフレーズをリモートからでは入力できなくて失敗する.
この鍵の ID を取得しておく.
$ gpg -k pub 1024D/6789ABCD 2010-04-07 uid foo (Debian Archive Key)
ここでは 6789ABCD が ID である.
Origin (アーカイブの作成者/管理者) が foo で Label (アーカイブの名称) が foo のアーカイブツリーを作成する.Origin, Label については Debian Repository HOWTO を参照.
$ cd /srv/www/example/debian $ mkdir -p mini-dinstall/incoming $ vi sign_release.sh #!/bin/sh rm -f Release.gpg gpg -u 6789ABCD -bao Release.gpg "$1" $ chmod 750 sign_release.sh
mini-dinstall の設定ファイルを作成する.
$ vi ~/.mini-dinstall.conf [DEFAULT] mail_to = foo architectures = all, i386 archive_style = flat archivedir = /srv/www/example/debian generate_release = 1 release_origin = foo release_label = foo release_description = foo release_signscript = /srv/www/example/debian/sign_release.sh [unstable] release_suite = unstable
手元のマシンでアップロードの設定を行う.
$ sudo aptitude install dupload $ vi ~/.dupload.conf package config; $cfg{'foo'} = { fqdn => "www.example.com", method => "scpb", incoming => "/srv/www/example/debian/pool/main", # The dinstall on ftp-master sends emails itself dinstall_runs => 1, }; $cfg{'foo'}{postupload}{'changes'} = "ssh foo@www.example.com mini-dinstall -b";
*.changes ファイルがあるディレクトリで下のようにする.
$ dupload --to foo .
インストールするマシンの apt を設定する.
$ sudo vi /etc/apt/sources.list deb http://www.example.com/debian/ unstable/ deb-src http://www.example.com/debian/ unstable/
リポジトリの公開鍵をインストールするマシンの鍵束に加える.
$ ssh foo@www.example.com gpg --export -a 6789ABCD | sudo apt-key add -
これで通常の操作をすればよい.
$ sudo aptitude update
見てきたように apt-ftparchive を使うより著しく簡単である.また,バージョンアップしたパッケージのアップロード時の問題も解決された.
ただ,apt-ftparchive を使う方法で可能だった component の利用ができないのが少し残念(sources.list の記述を deb http://www.example.com/debian/ unstable main のようにできないということ).
あと,debian ディレクトリの .htaccess を適切に設定してやる必要があるかも.
Debian の squid3 パッケージのデフォルトでは,1024 以下のポート番号との通信は特定のものだけに限られていて,rsync で使う 873 番との通信を中継することができない./etc/squid3/squid.conf の
acl SSL_ports port 443
あたりに以下のように追加すればよい。
acl SSL_ports port 873 acl Safe_ports port 873 # rsync
基本的にはシステム環境設定の「ネットワーク→詳細→プロキシ」で設定した proxy が用いられるが,port sync は rsync を用いて行われるので rsync 用の proxy を設定する必要がある.これは環境変数 RSYNC_PROXY で設定する.まず,
$ sudo visudo ... Defaults env_keep += "RSYNC_PROXY" ...
として sudo 時に環境変数が継承されるようにして,
$ RSYNC_PROXY=hostname:8080 sudo port sync
のようにする(あるいはシェルの初期設定ファイルに export RSYNC_PROXY=hostname:8080
を設定する).
もう一つの問題は,ミラーサイトからのソースのダウンロードが止まってしまうことがままあることである.例えば subversion のインストール中に cyrus-sasl2 のダウンロードで止まってしまったとしよう.このときには Ctrl-C を押して一度止めてから,以下のようにする.
$ MASTER_SITE_LOCAL=http://distfiles.macports.org/cyrus-sasl2/ sudo port install subversion
もちろん,事前に sudo を設定しておく必要がある.
$ sudo visudo ... Defaults env_keep += "MASTER_SITE_LOCAL" ...
オレオレ apt リポジトリにオレオレ deb パッケージを置いて,特定少人数に対して配布する.
標準インストールされている apt-ftparchive コマンドと,dupload パッケージを用いてオレオレ apt アーカイブを作成する方法を紹介する.
Chapter 2. Debian Package Management を参考に作成する.以下を仮定する.
$ ssh foo@www.example.com $ gpg --gen-key Please select what kind of key you want: DSA and Elgamal What keysize do you want? (2048) Key is valid for? (0) Real name: foo Email address: Comment: Debian Archive Key
パスフレーズは空にしておく.空にしていないと後で dupload でアップロードしたときにパスフレーズをリモートからでは入力できなくて失敗する.
この鍵の ID を取得しておく.
$ gpg -k pub 1024D/6789ABCD 2010-04-07 uid foo (Debian Archive Key)
ここでは 6789ABCD が ID である.
Origin (アーカイブの作成者/管理者) が foo で Label (アーカイブの名称) が foo のアーカイブツリーを作成する.Origin, Label については Debian Repository HOWTO を参照.
$ cd /srv/www/example/debian $ mkdir -p pool/main $ mkdir -p dists/unstable/main/binary-i386 $ mkdir -p dists/unstable/main/source $ vi dists/unstable/main/binary-i386/Release Archive: unstable Version: 4.0 Component: main Origin: foo Label: foo Architecture: i386 $ vi dists/unstable/main/source/Release Archive: unstable Version: 4.0 Component: main Origin: foo Label: foo Architecture: source $ vi aptftp.conf APT::FTPArchive::Release { Origin "foo"; Label "foo"; Suite "unstable"; Codename "sid"; Architectures "i386"; Components "main"; Description "Private archive for foo"; }; $ vi aptgenerate.conf Dir::ArchiveDir "."; Dir::CacheDir "."; TreeDefault::Directory "pool/"; TreeDefault::SrcDirectory "pool/"; Default::Packages::Extensions ".deb"; Default::Packages::Compress ". gzip bzip2"; Default::Sources::Compress ". gzip bzip2"; Default::Contents::Compress "gzip bzip2"; BinDirectory "dists/unstable/main/binary-i386" { Packages "dists/unstable/main/binary-i386/Packages"; Contents "dists/unstable/Contents-i386"; SrcPackages "dists/unstable/main/source/Sources"; }; Tree "dists/unstable" { Sections "main"; Architectures "i386 source"; };
なお,元記事では
Default::Sources::Compress "gzip bzip2";
となっていたが,これでは /etc/apt/sources.list に deb-src を指定したときに apt-get update でエラーになってしまうので上のように修正した.
手元のマシンでアップロードの設定を行う.
$ sudo aptitude install dupload $ vi ~/.dupload.conf package config; $cfg{'foo'} = { fqdn => "www.example.com", method => "scpb", incoming => "/srv/www/example/debian/pool/main", # The dinstall on ftp-master sends emails itself dinstall_runs => 1, }; $cfg{'foo'}{postupload}{'changes'} = " echo 'cd /srv/www/example/debian; apt-ftparchive generate -c=aptftp.conf aptgenerate.conf; apt-ftparchive release -c=aptftp.conf dists/unstable > dists/unstable/Release; rm -f dists/unstable/Release.gpg; gpg -u 6789ABCD -bao dists/unstable/Release.gpg dists/unstable/Release' | ssh foo@www.example.com 2>/dev/null; echo 'Package archive created!'";
*.changes ファイルがあるディレクトリで下のようにする.
$ dupload --to foo .
インストールするマシンの apt を設定する.リポジトリの公開鍵をインストールするマシンの鍵束に加える.
$ ssh foo@www.example.com gpg --export -a 6789ABCD | sudo apt-key add -
これで通常の操作をすればよい.
$ sudo aptitude update
このやりかただと,パッケージをバージョンアップしてアップロードすると,前のバージョンがそのままアーカイブに残るという問題点がある.この場合でもインストール時には最新バージョンがインストールされるが,apt-cache search や apt-cache show で複数のバージョンが表示されてしまい気持ち悪い.以下の記事を参照して新しい方法を試してみる方がいいかもしれない.
前記で無事大型モニタで X を起動することに成功したのだが,今度は caps lock キーを control キーにするのがうまくいかない.
Section "InputDevice" Identifier "Generic Keyboard" Driver "keyboard" Option "CoreKeyboard" Option "XkbRules" "xfree86" Option "XkbModel" "pc104" Option "XkbLayout" "us" Option "XkbOptions" "ctrl:nocaps" EndSection
ではうまくいかないのである(おそらく USB キーボードだから).「http://kmuto.jp/d/index.cgi/debian/input-hotplug-guide.htm」などによると,HAL や udev などからハードウェア情報を取っている関係上,xorg.conf からの情報は無視されるようである.
あちこち探し回った結果(上の文書にも記述がある),debian では console-setup パッケージがキーボードの設定の面倒を見てくれることが判明した.
$ sudo aptitude install console-setup $ sudo vi /etc/default/keyboard XKBMODEL="pc104" XKBLAYOUT="us" XKBVARIANT="" XKBOPTIONS="ctrl:nocaps"
としてリブートすると,何とコンソールでも caps lock キーが control になり,X Window でも同様に設定された.なお上記ではキーボードを us 配列に設定しているが普通の人は pc106, jp に設定することになるだろう.
なお,console-setup を設定するとコンソールのフォントがしょぼい感じに変わってしまう.これを元に戻すには,
$ sudo dpkg-reconfigure console-setup
として,フォントを Fixed その他ではなく,「ブート時のをそのまま使う」に設定してやればよい.
Dell の巨大なモニタの設定が終わったので,それまで使っていた Dell のそれほど巨大でもないモニタを縦置きしてデュアルディスプレイ構成で使うことにした.このとき片方だけ画面を回転しなければならないが,これにもそこそこ苦労したので記録しておく.
まずはデュアルディスプレイの設定から.X ではデュアルディスプレイは昔からサポートされている.ただしこの設定では独立した複数のデスクトップができてしまう.Xinerama を用いるとデスクトップを統合し,それを複数のディスプレイで表示するようにできる(nvidia ドライバでは TwinView という方法もある).しかし統合デスクトップで片方のディスプレイの表示だけ回転というのは敷居が高そうだし,そもそも今回のようにディスプレイの大きさ が異なる場合,独立したデスクトップの方が便利そうなので,独立デスクトップの方で設定することにする.
最近では画面の回転(や解像度の変更)に XRandR という拡張を用いるようである.この機能を有効にし,xorg.conf の中で Option "Rotate" "left" などとすると,一応回転ができるのであるが,この場合ドライバのアクセラレート機能がオフになってしまうようなのである.いろいろ試行錯誤した結果,以下 のような方法におちついた.
まず xorg.conf:
Section "ServerLayout" Identifier "Layout" Screen 0 "Screen 0" 0 0 Screen 1 "Screen 1" LeftOf "Screen 0" EndSection Section "Device" Identifier "Device 0" Driver "nvidia" Screen 0 EndSection Section "Device" Identifier "Device 1" Driver "nvidia" Option "RandRRotation" "true" Screen 1 EndSection Section "Screen" Identifier "Screen 0" Device "Device 0" DefaultDepth 24 EndSection Section "Screen" Identifier "Screen 1" Device "Device 1" DefaultDepth 24 EndSection
上の別項目で説明したような解像度の設定などは取り去っているが,nVidia Quadro FX 380 の出力はどちらも DVI なので,自動で認識するようだ.また,デュアルリンクを有効にする nv ドライバの設定も nvidia ドライバでは必要ないようだ.
上のような設定で起動した後で,
$ xrandr --screen 1 -o left
とすると片方のディスプレイだけ回転できた.これを ~/.xsession などに書いておけばよい.
man xrandr を見て設定していると,つい
$ xrandr --screen 1 --output default --rotate left
としたくなるが,これはうまくいかない.--rotate でなく -o (orientation) を使うのがポイントであった.
先日Dell の大型モニタが届いた.このモニタには Mac を接続する予定なのだが,到着はもう少し先になりそうなので,とりあえず Linux マシンをつないで凌ぐことにしたのだが,設定に大苦労したというお話.
このモニタの解像度は 2560x1600 で,DVI 接続ではデュアルリンクが必要である.これはケーブルがデュアルリンク対応というだけでなく,グラフィックカード側もデュアルリンク出力をする必要があ る.Linux マシンのグラフィックカードは nVidia Quadro FX 380 というもので,仕様的には十分対応できるものである.
Linux マシンも届いたばかりなので,まずはいつもの通り Debian をインストールし,squeeze に upgrade した.そして X Window をインストール開始.
$ sudo aptitude install xorg twm $ vi ~/.xsession exec twm $ chmod +x ~/.xsession $ startx
... 真っ暗.何故か kill ではうまく終了させられないので,リモートログインから,
$ sudo telinit s
で終了させる.で,いつものごとく /etc/X11/xorg.conf をいじろうとしたのだが...無い!
$ sudo dpkg-reconfigure xserver-xorg
としても何も起こらない.「Xサーバ1.7→1.8、HAL から udev « Kawaji's Weblog」 などによると,最近の X はハードウェア環境について HAL や udev から情報を取得して自らを構成するようになっていて,xorg.conf は必要なくなっているそうだ.しかし xorg.conf が無いと話が始まらない./var/log/Xorg.0.log を見たところ,内部で利用している xorg.conf の内容が表示されていたので,それを元に generic な xorg.conf を作成した.
Section "ServerLayout" Identifier "Builtin Default Layout" Screen "Builtin Default nv Screen 0" EndSection Section "Device" Identifier "Builtin Default nv Device 0" Driver "nv" EndSection Section "Screen" Identifier "Builtin Default nv Screen 0" Device "Builtin Default nv Device 0" DefaultDepth 24 SubSection "Display" Depth 24 Modes "2560x1600" EndSubSection EndSection
まず man nv をチェックしたところ,Option "AllowDualLinkModes" "boolean"
というのを発見した.
Section "Device" Identifier "Builtin Default nv Device 0" Driver "nv" Option "AllowDualLinkModes" "true" EndSection
としてみた.その結果,表示されるにはされたが,画面が点滅を繰り返して使い物にならない.いろいろ実験した結果,vesa ドライバで 1600x1200 ならなんとか表示されることが判明した.
Section "Device" Identifier "Builtin Default nv Device 0" Driver "vesa" EndSection Section "Screen" Identifier "Builtin Default nv Screen 0" Device "Builtin Default nv Device 0" DefaultDepth 24 SubSection "Display" Depth 24 Modes "1600x1200" EndSubSection EndSection
しかしこれでは宝の持ち腐れである.そこで nVidia 謹製のドライバをインストールすることを決意.さっそくダウンロードしてインストールしてみたところ,全然だめなことが判明した.
そこで検索したところ,「404 - Not Found」というページを発見!だが,このページの通りにやったところ,m-a clean,a-i -i -t -f nvidia-kernel-source
のところで,BUILD FAIL!
となって失敗に終わった.
調べてみると,nvidia-kernel-source パッケージのバグ報告ページに既に報告されていた.そこには,nvidia-kernel-source を一旦消して,apt リポジトリを sid に一時的に変更してビルドするというヤレヤレな workaround が報告されていた.いろいろやったところ成功したので,手順を記しておく.
まず,話をややこしくしないために,クリーンインストールを行い,上記の Option "AllowDualLinkModes" "boolean"
を xorg.conf に記入したところまでリプレイした.以下,「404 - Not Found」に従って nvidia ドライバをビルドする.
# aptitude install nvidia-kernel-common module-assistant # aptitude install pkg-config # 後に sid からインストールされるのを防ぐため # m-a -i -t prepare # m-a clean,a-i -i -t -f nvidia-kernel-source BUILD FAIL! # vi /etc/apt/sources.list {squeeze を sid に書き換え} # apt-get update # 安全のため aptitude でなく apt-get を使った # m-a clean,a-i -i -t -f nvidia-kernel-source # depmod -a # apt-get install nvidia-glx nvidia-glx-dev nvidia-xconfig nvidia-settings # vi /etc/apt/sources.list {sid を squeeze に書き換え} # aptitude update # nvidia-xconfig
で無事成功!
javac がどのようなバイトコードを吐いているかを知るにはどうすればいいだろうか?
class test { String f(String s, int n) { return s + n; } }
この test.java のバイトコードを調べてみよう.
$ vi test.java $ javac test.java $ javap -c test Compiled from "test.java" class test extends java.lang.Object{ test(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return java.lang.String f(java.lang.String, int); Code: 0: new #2; //class java/lang/StringBuilder 3: dup 4: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V 7: aload_1 8: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 11: iload_2 12: invokevirtual #5; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 15: invokevirtual #6; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 18: areturn }
javap -c classname
でクラスファイルの「逆アセンブル」ができる.オレオレ apt リポジトリにオレオレ deb パッケージを置いて,特定少人数に対して配布する.
前回「Septième Sens: Debian パッケージを意識した upstream パッケージの作成」で作成した upstream パッケージ my-hello を Debian 化するにあたって,最小限必要な構成はどのようなものだろうか?
Debian パッケージの生成作業は upstream パッケージの最上位ディレクトリで行う.
$ cd my-hello $ mkdir debian
debian ディレクトリの中の情報を利用する dpkg-xxx などのコマンドは,作業ディレクトリとして debian ディレクトリではなく upstream パッケージの最上位ディレクトリが設定されていることを前提としている.
今回は現時点でもっともモダンな方法であると考えられる dh コマンドを使って Debian 化してみる.man dh によるともっともシンプルな debian/rules は以下のようになる:
#!/usr/bin/make -f %: dh $@
debian/rules ファイルは実行可能でなければならない.
$ vi debian/rules $ chmod +x debian/rules
ビルドを試してみる前に,やや天下りになるが,debian/compat というファイルに 7 を書き込んでおく必要がある.man debhelper によると,dh_xxx コマンドの動作モードが debian/compat ファイルの内容によって変わるからである.過去に dh_xxx を利用して作成したパッケージが dh_xxx の仕様変更によってビルドできなくなることを防ぐためにこのような仕様になっている.
$ echo 7 >| debian/compat
それでは実際にビルドを実行してみよう.ビルドのためのコマンドは Chapter 6. Building the package によると以下のようになる.
$ fakeroot debian/rules binary dh binary dh: cannot read debian/control: No such file or directory
失敗した.debian/control ファイルが必要だと言われている.
Debian Policy Manual - Control files and their fields を読むと,debian/control ファイルはメールヘッダのようなパラグラフ複数からなり,第1パラグラフで必須なのが Source:
と Maintainer:
, 第2パラグラフで必須なのが Package:
と Architecture:
と Description:
となっている.
Source: my-hello Maintainer: Your Name <yourname@example.com> Package: my-hello Architecture: all Description: say hello This program says hello.
debian/control を上記の内容で作成し,ビルドしてみる.
$ fakeroot debian/rules binary ... dh_installchangelogs tail: cannot open `debian/changelog' for reading: No such file or directory ...
今度は debian/changelog が必要だと言われている.
debian/changelog ファイルは debchange コマンドで生成できる.
$ debchange --create my-hello (0.0.1) UNRELEASED; urgency=low * Initial release. -- Your Name <yourname@example.com> Sun, 21 Feb 2010 22:35:59 +0900
実行するとエディタが起動するので,PACKAGE と VERSION という記述を my-hello と 0.0.1 に修正する.
$ fakeroot debian/rules binary ... dh_auto_install install -d /home/yourname/my-hello/debian/my-hello/usr/local/bin install myhello /home/yourname/my-hello/debian/my-hello/usr/local/bin ... dh_usrlocal rmdir: failed to remove `debian/my-hello/usr/local/bin': Directory not empty ...
dh_usrlocal が失敗している,出力を読むと dh_auto_install で make install が実行され,debian/my-hello/usr/local/bin に実行ファイルがインストールされているのが問題なのがわかる.
man dh によると,特定の dh_xxx をオーバーライドするには override_dh_xxx ターゲットを定義すればよい.
#!/usr/bin/make -f %: dh $@ override_dh_auto_install: make install DESTDIR=debian/my-hello PREFIX=/usr
debian/rules を上のように修正し,再度ビルドする.
$ debian/rules clean $ fakeroot debian/rules binary … dpkg-deb: building package `my-hello' in `../my-hello_0.0.1_all.deb'.
となり .deb が生成された.
$ sudo dpkg -i ../my-hello_0.0.1_all.deb $ myhello hello $ sudo dpkg -P my-hello $ myhello -bash: myhello: No such file or directory
my-hello |-- Makefile # DESTDIR と PREFIX |-- debian/ | |-- changelog # debchange コマンド | |-- compat # 7 | |-- control # Source:, Maintainer:, Package:, Architecture:, Description: | `-- rules* # override_dh_xxx: `-- myhello
$ fakeroot debian/rules binary
$ debian/rules clean $ fakeroot debian/rules binary
オレオレ apt リポジトリにオレオレ deb パッケージを置いて,特定少人数に対して配布する.
例えば以下のようなコマンドがあるとする.
#!/bin/sh echo 'hello'
このコマンドを提供する Debian パッケージを作りたい.この Debian パッケージの upstream にあたるパッケージはどのように作成すればよいだろうか?
まずパッケージ名を決める必要があるが,パッケージ名に使ってよい文字などの規則はどこにあるだろうか?「debian パッケージ名の規則 - metalglue」に書いた通り,Debian Policy Manual - Control files and their fields (5.6.1 Source) にあり,[a-z0-9][a-z0-9.+-]+
となる.大文字やアンダースコア _
が使えないことに注意.
ここではパッケージ名を my-hello とする.
バージョン番号の付け方はどうしたらよいだろうか?「オープンソースソフトウェアの育て方 」を通じて知った「Versioning Numbering Concepts - The Apache Portable Runtime Project」 が参考になる.これによると,バージョン番号は . (ドット) で区切られた3つの数字で,MAJOR.MINOR.PATCH という構成を取る.ドットは小数点ではない.つまり例えば 3.1.9 の次のバージョンは 3.2.0 ではなく 3.1.10 である.
MAJOR と MINOR が同じなら PATCH が違っても機能が違ってはならない.PATCH が異なるバージョン間でアップグレードしてもダウングレードしてもバグでない挙動は同じでなくてはならない.平たく言うとバグフィックスによるバージョンアップである.
MAJOR が同じで MINOR が異なるバージョンにアップグレードしたときに,使えていた機能が使えなくなってはならない.一方,ダウングレードは安全に行えないかもしれない.
MAJOR が異なる場合は,アップグレードもダウングレードも安全に行えない可能性がある.
ここではバージョンを 0.0.1 とする.
以下のようにする.
my-hello/ |-- Makefile `-- myhello
普通,作成したコマンドは /usr/local/bin に,ライブラリなら /usr/local/lib にインストールされるように Makefile を記述する.今回の場合はどのような Makefile が都合がいいだろうか?
Debian のパッケージ生成では,あるディレクトリを仮のルートとしてファイルをインストールし,それをアーカイブしてパッケージとする(Chapter?3.?Modifying the source - 3.1 Installation in a subdirectory を参照).よって,任意のディレクトリをルートと考えてインストールできるようになっていることがのぞましい.
また,「Debian Policy Manual」によると,debian ではコマンドは /usr/bin に,ライブラリは /usr/lib にインストールすることになっている.この,/usr を基準にするか /usr/local を基準にするかが選択可能であることが望ましい.
これらを勘案し,また ldapscripts の upstream パッケージも参考にして,DESTDIR と PREFIX という変数でこれらを制御できるようにした.
DESTDIR = PREFIX = /usr/local all: install: myhello install -d $(DESTDIR)$(PREFIX)/bin install $< $(DESTDIR)$(PREFIX)/bin .PHONY: all install
以下のようにインストールする.
$ make install DESTDIR=/home/yourname/tmp PREFIX=/usr
debian/rules の書き方の情報源はあちこちにあるが,どれが時代遅れでどれが最新かよくわからないのでネットを逍遙して検討してみた.
binary-arch: install dh_installdocs -a NEWS dh_installchangelogs -a ChangeLog dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a dh_md5sums -a dh_builddeb -a
::
ターゲットでカスタマイズする雰囲気:#!/usr/bin/make -f include /usr/share/cdbs/1/rules/debhelper.mk package = hello-cdbs install/hello-cdbs:: $(MAKE) prefix=$(CURDIR)/debian/$(package)/usr install DEB_INSTALL_DOCS_ALL := NEWS DEB_INSTALL_CHANGELOGS_ALL := ChangeLog
#!/usr/bin/make -f %: dh $@
Debian マシンで Gauche で trace するには以下のようにする.
まず SLIB (portable scheme library) が必要である.
$ sudo aptitude install slib
プログラム中で以下のようにする.
(define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))) (use slib) (require 'trace) (trace fact) (fact 5)
Host github.com User git
$ ssh git@github.com Hi YOURNAME! You've successfully authenticated, but GitHub does not provide shell access
と出力されれば OK.
$ git clone git@github.com:YOURNAME/hellos.git $ cd hello $ vi README $ git add README $ git commit $ git push origin HEAD
$ cd proj/hellos $ git remote add origin git@github.com:YOURNAME/hellos.git $ git checkout master $ git push origin HEAD
$ git config user.name YOURNAME $ git config user.email YOURNAME@EXAMPLE.COM
Mac OS X 上で Java で開発する必要がでてきたので,Snow Leopard での Java について調べ始めた.順不同で箇条書きする.
$ LC_ALL= javac ...
$ javac -J'-Dfile.encoding=EUC-JP' ...
apt-get でインストールしたものの履歴,それもコマンドラインから陽に指定したパッケージの履歴が欲しいとずっと思っていた.自分がどういったパッケージを真に必要としたかがわかり,将来の環境構築の参考になるからである.
apt-get でなく,aptitude を用いれば可能である./var/log/aptitude* に aptitude 関連の履歴がとられており,その中から [INSTALL]
という表現を grep すればよい.
ただし,デフォルトでは aptitude 関連のログは 6 ヶ月以前のものは消されてしまうので,全ての履歴を調べたいなら debian をインストールしたときに /etc/logrotate.d/aptitude を変更しておかなければならない.
iTerm を 0.10 にしてから時々表示が欠けるようになった.
SourceForge.net: iTerm.app: Detail: 2888004 - Occasional ’missing’ lines によると,Preferences の Display Refereshing Rate を遅くすると顕在化しなくなるということだ.
このレートを調整するには以下のコマンドを用いればよい.
$ ruby -e 'loop do ; puts "foo" ; sleep(rand*0.01) ; end'
例えば今,配列を返す関数があるとする.
sub f { (1, 2, 3); }
f()
の返り値の n 番目の要素を得るには次のようにすればよい.
@a = f(); print $a[$n];
この時,一時変数 @a
を使わないようにするにはどうすればよいだろうか?
以下のようにすればうまくいくが不自然だ.
print [f()]->[$n];
あるいは,
print ${[f()]}[$n];
ともできるが,いずれにせよ不自然.
もっと素直な記法は無いのか?
上のような変なことをしなくても,
(f())[$n]
でよい.ただし,
print (f())[$n];
は print 関数と括弧の関係上文法エラーになるので,
print ((f())[$n]);
とする必要がある.上の print がうまくいかなかったので勘違いしていた .
ネットからダウンロードした Web ページの内容を少しだけエディタで書き換えることがよくある,これを Perl を使ってプログラムからやるにはどのようにすればよいだろうか.
HTML::TreeBuilder, HTML::Element あたりを使うことになるだろう.
まずは単に HTML をパーズし,HTML に戻すプログラムを書いてみよう.
use HTML::TreeBuilder::XPath; my $html = HTML::TreeBuilder::XPath->new_from_file('sample.html'); print $html->as_HTML('&<>');
as_HTML()
の引数としてエンティティ化する文字のリストを明示的にを渡さないと,漢字などは全てエンティティにされてしまうので注意.
ここで問題となるのは $html->as_HTML('&<>')
の文字コードは何なのか,そもそも sample.html の文字コードの扱いはどうなっているのかということである.HTML::TreeBuilder や HTML::Element には文字コードに関する記述は無い.
sample.html の文字コードを変えていろいろやってみたところ,どうやら入力をそのまま出力しているようである.当然 utf8::is_utf8($html->as_HTML('&<>'))
は偽となる.
sample.html の文字コードを UTF-16 にするとうまくいかなかったので,結局タグが ASCII である文字コードのときにうまくいくだけのようだ.
あちこち検索などした結果,求める情報は man HTML::Parser の METHODS > Unicode にあることが判明した.これによると utf8 文字列なら扱えるとあるので,次に得られた文字列を utf8 文字列に変換する方法について書くことにする.
perl の utf8 文字列についての詳しい話は別の場所を参照してほしい.
ここでは Encode::Guess を用いて,文字コードが不明の文字列を utf8 文字列に変換する方法について書くことにする.
Encode::Guess の guess_encoding 関数を用いて以下のように書けばよい.
use Encode::Guess; sub content_of_file { my ($fname) = @_; open my $fh, '<', $fname; local $/; my $content = <$fh>; close $fh; return $content; } while () { my $content = content_of_file($_); my $decoder = guess_encoding($content, qw{shift_jis euc-jp jis}); print "$_ => ", $decoder->name(), "\n"; my $decoded = $decoder->decode($content); }
これまで説明してきたことを合体すれば HTML をパーズし,HTML に元の文字コードのまま戻すコードを書くことができる.
use Encode::Guess; use HTML::TreeBuilder::XPath; sub content_of_file { ... } my $content = content_of_file('sample.html'); my $decoder = guess_encoding($content, qw{shift_jis euc-jp jis}); my $decoded = $decoder->decode($content); my $html = HTML::TreeBuilder::XPath->new_from_content($decoded); open my $fh, '>', "/tmp/sample.html"; print $fh $decoder->encode($html->as_HTML('&<>')); close $fh;
HTML を書き換えるには,delete_content()
, push_content()
, attr()
などを用いる.
use utf8; use Encode::Guess; use HTML::TreeBuilder::XPath; sub content_of_file { ... } my $content = content_of_file('sample.html'); my $decoder = guess_encoding($content, qw{shift_jis euc-jp jis}); my $decoded = $decoder->decode($content); my $html = HTML::TreeBuilder::XPath->new_from_content($decoded); my $nodeset = $html->findnodes('//a[@href]'); for my $element (grep { $_->attr('href') =~ /^index\.html$/ && $_->look_down('_tag', 'img'); } $nodeset->get_nodelist()) { my $href = $element->attr('href'); $href =~ s/index\.html/default.html/; $element->attr('href', $href); $element->delete_content(); $element->push_content('画像は削除されました.'); } open my $fh, '>', "/tmp/sample.html"; print $fh $decoder->encode($html->as_HTML('&<>')); close $fh;
Git の SHA-1 名のことを調べていたら Git の根本について書いてあったのでまとめておく.簡単のため正確さを犠牲にしているので注意.
Git のオブジェクトは,(type, length) というヘッダと,データ部分をつなげたバイナリである.このバイナリから SHA-1 ハッシュを計算して得られた 160 bit の値(表記は 40 桁の 16 進数)を名前とする.
オブジェクトには以下のものがある.
blob. これはファイルを表す.データ部分はファイルの内容そのものである.
tree. これはディレクトリを表す.データ部分は (type, name, object) をレコードとするリストで,name でソートされている.type に blob と tree とを取ることによりディレクトリツリーを表現できる.
commit. これはあるディレクトリツリーから別のディレクトリツリーへの変更を表す.データ部分は (before-tree, after-tree, time, author) である.
これらのオブジェクトは全て .git/objects に格納されている.この中から所望のデータを取り出す手がかりが必要だが,それらは .git/refs/heads に commit オブジェクトの SHA-1 名を内容とするファイルとして格納されている.これらを,そのファイル名を名前とするブランチと呼ぶ.
入門Git pp. 8--19
現在,ある Subversion リポジトリを Git によって追跡しているが,各コミットに関するベンチマーク値を Git 側のコミットの SHA-1 名をキーとして共有ディレクトリに格納している.
そこで疑問が生じるわけであるが,別の git svn clone で追跡を開始した Git リポジトリの各コミットの SHA-1 名は,元の subversion のコミットが同じなら同じになるのだろうか?それとも異なるものになるのだろうか?
予想としては,コミットというのはそもそもコミットした時の時刻情報を含んでいるので,コミット情報から生成される SHA-1 ハッシュは当然異なるものになるだろう,ということである.
しかし実験してみると,svn clone によって生成されたコミットの時刻情報は元の Subversion のコミットの時刻情報を使っているようで,SHA-1 値は全く同一となった.
オリジナルのレポジトリ repos があるとする.
$ cd repos $ git branch * master
これを clone に clone する.
$ cd .. $ git clone repos clone $ cd clone $ git branch * master remotes/origin/HEAD -> origin/master remotes/origin/master $ git checkout -b branch $ git branch * branch master remotes/origin/HEAD -> origin/master remotes/origin/master (ファイルを編集) $ git commit -a $ git checkout master
これをさらに clone する.
$ cd .. $ git clone clone cloneofclone $ cd cloneofclone $ git branch * master remotes/origin/HEAD -> origin/master remotes/origin/branch remotes/origin/master
さて,このブランチ remotes/origin/branch を repos の branch というブランチとして push するにはどうすればよいか?
$ git push ../repos remotes/origin/branch:refs/heads/branch
とすればよい...
本当か?ブランチのネーミングの本質がわかってないので確信できない.
(2012/3/1 追記)上のやり方で正しい。ただし、refs/heads/branch と branch は同値なので、
$ git push ../repos remotes/origin/branch:branch
Q: Debian のパッケージ名の規則はどこに書いてあるのか?
A: Debian Policy Manual - Control files and their fields (5.6.1 Source) にある.誰もが 3.1 The package name に書いてあると思うだろうが,そこの参照先の参照先である上記に記述してある.
これによると,[a-z][a-z0-9.+-]+ である.