【Python3】_ctypes と libffi のインストールに苦しんだ記録

経緯

さくらのレンタルサーバ上で MySQL のデータベースを使えるので、それを Python で操作したいと思ったのが始まりです。

mysqlclient を使えば良いらしかったので「pip3 install mysqlclient」を実行するも、エラーが出て成功しない。

なんとかインストールを終えるまでの過程が文系の私には?だいぶややこしかったのでその記録を書き留めます。

  1. 問題発生1(No module named '_ctypes')
  2. 調査と原因1(libffi が無い)
  3. libffi をインストール(sudo は使えない)
  4. 問題発生2(INFO: Could not locate ffi libs and/or headers)
  5. 調査と原因2(./configure 文の修正)
  6. 解決(mysqlclient インストール成功)

問題発生1(No module named '_ctypes')

上記の通り「pip3 install mysqlclient」を実行したところ、下記のエラーが出てインストールが失敗しました。

ModuleNotFoundError: No module named '_ctypes'

直訳すると「_ctypes モジュールがありません」

_ctypes モジュールとはなんぞや。そしてそれはどこにあるのでしょうか。。。

調査と原因1(libffi が無い)

取り敢えず知識ゼロから出来ることは、情報の海に身を投げてなんとなくわかるまで溺れること。。。調べた結果断片的ですが自分の理解できる範囲で下記の情報が得られました。

  • _ctypes と libffi
    • _ctypes は C 言語で書かれたライブラリを Python から利用するためのモジュールであり、libffiに依存している。
    • ソースからPythonをビルドする際、libffiが見つからない場合は _ctypes のビルドはスキップされる。
    • _ctypes がビルドされていない Python から _ctypes を利用しようとすると、当該エラー(ModuleNotFoundError: No module named '_ctypes')が発生する。
  • 原因
    • つまり、libffi がインストールされていない状態で Python をインストールしたため、_ctypes のビルドがされていなかった。
  • 解決方法
    • 当該エラーを解消するには、libffi をインストールした上で Python を再ビルド・再インストールする必要がある。

libffi をインストール(sudo は使えない)

「sudo apt-get install -y libffi-dev」というコマンドが出てきましたが、さくらのレンタルサーバー(スタンダード)では管理者権限が与えられていないので sudo が使えません。

% sudo apt-get install -y libffi-dev
/usr/local/bin/sudo: Permission denied.

この通り、拒否されてしまいます。諸々調べた結果下記の方法でインストール出来ました。

問題発生2(INFO: Could not locate ffi libs and/or headers)

めでたく libffi をインストール出来たので Python をインストールし直しました。が、念のため make のログを確認したところ下記を発見。

INFO: Could not locate ffi libs and/or headers

Failed to build these modules:
_ctypes               _zoneinfo 

直訳すると「ffi libs およびヘッダーが見つからず _ctypes モジュールと _zoneinfo モジュールをビルド出来ませんでした。」とのこと。ショック。

しょうがないのでとりあえず続けて make install しましたが、当然 _ctypes モジュールが無いので「pip3 install mysqlclient」を実行しても前回と全く同じエラーで失敗します。

ModuleNotFoundError: No module named '_ctypes'

調査と原因2(./configure 文の修正)

また海に放り出されました。今回は「INFO: Could not locate ffi libs and/or headers」の文言を頼りに調べました。

こちらのページ(英語)を見たところ、どうやら CPPFLAGS、LDFLAGS、PKG_CONFIG_PATH の設定が必要みたいです。PKG_CONFIG_PATH の設定は libffi のインストールの時に行っていたので、取り敢えず CPPFLAGS と LGFLAGS だけでやってみます。

python-3.9.0.tgz は残して他を消し、再度下記の「./configure」文で Python のインストールを試してみます。

./configure --prefix=$HOME/local/python/ --with-system-ffi LDFLAGS="-L $HOME/local/lib/" CPPFLAGS="-I $HOME/local/include/"
項目雑なメモ
--prefixPython3 をインストールする場所
--with-system-ffilibffi のシステムバージョンへの接続を有効にするスイッチ。
CPPFLAGSCプリプロセッサのオプション
include パスを入れるっぽい?
LDFLAGSリンク用のディレクトリを指定する。
lib フォルダを入れるっぽい?

解決(mysqlclient インストール成功)

上記の「./configure」文のあとで make を実行しました。そしてログを確認すると下記の通り _ctypes のエラーはなくなりました。_zoneinfo は何かよくわかりませんし特に今回必要だとも思ってないので無視します。

Failed to build these modules:
_zoneinfo

今回は「pip3 install mysqlclient」も無事成功しました。

% pip3 install mysqlclient
Collecting mysqlclient
  Using cached mysqlclient-2.0.3.tar.gz (88 kB)
Using legacy 'setup.py install' for mysqlclient, since package 'wheel' is not installed.
Installing collected packages: mysqlclient
    Running setup.py install for mysqlclient ... done
Successfully installed mysqlclient-2.0.3
WARNING: You are using pip version 20.2.3; however, version 20.3.3 is available.
You should consider upgrading via the '/home/xxxxxx/local/python/bin/python3.9 -m pip install --upgrade pip' command.
% 

