debパッケージの仕組みと簡易作成方法

株式会社 日立製作所 OSSソリューションセンタ の山田陽介です。
私は業務や自宅でdebパッケージを使う機会があり、自前でパッケージを作りたい場合がありましたので、特定の用途で配布するための簡単な方法を試してみました。
本記事は執筆者の試行内容を記したものであり、所属企業およびThe Debian Projectの公式ドキュメントではありません。

# はじめに
Debian GNU/Linuxは歴史のあるLinuxディストビリューションの一つで、dpkg/aptコマンドによる堅牢なパッケージ管理の仕組みを持ちます。Debianプロジェクトについて知るには、[Debian小史](https://www.debian.org/doc/manuals/project-history/index.ja.html) がわかりやすいです。

本記事では、実行すると "Hello, world!" を表示するhelloコマンドのパッケージを確認・再作成し、それを参考に簡単なコマンドmyhelloを含むパッケージを作り、リポジトリを作成してみることにします。

## 検証環境
Debian 9.11

## 1. helloパッケージの取得

```
debian@debian9:~$ apt download hello
Get:1 http://security.debian.org stretch/updates/main amd64 hello amd64 2.10-1+deb9u1 [55.7 kB]
Fetched 55.7 kB in 0s (93.6 kB/s)
debian@debian9:~$ ls
hello_2.10-1+deb9u1_amd64.deb
```

## 2. helloパッケージをarコマンドで展開
arコマンドはbinutilsパッケージに含まれます。xz形式のファイルはxz-utilsパッケージをインストールすると展開できます。

```
debian@debian9:~$ sudo apt install binutils xz-utils
(略)
debian@debian9:~$ mkdir arx
debian@debian9:~$ cd arx
debian@debian9:~/arx$ ar -x ../hello_2.10-1+deb9u1_amd64.deb
debian@debian9:~/arx$ ls -RF
.:
control.tar.gz  data.tar.xz  debian-binary
```

## 2.1. debian-binaryファイルの中身：debファイル形式のバージョン
debian-binaryファイルにはdebファイルのバージョンが記入されています。

```
debian@debian9:~/arx$ cat debian-binary
2.0
```

## 2.2. data.tar.xzファイルの中身：インストールされるファイル
data.tar.xzファイルにはインストールされるファイルとディレクトリが含まれています。

```
debian@debian9:~/arx$ tar tJvf data.tar.xz
drwxr-xr-x root/root         0 2017-04-19 09:23 ./
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/bin/
-rwxr-xr-x root/root     23240 2017-04-19 09:23 ./usr/bin/hello
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/doc/
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/doc/hello/
-rw-r--r-- root/root      1868 2014-11-16 11:51 ./usr/share/doc/hello/NEWS.gz
-rw-r--r-- root/root      4526 2017-04-19 09:23 ./usr/share/doc/hello/changelog.Debian.gz
-rw-r--r-- root/root      4493 2014-11-16 12:00 ./usr/share/doc/hello/changelog.gz
-rw-r--r-- root/root      2264 2017-04-19 09:23 ./usr/share/doc/hello/copyright
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/info/
-rw-r--r-- root/root     11596 2017-04-19 09:23 ./usr/share/info/hello.info.gz
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/locale/
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/locale/bg/
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/locale/bg/LC_MESSAGES/
-rw-r--r-- root/root      1316 2017-04-19 09:23 ./usr/share/locale/bg/LC_MESSAGES/hello.mo
(言語毎のLocaleファイル群、中略)
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/man/
drwxr-xr-x root/root         0 2017-04-19 09:23 ./usr/share/man/man1/
-rw-r--r-- root/root       790 2017-04-19 09:23 ./usr/share/man/man1/hello.1.gz
```

## 2.3 control.tar.gzファイルの中身：パッケージ管理に関するファイル
control.tar.gzファイルにはパッケージ管理のために使われるファイルが入っています。

```
debian@debian9:~/arx$ tar tzvf control.tar.gz
drwxr-xr-x root/root         0 2017-04-19 09:23 ./
-rw-r--r-- root/root       763 2017-04-19 09:23 ./control
-rw-r--r-- root/root      3601 2017-04-19 09:23 ./md5sums
debian@debian9:~/arx$ tar xzf control.tar.gz
```

- md5sumsファイルの中身：各ファイルのmd5チェックサム

```:md5sums
0ca8168fe86a62dacef175833417d458  usr/bin/hello
9dbc678a728a0936b503c0aef4507f5d  usr/share/doc/hello/NEWS.gz
866ca4c97b7cb699060b56371ff1cfb2  usr/share/doc/hello/changelog.Debian.gz
31aa50363c73b22626bd4e94faf90da2  usr/share/doc/hello/changelog.gz
bf4bad78d5cf6787c6512b69f29be7fa  usr/share/doc/hello/copyright
c5162d14d046d9808bf12adac2885473  usr/share/info/hello.info.gz
0b430a48c9a900421c8d2a48e864a0a5  usr/share/locale/bg/LC_MESSAGES/hello.mo
(中略)
29b51e7fcc9c18e989a69e7870af6ba2  usr/share/man/man1/hello.1.gz
```

- controlファイルの中身：debianパッケージのコントロール情報

```:control
Package: hello
Version: 2.10-1+deb9u1
Architecture: amd64
Maintainer: Santiago Vila <sanvila@debian.org>
Installed-Size: 272
Depends: libc6 (>= 2.14)
Conflicts: hello-traditional
Breaks: hello-debhelper (<< 2.9)
Replaces: hello-debhelper (<< 2.9), hello-traditional
Section: devel
Priority: optional
Homepage: http://www.gnu.org/software/hello/
Description: example package based on GNU hello
 The GNU hello program produces a familiar, friendly greeting.  It
 allows non-programmers to use a classic computer science tool which
 would otherwise be unavailable to them.
 .
 Seriously, though: this is an example of how to do a Debian package.
 It is the Debian version of the GNU Project's `hello world' program
 (which is itself an example for the GNU Project).
```

controlファイルの記述方法については、[Debian 新メンテナーガイド](https://www.debian.org/doc/manuals/maint-guide/index.ja.html)の下記が参考になります。
[第4章 debian/ ディレクトリー以下に無くてはならないファイル](https://www.debian.org/doc/manuals/maint-guide/dreq.ja.html)

## 3. helloパッケージを簡易的に再作成する
[dpkg-deb](https://manpages.debian.org/stretch/dpkg/dpkg-deb.1.en.html) コマンドを使うと、簡易的にdebパッケージを作成することができます。パッケージ化したいディレクトリを作成し、そこにDEBIANディレクトリを作ってcontrolファイルを置き、dpkg-debコマンドを実行します。debian-binaryファイルは自動で作成されます。

```:workディレクトリを作ってその中にdata.tar.xzを展開
debian@debian9:~$ mkdir work
debian@debian9:~$ tar xJf arx/data.tar.xz -C work
```

```:workディレクトリにDEBIANディレクトリを作ってcontrol.tar.gzを展開
debian@debian9:~$ mkdir work/DEBIAN
debian@debian9:~$ tar xzf arx/control.tar.gz -C work/DEBIAN
```

```:ディレクトリ構造
debian@debian9:~$ sudo apt install tree
(略)
debian@debian9:~$ tree work
work
├── DEBIAN
│   ├── control
│   └── md5sums
└── usr
    ├── bin
    │   └── hello
    └── share
        ├── doc
        │   └── hello
        │       ├── changelog.Debian.gz
        │       ├── changelog.gz
        │       ├── copyright
        │       └── NEWS.gz
        ├── info
        │   └── hello.info.gz
        ├── locale
        │   ├── bg
        │   │   └── LC_MESSAGES
        │   │       └── hello.mo
        │   (略)
        └── man
            └── man1
                └── hello.1.gz

94 directories, 51 files
```

[fakeroot](https://manpages.debian.org/stretch/fakeroot/fakeroot.1.en.html) コマンド経由でdpkg-debコマンドを実行してdebファイルを作成します。fakerootを使うのは、ファイルやディレクトリのuid/gidが0(root)に見える状態でパッケージを作成するためです。下記が参考になります。
[社内利用のための deb パッケージング入門 - Cybozu Inside Out | サイボウズエンジニアのブログ](https://blog.cybozu.io/entry/2016/05/16/111500)


```
debian@debian9:~$ sudo apt install fakeroot
(略)
(元のファイルを退避)
debian@debian9:~$ mkdir orig_deb
debian@debian9:~$ mv hello_2.10-1+deb9u1_amd64.deb orig_deb
(debファイルを作成)
debian@debian9:~$ fakeroot dpkg-deb --build work .
dpkg-deb: building package 'hello' in './hello_2.10-1+deb9u1_amd64.deb'.
```

debファイルが作成されました。
インストール、実行、アンインストールをしてみます。

```
debian@debian9:~$ sudo dpkg -i hello_2.10-1+deb9u1_amd64.deb
Selecting previously unselected package hello.
(Reading database ... 30822 files and directories currently installed.)
Preparing to unpack hello_2.10-1+deb9u1_amd64.deb ...
Unpacking hello (2.10-1+deb9u1) ...
Setting up hello (2.10-1+deb9u1) ...
Processing triggers for man-db (2.7.6.1-2) ...
debian@debian9:~$ hello
Hello, world!
debian@debian9:~$ sudo dpkg -r hello
(Reading database ... 30872 files and directories currently installed.)
Removing hello (2.10-1+deb9u1) ...
Processing triggers for man-db (2.7.6.1-2) ...
debian@debian9:~$ hello
bash: /usr/bin/hello: No such file or directory
```

## 4. myhelloパッケージの作成

ディレクトリを作成して新規にmyhelloコマンドを作成します。

```
debian@debian9:~$ mkdir -p work2/DEBIAN
debian@debian9:~$ mkdir -p work2/usr/bin
debian@debian9:~$ vi work2/usr/bin/myhello
```

```shell:myhello
#!/bin/sh
echo "My hello"
```

実行権限をつけてmd5sums、controlファイルを作成します。

```
debian@debian9:~$ chmod 755 work2/usr/bin/myhello
debian@debian9:~$ cd work2
debian@debian9:~/work2$ md5sum usr/bin/myhello | tee DEBIAN/md5sums
1010d39cae50f43a6868c92656fe7f9e  usr/bin/myhello
```

DEBIANディレクトリを除く容量(kB)を確認します。24-8=16(kB)。

```
debian@debian9:~/work2$ du -k
8       ./usr/bin
12      ./usr
8       ./DEBIAN
24      .
debian@debian9:~/work2$ vi DEBIAN/control
```

controlファイルを作成します。Installed-Sizeに計算した容量を記入します。

```:control
Package: myhello
Version: 0.01-1+deb9u1
Architecture: amd64
Maintainer: Yosuke Yamada <yosuke.yamada.no@example.com>
Installed-Size: 16
Section: devel
Priority: optional
Homepage: http://www.example.com/
Description: example package of myhello
 Sample Program for deb package
 .
 This is a sample program.
```

debパッケージを作成します。

```
debian@debian9:~/work2$ cd ..
debian@debian9:~$ fakeroot dpkg-deb --build work2 .
dpkg-deb: building package 'myhello' in './myhello_0.01-1+deb9u1_amd64.deb'.
```

インストール、実行、アンインストールします。

```
debian@debian9:~$ sudo dpkg -i myhello_0.01-1+deb9u1_amd64.deb
Selecting previously unselected package myhello.
(Reading database ... 30822 files and directories currently installed.)
Preparing to unpack myhello_0.01-1+deb9u1_amd64.deb ...
Unpacking myhello (0.01-1+deb9u1) ...
Setting up myhello (0.01-1+deb9u1) ...
debian@debian9:~$ myhello
My hello
debian@debian9:~$ sudo dpkg -r myhello
(Reading database ... 30823 files and directories currently installed.)
Removing myhello (0.01-1+deb9u1) ...
```

## 5. HTTP経由のインストール
公開用のディレクトリを作成して、Packages.gzを作成します。

```
debian@debian9:~$ sudo mkdir -p /srv/apt-repo/pool
debian@debian9:~$ sudo cp myhello_0.01-1+deb9u1_amd64.deb /srv/apt-repo/pool/
debian@debian9:~$ cd /srv/apt-repo
1 debian@debian9:/srv/apt-repo$ apt-ftparchive packages pool | gzip | sudo dd of=Packages.gz bs=1M
0+1 records in
0+1 records out
464 bytes copied, 0.000759072 s, 611 kB/s
```

Packages.gzにはaptで管理される情報が格納されています。

```
debian@debian9:/srv/apt-repo$ zcat Packages.gz
Package: myhello
Architecture: amd64
Version: 0.01-1+deb9u1
Priority: optional
Section: devel
Maintainer: Yosuke Yamada <yosuke.yamada.no@example.com>
Installed-Size: 16
Filename: pool/myhello_0.01-1+deb9u1_amd64.deb
Size: 846
MD5sum: f1e46e42d9246e71745562956dd5c7d5
SHA1: f618061ba5268c0be99d08e59d65311d0d93afeb
SHA256: a113517e326254ec14660e627165696521e8d398bc1b5918b97efe741d31591f
SHA512: 70aefb94c8cfe9bc2323045b2605e88615334c7f1560b7af19c0096f46d3f57a4257c726c81f7b946bf0862ebb3fb6c0e1dedf72aaa34d09f5d205b0b212b7fe
Homepage: http://www.example.com/
Description: example package of myhello
 Sample Program for deb package
 .
 This is a sample program.
```

PythonでHTTPサーバを仮に立てて試すことにします。

```
debian@debian9:/srv/apt-repo$ sudo python3 -mhttp.server 80
Serving HTTP on 0.0.0.0 port 80 ...
```

sources.list.d以下のlistファイルにapt-lineを書き込みます。

```
debian@debian9:~$ cat <<EOF | sudo dd of=/etc/apt/sources.list.d/my-apt-repo.list
deb [trusted=yes] http://127.0.0.1/ ./
EOF

0+1 records in
0+1 records out
39 bytes copied, 0.000118685 s, 329 kB/s
```

apt updateを実行します。

```
debian@debian9:~$ sudo apt update
Ign:1 http://127.0.0.1 ./ InRelease
Ign:2 http://127.0.0.1 ./ Release
Ign:3 http://127.0.0.1 ./ Packages
Ign:4 http://127.0.0.1 ./ Translation-en_US
Ign:5 http://127.0.0.1 ./ Translation-en
Ign:3 http://127.0.0.1 ./ Packages
Ign:4 http://127.0.0.1 ./ Translation-en_US
Ign:5 http://127.0.0.1 ./ Translation-en
Ign:3 http://127.0.0.1 ./ Packages
Ign:4 http://127.0.0.1 ./ Translation-en_US
Ign:5 http://127.0.0.1 ./ Translation-en
Get:3 http://127.0.0.1 ./ Packages [464 B]
Ign:4 http://127.0.0.1 ./ Translation-en_US
Ign:5 http://127.0.0.1 ./ Translation-en
Ign:4 http://127.0.0.1 ./ Translation-en_US
Ign:5 http://127.0.0.1 ./ Translation-en
Ign:4 http://127.0.0.1 ./ Translation-en_US
Ign:5 http://127.0.0.1 ./ Translation-en
Ign:6 http://deb.debian.org/debian stretch InRelease
Get:7 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB]
Hit:8 http://deb.debian.org/debian stretch Release
Get:9 http://security.debian.org stretch/updates InRelease [94.3 kB]
Fetched 186 kB in 0s (398 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
All packages are up to date.
```

aptでインストールします。

```
debian@debian9:~$ sudo apt install myhello
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  myhello
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 846 B of archives.
After this operation, 16.4 kB of additional disk space will be used.
Get:1 http://127.0.0.1 ./ myhello 0.01-1+deb9u1 [846 B]
Fetched 846 B in 0s (0 B/s)
Selecting previously unselected package myhello.
(Reading database ... 30822 files and directories currently installed.)
Preparing to unpack .../myhello_0.01-1+deb9u1_amd64.deb ...
Unpacking myhello (0.01-1+deb9u1) ...
Setting up myhello (0.01-1+deb9u1) ...
debian@debian9:~$ myhello
My hello
debian@debian9:~$ sudo apt remove myhello
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages will be REMOVED:
  myhello
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 16.4 kB disk space will be freed.
Do you want to continue? [Y/n]
(Reading database ... 30823 files and directories currently installed.)
Removing myhello (0.01-1+deb9u1) ...
```

インストールと実行とアンインストールができました。

## ライセンス
- 本稿で使用したhello_2.10-1 debパッケージのライセンス全文は [hello_2.10-1_copyright](https://metadata.ftp-master.debian.org/changelogs/main/h/hello/hello_2.10-1_copyright) で参照できます。
- [GNU Hello 2.10](https://www.gnu.org/software/hello/manual/hello.html)のライセンスはGPLv3で、ライセンスはサイトにて参照できます。
- マニュアルのライセンスは[GNU Free Documentation License v1.3](https://www.gnu.org/licenses/fdl-1.3.en.html)です。

## 参考文献

- [社内利用のための deb パッケージング入門 - Cybozu Inside Out | サイボウズエンジニアのブログ](https://blog.cybozu.io/entry/2016/05/16/111500)<br>社内でdebパッケージを作成してリポジトリを公開する方法について簡潔にまとめられています。
- [debianmeetingresume201704.pdf](https://tokyodebian-team.pages.debian.net/pdf2017/debianmeetingresume201704.pdf)<br>東京エリアDebian勉強会の資料です。lintianツールを使って、Debianポリシーに適合しているか確認する方法などが丁寧に解説されています。
- [第7章 Debian パッケージ管理システムの基礎](https://www.debian.org/doc/manuals/debian-faq/pkg-basics.ja.html)<br>Debian公式FAQのパッケージ管理に関する章です。関連する用語や概念がまとめられています。
- [Debian -- Debian 開発者向けマニュアル](https://www.debian.org/doc/devel-manuals)<br>Debian公式の開発者向けのマニュアルへのリンク集です。
- [Debian 新メンテナーガイド](https://www.debian.org/doc/manuals/maint-guide/index.ja.html)<br>Debian公式のパッケージ作成手順です。順を追って詳細な説明がされています。

# おわりに
本稿で紹介したのは、あくまでも簡易的なパッケージの作成とインストールの方法です。Debianのリポジトリで公開したいパッケージを作成するためには、一定のルールに従ってパッケージを作成し、信頼されたメンテナにパッケージの公開を依頼する必要があります。その方法については、Debianの公式のドキュメントを参照してください。私は公式のリポジトリには貢献はできていませんが、いずれはコミュニティへの貢献も目指していきたいと思います。