rpm/debを簡易的にMakefileで作成する

「パッケージングポリシーとか面倒臭いんじゃ社内用にちょっとしたファイルを所定のパスに配置するのに使いたいだけなんじゃ」という人のために。

「hello, world」を出力するhelloプログラムを/usr/local/bin/に配置する簡単な例を作ってみます。

```
$ uname -srm
Linux 6.5.0-25-generic x86_64
$ lsb_release -sd
Ubuntu 22.04.4 LTS
```

# 本文

## パッケージング対象

これらをパッケージにしていきます(hello.cは割愛)。
Makefileの`$(DESTDIR)`変数は以下を参考にしています。

https://www.gnu.org/prep/standards/html_node/DESTDIR.html

```
$ tree
.
├── Makefile
└── hello.c
```

```makefile: Makefile
BINARY := hello

.PHONY: all install clean rpm

all: $(BINARY)
install: $(BINARY)
        install -d $(DESTDIR)/usr/local/bin/
        install -m 0755 hello $(DESTDIR)/usr/local/bin/hello
clean:
        $(RM) $(BINARY)
```

この時点では`make`・`make install`すると/usr/local/bin/にhelloの実行ファイルがインストールされるだけです。

```
$ make
cc     hello.c   -o hello
$ sudo make install
install -d /usr/local/bin/
install -m 0755 hello /usr/local/bin/hello
$ hello
hello, world
```

## rpmターゲット
Ubuntuでrpmを作成するにはrpmパッケージが必要なのでまずはインストールします。

```
$ sudo apt update && sudo apt install rpm
```

rpmを作成出来る様に変更を加えます。

```
.
├── Makefile
├── hello.c
└── hello.spec
```

```makefile: Makefile
BINARY := hello

.PHONY: all install clean rpm

all: $(BINARY)
install: $(BINARY)
        install -d $(DESTDIR)/usr/local/bin/
        install -m 0755 hello $(DESTDIR)/usr/local/bin/hello
clean:
        $(RM) $(BINARY)

rpm: $(BINARY)
        make install DESTDIR=${PWD}/root
        rpmbuild --bb --buildroot ${PWD}/root \
                --define "%_topdir ${PWD}" \
                --define "%_builddir ${PWD}" \
                --define "%_buildrootdir ${PWD}/root" \
                --define "%_rpmdir ${PWD}/rpm" \
                --define "%_srcrpmdir ${PWD}/rpm" \
                hello.spec
        rm -rf root

```

```:hello.spec
Summary: hello world program
Name: hello
Version: 1.0
Release: 0
License: MIT
Packager: liqsuq <liqsuq@example.com>

%description
hello world program

%files
/usr/local/bin/hello
```

specファイルの各項目については以下を参照してください。

https://qiita.com/mypaceshun/items/5067561d6739cc9e5199

rpmではデフォルトでは~/rpmbuild/内にいくつかのディレクトリが作成されそこでビルドされるのですが、今回の様にMakefileでソースツリー内で作業するのには向いていません。なので内部変数をいじり、ソースツリー内に作業を行う様にしています。

またポイントとして、rpmターゲット内でinstallターゲットを呼び、ソースツリー内のrootディレクトリにインストールを行いこのディレクトリを元にrpm作成を行っています。rpmビルドではspecファイルにbuildセクションがあり、それを使うことも出来るのですが後述のdebターゲットと処理を共通化するために敢えてこうしています。

この状態で`make rpm`を行うと計算機のアーキテクチャに合わせたrpmファイルが作成されます。

```
$ make rpm
:
$ tree
.
├── Makefile
├── hello
├── hello.c
├── hello.spec
└── rpm
    └── x86_64
        └── hello-1.0-0.x86_64.rpm
```

:::note info
specファイルにBuildArch: noarchを追加すると〜.noarch.rpmと付くアーキテクチャに依存しないパッケージが作成されます。シェルスクリプトや設定ファイル等に便利でしょう。
:::

