unix: 2006年2月アーカイブ

/usr/opt/pkgs/foo-1.0.0/lib/libfoo.so.1
/usr/opt/pkgs/foo-1.0.1/lib/libfoo.so.1
/usr/opt/foo => /usr/opt/pkgs/foo-1.0.1

だったものを

/usr/opt/pkgs/foo-1.0.0/lib/libfoo.so.1
/usr/opt/pkgs/foo-1.0.1/lib/libfoo.so.1
/usr/opt/foo => /usr/opt/pkgs/foo-1.0.0

に変えたとかいう場合はどうでしょうか?

どのライブラリをロードするのかはライブラリのサーチパスに依存します。システムのサーチパスは ld.so.conf で設定しますが、これは環境変数の LIBRARY_PATH でも変更可能です。絶対パスでリンクされるのは /lib/ld.so のみ(linux の場合は /lib/ld-linux.so)です。/lib/ld.so が実行時にライブラリのサーチパスを検索してロードするライブラリを決定します。

例えばサーチパスが

/opt/foo/lib
/opt/pkg/foo-1.0.0/lib
/opt/pkg/foo-1.0.1/lib

の場合は、/opt/foo/lib/libfoo.so.1 がリンクされます。

メジャーバージョンが同じパッケージが複数存在する場合の切り替えは、残念ながらちょっと分かりません。
libfoo.so.x ではなく libfoo.so.x.y.z にリンクできれば可能なんでしょうけど。

ELFの思想的にはメジャーバージョンが同じ場合は後方互換性が保たれるので、問題がおきることはありません。
もし新しいバージョンで問題が起きた場合は、新しいバージョンのライブラリを削除してやれば自動的に古いバージョンのライブラリがロードされるので、問題なく実行されます。既に新しいバージョンのライブラリにリンクしているプログラムがあって、そのプログラムが新しいバージョンの後方互換のない機能を使っていれば失敗します。その場合は新しくコンパイルしなおすか、問題のあるバージョンのライブラリを使うことになるのですが、そのようなケースはまれですね。

メジャーバージョンが異なる場合は、問題なく共存できます。
例えば

/opt/pkgs/foo-1.0.0/lib/libfoo.so.1
/opt/pkgs/foo-2.0.0/lib/libfoo.so.2
/opt/foo => /opt/pkgs/foo-2.0.0

であっても /opt/pkgs/foo-1.0.0/lib をちゃんとライブラリのサーチパスに登録しておけば、libfoo.so.1 にリンクしているプログラムは問題なく実行できるわけです。

