MIDIスイッチャを作る:(備忘録)mididingsのpython3対応

# はじめに
ubuntu studioに普通にapt-getでmididings入れるとpython2.7で動いてしまうので
python3で動くようにしたときのメモ

# ソースの取得

どうやらpython3-mididingsとかいうパッケージはなさそうなので、
ソースからビルドして/usr/local/binにつっこむことにした

まずはソースの取得

```
$ git clone https://github.com/dsacre/mididings.git
```
mididingsってフォルダがあるのでそこのフォルダで色々作業する

※2021/03/25追記
なんか今アクティブなブランチは以下のものらしいpython3.7対応済みとのこと
https://github.com/marcan/mididings.git

# ソースの修正

修正箇所は４ファイル
- setup.py
- scripts/livedings
- scripts/mididings
- scripts/sendmidi

先頭行の`#!/usr/bin/env python` を `#!/usr/bin/env python3`に書き換え

# ビルド＆インストール

さぁビルドだ

```
# ./setup.py build

(中略)

86_64-linux-gnu-g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.8/src/engine.o build/temp.linux-x86_64-3.8/src/patch.o build/temp.linux-x86_64-3.8/src/python_caller.o build/temp.linux-x86_64-3.8/src/send_midi.o build/temp.linux-x86_64-3.8/src/python_module.o build/temp.linux-x86_64-3.8/src/backend/base.o build/temp.linux-x86_64-3.8/src/backend/alsa.o build/temp.linux-x86_64-3.8/src/backend/jack.o build/temp.linux-x86_64-3.8/src/backend/jack_buffered.o build/temp.linux-x86_64-3.8/src/backend/jack_realtime.o -lboost_python -lboost_thread -lasound -ljack -lglib-2.0 -o build/lib.linux-x86_64-3.8/_mididings.cpython-38-x86_64-linux-gnu.so
/usr/bin/ld: -lboost_python が見つかりません
/usr/bin/ld: -lboost_thread が見つかりません
collect2: error: ld returned 1 exit status


```

`boost_python`とがないよとか怒られる、ふーん。
ぐぐったら`libboost-all-dev`とかいうパッケージががないせいらしいので

```
# apt-get install libboost-all-dev
```

してからビルド。
すると `boost_thread`は見つかったみたいだけど`boost_python`が相変わらずみつからないらしい。
うーんこまった。

ぐぐったらこんなのが
 https://askubuntu.com/questions/944035/installing-libboost-python-dev-for-python3-without-installing-python2-7
ubuntuだと`libboost-python1.67`がpython2.x系で`libboost-python1.71`がpython3系らしい。
dpkgでインストールされてるパッケージをみると

```
$ dpkg -l | grep libboost-python
ii  libboost-python-dev                           1.71.0.0ubuntu2                             amd64        Boost.Python Library development files (default version)
ii  libboost-python1.67.0                         1.67.0-17ubuntu8                            amd64        Boost.Python Library
ii  libboost-python1.71-dev                       1.71.0-6ubuntu6                             amd64        Boost.Python Library development files
ii  libboost-python1.71.0                         1.71.0-6ubuntu6                             amd64        Boost.Python Library

```

両方入ってるやんけ！！
むしろpython2用の`libboost-python1.67-dev`は入ってない
`libboost-pthread`と何がちがうのかなーと思って調べてみたら

```
$ cd /usr/lib/x86_64-linux/gnu
$ ls -al libboost_python*
-rw-r--r-- 1 root root 276128  4月  1  2020 libboost_python27.so.1.67.0
-rw-r--r-- 1 root root 701312  4月  1  2020 libboost_python38.a
lrwxrwxrwx 1 root root     27  4月  1  2020 libboost_python38.so -> libboost_python38.so.1.71.0
-rw-r--r-- 1 root root 280224  4月  1  2020 libboost_python38.so.1.67.0
-rw-r--r-- 1 root root 259776  4月  1  2020 libboost_python38.so.1.71.0
$
$ ls -al libboost_thread*
-rw-r--r-- 1 root root 485178  4月  1  2020 libboost_thread.a
lrwxrwxrwx 1 root root     25  4月  1  2020 libboost_thread.so -> libboost_thread.so.1.71.0
-rw-r--r-- 1 root root 186536  4月  1  2020 libboost_thread.so.1.67.0
-rw-r--r-- 1 root root 174272  4月  1  2020 libboost_thread.so.1.71.0
```

あああああ、なるほどね、`-lboost_python`じゃなくて`-lboost_python38`にしなきゃダメなのね

シンボリックリンク張ってごまかそうかとも思ったけど、影響でかそうかもだったので、mididingsフォルダ直下のsetup.pyを修正しようと思って150行目あたりを見たら