さくらレンタルサーバーに libffi をインストールする

  1. SSH でサーバーに接続
  2. ファイルのダウンロードと解凍
  3. インストール
  4. インストール後の設定

SSH でサーバーに接続

% ssh abcabc@abcabc.sakura.ne.jp
abcabc@abcabc.sakura.ne.jp's password: 

ファイルのダウンロードと解凍

ダウンロード用のフォルダを作成

ファイルをダウンロードする前に、ダウンロード用のフォルダを作ります。下記のコマンドで「work」フォルダとその配下に「libffi」フォルダを作成します。

% mkdir -p ~/work/libffi

wget でファイルをダウンロード

作成した「libffi」フォルダに移動して、圧縮ファイルをダウンロードします。

% cd work/libffi
% wget ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz

*初めは最新の「libffi-3.3.tar.gz」で進めたのですが後述の make のところで下記のエラーを返され、すぐに原因も分からなかったので一旦全部ファイルを削除して「libffi-3.2.1.tar.gz」で再チャレンジしました。

gcc: warning: '-x assembler-with-cpp' after last input file has no effect
gcc: No input files specified
*** [libffi.map] Error code 1

上記の wget コマンドを実行してから ls を実行すると圧縮ファイルがダウンロードされたのが確認できます。

% ls
libffi-3.2.1.tar.gz
% 

ファイルを解凍する

ダウンロードした圧縮ファイルを解凍します。

% tar xvfz libffi-3.2.1.tar.gz

ファイルが解凍され、元々ダウンロードした圧縮ファイルとは別に「libffi-3.2.1」という名前のフォルダが作成されています。

インストール

「libffi-3.2.1」のフォルダへ移動し ./configure を実行します。prefix でインストール先を指定します。

% cd libffi-3.2.1
% ./configure --prefix=$HOME/local/libffi/3_2_1

実行するとプロセスが走ってドバドバとターミナルに表示されるので入力待ちになるのを待ちます。

make そして make install を順に実行します。

% make
% make install

.configure の prefix で指定した場所にインストールされました。

インストール後の設定

シンボリックリンクの作成

% mkdir -p ~/local/include
% ln -s $HOME/local/libffi/3_2_1/lib/libffi-3.2.1/include/ffi.h $HOME/local/include/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi-3.2.1/include/ffitarget.h $HOME/local/include/

% mkdir -p ~/local/lib
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.a $HOME/local/lib/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.la $HOME/local/lib/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.so $HOME/local/lib/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.so.6 $HOME/local/lib/

% mkdir -p ~/local/lib/pkgconfig/
% ln -s $HOME/local/libffi/3_2_1/lib/pkgconfig/libffi.pc $HOME/local/lib/pkgconfig/

上記を実行すると下記の様に local 配下に include、lib、lib > pkgconfig が作成され、その配下のシンボリックリンクも確認できます。

環境変数の設定

シンボリックリンクを作成したのでそれぞれ LD_LIBRARY_PATH と PKG_CONFIG_PATH に追加します。他のソースをビルドするときやコマンド実行するときに役立ちます。

シェルの種類ユーザプロファイル
Bourne シェル (sh) または Korn シェル (ksh).profile
Bourne Again シェル (bash).bash_profile
C シェル (csh).login と .cshrc
TC シェル (tcsh).tcshrc と .cshrc
Z シェル (zsh).zlogin と .zshrc

今回はサーバーのシェルが csh なので .cshrc を編集して環境変数を設定します。

.cshrc の編集

% cd ~/
% vi .cshrc

.cshrc に下記を追記します。

setenv  LD_LIBRARY_PATH $HOME/local/lib
setenv  PKG_CONFIG_PATH $HOME/local/lib/pkgconfig

.cshrc で追記した内容を反映させます。

% source ~/.cshrc
% rehash

echo コマンドで確認すると、指定したパスが表示されます。

% echo $LD_LIBRARY_PATH
/home/abcabc/local/lib
% echo $PKG_CONFIG_PATH
/home/abcabc/local/lib/pkgconfig

以上で libffi のインストール作業は完了です。

LD_LIBRARY_PATH と PKG_CONFIG_PATH

LD_LIBRARY_PATH 変数

LD_LIBRARY_PATHは、アプリケーションがリンクされている動的共有ライブラリを検索する際の対象ディレクトリを動的リンクローダーに指示します。複数のディレクトリをコロン (:) で区切ったもので、コンパイルされた検索パスと標準の場所(大抵 /lib、/usr/lib 等)の前に検索されます。

PKG_CONFIG_PATH 変数

pkg-config は、プログラムのコンパイルとシステムにインストールされているライブラリとの接続に必要な情報を提供します。
この情報は .pc ファイルに保存されており、pkg-config ツールで認識されている特定の場所にあります。

pkg-config が .pc ファイルを検索する際、デフォルトでは /usr/lib/pkgconfig と /usr/share/pkgconfig を検索します。しかし、一部のローカルモジュールは /usr/local などの別のプレフィックスにインストールされる場合があるため、PKG_CONFIG_PATH 変数で検索対象のパスを追加します。