折り見てこのネタいただこうと思います(苦笑

僕もELFさんのPHPインストールスクリプト参考にして、自分用の作りましたw

個人的にいはalternativesとかでこの辺もできたら面白いんだけど.

リンクするライブラリはコンパイル時に決定するものなので、切り替えるとしたらヘッダファイルでしょうね。

またまたELFさんとこからTBをもらいました。
ありがとうございます。

続: includeとlibはワイルドカード指定はよくないのでは?

シンボリックリンクにしなかったのはhttpd.confを修正したくなかったから(趣味の問題ですね

僕も極力オリジナルのファイルは修正しないようにしてます。なので別ファイルに

<Directory "/var/www/cgi-bin">
Options FollowSymLinks
</Directory>

と書いてます。

パーティションが同じなら cp ではなくてハードリンクでもいいと思います。ハードリンクならバイナリが書き換えられても大丈夫だし、設定ファイルも変更する必要がない。

ABIがデグレードした場合,foo-1.1.1のABIに依存するバイナリはことごとく実行できなくなります. また,アップグレードの場合でも動作しなくなる場合があります.

この辺は /etc/ld.so.conf.d でライブラリパスを追加してやると問題ないです。ライブラリにはバージョン番号がついている(はず)なので、たとえシンボリックリンクでデフォルトの参照先を変更しても実行時にはちゃんとコンパイル時にリンクしたバージョンのライブラリを参照してくれます。逆に言うと実行時にコンパイル時にリンクしたバージョンのライブラリを参照しようとするので、ちゃんと過去のバージョンのライブラリを残しておかないと、実行できなくなります。

Red Hat 系でベタなところだとcompatシリーズのパッケージ. これでgcc 4系(ABI的には3.4系も)3.2系,2.96系が共存します.opensslもありますね(openssl-*,openssl097fとか). 私も同様なやり方でいくつかの独自パッケージを共存させています(ABIが変化したときに依存関係を崩さずにアップデートとかでは常套手段)

できないって言い切っちゃうのは、よくない表現でしたね。すみません。

もちろん過去との互換性を保つために古いライブラリ類のパッケージが存在するのは知ってます。かつては僕もディストリ作ってましたから。この手のパッケージは過去の互換性のために存在するので、ライブラリや実行ファイルのみの存在なんですよね。ヘッダファイルの共存まではできない。なので、任意のバージョンのライブラリにリンクすることができないという問題があります。

よくきたblogからTBいただきました。
ありがとうございます。

includeとlibはワイルドカード指定はよくないのでは?

で,本題なんですがincludeやlibあたりをワイルドカードで一気に追加すると
ほとんどの場合特定のバージョンが使用されるってことは無いですか?
個人的にはPATHも含め,ビルド時に明示的にやるべきだと思います.

まず始めに前提条件として、tarballからインストールしたソフトウェアを簡単に管理するは汎用的な方法で特にPHPに特化した内容ではないです。

複数バージョンをインストールする場合は /opt/pkgs/hoge-x.x.x にインストールします。
この場合は /opt/pkgs/hoge-x.x.x のディレクトリにはパスには含まれません。
実際にパスが通るのはシンボリックリンクをはってる /opt/hoge 以下になります。

複数のバージョンをインストールしていても、使われるのは1つだけなので複数ディレクトリにパスを通しても意味はありません。
使用するバージョンを変更する場合は /opt/hoge のシンボリックリンクを変更します。

自分自身が精通しているパッケージなら応用はいくらでも利きますが、そうでない場合、例えばテストで複数バージョンのライブラリをテストしないといけないとかだと、結構大変です。
この方法だとシンボリックリンク先を変更するだけですむので楽ですし、どんなパッケージにも適用可能です。

ファイルの実体には固有の名前をつけておいて、汎用的な名前のファイルをシンボリックリンクにしておいて、シンボリックリンクの参照先を変更することでデフォルトの挙動をかえるのはUNIXでは昔から行われています。

例えばよくあるのがライブラリのバージョンの切り替えで、よくあるライブラリのファイル名は libhoge.so-x.x.x ですが、通常 libhoge.so.x.x.x に対するシンボリックリンク libhoge.so があります。
プログラムをコンパイルしてライブラリをリンクするときはライブラリのバージョンを含めずに libhoge.so を使用します。
こうしておくとリンカがライブラリをリンクするときはバージョンのことは考えずにすみ、ライブラリのバージョンを変更する場合は libhoge.so の参照先を変更するだけですみます。

この辺の知識はThe Linux ELF HOWTOを読んで勉強しました。
10年以上程前に書かれた文書ですがまったく色あせてない。この辺の技術は急激に変化しない基盤技術なので当然ですが。

デフォルトでアクセスするファイルはシンボリックリンクにしておいて、必要なときに参照先を変更するのがUNIX的なようです。

普通 tarball からインストールする時は

./configure
make
sudo make install

のようにインストールします。この場合は全てのファイルが /usr/local に入るわけですが、
これだと複数のソフトウェアをインストールした場合に、どのファイルがどのソフトウェアに所属するのか分からなくなって、アンインストールができなくなります。

なので僕はいつも tarball からソフトウェアをインストールする場合は、
例えば hoge というソフトウェアをインストールする場合、必ず /opt/hoge にインストールしています。

./configure --prefix=/opt/hoge
make
sudo make install

こうすると複数のソフトウェアをインストールしても、ソフトウェアごとにディレクトリが分かれるので、

sudo rm -rf /opt/hoge

とすれば一発でアンインストールできます。

環境変数 PATH の設定は、以前書いたエントリー環境変数 PATH を自動で設定する方法で自動で設定されるので、楽チンです。

さらに他の環境変数も次のように定義しています。

# MANPATH の設定
MANPATH=
add_env MANPATH "${HOME}/man" "${HOME}/opt/*/man/ja" "${HOME}/opt/*/man"
add_env MANPATH "/usr/local/man/ja" "/usr/local/man"
add_env MANPATH "/usr/local/*/man/ja" "/usr/local/*/man"
add_env MANPATH "/opt/local/man/ja" "/opt/local/man"
add_env MANPATH "/opt/local/*/man/ja" "/opt/local/*/man"
add_env MANPATH "/opt/*/man/ja" "/opt/*/man"
add_env MANPATH "/usr/share/jman"
add_env MANPATH "/usr/*/man/ja"
add_env MANPATH "/usr/*/man"
add_env MANPATH "/usr/man/ja" "/usr/man"
clean_env MANPATH
export MANPATH
# LD_LIBRARY_PATH の設定
LD_LIBRARY_PATH=
add_env LD_LIBRARY_PATH "${HOME}/lib" "${HOME}/opt/*/lib"
add_env LD_LIBRARY_PATH "/usr/local/lib" "/usr/local/*/lib"
add_env LD_LIBRARY_PATH "/opt/local/lib" "/opt/local/*/lib"
add_env LD_LIBRARY_PATH "/opt/*/lib"
add_env LD_LIBRARY_PATH "/lib"
add_env LD_LIBRARY_PATH "/usr/lib"
add_env LD_LIBRARY_PATH "/usr/*/lib"
clean_env  LD_LIBRARY_PATH
export LD_LIBRARY_PATH
# LIBRARY_PATH の設定
LIBRARY_PATH=
add_env LIBRARY_PATH "${HOME}/lib" "${HOME}/opt/*/lib"
add_env LIBRARY_PATH "/usr/local/lib" "/usr/local/*/lib"
add_env LIBRARY_PATH "/opt/local/lib" "/opt/local/*/lib"
add_env LIBRARY_PATH "/opt/*/lib"
add_env LIBRARY_PATH "/lib"
add_env LIBRARY_PATH "/usr/lib"
add_env LIBRARY_PATH "/usr/*/lib"
clean_env  LIBRARY_PATH
export LIBRARY_PATH
# C_INCLUDE_PATH の設定
C_INCLUDE_PATH=
add_env C_INCLUDE_PATH "${HOME}/include" "${HOME}/opt/*/include"
add_env C_INCLUDE_PATH "/usr/local/include" "/usr/local/*/include"
add_env C_INCLUDE_PATH "/opt/local/include" "/opt/local/*/include"
add_env C_INCLUDE_PATH "/opt/*/include"
add_env C_INCLUDE_PATH "/usr/include"
add_env C_INCLUDE_PATH "/usr/*/include"
clean_env  C_INCLUDE_PATH
export C_INCLUDE_PATH

こうしておくと、/opt にインストールしたソフトウェアの man もちゃんと読めますし、
コンパイルする時にヘッダファイルもちゃんとインクルードしてくれるし、
ライブラリもちゃんとロードしてくれます。

既にシステムにパッケージがインストールされているんだけど最新バージョンを試してみたいときなんかは、
とりあえず /opt にインストールしてテストすることができます。
/opt のパスは /usr よりも先に設定されるので /opt にインストールしたものが優先されます。
/etc/ld.so.conf を書き換えない限りは /opt にインストールした環境はシステムに影響を与えないので、
他のプログラムの動作に影響を与えることはありません。

上記の方法を使うと複数バージョンをインストールすることも簡単です。

複数バージョンをインストールする場合は /opt/pkgs/hoge-x.x.x にインストールします。

./configure --prefix=/opt/pkgs/hoge-1.0.0
make
sudo make install

このままではパスが通りませんので、シンボリックリンクをはってあげます。

sudo ln -s /opt/pkgs/hoge-1.0.0 /opt/hoge

違うバージョンを使いたい場合はシンボリックリンクをはりなおす。

sudo ln -s /opt/pkgs/hoge-2.0.0 /opt/hoge

複数バージョンのインストールなんかは RPM のようなパッケージ管理システムを使ってもできないので、かなり便利です。

みなさんは UNIX系 OS で環境変数 PATH の設定をどうしてますか?
UNIX系 OS では微妙にディレクトリ構成が違ってたり、
同じ OS でも後から追加したパッケージの関係で PATH が違ってたりして面倒です。

僕の場合、環境変数 PATH は自動で設定するようにしているので、
ほとんど数年間シェルの設定は変わっていません。

すごく便利なので、僕が使ってる方法を紹介します。

まずは次のようなシェル関数を定義する。
function add_env(){
env_name=$1
shift

for i in $@; do
if ! dirs=`eval echo $i` > /dev/null 2>&1; then
continue
fi

for i in `eval echo $dirs`; do
if eval echo \$$env_name | egrep '(\:|^)'$i'(\:|$)' >/dev/null 2>&1; then
continue
fi

if [ -d $i ]; then
eval $env_name=\$$env_name:$i
fi
done
done
}

この関数は受け取った第2引数以降(複数可、メタキャラクタ指定可)を評価して、
ディレクトリだったら第1引数で指定した変数に追加するという処理を行います。

あとはこんな感じで環境変数に追加したい候補を列挙するだけ。
PATH=
add_env PATH "${HOME}/bin" "${HOME}/sbin"
add_env PATH "${HOME}/opt/*/bin" "${HOME}/opt/*/sbin"
add_env PATH "/usr/local/bin" "/usr/local/sbin"
add_env PATH "/usr/local/*/bin" "/usr/local/*/sbin"
add_env PATH "/opt/local/bin" "/opt/local/sbin"
add_env PATH "/opt/local/*/bin" "/opt/local/*/sbin"
add_env PATH "/opt/*/bin" "/opt/*/sbin"
add_env PATH "/usr/ucb"
add_env PATH "/bin" "/sbin"
add_env PATH "/usr/bin" "/usr/sbin" "/usr/*/bin" "/usr/*/sbin"

自動的に存在するディレクトリだけ $PATH に追加してくれるので、
いろんな環境で存在しそうなディレクトリを列挙しておけば、
自動的に必要なパスだけ残るようになっているわけです。

僕はこれですごくハッピーなのですが、
もし他に良い方法とかあったら、ぜひ教えてください!!

このアーカイブについて

このページには、2006年2月以降に書かれたブログ記事のうちunixカテゴリに属しているものが含まれています。

次のアーカイブはunix: 2006年3月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。