FreeBSD Ports Collection における OPTIONS 機能の紹介 - OPTIONS_SET/OPTIONS_UNSET/NO_DIALOG の活用と実践 -

# はじめに
２０２０年にもなって、まだコンパイルしてるの？

と言われるくらい、現在のＦｒｅｅＢＳＤでは、ベースシステムはもちろんのこと、サードパーティ製アプリのバイナリパッケージの使用（インストール）が進んできました。
実際、軽くセットアップして使う分には十分過ぎるほどバイナリパッケージが用意されていると思います。
ただまぁ自分の場合、ガッチガチにカスタマイズして使ってる関係で、バイナリパッケージに満足することができず…と言う事情もあり、ポーツコレクションのカスタマイズ方法についてまとめてみました。

なおポート（FreeBSD ports）とは何？については参考文献をご覧ください。すみません。

## 動作対象環境
２０２０年現在、導入から５年以上経過していることもあり、現在サポート対象となっている全てのＦｒｅｅＢＳＤ環境で使用できます。まぁおおよそバージョン１０以降で考えてもらえばいいかと。

# ＯＰＴＩＯＮＳという仕組み
ＯＰＴＩＯＮＳ（`/usr/ports/Mk/bsd.options.mk`）という仕組みの導入[^備考１]により、ポートを使用してサードパーティ製アプリをコンパイルする時の、カスタマイズする方法が統一されました。

[^備考１]: ２０１２年０５月以降。もちろん仕組みが導入されてすぐ、全てのポートが対応したわけではないが、既に８年以上経過…。

これが導入される前後でＭａｋｅｆｉｌｅの書き方が変わっており、ＯＰＴＩＯＮＳ導入以前の仕組みは（ほぼ）残っていません[^備考２]。

