nixpkgsにPull Request送ってchainerを入れてもらった

# 使用するもの
## [NixOS](https://nixos.org)
- Linuxディストリビューションの１つ
  - 関数型パッケージ管理ツールNixを使用している
  - パッケージ自体は[nixpkgs](https://github.com/NixOS/nixpkgs)のリポジトリで管理・開発されている(個々のパッケージは[https://nixos.org/nixos/packages.html#](https://nixos.org/nixos/packages.html#) で検索できる)
- 環境の構築が楽にできる
  - １つのファイルでシステム全体の設定ができる -> 管理が楽
  - システムのアップデート -> システムのロールバック ということが楽にできる
  - sudoとかしなくてもパッケージを用意できる(たとえばpythonでnumpyとmatplotlibを使いたい時は以下のようにする)
 
```
$ nix-shell -p python3 -p pythonPackages.numpy -p pythonPackages.matplotlib
```

- NixOpsを使ってリモートに環境のデプロイができる

# はじめに
[前回](https://qiita.com/hyphon81/items/3580d99e18d8adaa78b3)の続きです。
前回はNixOS上でchainerを使えるよう、パッケージングを行うDerivationコードを書いてみました。
せっかくパッケージングを行うコードを書いたので、```nix-shell -p pythonPackages.chainer```とかで環境を構築できるようにしたいです。
そこで、今回はchainerをnixpkgsに入れてもらえるようPull Requestを送ってみた時の顛末を書きます。

# Pull Requestをする前にやったこと
私はこれまでPull Requestなんて過去に1回ぐらいしか送ったこと無いので、始める前からなんとなく緊張してしました。
とりあえずGitHubでnixpkgsのリポジトリをForkをしようと思ったら、過去に(NixOS 16.09の頃)すでにForkしたリポジトリがありました。
Forkしただけでとくに何かしてはいなかったです。なぜForkしたのか。
ForkしていたNixOS 16.09の頃のnixpkgsのリポジトリを[この記事](https://qiita.com/xtetsuji/items/555a1ef19ed21ee42873)を参考に最新の状態に更新しました。

Pull Requestの手順自体については、検索すればまとまった記事がいくつか出るので割愛します。([この記事](https://qiita.com/Commander-Aipa/items/d61d21988a36a4d0e58b)とか)

# Pull Request
顛末自体は、[ここ](https://github.com/NixOS/nixpkgs/pull/33123)に記録が残っているので読めばわかりますが、本題なので一応何か書いておきます。

最初に送った変更はこんな感じでした。
一応、送る前に```nix-shell -p pythonPackages.chainer -I ./nixpkgs```等でもビルドの確認をしています。

```diff
diff --git a/pkgs/development/libraries/science/math/nccl/default.nix b/pkgs/development/libraries/science/math/nccl/default.nix
new file mode 100644
index 00000000000..6d7ffd2a70c
--- /dev/null
+++ b/pkgs/development/libraries/science/math/nccl/default.nix
@@ -0,0 +1,43 @@
+{ stdenv, pkgs, fetchurl, cudatoolkit }:
+
+with pkgs;
+
+stdenv.mkDerivation rec {
+  name = "cudatoolkit-${cudatoolkit.majorVersion}-nccl-${version}";
+  version = "1.3.4-1";
+
+  src = fetchurl {
+    url = "https://github.com/NVIDIA/nccl/archive/v${version}.tar.gz";
+    sha256 = "117qz8zvc4r0zjd737knr482gf208v5xlwyrpaf8pcjvam2fpr0i";
+  };
+
+  nativeBuildInputs = [
+    gcc5
+    eject
+  ];
+
+  propagatedBuildInputs = [
+    cudatoolkit
+  ];
+
+  BUILDDIR = "./";
+  PREFIX = "$out";
+  CUDA_HOME = "${cudatoolkit}";
+  CUDA_LIB = "${cudatoolkit.lib}/lib";
+
+  postInstall = ''
+    cp -r ./lib $out
+    cp -r ./include $out
+  '';
+
+  meta = with stdenv.lib; {
+    description = ''
+      NVIDIA Collective Communications Library.
+      Multi-GPU and multi-node collective communication primitives.
+    '';
+    homepage = "https://developer.nvidia.com/nccl";
+    license = licenses.bsd3;
+    platforms = [ "x86_64-linux" ];
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/chainer/default.nix b/pkgs/development/python-modules/chainer/default.nix
new file mode 100644
index 00000000000..3997e64c7b6
--- /dev/null
+++ b/pkgs/development/python-modules/chainer/default.nix
@@ -0,0 +1,34 @@
+{ stdenv, pkgs, lib
+, python, buildPythonPackage, fetchPypi
+, numpy, cudaSupport ? false
+}:
+
+with pkgs;
+with python.pkgs;
+
+buildPythonPackage rec {
+  name = "${pname}-${version}";
+  pname = "chainer";
+  version = "3.2.0";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "0mbc8kwk7pvg03bf0j57a48gr6rsdg4lzmyj0dak8y2l4lmyskpw";
+  };
+
+  propagatedBuildInputs = [
+    filelock
+    protobuf
+    numpy
+  ] ++ lib.optionals cudaSupport [ cupy ];
+
+  # In python3, test was failed...
+  doCheck = !isPy3k;
+
+  meta = with stdenv.lib; {
+    description = "A flexible framework of neural networks for deep learning";
+    homepage = "https://chainer.org/";
+    license = licenses.mit;
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/cupy/default.nix b/pkgs/development/python-modules/cupy/default.nix
new file mode 100644
index 00000000000..597bb2febd7
--- /dev/null
+++ b/pkgs/development/python-modules/cupy/default.nix
@@ -0,0 +1,48 @@
+{ stdenv, pkgs, python, buildPythonPackage
+, fetchPypi, isPy3k, numpy
+, cudatoolkit, cudnn, nccl
+}:
+
+with pkgs;
+with python.pkgs;
+
+buildPythonPackage rec {
+  name = "${pname}-${version}";
+  pname = "cupy";
+  version = "2.2.0";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "0si0ri8azxvxh3lpm4l4g60jf4nwzibi53yldbdbzb1svlqq060r";
+  };
+
+  nativeBuildInputs = [
+    gcc5
+  ];
+
+  propagatedBuildInputs = [
+    cudatoolkit
+    cudnn
+    linuxPackages.nvidia_x11
+    nccl
+    fastrlock
+    numpy
+    six
+    wheel
+  ];
+
+  CUDA_PATH = "${cudatoolkit}";
+  CFLAGS = "-I ${cudnn}/include -I ${nccl}/include";
+  LDFLAGS = "-L ${cudnn}/lib -L ${nccl}/lib";
+
+  # In python3, test was failed...
+  doCheck = !isPy3k;
+
+  meta = with stdenv.lib; {
+    description = "A NumPy-compatible matrix library accelerated by CUDA";
+    homepage = "https://cupy.chainer.org/";
+    license = licenses.mit;
+    platforms = [ "x86_64-linux" ];
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/fastrlock/default.nix b/pkgs/development/python-modules/fastrlock/default.nix
new file mode 100644
index 00000000000..fb2c12b0db3
--- /dev/null
+++ b/pkgs/development/python-modules/fastrlock/default.nix
@@ -0,0 +1,19 @@
+{ stdenv, buildPythonPackage, fetchPypi }:
+
+buildPythonPackage rec {
+  name = "${pname}-${version}";
+  pname = "fastrlock";
+  version = "0.3";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "00mr9b15d539z89ng5nf89s2ryhk90xwx95jal77ma0wslixrk5d";
+  };
+
+  meta = with stdenv.lib; {
+    homepage = "https://github.com/scoder/fastrlock";
+    description = "A fast RLock implementation for CPython";
+    license = licenses.mit;
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/filelock/default.nix b/pkgs/development/python-modules/filelock/default.nix
new file mode 100644
index 00000000000..38f1c594716
--- /dev/null
+++ b/pkgs/development/python-modules/filelock/default.nix
@@ -0,0 +1,19 @@
+{ stdenv, buildPythonPackage, fetchPypi }:
+
+buildPythonPackage rec {
+  name = "${pname}-${version}";
+  pname = "filelock";
+  version = "2.0.13";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "1n67dw7np5gsy5whynyk8c46pjlr353d6j9735p5gryaszkpjl6h";
+  };
+
+  meta = with stdenv.lib; {
+    homepage = "https://github.com/benediktschmitt/py-filelock";
+    description = "A platform independent file lock for Python";
+    license = licenses.unlicense;
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}  
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index dc415f624c8..9b8a10cbcdc 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -3555,6 +3555,10 @@ with pkgs;
 
   nbd = callPackage ../tools/networking/nbd { };
 
+  nccl = callPackage ../development/libraries/science/math/nccl {
+    cudatoolkit = cudatoolkit8;
+  };
+
   ndjbdns = callPackage ../tools/networking/ndjbdns { };
 
   ndppd = callPackage ../applications/networking/ndppd { };
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index f425005c2fe..3cc29a7f2a7 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -1462,6 +1462,9 @@ in {
       substituteInPlace setup.py --replace "argparse" ""
     '';
 
+  chainer = callPackage ../development/python-modules/chainer {
+    cudaSupport = pkgs.config.cudaSupport or false;
+  };
 
   channels = callPackage ../development/python-modules/channels {};
 
@@ -1562,6 +1565,12 @@ in {
 
   cufflinks = callPackage ../development/python-modules/cufflinks { };
 
+  cupy = callPackage ../development/python-modules/cupy {
+    cudatoolkit = pkgs.cudatoolkit8;
+    cudnn = pkgs.cudnn6_cudatoolkit8;
+    nccl = pkgs.nccl;
+  };
+
   cx_Freeze = callPackage ../development/python-modules/cx_freeze {};
 
   cvxopt = buildPythonPackage rec {
@@ -8143,6 +8152,8 @@ in {
 
   fastimport = callPackage ../development/python-modules/fastimport { };
 
+  fastrlock = callPackage ../development/python-modules/fastrlock {};
+
   feedgen = callPackage ../development/python-modules/feedgen { };
 
   feedgenerator = callPackage ../development/python-modules/feedgenerator {
@@ -8261,6 +8272,8 @@ in {
     };
   };
 
+  filelock = callPackage ../development/python-modules/filelock {};
+
   fiona = callPackage ../development/python-modules/fiona { gdal = pkgs.gdal; };
 
   flake8 = callPackage ../development/python-modules/flake8 { };
```

前回のDerivationコードと大きく異なるところで言うと、metaのステートメントが増えています。
この部分は見た通り、パッケージのソフトウエアのライセンスやメンテナ等の情報を記入するところです。

さて、ここからいろいろなツッコミが入りまして修正を加えていくことになりました。
以下のようなツッコミをいただきました。

### もらったReview

- pkgs、python.pkgsは使うな。使用するパッケージは明記するべし。
- GitHubからソースをダウンロードする場合はfetchFromGitHubが推奨される。
- URLは""で囲むな。

このあたりはコーディング規約みたいな感じでしょうか。
ひょっとしたら、[マニュアル](https://nixos.org/nixpkgs/manual/)を読み込めばちゃんと書いてあるのかも知れません。
まだまだ、全然読めてないので精進したいです。

- cupyのビルド時のCUDA_PATH、CFLAGS、LDFLAGSは設定しなくても良いのでは？

はい、実際要りませんでした。
cupyビルド時にインストーラのpythonコードがPATHにあるライブラリとヘッダを探してくるので、
そのあたりはNixがうまいことやってくれたようです。

- ncclの環境変数設定もこっちの方が良い。![better-code.png](https://qiita-image-store.s3.amazonaws.com/0/226408/9b5f0f8a-a271-24dd-69a4-37438d4ff86e.png)

ごちゃごちゃしてたコードがスッキリしていくので非常にありがたいです。

- buildPythonPackageにnameステートメントは必要ない。

これには正直驚きました。たとえばflaskなんかでもnameステートメントがあるので要らないとは思わなかったです。

# Mergeへ
Reviewで指摘いただいた部分の修正をして、コードは以下のようになりました。

```diff
diff --git a/pkgs/development/libraries/science/math/nccl/default.nix b/pkgs/development/libraries/science/math/nccl/default.nix
new file mode 100644
index 00000000000..c4deccff5b7
--- /dev/null
+++ b/pkgs/development/libraries/science/math/nccl/default.nix
@@ -0,0 +1,41 @@
+{ stdenv, fetchFromGitHub
+, gcc5, eject, cudatoolkit
+}:
+
+stdenv.mkDerivation rec {
+  name = "cudatoolkit-${cudatoolkit.majorVersion}-nccl-${version}";
+  version = "1.3.4-1";
+
+  src = fetchFromGitHub {
+    owner = "NVIDIA";
+    repo = "nccl";
+    rev = "v${version}";
+    sha256 = "0fvnrfn572lc6i2a3xyhbifm53ivcrr46z6cqr3b0bwb1iq79m7q";
+  };
+
+  nativeBuildInputs = [
+    gcc5
+    eject
+  ];
+
+  propagatedBuildInputs = [
+    cudatoolkit
+  ];
+
+  makeFlags = [
+    "PREFIX=$(out)"
+    "CUDA_HOME=${cudatoolkit}"
+    "CUDA_LIB=${cudatoolkit.lib}/lib"
+  ];
+
+  meta = with stdenv.lib; {
+    description = ''
+      NVIDIA Collective Communications Library.
+      Multi-GPU and multi-node collective communication primitives.
+    '';
+    homepage = https://developer.nvidia.com/nccl;
+    license = licenses.bsd3;
+    platforms = [ "x86_64-linux" ];
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/chainer/default.nix b/pkgs/development/python-modules/chainer/default.nix
new file mode 100644
index 00000000000..06a455176a2
--- /dev/null
+++ b/pkgs/development/python-modules/chainer/default.nix
@@ -0,0 +1,36 @@
+{ stdenv, lib, python
+, buildPythonPackage, fetchPypi, isPy3k
+, filelock, protobuf, numpy, pytest, mock
+, cupy, cudaSupport ? false
+}:
+
+buildPythonPackage rec {
+  pname = "chainer";
+  version = "3.2.0";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "0mbc8kwk7pvg03bf0j57a48gr6rsdg4lzmyj0dak8y2l4lmyskpw";
+  };
+
+  checkInputs = [
+    pytest
+    mock
+  ];
+
+  propagatedBuildInputs = [
+    filelock
+    protobuf
+    numpy
+  ] ++ lib.optionals cudaSupport [ cupy ];
+
+  # In python3, test was failed...
+  doCheck = !isPy3k;
+
+  meta = with stdenv.lib; {
+    description = "A flexible framework of neural networks for deep learning";
+    homepage = https://chainer.org/;
+    license = licenses.mit;
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/cupy/default.nix b/pkgs/development/python-modules/cupy/default.nix
new file mode 100644
index 00000000000..6ac91c0aa25
--- /dev/null
+++ b/pkgs/development/python-modules/cupy/default.nix
@@ -0,0 +1,46 @@
+{ stdenv, python, buildPythonPackage
+, fetchPypi, isPy3k, linuxPackages, gcc5
+, fastrlock, numpy, six, wheel, pytest, mock
+, cudatoolkit, cudnn, nccl
+}:
+
+buildPythonPackage rec {
+  pname = "cupy";
+  version = "2.2.0";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "0si0ri8azxvxh3lpm4l4g60jf4nwzibi53yldbdbzb1svlqq060r";
+  };
+
+  checkInputs = [
+    pytest
+    mock
+  ];
+
+  nativeBuildInputs = [
+    gcc5
+  ];
+
+  propagatedBuildInputs = [
+    cudatoolkit
+    cudnn
+    linuxPackages.nvidia_x11
+    nccl
+    fastrlock
+    numpy
+    six
+    wheel
+  ];
+
+  # In python3, test was failed...
+  doCheck = !isPy3k;
+
+  meta = with stdenv.lib; {
+    description = "A NumPy-compatible matrix library accelerated by CUDA";
+    homepage = https://cupy.chainer.org/;
+    license = licenses.mit;
+    platforms = [ "x86_64-linux" ];
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/fastrlock/default.nix b/pkgs/development/python-modules/fastrlock/default.nix
new file mode 100644
index 00000000000..848f01d6e16
--- /dev/null
+++ b/pkgs/development/python-modules/fastrlock/default.nix
@@ -0,0 +1,18 @@
+{ stdenv, buildPythonPackage, fetchPypi }:
+
+buildPythonPackage rec {
+  pname = "fastrlock";
+  version = "0.3";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "00mr9b15d539z89ng5nf89s2ryhk90xwx95jal77ma0wslixrk5d";
+  };
+
+  meta = with stdenv.lib; {
+    homepage = https://github.com/scoder/fastrlock;
+    description = "A fast RLock implementation for CPython";
+    license = licenses.mit;
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}
diff --git a/pkgs/development/python-modules/filelock/default.nix b/pkgs/development/python-modules/filelock/default.nix
new file mode 100644
index 00000000000..fec81d95bd8
--- /dev/null
+++ b/pkgs/development/python-modules/filelock/default.nix
@@ -0,0 +1,18 @@
+{ stdenv, buildPythonPackage, fetchPypi }:
+
+buildPythonPackage rec {
+  pname = "filelock";
+  version = "2.0.13";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "1n67dw7np5gsy5whynyk8c46pjlr353d6j9735p5gryaszkpjl6h";
+  };
+
+  meta = with stdenv.lib; {
+    homepage = https://github.com/benediktschmitt/py-filelock;
+    description = "A platform independent file lock for Python";
+    license = licenses.unlicense;
+    maintainers = with maintainers; [ hyphon81 ];
+  };
+}  
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index dc415f624c8..9b8a10cbcdc 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -3555,6 +3555,10 @@ with pkgs;
 
   nbd = callPackage ../tools/networking/nbd { };
 
+  nccl = callPackage ../development/libraries/science/math/nccl {
+    cudatoolkit = cudatoolkit8;
+  };
+
   ndjbdns = callPackage ../tools/networking/ndjbdns { };
 
   ndppd = callPackage ../applications/networking/ndppd { };
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index f425005c2fe..3cc29a7f2a7 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -1462,6 +1462,9 @@ in {
       substituteInPlace setup.py --replace "argparse" ""
     '';
 
+  chainer = callPackage ../development/python-modules/chainer {
+    cudaSupport = pkgs.config.cudaSupport or false;
+  };
 
   channels = callPackage ../development/python-modules/channels {};
 
@@ -1562,6 +1565,12 @@ in {
 
   cufflinks = callPackage ../development/python-modules/cufflinks { };
 
+  cupy = callPackage ../development/python-modules/cupy {
+    cudatoolkit = pkgs.cudatoolkit8;
+    cudnn = pkgs.cudnn6_cudatoolkit8;
+    nccl = pkgs.nccl;
+  };
+
   cx_Freeze = callPackage ../development/python-modules/cx_freeze {};
 
   cvxopt = buildPythonPackage rec {
@@ -8143,6 +8152,8 @@ in {
 
   fastimport = callPackage ../development/python-modules/fastimport { };
 
+  fastrlock = callPackage ../development/python-modules/fastrlock {};
+
   feedgen = callPackage ../development/python-modules/feedgen { };
 
   feedgenerator = callPackage ../development/python-modules/feedgenerator {
@@ -8261,6 +8272,8 @@ in {
     };
   };
 
+  filelock = callPackage ../development/python-modules/filelock {};
+
   fiona = callPackage ../development/python-modules/fiona { gdal = pkgs.gdal; };
 
   flake8 = callPackage ../development/python-modules/flake8 { };
```

Pull Request自体は2018/12/28日に送ってその日のうちにReviewと修正、
2018/12/29にはMergeされたというくらいのスピードでした。

IRC Chat上でも色々話していて、そこにいた人がすぐにReviewしてくれたというのも良かったと思います。
またPull Requestとは別にChat上でcupyのビルドできるプラットフォームはlinux-x86_64だけなのかという話も上がったりしました。
(結局nixpkgsのcudatoolkit自体がlinux-x86_64のみに対応しているので、cupyもそうなることがわかりました。)
このようにnixpkgsはGitHubやIRC Chatでコミュニティが活発なので、Pull Requestでコミュニケーションを細かく取ることもできます。

# nix-channel unstableへ
nixpkgsのリポジトリへマージされた後、[nixpkgs-channels](https://github.com/NixOS/nixpkgs-channels)のnixos-unstableへcommitが行われ、
この時点でnix-channelにunstableを追加していればパッケージをダウンロードできるようになります。
![chainer-in-unstable.png](https://qiita-image-store.s3.amazonaws.com/0/226408/3a676e81-ce94-40d1-0939-38bafb1485e0.png)
というわけで、無事、chainerのパッケージをダウンロードできる形にできました。

今回は以上です。