えー記事本文に入る前に申し上げておきます。技術記事ではありません。日記です。たまに備忘録と呼ばれるもの。それくらい軽いノリでいきます。
デフォルトのソフトウェアが苦手
Python、gcc、viやrubyなどの有名なプログラムは、OSにデフォルトで同梱されていることがあります。 これらはセットアップ直後から使用できるようになっており、インストールする手間が省けて非常に便利です。
しかしそれらはバージョンが古かったり、日本語に対応していなかったり、まあ色々と問題を抱えていることが多く、 デフォルトのソフトウェアをそのまま使うということはあまりないような気がします。気がしているだけです。
UbuntuデフォルトのPython
Ubuntu 20.04にはデフォルトとしてPython 3.8系がインストールされています。(時期により違いがあるかもしれませんが未検証)
$ which python3 /usr/bin/python3 $ /usr/bin/python3 --version Python 3.8.10
/usr/bin
です。Linuxのことはよくわからないので適当ですが、どうやら/usr
というのは
各種プログラムなど (引用元: @IT)
ユーザーアプリケーションがインストールされるディレクトリ。実行ファイルは bin、依存ライブラリは lib などにインストールされる。 (引用元: Qiita)
…という目的で使用されるディレクトリのようです。
実際に見てみると…
# 一部抜粋 /usr ├── bin │ (省略) │ ├── echo │ (省略) │ ├── python3 # !? │ (省略) │ └── whoami ├── games (省略) └── src
こんな構造になっていました。
なぜ/usrにPythonがいるのか???
…なんかこのPython、ちょっと場違いじゃないでしょうか。超汎用的なコマンドの中にポツンと一軒Python。
このアウェー感はなんか見覚えがあります。
ここにいても、いいの?
(無言)
となってしまいそうです。Pythonのメンタルが心配です。このままでは周囲の重圧に押されてメジャーバージョンをひとつ落としてしまうかもしれません。
もう少し詳しく調べてみると、LinuCさんのページがヒットしました。
これによれば、
/bin
には、シングルユーザモードでも利用できるコマンド
/usr/bin
には、シングルユーザモードで利用しない かつ パッケージ管理システムによって、システムに管理されるコマンドやプログラム
/usr/local/bin
には、シングルユーザモードで利用しない かつ パッケージ管理システムによってシステムに管理されないコマンドやプログラム
が置かれるとのことなので、このpythonはaptで管理されているということになり…
$ apt info python3.8 Package: python3.8 Version: 3.8.10-0ubuntu1~20.04.2 Priority: important ...
パッケージの優先度がimportant
であることから、UbuntuにおいてPython3は重要なプログラムであり、"それが欠けていることに気付いた経験豊富なUnixの人が、「一体何が起こっているのか、python3はどこにあるのか」と言うことが期待される" パッケージであるということがわかります。
(引用: debian.org)
つまり、ここにいてもよかったというわけです。なるほど。
それはそれとして
でも /usr/bin
はなんかイヤです。バージョン管理くらい自分でさせてください。
random.randbytes
が3.9以降ということに気づかずN時間潰したトラウマがあるので常に把握しておきたいのです。
こういった需要に応えるためかは不明ですが、Pythonそのものを管理するツールがいくつか存在します。pyenv, virtualenv, Anaconda etc....
これらに頼るのもアリといえばアリです。というか素人なんだからそうするべきです。
ですが、本当にそれでいいのでしょうか。
このまま、PythonをどうビルドしたらいいのかわからないままPythonistaを名乗って、本当に良いのでしょうか?
インストールしていくぞ
というわけでPython 3.9.10をソースインストールしていきます。 前置きが長すぎて半数程度の方がブラウザバックしていそうです。
ソースコードのダウンロード
Pythonのソースコードは公式のダウンロードページにあります。
インストーラやスポンサーの紹介を軽く聞き流し、Sources の Read more から…
インストールしたいバージョンを選んで…
Gzipped source tarball を落とします。 今回はブラウザのある環境≠インストールしたい環境だったので、リンクをコピーしてwgetします。curlでもいいです。
ダウンロード・展開すると、中にREADME.rst
ファイルが見つかるはずです。
内容はこのようになっています。Pythonの概要からインストール方法、前回バージョンからの変更点などが記述されています。
このままだと見にくいので、以降はsphinx
のrst2html.py
によりhtmlに変換したものを貼っていきます。
例の儀式
まず見ていくのは Build Instructions
セクションです。
なんとたった数個のコマンドでインストールできてしまうとのこと。早速やっていきましょう。
先程のルールに従い、インストール先を/usr/local
とします。configure
に--prefix=/usr/local
を渡すことでこれを設定できます。
ここでうっかり/usr/locla
とかtypoすると面白いことに大変なことになります。パス確認ヨシ!
諸々の最適化も有効にしたいので、--enable-optimizations --with-lto
も設定しておきましょう。
pipもあわせて新規インストールしたいので、こちらも有効にします。
./configure --enable-optimizations --with-lto --prefix=/usr/local --with-ensurepip=install
環境にもよりますが、2分以上かかったことは手元ではありませんでした。成功するとMakefile
が生成されるので、make
します。
ここまでのメッセージは正直そこまで重要でもない(configureで何か起きていればエラーストップする)ので、いったんターミナルの履歴を消しておくのもアリかと思います。
というかここから先の出力の方が遥かに重要です。
make
makeが終了するまでには結構な時間を要します。ソシャゲの周回でもやっておきましょう。私は推しの誕生日が近いので必死に石集めをしています。
そういえば、最近遊戯王マスターデュエルを始めました。ウィッチクラフトが好きです。
ビルドしながら書いているのでどうでもいい話が挟まります。makeには20分もかかりました。
終了コード0だったので問題ないかと思いきや、すぐ近くになんかイヤな出力があります。
ここからが本番です。
エラーを潰す
Pythonのmakeは非常に優しく、どこかでコケてもなるべく./build
にその結果を保存しようとします。
そのため、エラーの原因を落ち着いて一つずつ潰してはmakeしてを繰り返すことができます。便利。
ffi.hが存在しない
ffiとは Foreign Function Interface (別の言語で定義された関数を使用するための仕組み?)のことです。
パスの並びから、PythonではC/C++の関数を呼び出すctypesモジュールで使用されるようです。
まずは、packages.ubuntu.comでパッケージとして公開されていないかを調べます。
検索フィールドにffi.h
を入力、右のドロップダウンにパッケージの内容
を選択して検索すると、このような結果になりました。
libffi-dev
というパッケージで提供されているようです。あとは通常通りapt
でインストールします。
(そりゃもちろん公式からソースは落とせますが、それはもうただの泥沼なのでスルーします)
INFO: Can't locate Tcl/Tk libs and/or headers
Tcl/Tkがないというメッセージ。これはそのまま、tcl
、tk
という名前のパッケージをそれぞれインストール…するだけではダメで、
しっかりとtcl-dev
、tk-dev
の方も併せてインストールする必要があります。罠です。
The necessary bits to build these optional modules were not found
オプションモジュールのビルドに必要な諸々が見つからないというエラー。今回は以下のように出力されていました:
The necessary bits to build these optional modules were not found: _bz2 _curses _curses_panel _dbm _gdbm _lzma _sqlite3 _uuid readline To find the necessary bits, look in setup.py in detect_modules() for the module's name.
…多いな。多すぎます。よくみるモジュールからなんだこれみたいなモジュールまで多種多様です。
一つ一つパッケージ検索にかけ…てもいいのですが、この作業は正直苦行以外の何者でもないので、 pyenvのsuggested-build-environmentに頼ります。
それによれば、Ubuntuでは以下のコマンドを実行せよとのこと。
sudo apt-get update; sudo apt-get install make build-essential libssl-dev zlib1g-dev \ libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \ libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
…マジで多いな…
ncursesが5だったり(執筆現在の最新は6)しますが、とりあえず5を入れておきます。メジャーバージョンが違うと関数名が丸ごと変わっていたりして大変なことになるからです(1敗)。
よく見るとさっきインストールしたtk
やらffi
やらがチラッと顔を覗かせています。最初からこれに頼るべきだったのでした。
…しかし。
ほとんどのモジュールのビルドには成功しているようですが、最終的にこの子たちが残ってしまいました。
The necessary bits to build these optional modules were not found: _dbm _gdbm To find the necessary bits, look in setup.py in detect_modules() for the module's name. The following modules found by detect_modules() in setup.py, have been built by the Makefile instead, as configured by the Setup files: _abc atexit pwd time
_dbm
、_gdbm
は先程のコマンドの中には含まれていませんでした。
3.9系で追加されたのかな?と思いきやdbm
モジュールは2.7時代からあったそうなので、どうやらそういうわけでもなさそう。
…というわけで再びインストールです。_gdbm
はlibgdbm-dev
パッケージ、_dbm
はlibgdbm-compat-dev
パッケージです。
dbmの方に至ってはわかるわけがありません。なんだこのパッケージ名は。
とりあえずビルドエラーの方はひととおり解決です。
テスト
インストールする前にmake test
を実行します。テストケースは425件もあるので気長に待ちましょう。私は今ドラゴンメイドでデッキを組んでいます。
test_embed test_tabnanny test_venv
のテストケースでfailしました。おい。
こういう時はテストケースを単体で実行します。./python -m test (モジュール名) -v
を実行することでモジュール単位でのテストが可能です。
ログを見ると…
AssertionError: {... - 'base_executable': '/home/enchantcode/\udce3\udc83\udc87\udce3\udc82\udcb9\udce3\udc82\udcaf\udce3\udc83\udc88\udce3\udc83\udc83\udce3\udc83\udc97/Python-3.9.10/_testembed', + 'base_executable': '/home/enchantcode/デスクトップ/Python-3.9.10/_testembed', ....}
はい。そりゃそうです。間違ってもパスに英数字以外が含まれるディレクトリでPythonをビルドしてはいけません。
適当な場所に移してconfigure
からやり直しです。もう面倒なので&&
でくっつけてまとめてやります。
test_pyexpat
でfailしました。なんで???? Pythonのソースインストールはこれが初めてではないのですが、今回はやたらコケます。
該当コードを確認すると…
def test_exception(self): parser = expat.ParserCreate() parser.StartElementHandler = self.StartElementHandler try: parser.Parse(b"<a><b><c/></b></a>", True) self.fail() except RuntimeError as e: self.assertEqual(e.args[0], 'a', "Expected RuntimeError for element 'a', but" + \ " found %r" % e.args[0]) ...... if sysconfig.is_python_build() and not (sys.platform == 'win32' and platform.machine() == 'ARM'): self.assertIn('call_with_frame("StartElement"', entries[1][3])
一旦意図的にfail
させ、その後アサーションしているようです。こういう組み方があるんですね…(間違っていたら教えてください)
その後、WindowsかARMでなく、pythonのビルド中であれば self.assertIn
以降が実行される、ということになっています。
該当行では 'call_with_frame("StartElement"' in entries[1][3]
が評価されるのですが、これがうまくいっていないようです。
これ公式でもテスト通ってないんじゃないの(大変失礼)と思いましたが、どうやらそうでもなさそう。
これが技術記事であれば真面目に検証するべきですが、あくまで日記なので今回は華麗にスルーします。
インストール
一抹の不安は残りますが、インストールしてしまいましょう。
sudo make install
を実行します。同じprefixで複数のPythonが共存する可能性のある場合はaltinstall
を使用しましょう。
インストールが完了したら、忘れずにPATHを通します。久々にbash_profileを開いたらめちゃくちゃ汚くてびっくりしました。部屋とプロファイルの掃除は定期的に行いましょう。
試してみる
$ python3 Python 3.9.10 (main, Feb 19 2022, 17:55:49) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
良好。
$ pip --version pip 21.2.4 from /usr/local/lib/python3.9/site-packages/pip (python 3.9)
いい感じです。試しにnumpy
でもインストールしてみましょう。
$ pip install numpy 省略 $ pip show numpy Name: numpy Version: 1.22.2 Summary: NumPy is the fundamental package for array computing with Python. Home-page: https://www.numpy.org Author: Travis E. Oliphant et al. Author-email: License: BSD Location: /home/enchantcode/.local/lib/python3.9/site-packages Requires: Required-by:
大丈夫そうですね!
若干インストール先のパスがキモいですが、これはまあPythonをインストールした場所のせいであり云々…
まあ~/local/
とかにインストールすればちゃんとそっちに入ってくれます。それはまた次の記事で。
疲れた
…はい。疲れました。備忘録とはいえ約9000文字は書きすぎです。
ソースインストールをやるたびにパッケージマネージャの恩恵の凄まじさがわかります。 今回もapt頼りの部分がありましたが、この記事がPV300を超えたら追える限りソースインストールする縛りでPythonを入れてみようと思います。
fflush(stdout)