Hatena::ブログ(Diary)

Hatena::Diary::Ubuntu このページをアンテナに追加 RSSフィード

July 08(Wed), 2009

ブログを移転しました。

新しいアドレスはこちらです。

http://d.hatena.ne.jp/Naruhodius/


はてなIDを変更しましたので、今後こちらのブログは更新されません。

フィードを購読しているかたは変更をお願いします。

June 15(Mon), 2009

Mac OS X Leopardをクリーンインストールして環境構築して使えるようになるまで(2009年6月版)

MacBook ProにLeopardをクリーンインストールして環境構築をした。この手の記事はよく見かけるし自分も以前に書いたのだが、もうだいぶ古くなってきた上に情報も散在しているので、あらためてまとめた。


システムのインストール

Mac OS X LeopardをDVDからインストールする。このとき、オプションで各言語環境とプリンタドライバは外してしまうのが良い。ほとんどの場合必要とならないし、どうしても必要になった場合は後から追加インストールできる。言語とプリンタドライバをすべて外すことで、OS自体のサイズを大幅に削減し、後述するTimeMachineでのバックアップ量も減らすことができる。


セキュリティ

とりあえず最低限のセキュリティ対策をしないと話にならない。


FileVault

暗号ファイルシステム。ファイルが増えてからだと暗号化に時間がかかるため、必ず最初にオンにしておく。

f:id:Ubuntu:20080212011930p:image


ファイアウォール

必須サービスのみ許可する。Apacheを使うならWeb共有、SSHを使うならリモートログインなどを有効にする。

f:id:Ubuntu:20080212011929p:image

f:id:Ubuntu:20080212011931p:image


ソフトウェアアップデート

必ず設定する。なお、実際にアップデートをおこなう際は、事前に必ずTimeMachineでバックアップする。

f:id:Ubuntu:20080212012108p:image


パスワード

管理者アカウントにパスワードを付与し、スリープやスクリーンセーバを解除するときにパスワードを要求する設定にする。

f:id:Ubuntu:20080212011928p:image


Open Firmware Password

LeopardのインストールDVDの /Applications/Utilities に入っているので、ターミナル等でデスクトップにコピーしてインストールをおこなう。Open Firmware PasswordによってEFIにパスワードを付与し、外部ディスクでのブートを回避できる。


rootパスワード

ターミナルからrootにパスワードを設定する。

sudo passwd root

管理者アカウント

別に管理者アカウントを作成して、ふだん利用するアカウントを一般ユーザーに降格させる。


SSHのセキュリティ設定

/etc/sshd_config を編集しパスワードログインを不可にする。これを設定しないと外に持ち出したときに総当たり攻撃を食らってしまう。なお、外部からSSHログインしたい場合はそのマシンの ~/.ssh/id_rsa.pub を ~/.ssh/authorized_keys に登録してやれば良い。この作業は面倒なのでシェルスクリプトなどで一元的におこなうのがおすすめ。

PermitRootLogin no
PasswordAuthentication no

アプリケーションのインストール

インストールするアプリケーションは厳選して必要最低限のものにする。



スクリーンのロック

上記のLockTightをインストールすることでショートカット一発でロックできる。外出時などは重宝する。


nmblookupを使えなくする

Macはネットワークのタイムアウトが発生するとシステム全体のレスポンス低下を招くことが多いようだ。そこで、Sambaサーバーを利用しないのであれば、nmblookupを使えなくしておくとプチフリーズの頻度を低下させることができる。

sudo chmod -x /usr/bin/nmblookup

本当はもっと良い方法があるのかもしれないが、ひとまずこれで問題なくMacを利用することができている。


他のMacからのデータ移行

Firefox、Thunderbird、Adium、GreaseKit等は ~/Library/Application Support の下をコピーすれば他のMacからそのまま環境を持ってくることができる。


LimeChatの透過設定

以前はソースコードを変更する必要があったが、現在のバージョンのLimeChatではメニューから普通に設定することができるので不要。


イー・モバイルの設定

ユーティリティ無しでイー・モバイルに接続する設定が役に立つ。


フォルダ名を英語化する

「ダウンロード」「ライブラリ」を「Downloads」「Library」にする。それぞれのディレクトリの下にある.localizedファイルを削除する。

rm ~/Downloads/.localized
rm ~/Library/.localized

ネットワーク接続時に .DS_Store ファイルの作成を抑制する

ターミナルから以下のコマンドを投入して再起動すれば反映される。

defaults write com.apple.desktopservices DSDontWriteNetworkStores true 

開発環境を構築する

MacPortsでほとんどの環境がそろう。シェルスクリプトを用意してまとめてインストールするのが良い。

sudo port -d selfupdate
sudo port -d install libiconv +enable_cp932fix
sudo port -d install coreutils
sudo port -d install findutils
sudo port -d install wget
sudo port -d install curl
sudo port -d install nkf
sudo port -d install screen
sudo port -d install ruby
sudo port -d install zlib
sudo port -d install openssl
sudo port -d install rb-rubygems
sudo port -d install subversion
sudo port -d install git-core
sudo port -d install -f svk
sudo port -d install lv
sudo port -d install chasen
sudo port -d install sqlite3
sudo port -d install libxml
sudo port -d install libxml2
sudo port -d install expat
sudo port -d install p7zip
sudo port -d install bzip2
sudo port -d install ctags
sudo port -d install ncurses
sudo port -d install vim
sudo port -d install smartmontools
# Python
sudo port -d install python25
sudo ln -s /opt/local/bin/python2.5 /usr/local/bin/python
sudo port -d install py25-hashlib
sudo port -d install py25-zlib
sudo port -d install py25-readline
sudo port -d install py25-mysql
sudo port -d install py25-twisted
sudo port -d install py25-pgsql
sudo port -d install py25-mechanize
sudo port -d install py25-openssl
sudo port -d install py25-paste
sudo port -d install py25-pastedeploy
sudo port -d install py25-simplejson
sudo port -d install py25-nose
sudo port -d install py25-sqlalchemy
sudo port -d install py25-sqlalchemy-migrate
sudo port -d install py25-turbogears
sudo port -d install py25-memcached
sudo port -d install py25-yaml
TARGET_PATH=`python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"`
wget http://peak.telecommunity.com/dist/ez_setup.py
sudo python ez_setup.py -U setuptools
sudo cp ez_setup.py $TARGET_PATH/ez_setup.py
rm ez_setup.py
sudo ln -s /opt/local/bin/easy_install-2.5 /usr/local/bin/easy_install
sudo easy_install ipython
sudo ln -s /opt/local/bin/ipython2.5 /usr/local/bin/ipython
svn co http://code.djangoproject.com/svn/django/trunk/
cd trunk
sudo python setup.py install
cd ..
rm -rf trunk/
# Ruby
export RUBYOPT=rubygems
sudo gem update --system
sudo gem update
sudo gem install mongrel
sudo gem install mongrel_cluster
sudo gem install mechanize
sudo gem install Selenium
sudo gem install vim-ruby
sudo gem install postgres-pr
sudo gem install BlueCloth
sudo gem install RedCloth
sudo gem install net-ssh
sudo gem install net-sftp
sudo gem install coverage
sudo gem install zentest
sudo gem install capistrano
sudo gem install magic_multi_connections
sudo gem install redgreen
sudo gem install rspec
sudo gem install rspec-rails
sudo gem install cucumber
sudo gem install rails
sudo gem cleanup

TimeMachineで環境をバックアップする

ここまで完了したら、TimeMachineを利用して初期環境構築直後の状態をバックアップする。TimeMachineはゲームをセーブするようなもので、今後なにか問題が発生しても現在の状態までシステムを戻すことができるので非常に便利なバックアップツールである。なお、パーティションを別にしたりデータフォルダを対象外にするなどして、システムの中核だけをバックアップすると容量も20〜40GB程度で済むのでおすすめである。TimeMachineはシステムの状態保存に利用して、データはファイルサーバーやNASに置いて別途バックアップするのが良いだろう。現在ではHDDも非常に安価だし、必要ならネットワークを経由してGNU/Linux環境にバックアップを取得することもできる。


ソフトウェアアップデートや大掛かりなシステム変更の前に必ずTimeMachineバックアップをおこなうのはもちろんのこと、他にもさまざまな活用方法が考えられる。たとえば同機種であれば別のマシンにも復元できるので、初期不良や故障等でやむを得ず別のマシンへ交換した場合でもすぐに環境を取り戻せる。また内蔵HDDの換装時にもTimeMachineで環境をそのまま移すことができる。自宅と仕事場で同じ機種を利用するなら片方で環境構築すればもう片方はTimeMachineから復元するだけで済む。まだTimeMachineを利用していないのであれば、今すぐ現在の環境をバックアップするのが良いだろう。

June 10(Wed), 2009

新しいMacBook Proを買った。

銀座アップルストアに立ち寄って、そのまま購入した。

f:id:Ubuntu:20091005165641j:image

13inch 1280×800
2.53GHz Intel Core 2 Duo
250GB 5400rpm SATA HDD
4GB 1066MHz DDR3 SDRAM (MAX 8GB)
NVIDIA GeForce 9400M
内蔵リチウムポリマーバッテリー(58Wh) 最大7時間
重量 2.04kg
¥168,800

さっそく開封。

f:id:Ubuntu:20091005165642j:image


液晶の品質がMacBook Proシリーズのグレードになっている。

f:id:Ubuntu:20091005165643j:image


つい先日iMacを買ったばかりだが、同世代のMacBook Proも購入。これでマシン台数が19台になった

June 08(Mon), 2009

Xfce4のインターフェイスのテーマをカスタマイズする。

GNOMEKDEと並んで人気の軽量デスクトップ環境であるXfceでは、豊富にインターフェイスのテーマが用意されている。このテーマは /usr/share/themes/テーマ名/gtk-2.0/gtkrc を編集することで、細部をカスタマイズすることができる。


ふだんは Xfce4-dusk テーマを利用しているのだが、インターフェイスの細かい部分に不満を感じていた。たとえばテキストの基本色に #fcfcfc という中途半端な白色が指定されているので、背景に白系統の色が指定されたウェブサイトなどで極端に文字の視認性が低下したり、背景色黒と文字色緑という統一性を欠いたりするためだ。


そこで /usr/share/themes/Xfce-dusk/gtk-2.0/gtkrc で以下のように編集してみた。

style "default"
{
    GtkEntry::cursor_color                       = "#00ff00"
    GtkEntry::secondary_cursor_color             = "#00ff00"
    GtkTextView::cursor_color                    = "#00ff00"
    GtkTextView::secondary_cursor_color          = "#00ff00"

    fg[NORMAL]        = "#00ff00"
    fg[PRELIGHT]      = "#00ff00"
    fg[SELECTED]      = "#00ff00"

    text[ACTIVE]      = "#00ff00"
    text[INSENSITIVE] = "#00ff00"
    text[NORMAL]      = "#00ff00"
    text[PRELIGHT]    = "#00ff00"
    text[SELECTED]    = "#00ff00"
}