```setup.py

    151 boost_python_suffixes = ['-py%d%d' % sys.version_info[:2]]
    152 if sys.version_info[0] == 3:
    153     boost_python_suffixes.append('3')
    154 libraries.append(boost_lib_name('boost_python', boost_python_suffixes))
    155 libraries.append(boost_lib_name('boost_thread'))
    156 
```

あああああ、python3のときだけなんか変なサフィックスつけてやがる.....
しかも`boost_lib_name()`は結局すりぬけてて単純に`boost_python`って名前が`libraries`に入ってるっぽい。
よくわからんので`libraries.append(boost_lib_name('boost_python',boost_python_suffixes))`
を
`libraries.append(boost_lib_name('boost_python38',boost_python_suffixes))`
に変更してビルド

やったーーービルド通ったァァァ、あとは`setup.py install`して終了

# 動作確認

で、ワクワクしながら起動してみるじゃないですか、そしたら

```
$ mididings
Traceback (most recent call last):
  File "/usr/local/bin/mididings", line 4, in <module>
    __import__('pkg_resources').run_script('mididings==2015+rbbec99a', 'mididings')
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 667, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 1463, in run_script
    exec(code, namespace, namespace)
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/EGG-INFO/scripts/mididings", line 20, in <module>
    import mididings
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/__init__.py", line 14, in <module>
    from mididings.setup import config, hook
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/setup.py", line 15, in <module>
    import mididings.arguments as _arguments
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/arguments.py", line 13, in <module>
    from mididings import misc
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/misc.py", line 21, in <module>
    import decorator
ModuleNotFoundError: No module named 'decorator'
```

なんかdecoratorとかいうモジュールが無いっぽい....またか.
とりあえず`apt-get install python3-decorator`してから起動してみると

```
$ mididings
Traceback (most recent call last):
  File "/usr/local/bin/mididings", line 4, in <module>
    __import__('pkg_resources').run_script('mididings==2015+rbbec99a', 'mididings')
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 667, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 1463, in run_script
    exec(code, namespace, namespace)
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/EGG-INFO/scripts/mididings", line 20, in <module>
    import mididings
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/__init__.py", line 15, in <module>
    from mididings.engine import run, process_file
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/engine.py", line 15, in <module>
    import mididings.patch as _patch
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/patch.py", line 15, in <module>
    import mididings.units as _units
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/units/__init__.py", line 19, in <module>
    from mididings.units.call import *
  File "/usr/local/lib/python3.8/dist-packages/mididings-2015+rbbec99a-py3.8-linux-x86_64.egg/mididings/units/call.py", line 36
    def __init__(self, function, async, cont):
                                 ^
SyntaxError: invalid syntax

```

ソース見ると：

```mididings/units/call.py
     35 class _CallBase(_Unit):
     36     def __init__(self, function, async, cont): ### ←これ！！
     37         def do_call(ev):
     38             # add additional properties that don't exist on the C++ side
     39             ev.__class__ = _event.MidiEvent
     40 
     41             # call the function
     42             ret = function(ev)
```

あああああああ、python3.6あたりで予約語になったasyncってキーワードが変数として使われてるぅぅぅ!!!
asyncって変数名をテキトーに変更して再度ビルド＆インストール

ふぅ、やっとおわった

# おわりに
まとめると

- パッケージとして`libboost-all-dev`,`python3-decorator`は入れとけ
- スクリプトの先頭に`#!/usr/bin/env python`になってるやつは`#!/usr/bin/env python3`になおしとけ
- setup.pyの`libraries_append`してるところの`boost_python`は`boost_python38`に変更
- mididings/units/call.pyの`class _CallBase`の`def __init__`の変数`async`を全てリネーム

ぐらいで動くっぽい。

python3+mididingsで自前アプリを動かす時には他にも色々細かいところの修正が必要かも。

同じ名前で中身の微妙に違うバイナリ(スクリプト)が/usr/binと/usr/local/binに存在するの気持ち悪いかなーーーー。やっぱpyenvですかね
個人的な感想としてはpython3に対応してるものは、もうどんどんそっちに移行したパッケージにしてほしいですね。
ただmididingsは全然メンテされてない....うーーーん

ちなみになんでmididingsをpython3で動かしたかったかというと
rtmidiと組み合わせてスイッチアプリを組み直したくなったから。
ubuntuのrtmidiパッケージはデフォルトで配布されてるのがpython3バージョンなので
そっちに合わせたってカンジです。

ソースとかいろいろ追いかけたんですけど、rtmidiもmididings、統一されないかなーー
とか思いました。