Pharo 5.0 を Ubuntu 16.04 で動かす

# 概要

Ubuntu 16.04 の上で [Pharo Zeroconf Script](http://get.pharo.org/) から取得した Pharo VM とイメージの組み合わせを動作させます。

なるべくパッケージでインストールできるものはパッケージに頼って、コンパイルの必要が発生しないように気をつけてまとめました。

# VM とイメージの取得

get.pharo.org から取得します。

```
$ mkdir pharo
$ cd pharo/
$ curl get.pharo.org | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12445  100 12445    0     0  19426      0 --:--:-- --:--:-- --:--:-- 19414
Downloading the latest 50 Image:
    http://files.pharo.org/get-files/50/pharo.zip
Pharo.image
Downloading the latest pharoVM:
	http://files.pharo.org/get-files/50/pharo-linux-stable.zip
pharo-vm/pharo
Downloading PharoV50.sources:
	http://files.pharo.org/get-files/50/sources.zip
Creating starter scripts pharo and pharo-ui
On a 64-bit system? You must install the 32-bit libraries
   Try `sudo aptitude install ia32-libs` or see http://pharo.org/gnu-linux-installation#64-bit-System-Setup for more info
```

取得できたものを確認してみます。

```
$ ls
Pharo.changes  Pharo.image  pharo  pharo-ui  pharo-vm
```

中身は以下のような内容となっています。

* Pharo.changes
  * Pharo.image イメージに対応する changes ファイル
* Pharo.image
  * Pharo 5.0 向けのイメージ
* pharo
  * バイナリを実行するラッパースクリプト
  * ヘッドレスで動作させるためのオプション付き
* pharo-ui
  * バイナリを実行するラッパースクリプト
  * X11 環境などがないと使えません
* pharo-vm
  * ラッパースクリプトが実行するバイナリや `*.so` などのランタイムが格納されています


# 実行

ラッパースクリプトの内容を確認してみます。

```
$ tail pharo
# some magic to find out the real location of this script dealing with symlinks
DIR=`readlink "$0"` || DIR="$0";
DIR=`dirname "$DIR"`;
cd "$DIR"
DIR=`pwd`
cd - > /dev/null
# disable parameter expansion to forward all arguments unprocessed to the VM
set -f
# run the VM and pass along all arguments as is
"$DIR"/"pharo-vm/pharo" --nodisplay  "$@"
```

`pharo` と `pharo-ui` はオプションを切り替えてバイナリを起動するスクリプトであることがわかります。

バイナリを実行してみます。引数なしで実行すればオプション引数にイメージ名が必要であるメッセージがでるはずです。

```
$ ./pharo-vm/pharo
-bash: ./pharo-vm/pharo: No such file or directory
```

なん…だと…

```
$ file ./pharo-vm/pharo
./pharo-vm/pharo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=48578da2df9b12949b98afcc92c361518ee317b8, not stripped
$ uname -a
Linux helium 4.4.0-47-generic #68-Ubuntu SMP Wed Oct 26 19:39:52 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
```

バイナリが32ビットでカーネルが64ビットなので認識できていないみたいです。


# 32ビット環境の作成

ダウンロードのときのメッセージを確認してみます。

```
On a 64-bit system? You must install the 32-bit libraries
   Try `sudo aptitude install ia32-libs` or see http://pharo.org/gnu-linux-installation#64-bit-System-Setup for more info
```

`ia32-libs` というパッケージをインストールすれば、32ビットのバイナリが動くようになるよ、みたいなことが書いてあります。
しかし、これは Ubuntu 14.04 のときに存在していたパッケージで Ubuntu 16.04 からは `ia32-libs` というパッケージが存在しない[^1]ので、インストールできないです。

[^1]: CentOS や Amazon Linux での動作にも挑戦しましたが、ムリでした。ビルドするべきなのでしょう。

[64 bit - How to run 32-bit app in Ubuntu 64-bit? - Ask Ubuntu](http://askubuntu.com/questions/454253/how-to-run-32-bit-app-in-ubuntu-64-bit) を参考にアーキテクチャの追加と必要なパッケージのインストールを行いましょう。

```
$ sudo dpkg --add-architecture i386
$ sudo apt-get update
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386
```

実行してみます。

```
$ ./pharo-vm/pharo
pharo: could not find any display driver
Aborted (core dumped)
```

**あっ、刺さるようになった。**
なんかとてもダメな気がしますが、見なかった事にします。

X11 環境を用意すればいいんですが、今回は動かすのが目的なので無視します。`--nodisplay` でヘッドレスで動くのか確認します。

# `Transcript show: 'hoge'.` の実行

```
$ ./pharo-vm/pharo --nodisplay Pharo.image eval "Transcript show: 'hoge'."
ioLoadModule(/home/ubuntu/pharo/pharo-vm/libFT2Plugin.so):
  libfreetype.so.6: cannot open shared object file: No such file or directory
ioLoadModule(/home/ubuntu/pharo/pharo-vm/libFT2Plugin.so):
  libfreetype.so.6: cannot open shared object file: No such file or directory
ioLoadModule(/home/ubuntu/pharo/pharo-vm/libFT2Plugin.so):
  libfreetype.so.6: cannot open shared object file: No such file or directory
Freetype2 primitive failed [error -1][can't get error string]
FreeTypeExternalMemory(FT2Handle)>>primitiveFailed:
FreeTypeExternalMemory(FT2Handle)>>primitiveFailed
FreeTypeExternalMemory>>primCopyToExternalMemory:
FreeTypeExternalMemory>>validate
EmbeddedFreeTypeFontInstaller>>addFromFileContents:baseName:
SourceCodeProRegular class(EmbeddedFreeTypeFontFontDescription class)>>installFontsIn:
[ :each | each installFontsIn: provider ] in EmbeddedFreeTypeFontFontDescription class>>installAllFontsIn: in Block: [ :each | each installFontsIn: provider ]
OrderedCollection>>do:
EmbeddedFreeTypeFontFontDescription class>>installAllFontsIn:
EmbeddedFreeTypeFontInstaller>>installAllFontsIn:
[ :each | each installAllFontsIn: self ] in FreeTypeFontProvider>>updateEmbeddedFreeTypeFonts in Block: [ :each | each installAllFontsIn: self ]
OrderedCollection>>do:
FreeTypeFontProvider>>updateEmbeddedFreeTypeFonts
FreeTypeSettings class>>updateFreeType
FreeTypeSettings class>>startUp:
ClassSessionHandler>>startup:
[ :each | each startup: isImageStarting ] in WorkingSession>>runStartup: in Block: [ :each | each startup: isImageStarting ]
[ aBlock value: each ] in [ :each |
[ aBlock value: each ]
	on: Exception
	do: [ :error | self errorHandler handleError: error ] ] in WorkingSession>>runList:do: in Block: [ aBlock value: each ]
BlockClosure>>on:do:
[ :each |
[ aBlock value: each ]
	on: Exception
	do: [ :error | self errorHandler handleError: error ] ] in WorkingSession>>runList:do: in Block: [ :each | ...
Array(SequenceableCollection)>>do:
WorkingSession>>runList:do:
WorkingSession>>runStartup:
WorkingSession>>start:
SessionManager>>snapshot:andQuit:
SmalltalkImage>>snapshot:andQuit:
ImageCleanerCommandLineHandler>>activate
ImageCleanerCommandLineHandler class(CommandLineHandler class)>>activateWith:
[ aCommandLinehandler activateWith: commandLine ] in PharoCommandLineHandler(BasicCommandLineHandler)>>activateSubCommand: in Block: [ aCommandLinehandler activateWith: commandLine ]
BlockClosure>>on:do:
hogeTranscript
```

なんかキッツい感じで中途半端に動いてますね。
じつはこのまま動かしていると運用に耐えられるようなものではありません。

ネタばらしするとさまざまな `*.so` が不足しているんですよね。

```
ioLoadModule(/home/ubuntu/pharo/pharo-vm/libFT2Plugin.so):
  libfreetype.so.6: cannot open shared object file: No such file or directory
ioLoadModule(/home/ubuntu/pharo/pharo-vm/libFT2Plugin.so):
  libfreetype.so.6: cannot open shared object file: No such file or directory
ioLoadModule(/home/ubuntu/pharo/pharo-vm/libFT2Plugin.so):
  libfreetype.so.6: cannot open shared object file: No such file or directory
```

ここのログが足がかりになります。

# 足りない `*.so` を調べる

ちょっと強引なんですが、バイナリが格納されているディレクトリで `ldd(1)` を使って足りない `*.so` を探します。

```
$ cd pharo-vm/
$ ls
PharoV50.sources            libJPEGReadWriter2Plugin.so  libSDL2-2.0.so.0.2.1     libgit2.so.0    vm-display-null
libB3DAcceleratorPlugin.so  libJPEGReaderPlugin.so       libSDL2DisplayPlugin.so  libssh2.so.1    vm-sound-ALSA
libFT2Plugin.so             libRePlugin.so               libSqueakSSL.so          pharo           vm-sound-null
libInternetConfigPlugin.so  libSDL2-2.0.so.0             libSurfacePlugin.so      vm-display-X11
$ ldd * | grep 'not found'
	libfreetype.so.6 => not found
	libSDL2-2.0.so.0 => not found
	libssl.so.1.0.0 => not found
	libz.so.1 => not found
	libssl.so.1.0.0 => not found
	libcrypto.so.1.0.0 => not found
	libcrypto.so.1.0.0 => not found
	libz.so.1 => not found
	libcrypto.so.1.0.0 => not found
	libz.so.1 => not found
	libGL.so.1 => not found
	libX11.so.6 => not found
	libasound.so.2 => not found
```

こんなに…あるの…

これらのうち、ないとヤバそうなものを入れていきます。

具体的には `libssl.so` とか `libz.so` とか `libfreetype.so` とか `libcrypto.so` あたりですね。


最近の通信はすぐに SSL を使いますので Zinc とかが刺さりまくります。

# 足りない `*.so` のパッケージを探す

インチキっぽいんですが、公式のレポジトリから Pharo をインストールした Ubuntu 14.04 を用意して `do-release-upgrade(8)` で Ubuntu 16.04 に上げます。
その Ubuntu 16.04 マシンで `dpkg(1)` を使って探していきます。

```
$ dpkg -S libfreetype.so.6
libfreetype6:amd64: /usr/lib/x86_64-linux-gnu/libfreetype.so.6.12.1
libfreetype6:i386: /usr/lib/i386-linux-gnu/libfreetype.so.6
libfreetype6:amd64: /usr/lib/x86_64-linux-gnu/libfreetype.so.6
libfreetype6:i386: /usr/lib/i386-linux-gnu/libfreetype.so.6.12.1
```

```
$ dpkg -S libSDL
dpkg-query: no path found matching pattern *libSDL*
```

```
$ dpkg -S libssl.so.1.0.0
libssl1.0.0:amd64: /lib/x86_64-linux-gnu/libssl.so.1.0.0
libssl1.0.0:i386: /lib/i386-linux-gnu/libssl.so.1.0.0
```

```
$ dpkg -S libz.so.1
zlib1g:i386: /lib/i386-linux-gnu/libz.so.1.2.8
zlib1g:amd64: /lib/x86_64-linux-gnu/libz.so.1
zlib1g:i386: /lib/i386-linux-gnu/libz.so.1
zlib1g:amd64: /lib/x86_64-linux-gnu/libz.so.1.2.8
```

```
$ dpkg -S libcrypto.so.1.0.0
libssl1.0.0:i386: /lib/i386-linux-gnu/libcrypto.so.1.0.0
libssl1.0.0:amd64: /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
```

```
$ dpkg -S libGL.so.1
libgl1-mesa-glx:i386: /usr/lib/i386-linux-gnu/mesa/libGL.so.1
libgl1-mesa-glx:amd64: /usr/lib/x86_64-linux-gnu/mesa/libGL.so.1
libgl1-mesa-glx:amd64: /usr/lib/x86_64-linux-gnu/mesa/libGL.so.1.2.0
libgl1-mesa-glx:i386: /usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0
```

```
$ dpkg -S libX11.so.6
libx11-6:i386: /usr/lib/i386-linux-gnu/libX11.so.6.3.0
libx11-6:amd64: /usr/lib/x86_64-linux-gnu/libX11.so.6
libx11-6:amd64: /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0
libx11-6:i386: /usr/lib/i386-linux-gnu/libX11.so.6
```

```
$ dpkg -S libasound.so.2
libasound2:amd64: /usr/lib/x86_64-linux-gnu/libasound.so.2.0.0
libasound2:i386: /usr/lib/i386-linux-gnu/libasound.so.2.0.0
libasound2:amd64: /usr/lib/x86_64-linux-gnu/libasound.so.2
libasound2:i386: /usr/lib/i386-linux-gnu/libasound.so.2
```

これらのうち、i386 のパッケージを入れていけばいいのです。

`libSDL` が見つかりませんでしたが、まあ、普通は使わないと思うので今回はパスです。

# 足りない `*.so` のパッケージをインストール

必要なパッケージは以下のものだということが分かりました。

* `libfreetype6:i386`
* `libssl1.0.0:i386 `
* `zlib1g:i386`
* `libgl1-mesa-glx:i386`
* `libx11-6:i386`
* `libasound2:i386`

というわけで、一撃必殺。

`$ sudo apt install -y libfreetype6:i386 libssl1.0.0:i386 zlib1g:i386 libgl1-mesa-glx:i386 libx11-6:i386 libasound2:i386`

入った。

# 帰ってきた `Transcript show: 'hoge'.` の実行

```
$ ./pharo-vm/pharo --nodisplay Pharo.image eval "Transcript show: 'hoge'."
hogeTranscript
```

スッと何も文句なしに動くようになりました。

`Transcript` って出てるのは何なんですかね… `eval` すると戻り値が print it されるのかな？

# まとめ

Pharo Zeroconf Script で取得した VM を動かすには32ビットのランタイムがいろいろと必要になります。

多く分けてふたつのステップをこなせば64ビットの Ubuntu 16.04 環境をなるべく汚さずに Pharo で生活できるようになります。

* アーキテクチャの追加とランタイムのインストール

```
$ sudo dpkg --add-architecture i386
$ sudo apt-get update
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386
```

* 不足している `*.so` のインストール

`$ sudo apt install -y libfreetype6:i386 libssl1.0.0:i386 zlib1g:i386 libgl1-mesa-glx:i386 libx11-6:i386 libasound2:i386`

これくらい入っていれば [ALLSTOCKER](https://allstocker.com/ja/)[^2] も動作するくらいのランタイムは揃います。

それではみなさんも Ubuntu 16.04 と Pharo 5 で快適な Smalltalk 生活をお送り下さい。

ではまた。

[^2]: SORABITO 株式会社が運営する中古建機売買プラットフォーム ALLSTOCKER は Pharo Smalltalk によって動作しています。