[Qiita] Bash用のLibrary manager作っている -- blib

この投稿は[学生LT Advent Calendar 2018](https://qiita.com/advent-calendar/2018/student-lt)の12日目の記事です。

学生LTについて、公式Discordから引用させていただきますと

>学生LTコミュニティへようこそ！
>ここは技術に関心のある学生と様々な交流が出来る場所となっております。

主にはDiscordでの交流、LT会の企画運営参加?などをしています。
Youtube等を使ったオンラインLT会もあるので遠方の方でも参加できます！（みんな割とバラバラな地域の人で構成されています）

学生LT公式HP: https://student-lt.tech/

---

まだbeta段階ですがちょうどいい機会なので、今作っているbash用ライブラリマネージャー、[Cj-bc/blib](https://github.com/Cj-bc/blib)を紹介します

  * 前半: 使い方
  * 後半: 技術的な話

---
軽く使い方。

# Installation

`homebrew`に対応しています

```bash
$ brew install Cj-bc/blib/blib
```

また、(個人的には)初の試みとしてMakefileを使ってみたのでそちらでもいけます(いけるはず)

```bash
$ git clone https://github.com/Cj-bc/blib
$ cd blib
$ make install
```

その他対応予定

  * `bpkg`

# 使い方

## ライブラリのインストール

1. 該当ライブラリ用のformulaレポジトリが存在することを確認します
2. `blib install <user>/<repo>`でインストールできます

この時、`<user>/<repo>`は**そのformulaレポジトリの<user>/<repo>である**ことに注意してください。
また、`<repo>`は`blib-`のプレフィックスを除いた状態で記載してください。

例:

```bash
$ blib install Cj-bc/libtar
```

## インストール済みライブラリの一覧取得

```bash
$ blib list
```

`bash-oo-framework`と`libblib`はデフォルトで表示されます(blibで使用するため)

# blibライブラリを使用してスクリプトを書くには

たった1行付け足すだけで対応できます

```bash
source "$(blib --prefix)/bash-oo-framework/lib/oo-bootstrap.sh"
```

これでblibのライブラリを`import`できるようになりました(勿論、`bash-oo-framework`の各種ライブラリも使用可能です)

blibでインストールされたライブラリを`import`する際は、prefix: `blib:`をつけてください

```bash:例
import blib:libtar
```

---

ここから後半

# 動機

  * コードスニペットを共有して使いたかった
  * `source`時にパスを考えなければならなかったりして少し面倒
  * 純粋に、同じコードは同じ場所から呼び出したい
  * パッケージマネージャーがあるのにライブラリマネージャーがないのが納得いかん
  * ~~だって楽しそうじゃん~~

ちなみに、bash用のパッケージマネージャーは幾つか存在します

* [bpkg/bpkg](https://github.com/bpkg/bpkg)
* [basherpm/basher](https://github.com/basherpm/basher)

# 主な仕様

## 現状

  * Homebrewライクなライブラリ管理システム。`formula`を作成することでライブラリとして認識できるようになる
    * これにより、他者のコードを使うこともできますね！やったね！
  * `bash-oo-framework`の`import`文を使用してライブラリ読み込み
    * `bash-oo-framework`は標準で使用可能になります。`import`するだけでお手軽。
  * 内部的にはファイルを`source`しているだけのため、`bash-oo-framework`を用いているものもそうでないものも管理可能
  * 管理者権限のいらないパスにライブラリを保存
  * どの場所からでも`blib`さえあれば動く！！

### 現状のコマンド群

```bash
$ blib --prefix # ライブラリの格納先
/usr/local/etc/blib/lib
$ blib list # install済みライブラリの一覧
bash-oo-framework libblib
$ blib install <user>/<repo> # github上の<user>/<repo>レポジトリのformulaを探してインストール
$ blib uninstall <library_name> # <library_name>をアンインストール(Cellarからは消えない)
```


## 追加予定の仕様

  * ライブラリ情報の表示コマンド
  * バージョン管理
  * ライブラリのドキュメント表示機能
  * `blib`対応バージョンの`bash-oo-framework`をいつか本家にPR出したい

### 追加予定のコマンド群

```bash
$ blib cleanup # `Cellar`内に残っているcacheを削除する
$ blib switch <library_name> <version> # ライブラリのバージョン切り替え
$ blib init # 冒頭に必要になる文字列を簡略化する（詳細はissueを参照）
```

# 実装

おそらくこのツールの実装面の一番の特徴はshellscriptで書かれていること、`bash-oo-framework`を使っていることだと思います。
今回の実装では、以下の機能を使って書かれています:

| ライブラリ名    | 説明                                |                                   |
|:-:              |:-:                                  |:-:                                |
| util/class      | オブジェクト指向で書けるようにする  |                                   |
| util/log        | ロガーの追加                        |                                   |
| util/tryCatch   | try-Catch構文を追加                 |                                   |
| util/exception  | 例外を追加                          |                                   |
| UI/Console      | 標準エラー出力への一律のアクセス    |                                   |
| UI/Color        | 色エイリアスの追加                  | util/logでエラーが出るため追加    |

## 大雑把な実装

大雑把に実装の流れを書いておきます。
その後、実装に際して詰まった部分を書いていきます。

### 1. `blib`クラスを作成

静的クラスとして`blib`クラスを作成します。
(高速化したい場合、個別に関数を作成した方が良いです)
(この場合、どちらの書き方でも速度以外の違いはありません。多分。)

```bash
class:blib() {
  # クラスメゾットを追加します
}

Type::InitializeStatic blib
```

### 2. クラスメゾットをいくつか作成

機能ごとにクラスメゾットを追加します(当たり前)
とりあえず必要なものをリストアップ

```bash
class:blib() {
  # オプションを取り扱う
  blib::option() {
    :
  }

  # インストール用処理
  blib::install() {
    :
  }

  # アンインストール処理
  blib::uninstall() {
    :
  }

  # インストールされたライブラリの一覧表示
  blib::list() {
    :
  }

  # manコマンド(未実装)
  blib::man() {
    :
  }

  # infoコマンド(未実装)
  blib::info() {
    :
  }
}

Type::InitializeStatic blib
```

### 3. 内容に応じて必要なライブラリを開発

本来、`blib`クラス自体も分けるべきなのですがまだ分けてません(気づいたのが後だった)
`blib`の依存ライブラリは`libblib`にまとめられています。
いずれ汎用性を上げたのち、別途ライブラリを分ける予定でいます。
今回使っているライブラリは二つです

| ライブラリ名  | 説明                                    |
|:-:            |:-:                                      |
| formula       | formulaの検証等するライブラリ           |
| user          | Githubのユーザーの検証等するライブラリ  |


### 4. main関数を書く

別に名前はなんでもいいのですが他に倣ってmain関数を書き、それを実行することにしました。

```bash
main() {
  # この中からblibを呼ぶ
  :
}

main $@
```

これはshellscriptとしてはどうなものかといったものですが、オブジェクト指向的、ということでスルーします。
とりあえずこれで大雑把な流れは終了

## 困ったところ、詰まったところ、etc

今後の人のリファレンスになればいいなぁというのも思っているので一問一答形式で書いていきます

### 1. ライブラリインストール先 [WIP]

  これに関してはまだあまり詰められてません
  今は暫定的なものとなっていますがより良いものがあれば知りたいです。

### 2. ライブラリはレポジトリごと置いておくか否か

  結論: Yes

  ライブラリをレポジトリごとおくことにより、バージョン管理が楽になります(`git checkout`で行えるので)
  （尚、この記事を書いていてタグによるバージョン管理を思いついたので、細部は現状の仕様とは異なります）
  （現状の仕様: バージョンごとにディレクトリを分けてクローン）

### 3. 完全なアンインストール処理に`rm -rf`を使って良いか否か

  結論: 使う

  (ここでの「完全なアンインストール」というのは、`blib cleanup`として実装予定の機能で、Cellar内も消去します)
  ライブラリがgitレポジトリである以上、`.git`を消去しないといけなくなるため、結局強制的にrm -rf`を使うことになります。
  なので元から使ってしまおうということです。

### 4. classからの例外が捕捉できない [BUG]

  **これは明確なバグです**
  正確には、`class`から例外が返ってきていないようです。
  `bash-oo-framework`本家にもissueが上がっています。
    * [niieani/bash-oo-framework](https://github.com/niieani/bash-oo-framework/issues/68)
  鋭意捜査中ですがまだ治ってません。

### 5. mainから存在しないはずの例外が返ってくる [BUG]

  結論: おそらく4.のバグ
  対処: main呼び出し元もtryCatchで囲む。

### 6. mainをtryCatchで囲むと、main関数の戻り値(=プログラムの戻り値)が戻らず、常に0になってしまう

  対処: main呼び出し元を囲むtryCatchにて、mainの戻り値を例外として投げる

  main関数からの例外は基本的に存在しないはずなため、緊急策としてmainの戻り値を変数に格納して例外として送出、それをそのままプログラムの戻り値として設定。

[2018-12-12]@ Qiita 学生LT Advent Calendar参加日

[2018-12-02 08:45]