makepkg-template で PKGBUILD の管理をちょっとだけ楽にする
これはAizu Advent Calendar 2019の 7 日目の記事です。
情報系の大学生にとって最も満足度が高い OS は Arch Linux だという結果が 2019 年全日本まちカドまぞく選手権大会で出ましたね(当まぞく比)。ちなみに私は Arch ではなく Manjaro Linux を使っていますごめんなさい。
そんな Arch 系 OS 使いにとって PKGBUILD の管理は大きな悩みの種なのではないでしょうか。いやまじでみんなどうしてるんだ。自分は基本的にジェネレートしたものやイチから書いたものをまとめて雑多に GitHub の(プライベート)リポジトリに放り込んでいたのですが、その方法だとそこまで大きな問題にもならないものの微妙に気持ち悪い点がいくつかありました。
- ジェネレート/コピペしたコードを Git で管理することになる
- 稀にジェネレートしたコードを更に自分で編集する必要がある
- ジェネレートされたコードが汚くて読めない(それを Git に入れたくない)
- どのジェネレーター/バージョンで生成されたものなのか生成物(PKGBULID)を読んでも書いていない
- ジェネレーター/コピー元がバージョンアップしたら今までの生成物も一括でアップデートしたいがその方法がない
などなどです。今回はそんな悩みを全ては解決できないのですが、多分ちょっとは解決できる方法をひとつご紹介します。
makepkg-template とは
Arch なら標準で入っている1 PKGBUILD の生成ツールです。非常にシンプルな仕組みでテンプレートファイルから PKGBUILD の生成を行います。
例えば /usr/share/makepkg-template/gobin-1.template
に
pkgname=$_pkgname-git
arch=('i686' 'x86_64')
pkgver() {
cd "$srcdir/$_pkgname"
( set -o pipefail
git describe --long --tags 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' ||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
)
}
build(){
cd "$srcdir/$_pkgname"
GO111MODULE=on go build -o "$srcdir/$_pkgname/$_pkgname"
}
package() {
cd "$srcdir/$_pkgname"
install -Dm755 $_pkgname "$pkgdir"/usr/bin/$_pkgname
}
と書いておき、 gobin.template
からシンボリックリンクを張っておきます。
$ ln -s gobin-1.template /usr/share/makepkg-template/gobin.template
次にどこでもいいので PKGBUILD に
_pkgname=longcat
pkgver=r55.a092fb6
pkgrel=1
url="https://github.com/mattn/longcat"
depends=()
makedepends=('go')
source=("git+git://github.com/mattn/longcat")
sha1sums=('SKIP')
# template start; name=gobin
と書いて makepkg-template
と実行すると、以下のように先程のテンプレートファイルの内容が挿入されます。
_pkgname=longcat
pkgver=r55.a092fb6
pkgrel=1
url="https://github.com/mattn/longcat"
depends=()
makedepends=('go')
source=("git+git://github.com/mattn/longcat")
sha1sums=('SKIP')
# template start; name=gobin; version=1;
pkgname=$_pkgname-git
arch=('i686' 'x86_64')
pkgver() {
cd "$srcdir/$_pkgname"
( set -o pipefail
git describe --long --tags 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' ||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
)
}
build(){
cd "$srcdir/$_pkgname"
GO111MODULE=on go build -o "$srcdir/$_pkgname/$_pkgname"
}
package() {
cd "$srcdir/$_pkgname"
install -Dm755 $_pkgname "$pkgdir"/usr/bin/$_pkgname
}
# template end;
↑書き換えられた PKGBUILD
このまま makepkg -Acs && pacman -U *.tar.xz
等すれば longcat がインストール出来ます。
これだけです
くわしいはなし
本当に上記の機能しかなく説明すべきことはほぼないのですが、もう少し詳しくいくつかお話しておきます。
まず /usr/share/makepkg-template
ですが、これは makepkg-template が探しに行く標準のテンプレートディレクトリであり、 --template-dir
オプションで明示的に変更することが出来ます。
そして今回は gobin-1.template
というファイルを作りましたが、テンプレートファイルは ${テンプレート名}-${バージョン}.template
という名前にしなければならないというルールがあります。後述しますが PKGBUILD のほうでテンプレート名と同時に利用するバージョンを指定できるようになっています。
しかし今回はバージョンを指定せず gobin
テンプレートを利用したかと思います。このときのためにテンプレートと同じディレクトリに ${テンプレート名}.template
という名前で最新のバージョンを指したシンボリックリンクが必要だというルールがあります。 makepkg-template
コマンドはバージョンの指定がなかった場合にこのファイルを見に行きます。
次に PKGBUILD ですが、
# template input; name=${テンプレート名}; version=${バージョン}
という行を含めておくことでその行がテンプレートに置き換えられます。更に一度置き換えたものも、テンプレートファイルを更新した後にもう一度 makepkg-template
コマンドを実行することで更新することが出来ます。
なので make とかとも相性が良いです。ちなみに入力の PKGBUILD ファイルは必ずこのファイル名でなければならないわけではなく、 -p
オプションで明示することが出来ますし、更に -o
オプションを指定すれば元ファイルを変更するのではなく標準出力や別ファイルとして出力することが出来ます。
あとは man makepkg-template
を読んでください _(:3」∠)_
なぜ「全ては解決できない」のか
…というようなことをこの記事の最初の方に書いたのですが読んでくださっている方がいれば幸いです。冒頭で「PKGBUILD ジェネレーター(コピペ) + Git 管理の問題点」をざっくりと書き殴ったのですが、上記でいくつかの PKGBUILD ジェネレーターは pkgbuild-template で置き換えられそうということがわかっていただけたかと思います。少なくとも「既にある PKGBUILD をコピペして…」というやり方をしていた場合は、間違いなく便利になることと思います。
しかし全てのジェネレーターを駆逐できないのも事実です。説明したとおり pkgbuild-template はほぼ「文字列の置換」でしかなく、本当に限られた機能しか提供しないため、それだけではカバーできない部分も多くあります。例えば拙作の genpkgbuild-go は内部で「該当のソースリポジトリを clone し、 pkgver を計算する」ということをしていますが、それを pkgbuild-template だけで行うことは出来ません。
ただ pkgbuild-template が基本的なテンプレートの仕組みや、「テンプレートファイルを更新すれば、成果物も簡単に更新することができる」という便利さを最もシンプルな形で、かつ標準コマンドとして提供してくれているのも事実です。ジェネレーターや Git との共存の仕方はいろいろと考えられるので、ベストな方法をこれからも模索していきたいと思います。