結果として、このように表示されるすべての文字色を統一できた。

f:id:Ubuntu:20090608150133p:image

文字色が常に緑なので、背景が黒でも白でも読み取ることができる。

f:id:Ubuntu:20090608144121p:image

インターフェイスももちろん全部緑。

f:id:Ubuntu:20090608144118p:image

UIも黒背景に緑文字。

f:id:Ubuntu:20090608144116p:image

これで、Emacsやターミナルのカラーと足並みを揃えることができた。

f:id:Ubuntu:20090608144110p:image


気軽に細部を編集できるXfceは、生産性とリッチさを兼ね備えた、すばらしいデスクトップ環境だ。


Ubuntu 8.04 LTS および Debian GNU/Linux 5.0 lenny の Xfce 4.4.2 で動作を確認した。

http://packages.ubuntu.com/hardy/xfce4

http://packages.debian.org/lenny/xfce4


参考

Xfceを使う。

http://d.hatena.ne.jp/Ubuntu/20080717/install_xfce

May 29(Fri), 2009

UbuntuでモバイルPCの起動速度が遅い問題を対処する。

ネットブックに、Ubuntuなどに代表されるDebianディストリビューションを入れて使用するという例が増えてきつつある。モバイルでUbuntuなどを利用していると、起動がやたら遅くなるケースがまれにある。このようなトラブルに備え、設定しておいたほうが良い箇所がいくつかある。


スプラッシュスクリーンを除去して起動時のメッセージを確認する。

Ubuntuでは起動時に派手なスプラッシュスクリーンが表示され、起動時のメッセージが隠蔽されている。これはこれで確かに見栄えは良いのだが、起動時のネットワークやディスクまわりのエラーメッセージを検知しにくいという欠点がある。特にモバイルで利用することがある場合では外しておいた方が良いだろう。


/boot/grub/menu.lst を編集する。以下は Ubuntu 8.04 LTS での例。

title		Ubuntu 8.04.2, kernel 2.6.24-24-generic
root		(hd0,4)
kernel		/vmlinuz-2.6.24-24-generic root=UUID=b088f146-118b-4f91-bd16-d83c096f67a5 ro locale=ja_JP
# 上記の文字列から、末尾の quiet splash を除去する
initrd		/initrd.img-2.6.24-24-generic

さらに、同ファイルのコメント内にある defoptions の文字列を変更しておけば、カーネルが新しく更新された場合も初期値として反映される。そこで defoptions からもquiet splashを除去する。

# defoptions=locale=ja_JP

イー・モバイル端末利用時は不要なネットワーク接続を起動しない。

LAN接続時にIPアドレスDHCPから取得するようにしていると、LANに接続しない状態でOSを起動したときに、DHCPクライアントが起動してIPアドレスを取得しようとして長い時間がかかっていることに気づく。モバイルで起動がやたら遅いことがあれば、たいてい原因はこのDHCPによるIPアドレス取得であることが多い。そこで、イー・モバイル端末を接続した状態でOSを起動したときは、不要なネットワーク接続を起動しないようにしておく。

/etc/init.d/networking を編集する。

case "$1" in
start)
    test -c /dev/ttyUSB0 && exit 0 # イー・モバイル端末が接続されていたらそのまま終了
    log_action_begin_msg "Configuring network interfaces"
    ...

あらかじめイー・モバイル端末のデバイスファイル名を調べておく。たいていは /dev/ttyUSB0 になるケースが多いようだ。OSが起動するときに /etc/init.d/networking start コマンドが発行されているので、このとき /dev/ttyUSB0 が存在していればそのまま戻り値0で終了するようにする。


ログイン時に自動的にイー・モバイル端末でネットワーク接続する。

OS起動時にイー・モバイル端末が接続されていたら pon コマンドを発行してネットワークへ接続する。これは /etc/rc.local で設定する。

test -c /dev/ttyUSB0 && pon em

参考: Debian系Linuxでイー・モバイルD02HWを使用する(プラグアンドプレイ対応)。

May 19(Tue), 2009

EmacsのキーバインドをVimライクにし、さらにLispをバイトコンパイルして高速化する。

Emacsは強力な編集機能と拡張性を持つが、ファイルの編集に関してはVimのように閲覧と編集のモードを分けてメリハリのある操作ができたほうが良い。そこでEmacs Lispをあれこれ書いた。なお、動作確認は GNU/LinuxGNU Emacs 23.0.60.1 及び Mac OS XCarbon Emacs でおこなっている。

;; .emacsは2行だけ
(byte-recompile-directory "~/.emacs.d/elisp")
(load "~/.emacs.d/elisp/startup")
;; startup.elでロードパスなどの設定をしてautoload.elを呼ぶ
(load "autoloads")

rubikitchさんのエントリで紹介されていた key-chord.el を利用。これは主に2つのキー同時押しでEmacs Lispの関数を呼び出す目的に利用するもので、これを導入するとEmacsの使用感は革命的に変化すると言っても良い。なお同時押しキーと対応する機能の定義は key-chord-define-global 関数でおこなう。

