nimのパッケージマネージャnimbleによるプロジェクトテンプレート

## 概要

なんとなくあるんだなーぐらいにしか感じていなかった[nimble](https://github.com/nim-lang/nimble)について、ビルドに関する部分を調べてテンプレート化してみました。

(2019/09/28追記)
Nim-1.0.0に対応しました  

### nimbleのプロジェクト管理機能について

nimbleのタスクはいくつかあるのですが、プロジェクト管理機能は、以下の4つくらいです。
* ```nimble init``` 新規プロジェクト作成
* ```nimble build``` プロジェクトのビルド
* ```nimble test``` テスト実行
* ```nimble install``` リリースビルド＆~/.nimbleへのインストール

```nimble init```によりカレントディレクトリに```*.nimble```ファイル**のみ**が生成されます。
その後、ソースは```src/```フォルダ配下にするとか、実行モジュールは```bin/```フォルダに生成するなどの設定は、生成された```*.nimble```ファイルに記述していくことになります。
```nimble init```から始まる毎回同じような設定やらフォルダ作成等が面倒だったので、githubにcliツール用のnimbleプロジェクトテンプレートを作成しました。

テンプレート
https://github.com/6in/nimapp_template

テンプレート用README
https://github.com/6in/nimapp_template/blob/master/TEMPLATE-README.md

### テンプレートの利用方法について

利用方法は以下のような感じです

```
# sampleフォルダにclone
$ git clone https://github.com/6in/nimapp_template.git sample

# フォルダに移動
$ cd sample

# プロジェクト名を変更(nimapp_template -> sample)
$ nimble rename

# ビルド
$ nimble build

# 実行
$ bin/sample hoge
{{hello [[hoge]]}}

# ユニットテスト
$ nimble test
or
$ nimble test2

# 実行
$ nimble run

# リリース
$ nimble install
```

nimbleプロジェクトのパッケージ名は、リポジトリ名と同じ```nimapp_template```としており、内包するファイルやフォルダは、nimbleの規約に則った命名規約となっています(たぶん)。

テンプレートから個別のアプリケーションを作るためには、パッケージ名が付与されたファイル・フォルダおよびソース内でimportしているパッケージ名付きフォルダ名の変更等が必要になり、意外と面倒くさい作業が発生します。

本テンプレートは、nimbleの拡張タスク**rename**を実行することで、
* cloneしたフォルダ名を新たなパッケージ名とし、
* パッケージ名を伴ったファイル・フォルダ・ソースの置換
を行います。

以下、置換前、置換後のファイル・フォルダ構成となります。

#### clone直後のファイルフォルダ

```
$ git clone https://github.com/6in/nimapp_template.git sample
$ cd sample
$ tree .
.
├── nimapp_template.nimble        # nimbleファイル
├── README.md                     # アプリケーション用のREADME.md
├── TEMPLATE-README.md            # テンプレートのREADME.md
├── src                           # ソースフォルダ
│   ├── nimapp_template.nim       # 起動ファイル(docoptによるコマンドラインパース)
│   └── nimapp_templatepkg        # パッケージフォルダ
│       ├── main.nim              # mainソースファイル(メイン処理をここに)
│       └── private
│           └── main_impl.nim     # お好きに利用ください
├── tests                         # ユニットテストフォルダ
│   ├── alltest.nim               # nimble test2タスクが呼び出すソース
│   ├── nim.cfg                   # テスト用コンパイルオプション
│   ├── test1.nim                 # ユニットテストソース(サンプル)
│   └── test2.nim
└── util                          # nimbleから呼び出すツール
    └── rename_app.nim            # nimble renameタスクの実際の処理
```

#### nimble rename後のファイルフォルダ
```
$ nimble rename
$ tree .
.
├── sample.nimble                 # ファイル名・内容が変更
├── README.md                     # 内容が変更
├── TEMPLATE-README.md
├── src
│   ├── sample.nim                # ファイル名・内容が変更
│   └── samplepkg                 # フォルダ名が変更
│       ├── main.nim              
│       └── private
│           └── main_impl.nim
├── tests
│   ├── alltest.nim               # 内容が変更
│   ├── nim.cfg           
│   ├── test1.nim                 # 内容が変更
│   └── test2.nim                 # 内容が変更
└── util
    └── rename_app.nim
```

### テンプレートの内容について

* ```{prject_name}.nimble```は、カスタムタスクを含めたプロジェクト設定が記述されています
* ```src/```はアプリケーションのソースを配置します
* アプリケーションのエントリポイントは、[src/{project_name}.nim](https://github.com/6in/nimapp_template/blob/master/src/nimapp_template.nim)です
 * コマンドラインパーサーのdocoptを利用し、コマンドライン解析を行います
 *  コマンドラインをパースし、ハッシュオブジェクトを作成します
 *  src/{project_name}pkg/main.nimのmain()を呼び出します
* [src/{project_name}pkg/main.nim](https://github.com/6in/nimapp_template/blob/master/src/nimapp_templatepkg/main.nim)では、main関数を定義し、コマンドラインパースされたハッシュオブジェクトを受け取ります
* ```tests/```はユニットテスト用のフォルダです
 * **t**で始まるnimファイルを配置することで、```nimble test```でそれらのファイルが自動実行されます
 * tests/nim.cfgにて```src/```へパスを通すように設定しています

```nim:src/{project_name}.nim
# エントリポイント
import {project_name}pkg/main
import docopt

# docopt用のコマンドライン定義
let doc = """
nimapp_template.
Usage:
  nimapp_template <name>
  nimapp_template (-h | --help)
  nimapp_template --version
Options:
  -h --help     Show this screen.
  --version     Show version.
"""

when isMainModule:
  # コマンドライン解析(定義通りでないと、ここでUsageを表示して、アプリケーション終了)
  let args = docopt(doc, version = "nimapp_template 0.1.0")
  echo "args=>",args
  # main関数呼び出し
  let retCode = main(args)
  quit(retCode)
```

```nim:src/{project_name}pkg/main.nim
import private/main_impl
import docopt

proc main*(args:Table[string,Value]) : int =
  ## main関数
  ## 引数は、docoptのパース結果
  result = 0
  let name = $args["<name>"]
  echo say_hello_to(name)
```

### カスタマイズなど

* nimbleのカスタムタスクのサンプルが```*.nimble```に記載されています
* コンパイラをCからCPPに変えたいとき
  * ```*.nimble```の```backend```を修正します
* 実行時(runタスク)の引数を変更したい
  * *.nimbleのrunタスクを修正
* nimbleのタスクから別のタスクを呼び出したい
  * exec "nimble タスク名" 
  
### まとめ？

* nimbleによるcli系ツール開発用のテンプレートの紹介でした
* nimを勉強し始めるとちょっとしたツールを作りたくなってきますので、その時の参考になれば幸いです
* 今回テンプレートの展開処理(rename)ができたので、今後は同じような形式で様々なテンプレートをコツコツ作っていこうかと思います
 * パイプ処理テンプレート
 * DBアクセス処理テンプレート
 * WEBアプリテンプレート
 * GUIアプリテンプレート
 * クロスコンパイラテンプレート

### (追記)VSCodeでのデバッグについて

* nim 0.19.0対応に伴い、Native Debugプラグインをインストールすることにより、VSCodeでのデバッグ(ステップ実行)ができる設定を追加しています。
* ソースを編集し保存すると、デバッグビルドが自動で実行されます。
  * この機能をOFFにするには、```.vscode/setting.json```の"nim.buildOnSave"をfalseにします
* ```ctrl+shift+b```でビルド実行します。
* ```F5```キーでデバッグ実行を開始します。