DUB とはD 言語のためのパッケージ管理ツールです。 多分 Clojure の lein や Scala の sbt に近いです。 基本的な使い方は、このページによくまとまっています

D言語でビルドツールDUBを用いて便利なライブラリをより簡単に利用する

本稿では、そこに書いてない便利な使い方をまとめます。

ローカルにあるライブラリとの連携

- dub パッケージの利用

DUB プロジェクトでは主に DUB リポジトリに登録されたリモートなパッケージをよく使うと思いますが、ローカルに用意した DUB パッケージを追加するときは、dub.jsonにバージョンではなくパスを書きます。

"dependencies": {
    "mir": { "path": "./mir" },
    "ggplotd": ">=0.4.5",
    ...
}

これで、再帰的にきちんと依存解決してくれます、便利。 きちんとローカルのパッケージもバージョン管理をするためには、git submodule などを利用すると良いと思います。

fork して自前パッチを当てたmirパッケージを使用したd-svmパッケージの例

- ライブラリファイル(*.so等)の利用

例えばデータベースや CUDA ライブラリなどパスの通ったものを、 リンカオプション-lcudaをつけて渡したいことがあります。 そんなときは、libsオプションを使います。

"libs": [
    "cuda",
    "nvrtc",
    "cudart"
]

さらに複雑なオプションが必要なときは、dflagsオプションでコンパイラに渡すこともできます。

"dflags" : [
    "-J/usr/local/cuda/include/"
]

ちなみに-Jオプションはコンパイル時にファイルを読むimport(filename)でパスを通すオプションで、 コンパイル時にCヘッダーファイルを読んでD言語用のラッパーを生成するときなどに使います。

単体テストの実行

D 言語には言語機能として、単体テスト(unittest)やカバレッジ計測がサポートされています。

import mir.ndslice;

auto diag(S)(S s, long k=0) {
  auto sk = k >= 0 ?  s[0 .. $, k .. $] : s[-k .. $, 0 .. $];
  return sk.diagonal;
}

unittest {
  //  -------
  // | 0 1 2 |
  // | 3 4 5 |
  //  -------
  auto a = iota(2, 3).canonical;
  assert(a.diag == [0, 4]);
  assert(a.diag(1) == [1, 5]);
  assert(a.diag(-1) == [3]);
}

コマンド dub testdub test -b unittest-covで、パッケージ内全体のテスト実行やカバレッジ計測(結果は*.lstファイルに出力)できます。これだけでも十分に便利ですが、外部のサービスと連携するとさらに便利です。

外部サービスとの連携

BitbucketやGitHubリポジトリのREADMEによく、このようなバッジ

Build Status Coverage Status Dub version

を見かけると思います。具体的にはTravisという自動で様々な環境(OS, コンパイラ...)でテストを行うCIと、 テストのカバレッジを見やすくしてくれる Coveralls というサービスをよく使います。 さらに他の人からも簡単に使えるように、dubのWebページに登録することも多いでしょう。

- Travis CI, Coveralls

Travis CI, Coveralls にGitHubなどのアカウントでログインして既存のリポジトリを追加できます。 あまり知られていませんが、TravisではLinuxだけでなくOSXも動きます。 Coverallsとの連携には doveralls というツールがおすすめです。 次のような.travis.ymlファイルをGitリポジトリの配下におくといい感じにやってくれます。

os:
  - linux
  - osx

language: d

d:
  - ldc-1.1.0
  - dmd

install:
  - dub fetch doveralls

script:
  - dub test -b unittest-cov
  - dub run doveralls

もし、プライベートであったり、GPGPUなどTravisでは実行できないようなライブラリを作っている場合は、 ローカルでテストを実行して、カバレッジ計測だけCoverallsを利用したいということがあると思います。 その場合は、ローカルで次のようなシェルコマンドを打つと良いでしょう。

$ dub fetch doveralls
$ dub test -b unittest-cov
$ dub run doveralls -- -t <coverallsのrepo_token>

- DUB レポジトリの登録

DUB ではアカウントを作って下記のページから自作パッケージを追加できます。 GitHubとBitbucketに登録されているリポジトリから登録できます。

https://code.dlang.org/my_packages

git の tag を打つ必要があります、GitHubのリリースページなどから打つとよいでしょう。 これで、他のパッケージから簡単に登録したパッケージのモジュールをimportして呼び出せます。 一つの完結したプロジェクトではあまり使いませんが、D言語にはpublic importpackage.d という便利な機能があります。外部から呼ばれるパッケージでは、それらを駆使すると良いでしょう。

https://dlang.org/spec/module.html

ドキュメントの生成

D 言語には Javadoc みたいなコード中のコメント(ddoc)によるドキュメント生成が、unittestみたいに言語機能としてサポートされています。

http://www.kmonos.net/alang/d/ddoc.html

http://dlang.org/spec/ddoc.html

http://stackoverflow.com/questions/29762668/using-dub-to-build-documentation

dub でサポートされた方法は2つあります,

  1. dmd コンパイラの機能を使う: dub build --build=docs
  2. 標準ライブラリPhobos のドキュメント生成に使われる ddox: dub build --build=ddox

ところで、ddox は筆者の環境ではビルドできませんでしたので、dmdコンパイラの機能を使っています。こんな感じで /++ +/ (複数行) や /// (一行) を使ってドキュメントを書くと(正確にはかかなくても多少自動生成されます)、

/++
construct new uninitialized slice with specified shape and element type

Params:
    length = elements of shape
Returns:
    new uninitialized slice
+/
auto empty(E=double, size_t N)(size_t[N] length...) {
  return uninitializedSlice!E(length);
}

///
unittest {
  auto e = empty!int(2, 3);
  assert(e.shape == [2, 3]);
  static assert(is(DeepElementType!(typeof(e)) == int));
}

いい感じに unittest なども Examples として表示してくれたりします。 この辺はBDDなどの考え方と相性が良いのではないでしょうか?

例: https://shigekikarita.github.io/numir/

ちなみにGithub では Pages という設定で、docsディレクトリにhtmlファイルを置くと ウェブページをホストしてくれるので便利です。

まとめ

DUBはあまりドキュメント化されていない部分もあって、stackoverflowやgithubリポジトリを掘り返しながら、まとめました。DUBはパッケージマネージャとしては優秀ですが、ビルドシステムとしてはMakeなどと比べると制限がつよくて、シェルスクリプトなどがdub.jsonにかけると便利かなとは思います。D言語は言語機能として便利な機能(単体テスト、カバレッジ計測、ドキュメント生成)がサポートされているのは最高ですね。