[^備考２]: `WITHOUT_X11` 等のメジャーなカスタマイズ設定は…残って…いませ…ん（正確には間違った使い方です）。  f( --;

いや厳密に調べてみると残っているというか、使われてたりすることもありますが、あまりに特殊な、ハッキング的カスタマイズ用途で使われたりするので、今回はスルースキルを発動することにします。

## ＯＰＴＩＯＮＳとは
各ポートのカスタマイズ出来る項目のオン・オフを制御するための仕組みで、以下のコントロールを行うための仕組みを提供しています。

- 何をカスタマイズできるのか（オプション名）
- どのような機能であるか（解説）
- 何をデフォルトで、オンにするかオフするか（デフォルト値）
- 振る舞い（依存関係の明示やインストール方法等）についての補助

ポートメンテナーはこれらをＭａｋｅｆｉｌｅに記載することにより、利用者はオンオフを選ぶだけでカスタマイズできるようになっています。

## ダイアログでの選択
コンパイル時にダイアログが表示され、これらカスタマイズをどうするかインタラクティブに取捨選択ができます。
具体的には以下のコマンドにより設定できる項目となります[^備考３][^備考４]。

[^備考３]: `config` は設定、`showconfig` は現在の設定の閲覧、`rmconfig` は設定の削除。
[^備考４]: `-recursive` はそれぞれ依存関係を追って処理します。

```shell-session:コマンド実行例
make config
make config-recursive
make showconfig
make showconfig-recursive
make rmconfig
make rmconfig-recursive
```

特に、一度ダイアログで確定してしまった場合、`make config` を明示的に実行するか、あるいは、新しくオプションが追加されたときしか、ダイアログを表示することはありません。
またデフォルトに戻したい場合は `make rmconfig` とすることで、ダイアログで設定した内容が削除されます。

## グローバルな設定
`/etc/make.conf` 中に記載することで、大域的にカスタマイズ内容（オン・オフ）を定義することができます。
Ｘ１１が不要である場合や、圧縮アルゴリズムとしてＬＺ４を使用したいなど、全てのポートで統一的に設定を図りたい場合に設定します。

```makefile:/etc/make.conf&nbsp;の設定例
OPTIONS_SET+=   LZ4 LZO LZO2 LZMA ZLIB ZSTD BROTLI
OPTIONS_UNSET+= X11 CUPS GDBM TEST DEBUG TESTS
```

## グローバルなポート別設定
`/etc/make.conf` 中には更に、ポート別にカスタマイズを定義することができます。
たとえば、基本的にはドキュメント類はインストールしておきたいが、そのドキュメントをインストールするためにＴｅＸまでインストールし始める、みたいなケースではドキュメントのインストールを諦めたいのが心情かと思います。
そのようなケースでは `${OPTIONS_NAME}_SET` ないしは `${OPTIONS_NAME}_UNSET` で定義してやります。

```makefile:/etc/make.conf&nbsp;の設定例
emulators_open-vm-tools_UNSET= DOCS FUSE LIBNOTIFY
```

`${OPTIONS_NAME}` は各ポートで `make -VOPTIONS_NAME` を実行して得られる文字列となります。
上記の例のように、中には変数名に `-` や `+` を含むことがあるようですが、Ｍａｋｅｆｉｌｅ的には問題無いようで。

なおＯＰＴＩＯＮＳ導入当時 `OPTIONS_NAME` ではなく `UNIQUENAME` を使う、とありましたが、他の機構と衝突するとのことで、`OPTIONS_NAME` に変更になっています。

## bsd.options.mk でのデフォルト設定
bsd.options.mk の中で以下の設定についてはポート側のデフォルト設定に関係無く、有効化されています。

- `DOCS`
- `NLS`
- `EXAMPLES`
- `IPV6`

ダイアログの選択肢に `DOCS` があるのに、ポート側のデフォルト設定には `DOCS` が無いにもかかわらず、実際には `DOCS` がオンになっているのは、これの影響となります。

## ＯＰＴＩＯＮＳの優先順位
1. `/etc/make.conf` （`${OPTIONS_NAME}_SET_FORCE`／`${OPTIONS_NAME}_UNSET_FORCE`）
2. `/etc/make.conf` （`OPTIONS_SET_FORCE`／`OPTIONS_UNSET_FORCE`）
3. `make config`
4. `/etc/make.conf` （`${OPTIONS_NAME}_SET`／`${OPTIONS_NAME}_UNSET`）
5. `/etc/make.conf` （`OPTIONS_SET`／`OPTIONS_UNSET`）
6. `make WITH="オプション１ オプション２..." WITHOUT="オプション１ オプション２..."` と指定したケース[^備考５]
7. `bsd.options.mk`
8. `Makefile`／`Makefile.local`

[^備考５]: `WITH` で指定されたオプションは `SET` され、`WITHOUT` で指定されたオプションは `UNSET` されます。

なお、`_SET` が処理されてから `_UNSET` の処理が行われるので、優先順位は `_UNSET` ＞ `_SET` となり、`_UNSET` の効果の方が強くなります。

# カスタマイズ不要！
つまりパッケージシステムはカスタマイズ無しでコンパイルされてるはずで、ダイアログの表示無く処理されているはずです！ [^備考６]

[^備考６]: パッケージビルドシステムの運用について軽く調べたのですが、どう運用してるという情報は無く、使用しているツールと、カスタマイズなんてしてられないだろう？という予測からの,、個人的かつ無責任な断定。

実際にそのようなオプションはあります。`/etc/make.conf` に下記のように書いておけば、全てのポートのダイアログ表示無し（≒カスタマイズ無し）でコンパイル始めます。

```makefile:/etc/make.conf&nbsp;の設定例
NO_DIALOG= yes
```

実際にはダイアログの表示がスキップされるだけで、以前に実行した `make config` の結果や先に説明した `OPTIONS_SET`／`OPTIONS_UNSET` 等の設定が反映された上で処理されます。
そういう意味では完全にデフォルトではないですが、バイナリパッケージ互換の運用はこうすることで提供可能であることが分かるかと思います。

# ports-mgmt/portconf
[ｐｏｒｔｃｏｎｆ](https://www.freshports.org/ports-mgmt/portconf/) は `/usr/local/etc/ports.conf` という設定ファイルで各ポートのカスタマイズ設定を記述できるツールです。

インストール時に `/etc/make.conf` へのフックを入れ、`/usr/local/etc/ports.conf` → `/usr/local/libexec/portconf` → `/etc/make.conf` と連携することで、あたかも最初からそこに設定が書いてあったがごとく振る舞います。よってこのフックを `/etc/make.conf` から削除してはいけません。

やってることは設定ファイル中のポートディレクトリ名とカレントディレクトリを比較（グロブでマッチング）して、該当すればその内容を取り込む、という動きになります。

例えば下記の例では、`/usr/ports/databases/sqlite3` のディレクトリで `/usr/local/libexec/portconf` を実行してやれば、`|NO_DIALOG=yes` と表示されることが確認できると思います。

```:/usr/local/etc/ports.conf&nbsp;の設定例
databases/sqlite3: NO_DIALOG=yes
lang/perl5.*:      NO_DIALOG=yes
```

本例でのＳＱＬｉｔｅ３のように、かなり細かくカスタマイズできるのですが、バイナリ互換性を失うようなカスタムが後から入ることもあり（その場合デフォルトオフで入る）、下手にカスタマイズしない方がいい類いのポートとなっています。

## ＯＰＴＩＯＮＳとの使い分け
ＯＰＴＩＯＮＳの場合 `${OPTIONS_NAME}_SET`／`${OPTIONS_NAME}_UNSET` のように、ポート毎（`${OPTIONS_NAME}`）に `OPTIONS_SET`／`OPTIONS_UNSET` が明示できます。しかし `NO_DIALOG` のようなグローバルな名前だと、ポート個別に設定することはできません。よってそういうところでｐｏｒｔｃｏｎｆを使用します。

もちろん `${OPTIONS_NAME}_SET`／`${OPTIONS_NAME}_UNSET` を使用せず `OPTIONS_SET`／`OPTIONS_UNSET` を使用するために、`ports.conf` に記述してもいいのですが、ｐｏｒｔｃｏｎｆがインストールされてない環境で構築した場合の影響が多大なので、あまり影響ないところで使いこなすのが粋な使い方となります。

上記理解が済めば、それ以外の指定方法についても `pkg info -D portconf` を実行して、内容を読めば書けると思います。

# よくある質問とその答え

## Ｑ．ポートからコンパイルしたものとバイナリパッケージは共用運用できないの？
全てのポートを `NO_DIALOG` 付でかつカスタマイズ無しの時に可能です。
パッケージの反映が遅いな、ポートからコンパイルしちゃえ、という運用をしたいなら、カスタムしないようにしてください。

## Ｑ．カスタムしたポートとバイナリパッケージは共用運用できないの？
基本できないと思ってください。限定して運用すればできることがあるかもしれませんが、試行錯誤が必要です。またその試行錯誤の結果が今後も保証される保証はありません。
カスタマイズした場合に、その機能の有効または無効によって、あるべき機能が無かったり、欲しい機能が無いことがあえります。ポートの依存関係では分からず、インストール後（ランタイム）に判明する場合があります（バイナリ互換性が無い）。
この種の変更（カスタマイズ）はバージョンアップによる影響よりも大きいことがあります。

まあちょっと脅しになってしまいましたが、ミドルウェア（特にライブラリ）と言われるポートで「なければ」、アプリケーションと言われるポートで「あれば」、問題無く動くと思います。

## Ｑ．オンとオフ、`SET` と `UNSET` 何か飛躍があるような気がします！
Ａ．大丈夫だ。問題無い。その認識は正しい。ちゃんと説明するべきであるが疲れたので寝る。

# 参考文献
- [[FreeBSD-Ports-Announce] Configuring options in make.conf](https://lists.freebsd.org/pipermail/freebsd-ports-announce/2013-June/000062.html)
- アプリケーションのインストール - packages と ports [【日本語版】](https://www.freebsd.org/doc/ja_JP.eucJP/books/handbook/ports.html) [【英語版】](https://www.freebsd.org/doc/handbook/ports.html)[^備考７]
- Ports Collection の利用 [【日本語版】](https://www.freebsd.org/doc/ja_JP.eucJP/books/handbook/ports-using.html) [【英語版】](https://www.freebsd.org/doc/handbook/ports-using.html)[^備考７]
- [bsd.port.mk](https://github.com/freebsd/freebsd-ports/blob/master/Mk/bsd.port.mk)
- [bsd.port.options.mk](https://github.com/freebsd/freebsd-ports/blob/master/Mk/bsd.port.options.mk)
- [portconf](https://www.freshports.org/ports-mgmt/portconf/) 

[^備考７]: 日本語版の記述が古いため英語版を読むときの参考にしてください。特にｐｋｇｎｇについての記述は完全に無視してください（ｐｋｇｎｇになってからずいぶん時間が経過しているため）。