## debターゲット
debを作成出来る様に変更を加えます。

:::note warn
以下のtree出力中の`deb`はディレクトリです。dpkg-debの引数解釈の都合、ディレクトリが無いとパッケージファイル名と認識されてしますので予め作成しておきます。

:::

```
$ tree
.
├── DEBIAN
│   └── control
├── Makefile
├── deb
├── hello.c
└── hello.spec
```

```makefile: Makefile
BINARY := hello

.PHONY: all install clean rpm deb

all: $(BINARY)
install: $(BINARY)
        install -d $(DESTDIR)/usr/local/bin/
        install -m 0755 hello $(DESTDIR)/usr/local/bin/hello
clean:
        $(RM) $(BINARY)

rpm: $(BINARY)
        make install DESTDIR=${PWD}/root
        rpmbuild --bb --buildroot ${PWD}/root \
                --define "%_topdir ${PWD}" \
                --define "%_builddir ${PWD}" \
                --define "%_buildrootdir ${PWD}/root" \
                --define "%_rpmdir ${PWD}/rpm" \
                --define "%_srcrpmdir ${PWD}/rpm" \
                hello.spec
        rm -rf root

deb: $(BINARY)
        make install DESTDIR=${PWD}/root
        cp -r DEBIAN root/
        fakeroot dpkg-deb --build root deb
        rm -rf root
```

```:DEBIAN/control
Package: hello
Maintainer: liqsuq <liqsuq@example.com>
Architecture: amd64
Version: 1.0-0
Description: hello world program
```

こちらではrootディレクトリにインストールした後にDEBIANディレクトリをコピーしてdebの作成を行っています。DEBIANディレクトリをコピーする理由や各項目の説明は以下を参照してください。

https://blog.cybozu.io/entry/2016/05/16/111500


この状態で`make deb`を行うとDEBIAN/controlの`Archtecture:`に対応するdebファイルが作成されます。

```
$ make deb
:
$ tree
.
├── DEBIAN
│   └── control
├── Makefile
├── deb
│   └── hello_1.0-0_amd64.deb
├── hello
├── hello.c
└── hello.spec
```

:::note info
debではDEBIAN/controlに`Archtecture: all`と記述するとアーキテクチャに依存しないパッケージが作成されます。
:::

## ソースツリー
最終的なソースツリー及びMakefileは以下の様になります。

```
$ tree
.
├── DEBIAN
│   └── control
├── Makefile
├── deb
├── hello.c
└── hello.spec
```

```makefile: Makefile
BINARY := hello

.PHONY: all install clean rpm deb

all: $(BINARY)
install: $(BINARY)
        install -d $(DESTDIR)/usr/local/bin/
        install -m 0755 hello $(DESTDIR)/usr/local/bin/hello
clean:
        $(RM) $(BINARY)

rpm: $(BINARY)
        make install DESTDIR=${PWD}/root
        rpmbuild --bb --buildroot ${PWD}/root \
                --define "%_topdir ${PWD}" \
                --define "%_builddir ${PWD}" \
                --define "%_buildrootdir ${PWD}/root" \
                --define "%_rpmdir ${PWD}/rpm" \
                --define "%_srcrpmdir ${PWD}/rpm" \
                hello.spec
        rm -rf root
        
deb: $(BINARY)
        make install DESTDIR=${PWD}/root
        cp -r DEBIAN root/
        fakeroot dpkg-deb --build root deb
        rm -rf root
```

# 結び
後は必要に応じて依存関係やプリ/ポストスクリプトを記述してみてください。

debでアーキテクチャを決め打ちせずビルド中に自動判定するとかソースパッケージを作りたいとかがしたい場合には各パッケージングガイドを読んでみてください。自分は諦めました。特にdeb。

// ビルド中にDebianポリシーに違反していないか等のチェックが走るため軽率に使えない

https://www.debian.org/doc/manuals/debmake-doc/index.ja.html

https://rpm-packaging-guide.github.io/