エックスサーバー(xserver.ne.jp)にpipenvでPython3.9をインストール

[エックスサーバー](https://www.xserver.ne.jp/)は安価なレンタルサーバですがsudoによる管理者操作ができないためパッケージマネージャからシステム領域に新規パッケージをインストールできません。そこでlinuxbrew（現在はHomebrewへ統合）をログインユーザとしてビルドしbrewを使ってパッケージ管理を行う記事が色々書かれています。

ここでは2021年8月現在のやり方でpipenvがbrewでインストールできpythonのプロンプトが表示されるようになるまでの流れをメモとしてまとめておきます。


# linuxbrewのインストール

まずbrewをインストールする必要があり、次を使うことになります。しかし実行するとOSにインストールされているgitとcurlのバージョンが古くinstall.shが期待するオプションが実行できないため、バージョンを確認する部分で警告が出ます。

```
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
```

表示される警告の例

```
Error: Please update your system curl.
Minimum required version: 7.41.0
Your curl version: 7.29.0
Your curl executable: /usr/bin/curl
Error: Please update your system Git.
Minimum required version: 2.7.0
Your Git version: 1.8.3.1
Your Git executable: /usr/bin/git
```

yum, apt などのパッケージマネージャがあればupdateして最新バージョンを取得することもできますがそれらがインストールされている場所に上書きする権限がないのでできません。したがって自分のワークディレクトリにアップデートしたいツール、具体的には git, curl, opensslをソースからビルドすることになります。

# git, curl, opensslのインストール方法

まずワークディレクトリの構成を考えます。例えばOSのサードパーティのツールが/opt などにインストールされることを踏まえると、~/opt に入れていくと分かりやすそうです。
各ツールをビルドするには、それぞれのソースコードを取得してmakeする手順を繰り返していくことになります。ソースコードはビルドが終わったら消してもいいので、ワークの直下に次のようなディレクトリを作ることにします。各サブフォルダの配下にはビルドした実行形式・ライブラリ・ドキュメントがデイレクトリを分けてインストールされます。

```
# インストール先
~/opt/curl
~/opt/git
~/opt/ssl

# ソースと展開先
~/curl-7.65.3.tar.gz
~/curl-7.65.3\
~/git-2.17.0.tar.gz
~/git-2.17.0\
~/openssl-1.0.2k.tar.gz
~/openssl-1.0.2k\
```

ツールに付与されたバージョンはこれを執筆時点でテストしたXサーバーの環境で動作したものです。opensslはhttps対応で他のツールの依存があるためビルドします。参考まで使ったXサーバーのOSを示します。

```
$ uname -a
Linux XXXXX.xserver.jp 5.4.0-73-generic #82~18.04.1-Ubuntu SMP Fri Apr 16 15:10:02 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
```

最終的に作業の流れは次のようになります。

- openssl, curl, git のダウンロード
- 上の順に各ツールのビルド
- linuxbrewのインストール

またビルド時のコンパイラフラグとして次の二つを意識しました。

- Position Independent Code(-fPIC)
- sharedライブラリのビルド(--enable-shared)

前者はCFLAGSとして環境変数に設定、後者はconfigureにあたえるオプションとして対応しました。

```
$ export CFLAGS=-fPIC
```


# opensslのビルド

```
$ wget http://www.openssl.org/source/openssl-1.0.2k.tar.gz
$ tar xvfz openssl-1.0.2k.tar.gz
$ cd openssl-1.0.2k
$ ./config shared --prefix=$HOME/opt/ssl --openssldir=$HOME/opt/ssl
$ make
$ make install

#確認
$ which openssl # $HOME/opt/ssl/bin などPATHに含まれるパスを表示
```

# curlのビルド

```
$ wget https://curl.haxx.se/download/curl-7.65.3.tar.gz
$ tar zxvf curl-7.65.3.tar.gz
$ cd curl-7.65.3
$ ./configure --with-ssl=$HOME/opt/ssl --enable-libcurl-option --prefix=$HOME/opt/curl --enable-shared
$ make
$ make install

# 確認
$HOME/opt/bin/curl -V
```

# gitのビルド

opensslがconfigureから見えていないと、git-2.17.0に git-git-remote-https 実行形式がビルドされません。--with-opensslに正しいディレクトリを与える必要があります。次が必須かは分かりませんが次を設定してconfigureしていました。

```
$ export LDFLAGS="-L$HOME/opt/ssl/lib -L$HOME/opt/curl/lib"
```

```
$ wget https://www.kernel.org/pub/software/scm/git/git-2.17.0.tar.gz
$ cd git-2.17.0
$ ./configure --prefix=$HOME/opt/git --with-openssl=$HOME/opt/ssl  --with-curl=$HOME/opt/curl --enable-shared
# ビルドをやり直した場合、次をしてからmakeを実施
# $ make clean
$ make
$ make install

# 確認
$ $HOME/opt/git/bin/git clone https://github.com/<リポジトリ>
```

# linuxbrewのインストール

上が終わるとようやく元々のスクリプトが動作するようになるはずです。強制的に自分でビルドしたgit/curlを使ってインストーラが実行されるようにするため、次の環境変数を設定します。

```
$ export HOMEBREW_FORCE_BREWED_GIT=True
$ export HOMEBREW_FORCE_BREWED_CURL=True
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
```

# brew install pipenv

ようやくbrewが使えるようになったはずですが、実際にパッケージをインストールしようとするとビルドしたgit, curlを使ってくれません。

```
$ brew install pipenv
/home/XXXXX/.linuxbrew/opt/curl/bin/curl: error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory
Error: Please update your system curl.
Minimum required version: 7.41.0
Your curl version: 7.29.0
Your curl executable: /usr/bin/curl
/home/XXXXX/.linuxbrew/opt/curl/bin/curl: error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory
/home/XXXXX/.linuxbrew/opt/curl/bin/curl: error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory
```

brewの中身を少し調べたところ、git, curlを呼び出しているのは次のようなファイルのようです。

```
$HOME/.linuxbrew/Homebrew/Library/Homebrew/cmd/update.sh
$HOME/.linuxbrew/Homebrew/Library/Homebrew/brew.sh
```

上のファイルやbrewの振る舞いを見ると、~/.linuxbrew/opt/以下に依存ツールを格納する場所があるようです。ここに~/opt/へのシンボリックリンクを作っておきます。

```
$ cd ~/.linuxbrew/opt
$ ln -s ~/opt/curl .
$ ln -s ~/opt/git .
```

上を追記してもまだXサーバーにバンドルのcurl, gitを見に行きます。シェルスクリプトの中でPATH環境変数を設定すれば~/optにインストールしたものが使われるかを調べてみましたがbrew実行後に上書きされてしまいます。brewの設定を上書きするためには規定されたやり方で行う必要があります。具体的にはHOMEBREW_で始まる環境変数を使ってbrewの設定を使わせないようにします。先のHOMEBREW_FORCE_BREWED_{GIT,CURL}環境変数の他に、ここで起きてている問題を解消するには次の非公式フラグを設定します。

```
$ HOMEBREW_NO_ENV_FILTERING=True
```

これでbrew installが動作するようになりました。起動時には毎回次のような警告が表示されます。またこのような使い方は動作保証されていないためアップデート時は注意が必要です。

```
$ brew install pipenv
Warning: HOMEBREW_NO_ENV_FILTERING is undocumented, deprecated and will be removed in a future Homebrew release (because it breaks many things)!
```

# 追記：pipenv固有の問題

pipenv のビルドはgcc@5がないためエラーとなります。この[リンク](https://github.com/Homebrew/linuxbrew-core/issues/23460)にある次の方法で回避できます。

```
$ brew install --force-bottle gcc@5
```

次のエラー(`Error: undefined method `fetch' for nil:NilClass`)も回避策が示されています。

```
$ brew edit gcc@5

# 変更箇所
（変更前）
  bottle do
    sha256 cellar: :any, high_sierra:  "dcc9059b725fd7c87842287bbedf60a28745417652d42a300dcd944e15986f36"
    sha256               x86_64_linux: "cd94b6bc2189df7861c2c32c480f777984865dbab4107f493188feda5a05b80d"
  end

（変更後）
  bottle do
    sha256 cellar: :any, high_sierra:  "dcc9059b725fd7c87842287bbedf60a28745417652d42a300dcd944e15986f36"
    sha256 cellar: :any, x86_64_linux: "cd94b6bc2189df7861c2c32c480f777984865dbab4107f493188feda5a05b80d"
  end

```

pipenv shellの実行結果です。

![Screen Shot 2021-08-02 at 3.37.04 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1837724/13bf2e0f-fe71-28c4-c87f-8db2e96b1b4d.png)


# 設定まとめ

上で使用した環境変数を.bash_profileに設定しておくとbrewがそのまま動作するようになります。またPATHも張っておくと良いです。

```
export HOMEBREW_FORCE_BREWED_CURL=True
export HOMEBREW_FORCE_BREWED_GIT=True
export HOMEBREW_NO_ENV_FILTERING=True
export PATH=$HOME/opt/ssl/bin:$HOME/opt/curl/bin:$HOME/opt/git/bin:$PATH
export HOMEBREW_PATH=$HOME/opt/ssl/bin:$HOME/opt/bin:$HOME/opt/git/bin:$PATH
export LD_LIBRARY_PATH="$HOME/opt/ssl/lib:$HOME/opt/curl/lib:$LD_LIBRARY_PATH"
```

作業中のメモ書き、historyから起こした内容なので編集エラーがある場合コメントいただけますと幸いです。