;; autoloads.elでは様々なファイルをロードするがここで一緒にrequireする
(require 'key-chord)
(setq key-chord-two-keys-delay 0.04)
(key-chord-mode 1)
;; autoloads.elの最後でconfigs.elを呼び環境設定する
;; キーバインド設定
(load "global-set-key")
;; view-modeキーバインド設定
(load "view-mode-key")
;; key-chord.el専用キーバインド設定
(load "key-chord-define-global")

キーバインドは、全体的なキーバインド設定(global-set-key.el)と、Viewモードで閲覧するためのキーバインド設定(view-mode-key)、key-chord.elによる同時押し専用のキーバインド設定に分割しておく。分割することで、どの設定がどのファイルに依存するのかわかりやすくする狙いがある。


Viewモード専用のキーバインド設定を抜粋。以下の記事を参考にした。

http://d.hatena.ne.jp/rubikitch/20081104/1225745862

http://d.hatena.ne.jp/yaotti/20081104/1225809687

(setq view-read-only t)
(defvar pager-keybind
      `( ;; vi-like
        ("h" . backward-word)
        ("l" . forward-word)
        ("j" . next-line)
        ("k" . previous-line)
        ("J" . next-window-line)
        ("K" . previous-window-line)
        ("b" . scroll-down)
        ("f" . scroll-up)
        (" " . scroll-up)
        ("w" . forward-word)
        ("e" . backward-word)
        ("n" . ,(lambda () (interactive) (scroll-up 1)))
        ("p" . ,(lambda () (interactive) (scroll-down 1)))
        ("[" . forward-sexp)
        ("]" . backward-sexp)
        ("." anything-c-moccur-occur-by-moccur)
        ("c" . scroll-other-window-down)
        ("v" . scroll-other-window)
        ))
(defun define-many-keys (keymap key-table &optional includes)
  (let (key cmd)
    (dolist (key-cmd key-table)
      (setq key (car key-cmd)
            cmd (cdr key-cmd))
      (if (or (not includes) (member key includes))
        (define-key keymap key cmd))))
  keymap)

(defun view-mode-hook0 ()
  (define-many-keys view-mode-map pager-keybind)
  (hl-line-mode 1)
  (define-key view-mode-map " " 'scroll-up))
(add-hook 'view-mode-hook 'view-mode-hook0)

(do-not-exit-view-mode-unless-writable-advice view-mode-exit)
(do-not-exit-view-mode-unless-writable-advice view-mode-disable)

(provide 'view-support)

Viewモードではファイルにテキストを入力しないはずなので、このようにhjklなどVimライクなカーソル移動にキーバインドを割り振っている。Vimではファイルを開くとき、最初はコマンドモードとなり、実際にテキストに変更を加えるときにインサートモードに推移して編集をおこなう。それと同じ操作感をEmacsでも実現するために、EmacsでもViewモードと通常モードを切り替えて編集作業をおこなうようにする。まずは configs.el で find-file-hooks をフックして、Emacsでファイルを開くときは必ず読み取り専用でオープンするようにしておく。

;; ファイルオープン直後は読み取り専用
(add-hook 'find-file-hooks
  (lambda ()
    (cond (view-mode)
      (t
        (view-mode)))))

これで find-file や引数渡しでファイルを開いても find-file-read-only で開くのと変わらなくなる。次に、Viewモードと通常モードのトグル切り替えのための操作が必要なので global-set-key.el で定義する。

;; C-x C-j または C-x j で view-mode を切り替える
(defun toggle-view-mode ()
  (interactive)
  (cond (view-mode
      (view-mode nil)
      (setq hl-line-mode nil))
    (t
      (view-mode))))
(define-key global-map "\C-x\C-j" 'toggle-view-mode)
(define-key global-map "\C-x\ j" 'toggle-view-mode)
(define-key global-map [C-backspace] 'toggle-view-mode)

これでC-x jで閲覧と編集の切り替えが可能、GUIではC-backspaceも有効。ここまでは以前も書いたが、さらにkey-chord.el専用キーバインドでも同時押しによる切り替えを定義しておく。

;; jk で view-mode を切り替える
(key-chord-define-global "jk" 'toggle-view-mode)

せっかくなので、key-chord.el専用のキーバインドを紹介。よく使用する機能を同時押しで実現できるようにしておくと大変べんりである。バッファリストやファイルオープン、画面移動などは隣り合う2つのキーを同時押しでできるように設定しておく。

;; yu で auto-complete-modeの有効/無効を切り替える
(key-chord-define-global "yu" 'auto-complete-mode)
;; 分割したウィンドウを移動(上下左右)
(key-chord-define-global "io" 'windmove-up)
(key-chord-define-global ",." 'windmove-down)
(key-chord-define-global "hj" 'windmove-left)
(key-chord-define-global "l;" 'windmove-right)
;; fg で keyboard-escape-quit する
(key-chord-define-global "fg" 'keyboard-escape-quit)
;; jk で view-mode を切り替える
(key-chord-define-global "jk" 'toggle-view-mode)
;; バッファ切り替え
(key-chord-define-global "m," 'previous-buffer)
(key-chord-define-global "ui" 'backward-buffer)
;; バッファリスト
(key-chord-define-global "kl" 'electric-buffer-list)
;; バッファ先頭/末尾へのカーソル移動
(key-chord-define-global "rt" 'beginning-of-buffer)
(key-chord-define-global "vb" 'end-of-buffer)
;; アンドゥ/リドゥ
(key-chord-define-global "as" 'undo)
(key-chord-define-global "sd" 'redo)
;; ファイルを開く
(key-chord-define-global "df" 'find-file)
;; スクロール
(key-chord-define-global "er" 'scroll-down)
(key-chord-define-global "cv" 'scroll-up)

さて、Emacs Lispが複雑、巨大化してくるとLispインタプリタの遅さが気になってくる。そこで事前にバイトコンパイルすることにより高速化を図る。Emacs Lispをインストールするためのシェルスクリプトを用意し、その中でバイトコンパイルするようにしておく。

#!/bin/sh

setup_dotemacs() {
    test -d ~/.emacs.d && rm -rf ~/.emacs.d/
    test -f ~/.emacs && rm -f ~/.emacs
    cp $OPTIONS $SCRIPTS/dot_files/dot_emacs $HOME/.emacs
    test -d $TARGET || mkdir -p $TARGET
    cp $OPTIONS $SCRIPTS/dot_files/dot_emacs.d/* $TARGET/
}

batch_byte_compile() {
    cd ~/.emacs.d/elisp/3rd-party
    emacs --batch --eval '(byte-compile-file "js2.el")'
    emacs --batch --eval '(byte-compile-file "redo.el")'
    emacs --batch --eval '(byte-compile-file "ruby-block.el")'
    emacs --batch --eval '(byte-compile-file "auto-complete.el")'
    emacs --batch --eval '(byte-compile-file "key-chord.el")'
    emacs --batch --eval '(byte-compile-file "twitter1-mode.el")'
    emacs --batch --eval '(byte-compile-file "twitter2-mode.el")'
    emacs --batch --eval '(byte-compile-file "twitter3-mode.el")'
    emacs --batch --eval '(byte-compile-file "twitter4-mode.el")'
    cd ~/.emacs.d/elisp
    emacs --batch --eval '(byte-compile-file "delete-empty-file.el")'
    emacs --batch --eval '(byte-compile-file "emacs-w3m.el")'
    emacs --batch --eval '(byte-compile-file "global-set-key.el")'
    emacs --batch --eval '(byte-compile-file "key-chord-define-global.el")'
    emacs --batch --eval '(byte-compile-file "kill-all-buffers.el")'
    emacs --batch --eval '(byte-compile-file "new-file-p.el")'
    emacs --batch --eval '(byte-compile-file "persistent-scratch.el")'
    emacs --batch --eval '(byte-compile-file "physical-line.el")'
    emacs --batch --eval '(byte-compile-file "proxy.el")'
    emacs --batch --eval '(byte-compile-file "startup.el")'
    emacs --batch --eval '(byte-compile-file "tab4.el")'
    emacs --batch --eval '(byte-compile-file "twitter-key.el")'
    emacs --batch --eval '(byte-compile-file "unix-defaults.el")'
    emacs --batch --eval '(byte-compile-file "utils.el")'
    emacs --batch --eval '(byte-compile-file "view-mode-key.el")'
}

test -d $SCRIPTS/dot_files/dot_emacs.d || exit 1
TARGET=$HOME/.emacs.d

case $OSTYPE in
  *darwin*)
    OPTIONS=-Rv
    ;;
  *)
    OPTIONS=-Rvd
    ;;
esac

setup_dotemacs
batch_byte_compile

autoloads.el や configs.el のように他のファイルをロードするものはコンパイルの対象外とするが、単体で機能できるものについてはすべて事前にバイトコンパイルしておく。同じ要領で、rubyに付属のEmacs Lispもバイトコンパイルしておく。まず、以下のシェルスクリプトでrubyのソースコードリポジトリからチェックアウトしてビルド。ソースコードのmiscディレクトリにruby用のEmacs Lispが付属している。

install_branch() {
    test -d /usr/local/src/ruby/branches || sudo mkdir -p /usr/local/src/ruby/branches
    cd /usr/local/src/ruby/branches
    sudo svn co http://svn.ruby-lang.org/repos/ruby/branches/$1/ $1
    cd $1
    sudo autoconf
    sudo ./configure
    sudo make
    sudo make install
    test -x $SCRIPTS/installer/install_emacs_ruby.sh && $SCRIPTS/installer/install_emacs_ruby.sh /usr/local/src/ruby/branches/$1/misc
}

install_branch ruby_1_8_7

これをEmacsのロードパスである /usr/local/share/emacs/site-lisp にシンボリックリンクして、バイトコンパイルする。

install_emacs_ruby() {
    test -L $1/ruby-mode.el && sudo rm $1/ruby-mode.el
    sudo ln -s $2/ruby-mode.el $1/ruby-mode.el
    test -L $1/ruby-style.el && sudo rm $1/ruby-style.el
    sudo ln -s $2/ruby-style.el $1/ruby-style.el
    test -L $1/ruby-electric.el && sudo rm $1/ruby-electric.el
    sudo ln -s $2/ruby-electric.el $1/ruby-electric.el
    test -L $1/inf-ruby.el && sudo rm $1/inf-ruby.el
    sudo ln -s $2/inf-ruby.el $1/inf-ruby.el
    test -L $1/rubydb2x.el && sudo rm $1/rubydb2x.el
    sudo ln -s $2/rubydb2x.el $1/rubydb2x.el
    test -L $1/rubydb3x.el && sudo rm $1/rubydb3x.el
    sudo ln -s $2/rubydb3x.el $1/rubydb3x.el
    cd $1
    test -f ruby-mode.elc && sudo rm ruby-mode.elc
    sudo emacs --batch --eval '(byte-compile-file "ruby-mode.el")'
    test -f ruby-style.elc && sudo rm ruby-style.elc
    sudo emacs --batch --eval '(byte-compile-file "ruby-style.el")'
    test -f ruby-electric.elc && sudo rm ruby-electric.elc
    sudo emacs --batch --eval '(byte-compile-file "ruby-electric.el")'
    test -f inf-ruby.elc && sudo rm inf-ruby.elc
    sudo emacs --batch --eval '(byte-compile-file "inf-ruby.el")'
    test -f rubydb2x.elc && sudo rm rubydb2x.elc
    sudo emacs --batch --eval '(byte-compile-file "rubydb2x.el")'
    test -f rubydb3x.elc && sudo rm rubydb3x.elc
    sudo emacs --batch --eval '(byte-compile-file "rubydb3x.el")'
}

install_emacs_ruby /usr/local/share/emacs/site-lisp $1

Lispはインタプリタで動作するが、事前にバイトコンパイルしておくと特にEmacsの起動時間が高速化する。JavaScriptの編集に活躍するjs2-mode.elのようにバイトコンパイルを必須とするものもあるので、Emacs Lispはできるだけ事前にバイトコンパイルすると良いだろう。

May 01(Fri), 2009

Railsアプリ開発のために書いたEmacs Lispあれこれ。

Ruby on RailsによるアプリをEmacsで編集しながら、同時にいろいろEmacs Lispを書くなどしたのでまとめ。


バッファ破棄/再読み込み

Railsアプリのソースコードを一度に開くと大量のバッファが作成されるので、一度に破棄したり再読み込みしたりする機能が欲しくなってくる。そこでいくつかの関数を定義。

;; 開いているすべてのバッファをkillする
(defun kill-all-buffers ()
  (interactive)
  (dolist (buf (buffer-list))
	  (kill-buffer buf)))
;; バッファ再読み込み
(defun revert-current-buffer ()
  (interactive)
  (revert-buffer t t))
(defun revert-all-buffers ()
  (interactive)
  (let ((cbuf (current-buffer)))
    (dolist (buf (buffer-list))
      (if (not (buffer-file-name buf))
   nil
 (switch-to-buffer buf)
 (revert-buffer t t)))
    (switch-to-buffer cbuf)
    ))
(define-key global-map "\C-c\C-c\C-p" 'revert-current-buffer)
(define-key global-map "\C-c\C-c\ p" 'revert-all-buffers)
(define-key global-map "\C-c\C-c\ 0" 'kill-all-buffers)

参考

http://q.hatena.ne.jp/1134532052


ちなみにrevert-bufferだとundoできない。Meadow/Emacsメモによるとelispで独自に実装する手もあるようだ。

http://www.bookshelf.jp/soft/meadow_24.html#SEC254


ただこの方法だとバッファ再読み込み後に編集せず閉じたときの挙動が気に入らなかったので、結局revert-bufferを素直に使うことにした。


空ファイル削除

編集して0バイトになったファイルをファイルシステム上から除去できるようにする。選択肢で残すことも可能にする。

(if (not (memq 'delete-file-if-no-contents after-save-hook))
    (setq after-save-hook
          (cons 'delete-file-if-no-contents after-save-hook)))

(defun delete-file-if-no-contents ()
  (when (and
         (buffer-file-name (current-buffer))
         (= (point-min) (point-max)))
    (when (y-or-n-p "Delete file and kill buffer?")
      (delete-file
       (buffer-file-name (current-buffer)))
      (kill-buffer (current-buffer)))))

参考

http://www.bookshelf.jp/soft/meadow_24.html#SEC264


行末スペース除去

行末にスペースが付いていたりすると気になる性格なのでこれも関数を定義して対処する。

(defun trim-buffer ()
  "Delete excess white space."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (re-search-forward "[ \t]+$" nil t)
      (replace-match "" nil nil))
	  (goto-char (point-max))
    (delete-blank-lines)
	(mark-whole-buffer)
    (tabify (region-beginning) (region-end))))

Rinari

従来はemacs-railsが使われたが、現在ではRinariが熱いらしい。標準だとrhtml(erb)のシンタックスハイライトが無かったりするのでこのあたりも必須。そこでまとめて追加する。


まずはRinariとrhtml-modeを一次配布元のgithubからチェックアウトして、Emacsのロードパスにシンボリックリンクする。ruby-mode.elはRubyのソースコードのmiscディレクトリに付属しているので、これも同じくシンボリックリンクする。

#!/bin/sh

setup_rhtml() {
    if [ -d ~/local/github/rhtml ]; then
        cd ~/local/github/rhtml
        git pull
    else
        cd ~/local/github
        git clone git://github.com/eschulte/rhtml.git
        cd ~/local/github/rhtml
    fi
    # rhtml-modeへのリンクを作成
    ln -s ~/local/github/rhtml ~/.emacs.d/elisp/3rd-party
}

setup_rinari() {
    if [ -d ~/local/github/rinari ]; then
        cd ~/local/github/rinari
        git pull
    else
        cd ~/local/github
        git clone git://github.com/eschulte/rinari.git
        cd ~/local/github/rinari
    fi
    # チェックアウト後に必要
    git submodule init
    git submodule update
    # Rinariへのリンクを作成
    ln -s ~/local/github/rinari ~/.emacs.d/elisp/3rd-party
}
setup_rhtml
setup_rinari

install_emacs_ruby() {
    test -L $1/ruby-mode.el && sudo rm $1/ruby-mode.el
    sudo ln -s $2/ruby-mode.el $1/ruby-mode.el
    test -L $1/ruby-style.el && sudo rm $1/ruby-style.el
    sudo ln -s $2/ruby-style.el $1/ruby-style.el
    test -L $1/ruby-electric.el && sudo rm $1/ruby-electric.el
    sudo ln -s $2/ruby-electric.el $1/ruby-electric.el
    test -L $1/inf-ruby.el && sudo rm $1/inf-ruby.el
    sudo ln -s $2/inf-ruby.el $1/inf-ruby.el
    test -L $1/rubydb2x.el && sudo rm $1/rubydb2x.el
    sudo ln -s $2/rubydb2x.el $1/rubydb2x.el
    test -L $1/rubydb3x.el && sudo rm $1/rubydb3x.el
    sudo ln -s $2/rubydb3x.el $1/rubydb3x.el
}

# Rubyのソースコード(svnからチェックアウト)に付属のEmacs LispへのシンボリックリンクをEmacsロードパスに作成する
install_emacs_ruby /usr/local/share/emacs/site-lisp /usr/local/src/ruby/branches/ruby_1_8_7/misc

次にEmacsのロードパスを正しく設定しておく。

(defvar default-load-path load-path
  "*Base of `load-path'.
It is used as a default value of target path to search file or
subdirectory under load-path.")
(setq my-load-path
      (list "/usr/local/share/emacs/site-lisp"
	    (expand-file-name "~/.emacs.d/elisp")
	    (expand-file-name "~/.emacs.d/elisp/3rd-party")
	    (expand-file-name "~/.emacs.d/elisp/3rd-party/rhtml")
	    (expand-file-name "~/.emacs.d/elisp/3rd-party/rinari")))
(setq load-path (append my-load-path default-load-path))

これらを利用するためRubyに関する一連のEmacs Lispを書く。

;; ruby-mode
(when (autoload-p 'ruby-mode "ruby-mode" "Ruby" 'interactive)
  (setq auto-mode-alist (cons '("\\.rb$" . ruby-mode) auto-mode-alist))
  (setq interpreter-mode-alist (cons '("ruby" . ruby-mode) interpreter-mode-alist))

  ;; inf-ruby
  (autoload 'run-ruby "inf-ruby"
  "Run an inferior Ruby process")
  (autoload 'inf-ruby-keys "inf-ruby"
  "Set local key defs for inf-ruby in ruby-mode")
  (add-hook 'ruby-mode-hook
          '(lambda () (inf-ruby-keys)))
 
  ;; ruby-electric.el
  (require 'ruby-electric)
  (add-hook 'ruby-mode-hook '(lambda () (ruby-electric-mode t)))
 
  ;; rubydbnx.el
  (autoload 'rubydb "rubydb2x"
  "run rubydb on program file in buffer *gud-file*.
  the directory containing file becomes the initial working directory
  and source-file directory for your debugger." t)
 
  ;; ruby-block.el
  (require 'ruby-block)
  (ruby-block-mode t)
  (setq ruby-block-highlight-toggle t))

;; Rinari
(require 'rinari)

;; rhtml-mode
(require 'rhtml-mode)
(setq auto-mode-alist (cons '("\\.erb$" . rhtml-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("\\.rhtml$" . rhtml-mode) auto-mode-alist))
(add-hook 'rhtml-mode-hook
  (lambda () (rinari-launch)))

参考

http://d.hatena.ne.jp/willnet/20090110/1231595231


これで、だいぶEmacsが快適になってきた。

April 24(Fri), 2009

カレントバッファの強制再読み込みをおこなう。

ログファイルをEmacsで開いているときや、IDEなど他のエディタを併用しているとき、カレントバッファの内容を再読み込みしたくなる。そこでelispを書いた。

(defun revert-buffer-force ()
  (interactive)
  (revert-buffer t t))
(define-key global-map "\C-c\C-x\C-j" 'revert-buffer-force)
(define-key global-map "\C-c\C-x\ j" 'revert-buffer-force)
(define-key global-map "\C-x\C-j" 'revert-buffer)
(define-key global-map "\C-x\ j" 'revert-buffer)

C-x j なら確認の上再読み込みをし、確認が不要な場合は C-c C-x j で強制再読み込みをする。こまめにセーブする性格でないと編集内容を失いかねないので少し複雑なキー・バインドに割り当てた。

April 17(Fri), 2009

Emacsの終了時に確認するようにする。

C-x C-c の両方とも頻繁に入力するキーなので、誤って大量に開いたバッファを失うことが多く、これを対策する。

(defun confirm-save-buffers-kill-emacs ()
  (interactive)
  (if (y-or-n-p "quit emacs? ")
    (save-buffers-kill-emacs)))
(global-set-key "\C-x\C-c" 'confirm-save-buffers-kill-emacs)

いたって簡単な話だった。


参考

http://q.hatena.ne.jp/1161985770

March 31(Tue), 2009

iMacを買った

24inch 1920×1200
2.66GHz Intel Core 2 Duo
640GB 7200rpm SATA HDD
4GB 1066MHz DDR3 SDRAM (MAX 8GB)
テンキーレス Apple Keyboard

いわゆるiMac竹(2009年3月モデル)、ヤマダ電機で買うのが一番安くてポイントを含めると実質12万円台で買えた。


現在の作業環境はこんな感じ。これでサーバーだけでなくデスクトップも充実してきた。

f:id:Ubuntu:20090330050818j:image


中央にiMac。

右手はDebian GNU/Linux用コンソール(2台切替使用)。

左手はモバイル用ノートPC置き場(MacBook/ThinkPad/AspireOne)。

f:id:Ubuntu:20090330050819j:image


iMacの24インチモニタは大きくて快適。となりのMacBookが小さく見える。

March 21(Sat), 2009

Debian sid から Xubuntu 8.10 に移行した。

Debian sid を最新化したところ、イー・モバイルの接続が不安定になった。起動時に D02HW を接続していないと起動後に接続してもイー・モバイルにつながらなかったり、接続状態で起動しても pon コマンドでダイアルアップできなかったりする。ThinkPad と AspireOne の両方で同じ状態なのでハードウェアの問題では無さそう。/dev/ttyUSB0 は見えているので、デバイスの認識はできていると思うのだが原因がはっきりしない。このままでは不便を感じるので Debian sid 環境を捨て Ubuntu 8.10 に移行することにした。


1. Ubuntu の Server Edition から CUI 環境をインストールする。

手元に Ubuntu 7.10 Server の CD があったのでそこからシェルのみの環境をインストールした。サーバー版では Debian の昔ながらのインストーラーが採用されており、暗号化された LVM を導入したり、tasksel で LAMP 環境をまるごとインストールしたりできる。こうすることで Ubuntu でも小さな環境から必要なものだけを入れて小回りの利いた環境構築をしていくことができる。


2. 長期安定版にアップグレードし、環境を構築する。

sudo vim /etc/apt/sources.list # gutsyからhardyに変更
sudo aptitude update && sudo aptitude -y dist-upgrade

各種プログラム言語とライブラリ、フレームワークRDBMSも一通りそろえる。この時点で、すでにメインのサーバー環境として稼働中の Ubuntu 8.04 LTS Server と同じ環境になった。これでサーバーにデプロイする機能を手元で試験的に動作させる環境が出来上がる。さらに加えて、イー・モバイルや無線LANの接続設定をおこなう。


3. カーネルをサーバー版からデスクトップ版に入れ替える。

カーネルの入れ替えは apt だけでできる。

sudo aptitude install linux-image-2.6.24-23-generic
sudo shutdown -r 0
sudo aptitude purge linux-image-2.6.24-23-server

4. デスクトップ環境をインストールする。

sudo aptitude install xubuntu-desktop

5. 開発環境の動作確認をする。

GUI で動作する管理ツール等をこの段階でインストールする。デスクトップのテーマやキー・バインド設定もおこなう。


[参考] Xfceを使う。

http://d.hatena.ne.jp/Ubuntu/20080717/install_xfce


6. Intrepid にアップグレードする。

この時点で環境構築は完成なのだが、現在稼働中の Hardy の環境もいずれはアップグレードするだろうし、将来の移行を見据えた試験も兼ねて手元の環境は最新版にすることにした。Ctrl-Alt-F1 で tty1 へ切り替えてコマンドでアップグレードをする。

sudo vim /etc/apt/sources.list # hardyからintrepidに変更
sudo apt-get update && sudo apt-get dist-upgrade

7. キー・マップの再配置をする。

Intrepid ではキー・コードが Hardy から大幅に変更になっている。そこで xev で調べつつ、新しく xmodmap 用の定義ファイルを書き直した。

keycode 66 = Control_L
keycode 37 = Control_L
keycode 105 = Control_L
clear lock
add lock = Caps_Lock
clear control
add control = Control_L Control_R
keycode 49 = Zenkaku_Hankaku Kanji
keycode 9 = Escape
keycode 102 = Escape
keycode 100 = Return
keycode 101 = Down
keycode 151 = Up
keycode 117 = Up
keycode 133 = Up
keycode 111 = Up
keycode 116 = Down
keycode 112 = Prior
keycode 117 = Next
keycode 97 = backslash underscore
keycode 98 = Up
keycode 36 = Return

8. GNU Screen の問題を対処する。

ターミナルを起動しようとしたら起動できない。tty1 のシェルで確認したところ screen を起動する際になぜか /var/run/screen が無いので起動失敗していた。こちらもすぐに原因がつかめていなかったので、とりあえずスタートアップ時に実行されるスクリプト /etc/rc.local に以下の通り記述して暫定的に対処した。

test -d /var/run/screen || mkdir -p /var/run/screen
chmod 775 /var/run/screen
chown root:utmp /var/run/screen

所持しているマシンのうちメインのモバイルマシンである ThinkPad X60 はこうして Debian sid から Xubuntu 8.10 に移行した。もう1台の Debian sid マシンである AspireOne は再インストール中にいきなり起動ができなくなってあせった。原因は BIOS の突然死だったのでこちらの方法で BIOS をアップデートしたら無事に復旧した。AspireOne には Debian lenny を再インストールすることにした。


[参考資料] 第48回東京エリアDebian勉強会資料

http://hackerscafe.g.hatena.ne.jp/Ubuntu/20090118/1232279226

March 18(Wed), 2009

/etc/modprobe.d/ 配下のファイルについて大量の警告が出力される現象の対処

Debian squeeze/sid を使っていて、3月に入ってからフルアップグレードしたところ、システムのブート時に以下のような大量の警告が表示されるようになった。

WARNING: All config files need .conf: /etc/modprobe.d/aliases, it will be
ignored in a future release.
WARNING: All config files need .conf: /etc/modprobe.d/arch-aliases, it will be
ignored in a future release.
WARNING: All config files need .conf: /etc/modprobe.d/blacklist, it will be
ignored in a future release. 
...

これは module-init-tools パッケージで仕様変更がおこなわれ /etc/modprobe.d/ 配下のファイル名称については .conf というサフィックスを付与されるものと決定したのが原因だ。なお、この件についてはメーリングリストに情報がある


lenny の module-init-tools 3.4-1 では /etc/modprobe.d/aliases だったコンフィギュレーションファイルは squeeze/sid (3/17現在) の 3.7-pre9-1 では /etc/modprobe.d/aliases.conf となる。現時点では旧ファイル名称でも有効だが、将来のリリースではやがて完全に移行することになるので、今のうちに変更しておいたほうが良いだろう。

#!/bin/sh

change_modprobe() {
    sudo mv /etc/modprobe.d/$1 /etc/modprobe.d/$1.conf
}

change_modprobe aliases
change_modprobe arch-aliases
change_modprobe blacklist
change_modprobe display_class
change_modprobe madwifi
change_modprobe pnp-hotplug

来月リリースが予定されている Ubuntu 9.04 Jaunty では、元々が lenny/sid 時代の Debian なので、ファイル名称も古いままである。予定では Ubuntu 9.10 Karmic から squeeze/sid ベースになるので、おそらく 9.04 → 9.10 のアップグレードの際に、これと同じファイル名称の変更が必要になるだろう。


(3/18 19:56追記)

Jaunty で 3.7-pre9-1 が採用されているので 8.10 → 9.04 で変更になるのが正解。

http://packages.ubuntu.com/jaunty/module-init-tools

February 28(Sat), 2009

Happy Hacking Keyboard Professional JP用のキーマップを書いた。

Happy Hacking Keyboard Professional JP 墨 (PD-KB420B)を購入した。HHKLite2もコストパフォーマンスの割に使い勝手は非常に良いが、やはりProfessionalは定価2万5千円もするだけあって別格の使い心地だ。なお例によってスペースの左右にESCとEnterを配置するため日本語配列をチョイス。GNU/Linuxでしか使用しないのでXmodmapの設定でキーバインドを変更する。さっそく書いてみた。

keycode 66 = Control_L
keycode 37 = Control_L
clear lock
add lock = Caps_Lock
clear control
add control = Control_L Control_R
keycode 49 = Alt_L
keycode 9 = Zenkaku_Hankaku Kanji
keycode 131 = Escape
keycode 129 = Return
keycode 208 = Down
keycode 117 = Alt_L
keycode 116 = Alt_L
keycode 115 = Alt_L
keycode 113 = Up
keycode 98 = backslash underscore
keycode 211 = Up
keycode 36 = Return

このキーマップのポイントは右Shiftのすぐ左にbackslash/underscoreを配置し、カーソルをその左へとシフトしている点。HHKProJPはこの右Shift配列が変態的なので工夫が必要。ディップスイッチ4番をONにすればカーソルとShiftが反転するようだが、あえて211番のキーと入れ替えをしてみた。

f:id:Ubuntu:20090228222357j:image


さらに左側はFnキーとESC(スペースの左)の中間にあるキーすべてをAlt_Lに変更。Enter(スペースの右)の右側にDownとUpを配置してThinkPadのキーバインドと統一した。アバウトな感覚でEmacsのメタキーを使えるので満足。

f:id:Ubuntu:20090228222358j:image

February 22(Sun), 2009

PythonでTwitterのタイムラインを監視する

自分がfollowしているユーザーの発言をcronで定期的に収集する。テキストで取得できるので、メールにしてGmailなどに送ったりログファイルとして蓄積するなどいろいろ使い道がある。

#!/usr/bin/python

import os, sys
import urllib2
from xml.dom.minidom import parse
import re
import time
import pynotify

password = 'xxxxxxxx'
timeline_url ='http://twitter.com/statuses/friends_timeline.xml' 
last_id = 0

def notify(username, tmpfile, ban_file):
  auth_handler = urllib2.HTTPBasicAuthHandler()
  auth_handler.add_password(
    'Twitter API', 'http://twitter.com/',
    username, password)
  opener = urllib2.build_opener(auth_handler)
  urllib2.install_opener(opener)

  tagText = lambda node, tagName: \
  node.getElementsByTagName(tagName)[0].firstChild.nodeValue
  print >>sys.stderr, time.strftime("[%H:%M:%S] "), "checking update"

  global last_id
  f = open(tmpfile,'r')
  last_id = int(f.read())
  f.close
  ban_list = open(ban_file,'r')
  banlist = []
  for ban in ban_list:
      banlist.append(ban.rstrip())
  ban_list.close()
  r = urllib2.Request(timeline_url)
  r.add_data("since_id="+str(last_id))
  e = parse(file=urllib2.urlopen(r))
  print >>sys.stdout, time.strftime("[%Y-%m-%d %H:%M:%S] "), username, "friends timeline"
  for s in reversed(e.getElementsByTagName("status")):
    if int(tagText(s, "id")) > last_id:
      screen_name = tagText(s, "screen_name")
      text = tagText(s, "text")
      try:
          banlist.index(screen_name)
      except ValueError:
          print >>sys.stdout, "%s: %s" % (screen_name, text)
      else:
          pass
  last_id = int(tagText(s, "id"))
  print >>sys.stderr, time.strftime("[%H:%M:%S] "), "last_id is", last_id
  f = open(tmpfile,'w')
  f.write(str(last_id))
  f.close()

if __name__ == '__main__':
  from optparse import OptionParser
  usage = "usage: %prog [options]"
  parser = OptionParser(usage)
  parser.add_option("-u", "--username", dest="username",
                    help="your username")
  parser.add_option("-t", "--tmpfile", dest="tmpfile",
                    help="temporary file")
  parser.add_option("-b", "--banfile", dest="banfile",
                    help="banlist")
  (options, args) = parser.parse_args()
  pynotify.init("TwitterNotifier")
  if options.username and options.tmpfile:
    notify(options.username, options.tmpfile, options.banfile)
  else:
    parser.error("Incorrect options, Add -h option for help.")

これをtwitter-client.pyとして名前をつけて保存し、以下のようにシェルスクリプトを用意してcronで定期的に実行する。ユーザー名や一時ファイルは引数で与え、パスワードはスクリプト内に直接記述する方式をとっている。なお引数 -b で指定するファイルには、followしているのだけど除外したいアカウントを記述しておく。この機能は円滑な人間関係を維持するのに一役買うだろう。

#!/bin/sh

JOBLOG=/var/log/twitter-update.log
TWITTER_LOG=/var/log/twitter.log

test -x /home/hoge/bin/twitter-client.py && /home/hoge/bin/twitter-client.py -u xxxxxxxx -b /home/hoge/etc/banlist -t /home/hoge/tmp/last_id>$JOBLOG
cat $JOBLOG>>$TWITTER_LOG

# ADMIN_MAIL_ADDRESS=xxxxxx@gmail.com
case "$ADMIN_MAIL_ADDRESS" in
  *@*)
    cat $JOBLOG | mail -s "[cron-log][`/bin/hostname`] Twitter Logger" $ADMIN_MAIL_ADDRESS
    ;;
esac

数分ごとに発言を収集してGmailに送ってみた。こんな感じでTwitterのタイムラインを時系列に蓄積することができる。

f:id:Ubuntu:20090222030618p:image

TwitterNotifierソースコードを参考にして実装しました。

February 20(Fri), 2009

Emacs起動後に4つのTwitterアカウント用バッファを同時起動する

id:hayamiz さんの twittering-mode.el を、単純に名前空間を分離して使っている。

毎回個別に起動するのが面倒なので Emacs 起動後にキー1発で4つのアカウントを縦に並べるようにしてみた。

;; Twitter
(defun switch-to-twitter1-mode ()
  (interactive)
  (twitter1-mode))
(defun switch-to-twitter2-mode ()
  (interactive)
  (twitter2-mode))
(defun switch-to-twitter3-mode ()
  (interactive)
  (twitter3-mode))
(defun switch-to-twitter4-mode ()
  (interactive)
  (twitter4-mode))
(defun switch-to-twitter-all-mode ()
  (interactive)
  (split-one-window-p)
  (split-window-vertically)
  (twitter1-mode)
  (split-window-vertically)
  (windmove-down)
  (twitter2-mode)
  (windmove-down)
  (twitter3-mode)
  (split-window-vertically)
  (windmove-down)
  (twitter4-mode))
(define-key global-map "\C-c\C-c\ 1" 'switch-to-twitter1-mode)
(define-key global-map "\C-c\C-c\ 2" 'switch-to-twitter2-mode)
(define-key global-map "\C-c\C-c\ 3" 'switch-to-twitter3-mode)
(define-key global-map "\C-c\C-c\ 4" 'switch-to-twitter4-mode)
(define-key global-map "\C-c\C-c\ 5" 'switch-to-twitter-all-mode)

4つのバッファが左側に縦4段で表示されるのでもう半分で別な作業ができる。

ThinkPadの1024x768の画面でも広々と使える。だいぶ快適になった。

f:id:Ubuntu:20090220134824p:image

February 19(Thu), 2009

Python 2.6をインストールする。

Python本体をビルドする

現時点での 2.6 系の最新は 2.6.1。

make_and_install() {
    cd Python-$1
    test -n "$2" || ./configure
    test -n "$2" && ./configure --prefix $2
    make
    test -n "$2" || sudo make install
    test -n "$2" && make install
    cd ..
}

get_python() {
    mkdir install_python
    cd install_python
    wget http://www.python.org/ftp/python/$1/Python-$1.tar.bz2
    test -f Python-$1.tar.bz2 || exit 1
    tar xjvf Python-$1.tar.bz2
    test "$2" = "sourceonly" || make_and_install $1 $2
    test -d /usr/local/src/python || sudo mkdir -p /usr/local/src/python
    sudo cp $OPTIONS Python-$1 /usr/local/src/python
    cd ..
    rm -rf install_python
}

case $OSTYPE in
  *darwin*)
    OPTIONS=-pR
    ;;
  *)
    OPTIONS=-a
    ;;
esac

test -n "$1" || exit 1
get_python $1 $2

case $OSTYPE in
  *darwin*)
    sudo chown -R root:wheel /usr/local/src/python
    ;;
  *)
    sudo chown -R root:root /usr/local/src/python
    ;;
esac

python -V

上記のスクリプトを install_python.sh として保存し install_python 2.6.1 と打てば良い。


EasyInstallをセットアップする

これが無いと easy_install コマンドが使えないので入れる。

TARGET_PATH=`python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"`

wget http://peak.telecommunity.com/dist/ez_setup.py
sudo python ez_setup.py -U setuptools
sudo cp ez_setup.py $TARGET_PATH/ez_setup.py
rm ez_setup.py

EasyInstallを利用してライブラリをインストールする

sudo easy_install ipython
sudo easy_install mechanize
sudo easy_install nose
sudo easy_install -Z SQLAlchemy
sudo easy_install migrate

特に ipython は必須とも言える。


その他のパッケージをインストールする

EasyInstallで入らないものなどはソースから入れる。たとえば MySQL-python など。

install_python_packages() {
    sudo aptitude install libmysqlclient15-dev
    mkdir install_python_packages
    cd install_python_packages
    wget "http://downloads.sourceforge.net/mysql-python/MySQL-python-1.2.2.tar.gz"
    tar xzvf MySQL-python-1.2.2.tar.gz
    cd MySQL-python-1.2.2
    python setup.py build
    sudo python setup.py install
    cd ..
    test -d /usr/local/src/python/packages || sudo mkdir -p /usr/local/src/python/packages
    sudo cp $OPTIONS MySQL-python-1.2.2 /usr/local/src/python/packages/
    cd ..
    sudo rm -rf install_python_packages
}

case $OSTYPE in
  *darwin*)
    OPTIONS=-R
    ;;
  *)
    OPTIONS=-a
    ;;
esac

install_python_packages

case $OSTYPE in
  *darwin*)
    sudo chown -R root:wheel /usr/local/src/python/packages
    ;;
  *)
    sudo chown -R root:root /usr/local/src/python/packages
    ;;
esac

Djangoフレームワークをインストールする

以下の内容がそのまま使える。現段階での Django の最新は 1.0.2。

http://d.hatena.ne.jp/Ubuntu/20080908/django


TurboGearsフレームワークをインストールする

現時点の安定版 1.0 は Python 2.5系 までしか対応していない。

2.6 で動かしたければベータバージョンをインストールする。

TARGET_PATH=`python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"`

wget http://www.turbogears.org/download/tgsetup-betaversion.py
sudo python tgsetup.py
sudo cp tgsetup.py $TARGET_PATH/tgsetup.py
rm tgsetup.py

動かしてみる

setsモジュールが 2.6 から非推奨になったため、警告メッセージが表示される。(無視して良い)

$ ipython
/usr/local/lib/python2.6/site-packages/ipython-0.9.1-py2.6.egg/IPython/Magic.py:38: DeprecationWarning: the sets module is deprecated
  from sets import Set
Python 2.6.1 (r261:67515, Feb 14 2009, 09:43:26)
Type "copyright", "credits" or "license" for more information.

IPython 0.9.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]: import sys

In [2]: sys.version_info
Out[2]: (2, 6, 1, 'final', 0)

In [3]: import django

In [4]: django.VERSION
Out[4]: (1, 0, 2, 'final', 0)

In [5]: import MySQLdb

In [6]: MySQLdb.version_info
Out[6]: (1, 2, 2, 'final', 0)

In [7]: import mechanize

In [8]: mechanize.__version__
Out[8]: (0, 1, 11, None, None)

In [9]: import sqlalchemy

In [10]: sqlalchemy.__version__
Out[10]: '0.5.2'

In [11]: turbogears.__version__
Out[11]: '1.1b3'

January 21(Wed), 2009

Emacsで画面が分割されていなければ縦に二分割する。

単純にone-window-pで判定しているので、かなり手抜き。

;; ウィンドウが1つしかない場合は縦に分割する関数をあらかじめ定義しておく
(defun split-one-window-p ()
  (if (one-window-p)
    (split-window-horizontally)))
(defun split-one-window ()
  (interactive)
  (split-one-window-p))

;; C-c C-c j で縦に二分割する
(define-key global-map "\C-c\C-c\C-j" 'split-one-window)
(define-key global-map "\C-c\C-c\ j" 'split-one-window)

;; twittering-mode.elを開始時に縦分割する
(defun switch-to-twitter1-mode ()
  (interactive)
  (split-one-window-p)
  (twitter1-mode))
(defun switch-to-twitter2-mode ()
  (interactive)
  (split-one-window-p)
  (twitter2-mode))
(defun switch-to-twitter3-mode ()
  (interactive)
  (split-one-window-p)
  (twitter3-mode))
(defun switch-to-twitter4-mode ()
  (interactive)
  (split-one-window-p)
  (twitter4-mode))
(define-key global-map "\C-c\C-c\ 1" 'switch-to-twitter1-mode)
(define-key global-map "\C-c\C-c\ 2" 'switch-to-twitter2-mode)
(define-key global-map "\C-c\C-c\ 3" 'switch-to-twitter3-mode)
(define-key global-map "\C-c\C-c\ 4" 'switch-to-twitter4-mode)

;; Navi2chを開始時に縦分割する
(defun switch-to-navi2ch()
  (interactive)
  (split-one-window-p)
  (navi2ch))
(define-key global-map "\C-c\C-c\C-i" 'switch-to-navi2ch)
(define-key global-map "\C-c\C-c\ i" 'switch-to-navi2ch)

January 19(Mon), 2009

Dynamically changing window title of zsh/screen.

I wanted to change the window title of GNU screen dynamically by this feeling.

f:id:Ubuntu:20090119212551p:image


I wrote .zshrc as follows.

show_window_title() {
    if [ "$TERM" = "screen" ]; then
      chpwd() { echo -n "^[_`dirs`^[\\" }
      preexec() {
        emulate -L zsh
        local -a cmd; cmd=(${(z)2})
        case $cmd[1] in
          fg)
              if (( $#cmd == 1 )); then
                cmd=(builtin jobs -l %+)
              else
                cmd=(builtin jobs -l $cmd[2])
              fi
              ;;
          %*)
              cmd=(builtin jobs -l $cmd[1])
              ;;
          cd)
              if (( $#cmd == 2 )); then
                cmd[1]=$cmd[2]
              fi
              ;;
          ls|clear)
              echo -n "^[k$ZSH_NAME^[\\"
              return
              ;;
          screen|pwd)
              return
              ;;
          *)
              echo -n "^[k$cmd[1]:t^[\\"
              return
              ;;
        esac
        local -A jt; jt=(${(kv)jobtexts})
        $cmd >>(read num rest
                cmd=(${(z)${(e):-\$jt$num}})
                echo -n "^[k$cmd[1]:t^[\\") 2>/dev/null
      }
      chpwd
    fi
}

show_window_title

January 15(Thu), 2009

MadWifiで無線LANを利用する。

MadWifiGNU/Linuxにおける優れた無線LANドライバのひとつであり、ath5kやath9kのような公式にサポートされていない無線LANカードのサポートを主眼において開発が進められている。AspireOneやEeePC901等に幅広く搭載されているAtheros AR5007チップで無線LAN接続するときにも有効だ。


MadWifiの最新のソースコードを取得してビルドする方法。

#!/bin/sh

set_network_permission() {
    case $OSTYPE in
      *darwin*)
        sudo chown -R root:wheel /usr/local/src/network
        ;;
      *)
        sudo chown -R root:root /usr/local/src/network
        ;;
    esac
}

install_trunk() {
    test -d /usr/local/src/network/madwifi || sudo mkdir -p /usr/local/src/network/madwifi
    cd /usr/local/src/network/madwifi
    sudo svn co http://svn.madwifi-project.org/madwifi/trunk
    cd trunk
    sudo make
    sudo make install
    sudo modinfo ath_pci
    sudo vim /etc/modules # add line "ath_pci"
}

install_trunk
set_network_permission

Debianの場合、ソースからビルドする代わりにmodule-assistantを利用してインストールしても良い。

sudo aptitude install build-essential module-assistant madwifi-source
sudo m-a prepare
sudo m-a auto-install madwifi

インストールすれば、あとはドライバさえ読み込めば無線LANが使用可能になるはず。

たとえば Debian/UbuntuでWEP接続するなら /etc/network/interfaces に以下のように設定する。

auto ath0
iface ath0 inet dhcp
wireless-key s:xxxxxxxxxxxxx
wireless-essid XXXXXXXXXX

(1/16)追記

ドライバを読み込まないと有効にならない。

方法はディストリビューションによるがDebian/Ubuntuなら /etc/modules に以下の一行を追加する。

ath_pci

January 07(Wed), 2009

さまざまなバージョンのRailsを併用する。

Railsの日々の進歩は目覚ましいものがある。Rails3ではmerbと統合され下位互換性が切り捨てられるという話があったり、そうでなくてもRails2.2では謎なメソッドが追加されたMySQLドライバが標準でなくなったりと、思い切った変更が多く見受けられる。さらに今月リリースされる予定のRails2.3でも大幅な変更が加えられることになっている。


このような特徴は企業などで長期的に取り扱うソフトウェアを開発するツールとしては不向きとなることもある。また、ここしばらくの1.2→2.0→2.1という流れの中で開発したRailsアプリの資産の数もそろそろ増えてきている頃だろう。アプリケーションの保守や追加開発をするにあたり、手元で複数のバージョンのRailsを同時に使用したいこともある。


gemでバージョンを指定してインストールするには以下のようにする。

sudo gem install rails -v 2.1.2
gem list --local

gem list --localすると該当バージョンのRailsが存在することがわかる。


これ以外にローカルにスタンドアロンなパッケージがあると試験や納品の際になにかと便利なので、install_rails.shという名前でスクリプトを書いた。LinuxまたはMac OS Xで動作する。

#!/bin/sh

set_rails_permission() {
    case $OSTYPE in
      *darwin*)
        sudo chown -R root:wheel /usr/local/src/rails
        ;;
      *)
        sudo chown -R root:root /usr/local/src/rails
        ;;
    esac
}

extract_rails_zip() {
    test -d $1 && sudo rm -rf $1
    sudo mkdir $1
    cd $1
    sudo unzip ../$1.zip
    sudo rm ../$1.zip
    set_rails_permission
}

wget_rails_zip() {
    sudo wget http://rubyforge.org/frs/download.php/$2/rails-$1.zip
}

select_rails_package() {
    case "$1" in
      222)
        RAILS_VER=2.2.2
        wget_rails_zip $RAILS_VER 47183
        ;;
      212)
        RAILS_VER=2.1.2
        wget_rails_zip $RAILS_VER 45625
        ;;
      210)
        RAILS_VER=2.1.0
        wget_rails_zip $RAILS_VER 37770
        ;;
      205)
        RAILS_VER=2.0.5
        wget_rails_zip $RAILS_VER 45369
        ;;
      202)
        RAILS_VER=2.0.2
        wget_rails_zip $RAILS_VER 29361
        ;;
      126)
        RAILS_VER=1.2.6
        wget_rails_zip $RAILS_VER 28340
        ;;
      116)
        RAILS_VER=1.1.6
        wget_rails_zip $RAILS_VER 12324
        ;;
    esac
    extract_rails_zip rails-$RAILS_VER
    sudo gem install rails -v $RAILS_VER
}

install_rails_standalone() {
    test -d /usr/local/src/rails || sudo mkdir -p /usr/local/src/rails
    cd /usr/local/src/rails
    select_rails_package $1
}

install_rails_gem() {
    sudo gem install -v rails
}

export RUBYOPT=rubygems
case "$1" in
  *[0-9]*)
    install_rails_standalone $1
    ;;
  *)
    install_rails_gem
    ;;
esac
gem list --local

使い方としては、以下のように利用したいRailsのバージョンを引数に指定する。これにより該当のgemとスタンドアロンパッケージが両方インストールされる。

install_rails.sh 212
install_rails.sh 210
install_rails.sh 205

/usr/local/src/rails/rails-x.x.x/rails/vendor/rails をアプリケーションのvendor/railsに対してシンボリックリンクするか、実ファイルをコピーなどすると良い。これによりrubyさえあればgem不要で動作する(rubygemsは必要)ので運用環境でも役立つだろう。引数無しの場合は単純に最新のRailsをインストールする。


関連

Rubyをインストールする。

http://d.hatena.ne.jp/Ubuntu/20080617/install_ruby

December 26(Fri), 2008

Emacsで編集と閲覧を切り替える

view-mode専用のキーバインドとしては以下のものが完成度が高い。

http://d.hatena.ne.jp/rubikitch/20081104/1225745862


元記事では2つのキーの同時押しでview-modeを切り替えているが、タイミングがずれると誤入力が発生しがちなので、素直にC-x C-yで編集と閲覧を切り替えるようにした。

;; C-x C-y または C-x y で view-mode を切り替える
(defun toggle-view-mode ()
  (interactive)
  (cond (view-mode
      (view-mode nil)
      (setq hl-line-mode nil))
    (t
      (view-mode))))
(define-key global-map "\C-x\C-y" 'toggle-view-mode)
(define-key global-map "\C-x\ y" 'toggle-view-mode)

やはりC-x C-yのような操作のほうがEmacsらしくて愛着が持てる。


ちなみにview-mode-key.elは元記事のまま利用し、単純にロードしておく。

(load "view-mode-key")

hl-line-modeの有無により、編集と閲覧の切り替えを直感的に把握できるのが良い感じだ。


12/28追記 hl-line-modeの切り替えはid:znzさんのやり方が良さそう。

http://d.hatena.ne.jp/znz/20081226/emacs

December 25(Thu), 2008

Emacsで細かい誤操作をふせぐ

Escを押して離した直後にC-gを押してしまうとエラーになり、これが煩わしいので以下のようにふせぐ。

(global-set-key "\C-\M-g" 'keyboard-escape-quit)

EmacsでC-x kをするときに左手小指がキーボードから離れずC-x C-kとしてしまうことが多いので、これもkill-bufferにしてしまう。

(define-key global-map "\C-x\C-k" 'kill-buffer)

なお以下はVimで同様の操作をする設定。

nmap <C-H><C-H>  :bdelete<CR>
nmap <C-X><C-H>  :bdelete!<CR>
nmap <C-X><C-K>  :close<CR>

VimではC-x C-hでバッファを閉じ、C-x C-kは分割したウィンドウを消すのに使う。


Emacsで、Escを押して離した直後に上書き保存を押してエラーになることが多いので対策する。

;; C-x C-wを上書き保存にする(別名保存はC-x w)
(define-key global-map "\C-x\C-w" 'save-buffer)
(define-key global-map "\C-x\ w" 'write-file)
;; C-M-x C-wでも上書き保存する
(global-set-key "\C-\M-x\C-w" 'save-buffer)

Vimでは以下のようにして操作を共通化する。

nmap <C-W><C-W>  :w<CR>
nmap <C-X><C-W>  :w!<CR>

細かい操作だけど、いずれもよく使うので快適さに大きく影響する。

December 11(Thu), 2008

Anthyでことえり風にカタカナ変換する

Anthyの変換は全体的にEmacs風のキーが割り当てられており地味に使いやすい。しかしカタカナ変換したいときなどにファンクションキーを押すことになりイラッとする。そこで、ことえり風のカタカナ変換を割り当て。


確定からSKK風のC-jを除去し、変換開始にC-nとC-pを追加する。

f:id:Ubuntu:20081210151417p:image


ことえり風に。ついでにC-:も追加。

f:id:Ubuntu:20081210032259p:image


まぁ、SKKを使えば済む話ではある。

December 09(Tue), 2008

システムのアップグレードを自動化する

この記事を読んだ。

http://d.hatena.ne.jp/hijouguchi/20081209/1228823713


自動でaptしたい、それならcron-aptというパッケージがある。

http://packages.ubuntu.com/ja/hardy/cron-apt

http://packages.debian.org/ja/etch/cron-apt

セキュリティパッチのみを自動適用したりなど、きめ細やかな運用が可能だ。


特別なパッケージを使わなくても、cronとシェルスクリプトでシステムを自動更新するのは非常にシンプルで古典的な方法だ。以下のスクリプトをauto-upgradeという名前でcronに登録し定期的に実行すれば良い。ちなみに利用しているUbuntuまたはDebian GNU/Linuxの該当のaptitudeのコメントアウトを削除し、ADMIN_MAIL_ADDRESSに自分のメールアドレスを記入して利用すること。

JOBLOG=/var/log/auto-upgrade.log

echo -n "*** $0: Job start at `/bin/hostname` on ">>$JOBLOG 2>&1
date "+%Y/%m/%d %T">>$JOBLOG 2>&1

# Debian unstable (simulate only)
#aptitude update >> $JOBLOG 2>&1 && aptitude -s -v -y full-upgrade >> $JOBLOG 2>&1

# Debian stable/testing
#aptitude update >> $JOBLOG 2>&1 && aptitude -y safe-upgrade >> $JOBLOG 2>&1 && aptitude autoclean >> $JOBLOG 2>&1

# Ubuntu Server
#aptitude update >> $JOBLOG 2>&1 && aptitude -y safe-upgrade >> $JOBLOG 2>&1 && aptitude -y install linux-image-server linux-server gnupg >> $JOBLOG 2>&1 && aptitude autoclean >> $JOBLOG 2>&1

# Ubuntu Desktop
#aptitude update >> $JOBLOG 2>&1 && aptitude -y safe-upgrade >> $JOBLOG 2>&1 && aptitude -y install linux-image-generic linux-generic linux-restricted-modules-generic linux-headers linux-headers-generic gnupg >> $JOBLOG 2>&1 && aptitude autoclean >> $JOBLOG 2>&1

# AntiVirus Database
freshclam >> $JOBLOG 2>&1

echo -n "*** $0: End of Job at `/bin/hostname` on ">>$JOBLOG 2>&1
date "+%Y/%m/%d %T">>$JOBLOG 2>&1
echo>>$JOBLOG 2>&1

# ADMIN_MAIL_ADDRESS=xxxxxx@gmail.com
case "$ADMIN_MAIL_ADDRESS" in
  *@*)
    cat $JOBLOG | mail -s "[cron-log][`/bin/hostname`] APT Upgrade Log" $ADMIN_MAIL_ADDRESS
    ;;
esac

Ubuntuのデスクトップ/サーバー版ならカーネルのアップグレードも自動でやってくれる。Debian GNU/Linuxの場合、stable/testingなら単純にsafe-upgradeを、sidの場合はfull-upgradeのシミュレーションのみをおこなう。その後、古いパッケージのキャッシュをクリアするためaptitude autocleanを実行。それからウイルススキャンソフトのClamAVを使っているならfreshclamコマンドでパターンファイルも更新。最後にADMIN_MAIL_ADDRESSに記載した自分のメールアドレスへログを送信する。ログをlogrotateで循環させるのも忘れないように。


さすがにsidを自動更新というのはあまりにも無茶すぎるので、ログを見て安全であることを確認してから手動でaptitude full-upgradeするのが良いだろう。他はこのスクリプトを仕掛けてあとはログを確認するだけでも大丈夫。

December 06(Sat), 2008

MacBookでGNU/Linuxを利用するときのキー・バインド

MacBookで、キー・バインドを三籟の布陣に設定しており、なおかつVMware FusionでGNU/Linuxを起動して使用する場合のxmodmaprcを記述した。

keycode 66 = Control_L
keycode 37 = Control_L
clear lock
add lock = Caps_Lock
clear control
add control = Control_L Control_R
keycode 49 = Zenkaku_Hankaku Kanji
keycode 9 = Escape
keycode 131 = Escape
keycode 129 = Return
keycode 208 = Down
keycode 227 = Up
keycode 37 = Alt_L
keycode 117 = Up
keycode 116 = Alt_L
keycode 115 = Zenkaku_Hankaku Kanji
keycode 211 = backslash underscore
keycode 234 = underscore
keycode 233 = asciitilde
keycode 36 = Return

December 04(Thu), 2008

Debian sid(不安定版)を常用する

Debian sidを常用環境として利用しているので、利用に関するあれこれを記してみた。

すべて独自の経験から導かれたノウハウを記したものであり独学なので信憑性は必ずしも高くない。正誤の指摘やより良い改善があれば歓迎したい。


Debian GNU/Linuxの種類

Debian GNU/Linuxには主に3つのリリースがある。

  • 安定版(stable) ...現在はetch、次期はlenny
  • テスト版(testing) ...現在はlenny、次期はsqueeze
  • 不安定版(unstable) ...永遠にsidのまま

サーバー用途には環境の変化を伴わない安定版、ローカル環境には常に最新化される不安定版が特に適している。テスト版というのは次期安定版のことであり、アップロードから一定期間を経てRCバグの出なかったパッケージが自動的に組み込まれる。不安定版というのは名前からするとなにやら物騒に聞こえるが、早い話が「安定版では無い」という意味であり不安定なわけではない。適切に扱えば常に新しいパッケージが利用できるので大変便利である。


ローカル開発環境としてDebianを選択する

公式には安定版こそがDebian GNU/Linuxのリリース版なわけだが、安定版のリリースは非常に遅くパッケージも古い。たとえば現安定版であるetchのブラウザとメーラーはFirefox 2.0/Thunderbird 1.5相当のパッケージである。これはデスクトップで利用するにはあまりにも古い。かといって外からあれこれパッケージを持ってきたのではせっかくの安定版の意味が薄れる。サーバーとしての利用であれば安定版が適切だが、ローカルの開発環境としてDebian GNU/Linuxを利用するならおのずとテスト/不安定版を選択することになるだろう。公式にセキュリティサポートが提供されるのは安定版のみだが、限定的ながらテスト版にもセキュリティサポートが存在する。また不安定版は最新のパッケージが常に提供されるので、結果的に見るとセキュリティの問題が真っ先に解決されるケースが多い。


サーバーとしてDebianを選択する

サーバーとしてはDebianの安定版、またはUbuntu LTSのServer版を利用するのが良いだろう。DebianとUbuntuでは比較記事はいろいろあるし、nonfreeなソフトウェアの扱い、商用サポートの有無、実績の長さなど違いを挙げればキリが無いが、システムの根幹をなすコンポーネントは細かいバージョンの差異こそあれ基本的に一緒である。


乱暴に特徴を表現するならこうだろう。

  • Debian ...期日より品質重視
  • Ubuntu ...品質より期日重視

Debian GNU/Linuxはリリース品質を重視するがそのためいつ次期安定版がリリースされるかわからない。リリースが予定日から当たり前のようにずるずると遅れる。2008年9月リリース予定のlennyがいまだテスト版のままなことからもうかがえる。またDebianの品質がUbuntuより本当に高いかというと、BTSを見る限り一概にそうとも言い難い面がある。


Ubuntuは期日が来たら原則として必ずリリースする。そのため非常につまらないバグが埋め込まれることもある。しかしながらその後のアップグレードでいずれ解消されることが多い。Ubuntu LTSのServer版は5年サポートされる。Debian GNU/Linuxはより新しいバージョンがリリースされてから原則として1年サポートされる。長期サポートをおこなう案も一応あるようだが、それにしても1年では短いと言わざるを得ない。このように考えると、5年サポートを掲げ計画的なライフサイクルを持つUbuntu LTS Serverに優位性があるように思える。ただUbuntu自体が2004年後半に登場したものであり、15年の歴史を持つDebian GNU/Linuxのほうが実績面で信頼できるのは確かである。


結論としては、どれを選択しても結局は使い方次第なので大きな差は無い。したがって好きなものを使えば良い。


sidを選択する理由

ではなぜローカルにsidを使っているかという話だが、開発環境として利用しているので常に最新版のパッケージが欲しいというのが個人的には大きな理由である。たとえば例を挙げると今日(12/4)の時点でVimは7.2.49、MySQLは5.0.67である。lennyのVimは7.1.314、MySQLは5.0.51aだから最新の機能を使いたいのであれば迷わずsidを選ぶだろう。


また、日々最新に更新し続けたほうが、Ubuntuのように半年に一度大きな更新をかけるより、影響が小さく変更箇所が特定しやすい。絶え間なく変化するので安定しないわけだが、例えばMacやWindowsで使っているソフトの最新版が公開されたらすぐ入れて試すという性格(気の早い開発者にありがち)の場合、それを簡単にやってくれるsidが適しているというわけだ。


sidをインストールする

sidをインストールするには主に2種類の方法が存在する。

すでに構築済みで運用中のDebianテスト版があるなら、環境を最新状態に更新した上で前者の方法を採ると環境をそのままアップグレードできる。しかしおすすめは後者で、どうせなら最初から不安定版を入れたほうがトラブルが少なくて済む。なおnetinst.isoではなくbusinesscard.isoでないとエキスパートモードでも不安定版をインストールできないので間違えないこと。


sidをアップグレードする

安定版であれば適当にaptitude safe-upgradeしていれば良く、cronで自動アップグレードして後からログを検査してもまず問題無い。安定版でのaptitude full-upgradeはメジャーバージョンアップ時を除いて実施しない。これに対してリスキーな変化を伴う不安定版ではアップグレード前に注意が必要となる。基本的に不安定版ではaptitude full-upgradeのみをおこなう。

  • アップグレード前に仮想環境のsidを最新化し事前検証してから実機のsidを最新化する
  • aptitude --simulateオプションを利用する

一番確実なのは、VMwareなど仮想環境のゲストOSとして同じ構成のsidを用意しておいて、そちらを先にアップグレードする方法である。何か問題があればOSイメージを破棄して以前のものに戻せば良いし、実機は問題解決までアップグレードをしばらく控えれば良い。それとは別におすすめなのがaptitude -v -y -s(--simulateオプション)を定期的に実行する方法で、これは実際にパッケージの更新はしないが更新予定のパッケージの詳細を表示することができる。したがってこのログを見て既存パッケージの依存関係に影響を及ぼさないと判断してから実際にaptitude full-upgradeすればトラブルを事前に回避できる。こうして日々こまめにシステムを更新していけば、リリースの遅れなどに関係なく常に最新版のDebianを利用することができる。


特定のバージョンを利用する

開発環境としてDebianを利用する場合、基本的に最新のパッケージを利用すれば良いのだが、場合によっては(バージョンを運用環境とあわせる等の理由で)特定のバージョンのパッケージを利用したり、あるいはDebianに無いフレームワークやライブラリを利用することがあるだろう。


このようなときにはやむなくソースからビルドしたり、あるいはRubyPythonPerlにあるパッケージマネージャを利用している。Rubyにはgem、PythonにはEasyInstall、PerlにはCPANなどがあり、LL系のWebアプリケーションフレームワーク等を利用していると、これらをフル活用するのが常である。


仮にRuby on Railsで開発をするとして、手元のDebianでgem listしてみた。

$ gem list --local

*** LOCAL GEMS ***

actionmailer (2.2.2)
actionpack (2.2.2)
activerecord (2.2.2)
activeresource (2.2.2)
activesupport (2.2.2)
BlueCloth (1.0.0)
capistrano (2.5.2)
cgi_multipart_eof_fix (2.5.0)
coverage (0.3)
daemons (1.0.10)
fastthread (1.0.1)
gem_plugin (0.2.3)
highline (1.5.0)
hpricot (0.6.164)
magic_multi_connections (1.2.1)
mechanize (0.8.5)
mongrel (1.1.5)
mongrel_cluster (1.0.5)
net-scp (1.0.1)
net-sftp (2.0.1)
net-ssh (2.0.4)
net-ssh-gateway (1.0.0)
passenger (2.0.4)
postgres-pr (0.4.0)
rack (0.4.0)
rails (2.2.2)
rake (0.8.3)
RedCloth (4.1.1)
redgreen (1.2.2)
rspec (1.1.11)
Selenium (1.1.14)
vim-ruby (2007.05.07)

gemパッケージだけでもこれだけ利用しており、これらのライブラリ類は /usr/local/lib/ruby/gems/1.8/gems に存在し当然Debianのパッケージマネージャの管轄外である。またRuby自体も、まだDebianにはない1.8.7-p73に独自のコンパイルオプションを付与してビルドしている。(sidではp72だが、諸事情によりp73が必要)


今日の"Debian Meeting" with Coffeeで少し話題に挙がったので、こんなときに自前でビルドインストールしていると話したところ眉をひそめられたように思う。だが、開発環境を構築するにあたりDebianのパッケージシステムだけでは現実的には厳しいのではないかというのが実感だ。せいぜいmake installする前にcheckinstallするくらいだが、ここまで来ればもう意味ないと考え普通にインストールしている。このあたり、せめてローカルインストールしたファイルの追跡と削除くらいできればと思うのだが、何か良い方法があるのであれば知りたいところである。


(12/4 16:00追記) シンプルなインストールであればpacoで追跡/削除できることを確認した。

http://d.hatena.ne.jp/rx7/20081011/p2


(2009/6/16 追記) とはいえ自分はその後実機でsidを使うのを結局やめました。ご利用は自己責任でどうぞ。

http://d.hatena.ne.jp/Ubuntu/20090321/1237625898

December 02(Tue), 2008

配列への要素追加と環境変数の参照

非常にシンプルな命題。環境変数SCRIPTSで指定されたパス、もしくはスクリプトの存在するパスにある、libという名称のサブディレクトリに自作のライブラリを置いていたとする。これを呼び出すためロードパスに追加するのにPythonRubyでどう書くかを比較した。


まずPythonの場合。

#!/usr/bin/env python
import sys, os
p = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib')
if not p in sys.path:
    sys.path.append(p)
try:
    p = os.path.join(os.environ['SCRIPTS'], 'lib')
    if not p in sys.path:
        sys.path.append(p)
except KeyError:
    pass
print sys.path

実行結果。先頭はカレントディレクトリ。

['/home/hoge/local/github/scripts', '/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.0rc2-py2.5.egg', '/usr/lib/python2.5/site-packages/simplejson-2.0.3-py2.5-linux-i686.egg', '/usr/lib/python2.5/site-packages/python_twitter-0.5-py2.5.egg', '/usr/lib/python2.5', '/usr/lib/python2.5/plat-linux2', '/usr/lib/python2.5/lib-tk', '/usr/lib/python2.5/lib-dynload', '/usr/local/lib/python2.5/site-packages', '/usr/lib/python2.5/site-packages', '/usr/lib/python2.5/site-packages/Numeric', '/usr/lib/python2.5/site-packages/PIL', '/usr/lib/python2.5/site-packages/gst-0.10', '/var/lib/python-support/python2.5', '/usr/lib/python2.5/site-packages/gtk-2.0', '/var/lib/python-support/python2.5/gtk-2.0', '/home/hoge/local/github/scripts/lib', '/home/hoge/scripts/lib']

次にRubyの場合。配列の末尾に追加するのならpushなのだろうけど、カレントディレクトリが末尾なのでなんとなく逆に。

#!/usr/bin/env ruby
$:.unshift File.join(ENV['SCRIPTS'], 'lib') unless ENV['SCRIPTS'] == nil
$:.unshift File.join(File.dirname(__FILE__), 'lib')
p $:

実行結果。

["./lib", "/home/hoge/scripts/lib", "/usr/local/lib/ruby/site_ruby/1.8", "/usr/local/lib/ruby/site_ruby/1.8/i686-linux", "/usr/local/lib/ruby/site_ruby", "/usr/local/lib/ruby/vendor_ruby/1.8", "/usr/local/lib/ruby/vendor_ruby/1.8/i686-linux", "/usr/local/lib/ruby/vendor_ruby", "/usr/local/lib/ruby/1.8", "/usr/local/lib/ruby/1.8/i686-linux", "."]

もしSCRIPTSが指定されてなくても、例外が返らずに黙ってnilになる。

December 01(Mon), 2008

Python公式ドキュメントのグローバルモジュールインデックスがなぜか空だった件。

Python公式ドキュメント翻訳の2.5対応が完了したという知らせを受けて、ここから html-2.5.tar.bz2 をダウンロードしてイントラ内のサーバーに配備した。確か11月前半だったと思う。


その後11月末頃になってイントラ内のPythonドキュメントを参照しようとしたら、一番重要なページであるはずのグローバルモジュールインデックスがなぜか空だった。いまtarボールを落として展開してみたらちゃんと modindex.html の中身が書かれている。あらためてディレクトリのタイムスタンプを確認したら 2008-11-14 になっているから、配布当初になにかミスでもあったのだろうか。


とりあえず面倒なのでHTMLを直接落として上書きした。

wget http://www.python.jp/doc/2.5/modindex.html
sudo cp modindex.html /var/www/Python-Docs-2.5/
rm modindex.html