jq
とは
jq
コマンドは,便利なJSONパーサーとして(そして単なるパーサーを超えてプログラミング言語としても)知られています。jq
で何ができるかは,次のページが参考になるでしょう。
jq
のインストール法3選
さて,macOS に jq
をインストールする方法としては,次のような方法が考えられます。
- Homebrew や MacPorts といったパッケージ管理システムを利用する
- 公式配布バイナリを利用する
- 自分でソースコードからビルドする
既に Homebrew や MacPorts を使っている方であれば,1. が簡単でしょう。
$ brew install jq
$ sudo port install jq
パッケージ管理システムによるインストールの欠点
自分一人で使う分には Homebrew や MacPorts で困らないのですが,この jq
のバイナリを自作アプリに同梱して配布するなど,他のマシンへと移植する場合を考えると,バイナリのポータビリティが気になります。
ライブラリの依存性
MacPortsの場合
例えば,MacPorts でインストールされる /opt/local/bin/jq
が何に依存しているか,その依存ライブラリを調べてみましょう。otool
コマンドが役立ちます。
$ otool -L /opt/local/bin/jq
/opt/local/bin/jq:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.0.0)
/opt/local/lib/libonig.5.dylib (compatibility version 10.0.0, current version 10.0.0)
/opt/local/lib/
から始まるパスが現れていることから,この jq
バイナリは,MacPorts でインストールされる oniguruma
ライブラリ(これは正規表現ライブラリ鬼車です)に依存してしまっていることが分かります。これでは,この jq
バイナリを単に他のマシンにコピーするだけでは動きません。
Homebrewの場合
Homebrew でインストールされる /usr/local/bin/jq
についても同様に,Homebrew の依存関係解決に基づきインストールされる oniguruma
ライブラリの存在に依拠しています。
$ otool -L /usr/local/bin/jq
/usr/local/bin/jq:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.100.3)
/usr/local/opt/oniguruma/lib/libonig.5.dylib (compatibility version 9.0.0, current version 9.0.0)
バイナリの対象アーキテクチャ
また,バイナリの対象アーキテクチャを調べてみましょう。file
コマンドが使えます。
$ file /opt/local/bin/jq
/opt/local/bin/jq: Mach-O 64-bit executable arm64
この MacPorts は Apple Silicon 上で実行しているので,arm64
バイナリが生成されています。これでは,Intel Mac 上にこのバイナリを持っていったときに実行できません。
逆に,x86_64
バイナリを Apple Silicon 上で実行するのは,Rosetta 2 をインストールしてあれば可能ではありますが,Rosetta 2 がインストールされているマシンでなければ動きませんし,動作パフォーマンスも下がるので,好ましくありません。
起動OSの最低バージョン
このバイナリが起動できる最低OSバージョンを調べてみます。otool -l
が役立ちます。
$ otool -l /opt/local/bin/jq | grep minos
minos 14.0
この MacPorts は,macOS 14 Sonoma 上で実行しているため,その上でデフォルトで生成されたバイナリは,起動できるOSバージョンの最低値が macOS 14 と設定されてしまっています。これでは,macOS 13 以前の環境に持っていったときに使えません。Homebrew の場合も同様に,実行しているホストOSを最低動作環境とするバイナリがインストールされます。
公式配布バイナリ
次に,jq
の公式配布バイナリの状況を調べてみます。公式サイトでは,AMD64
(Intel CPU) 向けバイナリと ARM64
(Apple Silicon) 向けバイナリがそれぞれ配布されています。
最新の jq Ver.1.7 の公式バイナリについて,それぞれの状況を調べてみましょう。
Intel CPU 向け公式バイナリ
$ file jq-macos-amd64
jq-macos-amd64: Mach-O 64-bit executable x86_64
$ otool -L jq-macos-amd64
jq-macos-amd64:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.100.3)
$ otool -l jq-macos-amd64 | grep minos
minos 13.0
Apple Silicon 向け公式バイナリ
$ file jq-macos-arm64
jq-macos-arm64: Mach-O 64-bit executable arm64
$ otool -L jq-macos-arm64
jq-macos-arm64:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.100.3)
$ otool -l jq-macos-arm64 | grep minos
minos 13.0
このように,Intel CPU 向けバイナリ,Apple Silicon 向けバイナリのどちらも,
- 単一アーキテクチャ用バイナリ
- 依存性はOS標準のライブラリのみ
- 要求OSバージョンは macOS 13 Ventura 以降
となっていることが分かります。macOS 13 Ventura は,本記事執筆時点では最新OSの1つ前のバージョンです。ちょっと要求水準が高めですね。
もっと広範囲の環境で動くようにしたい
他のマシンへのポータビリティを高めるべく,次のような条件を見たす jq
バイナリが欲しいです。
- Intel CPU / Apple Silicon のどちらでもネイティブ起動できる Universal Binary である
- OS標準のライブラリにのみしか依存しない
- できるだけ広い範囲のOS環境で起動できる
このようなバイナリを得るためには,ソースコードから自前で jq
をビルドする必要があるでしょう。
ソースコードからの jq
バイナリのビルド
公式サイトの説明によると,バイナリのビルド&インストール方法は次のように説明されています。
git clone --recursive https://github.com/jqlang/jq.git
cd jq
autoreconf -i
./configure
make
sudo make install
この案内に沿ってバイナリのビルドを進めましょう。
autoreconf
できるようにする
このためには,autoreconf
コマンドが必要です。そのためには,autoconf
をインストールしておきましょう。ここは Homebrew や MacPorts を使うのが簡単です。
$ brew install autoconf
$ sudo port install autoconf
続きをやってみます。
$ git clone --recursive https://github.com/jqlang/jq.git
$ cd jq
$ autoreconf -i
Can't exec "aclocal": No such file or directory at /usr/local/Cellar/autoconf/2.71/share/autoconf/Autom4te/FileUtils.pm line 274.
autoreconf: error: aclocal failed with exit status: 2
何やらエラーが出ました。これは autoreconf
は aclocal
も必要とするからです。このためには,automake
をインストールしておきましょう。aclocal
は automake
に付随してインストールされます。
$ brew install automake
$ sudo port install automake
改めて autoreconf
にチャレンジします。
$ autoreconf -i
src/Makefile.am:24: error: Libtool library used but 'LIBTOOL' is undefined
またエラーが出ました。今度は libtoool
をインストールします。
$ brew install libtool
$ sudo port install libtool
これで,autoreconf -i
が通るようになります。
まずはビルドしてみる
では,この状態でビルドしてみましょう。configure
& make
をしてみます。
$ ./configure --disable-shared \
--enable-static \
--with-oniguruma=builtin
$ make
--disable-shared --enable-static --with-oniguruma=builtin
の部分は,onigurumaなど外部ライブラリへの依存関係をなくすための指定です。
すると,同ディレクトリ内に jq
バイナリが生成されます。
まずは依存ライブラリを確認します。
$ otool -L jq
jq:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.0.0)
システム標準のライブラリのみを参照しており,期待通りです。
ただし,この方法でビルドするだけでは,
- ホストマシンと同じCPUアーキテクチャのみを動作対象とする
- ホストマシンのOSバージョンを最低動作要件とする
バイナリが生成されてしまいます。これでは困るので,対象アーキテクチャと最低動作要件を明示指定したバイナリを作りましょう。
そのためには,CFLAGS
, CXXFLAGS
, LDFLAGS
にオプションを付け加えます。
Apple Silicon 向けバイナリのビルド
Apple Silicon 搭載 Mac は,最低でも macOS 11 Big Sur です。よって,動作要件としては macOS 11 以上を指定すれば十分です。
clangの -arch
オプションによるクロスコンパイル機能を使えば,コンパイルを実行しているホストマシンのCPUが Intel CPU / Apple Silicon のいずれであっても,x86_64
/ arm64
の双方のバイナリを生成できます。
次のように,CFLAGS
, CXXFLAGS
, LDFLAGS
のオプションを指定して,autoreconf -i
を実行します。
$ export CFLAGS="-arch arm64 -mmacosx-version-min=11.0"
$ export CXXFLAGS="-arch arm64 -mmacosx-version-min=11.0"
$ export LDFLAGS="-arch arm64 -mmacosx-version-min=11.0"
$ autoreconf -i
次に configure
スクリプトを実行します。ただし,クロスコンパイルになる場合(ホストマシンのアーキテクチャと生成バイナリのアーキテクチャが異なる場合)は,configure
スクリプトの実行に,--host
オプションでホストマシンのアーキテクチャを明示指定しておく必要があります。ここでは,--host=$(uname -m)
によって,ホストマシンのアーキテクチャを常に明示指定しておくことにしましょう。
$ ./configure --disable-shared \
--enable-static \
--with-oniguruma=builtin \
--host=$(uname -m)
そしていよいよ make
します。既に make
を実行していたのであれば,一旦 make clean
しておきましょう。
$ make clean
$ make
そして,生成された jq
バイナリの状況をチェックします。
依存ライブラリのチェック
$ otool -L jq
jq:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.0.0)
対象アーキテクチャのチェック
$ file jq
jq: Mach-O 64-bit executable arm64
最低動作OS要件のチェック
$ otool -l jq | grep minos
minos 11.0
いずれも期待通りの結果となりました。このバイナリを別名で退避しておきます。
$ cp jq jq-arm64
Intel CPU 向けバイナリのビルド
Intel CPU の Mac は昔からあります。最初期のものは MacOS X 10.5 Leopard のあたりです。よって,動作要件を MacOS X 10.5 Leopard 以上とするバイナリを生成してみましょう。
$ export CFLAGS="-arch x86_64 -mmacosx-version-min=10.5"
$ export CXXFLAGS="-arch x86_64 -mmacosx-version-min=10.5"
$ export LDFLAGS="-arch x86_64 -mmacosx-version-min=10.5"
$ autoreconf -i
そして,configure
スクリプトを実行します。
$ ./configure --disable-shared \
--enable-static \
--with-oniguruma=builtin \
--host=$(uname -m)
そして make
します。
$ make clean
$ make
そして,生成された jq
バイナリの状況をチェックします。
依存ライブラリのチェック
$ otool -L jq
jq:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.0.0)
対象アーキテクチャのチェック
$ file jq
jq: Mach-O 64-bit executable x86_64
最低動作OS要件のチェック
最低動作OS要件のチェックは otool -l
で行えますが,少し昔のOS用のバイナリの場合,出力の形式が異なり,grep minos
で探せません。otool -l
の出力の中の LC_VERSION_MIN_MACOSX
という行の2行下の version
でチェックします。
$ otool -l jq
(中略)
cmd LC_VERSION_MIN_MACOSX
cmdsize 16
version 10.5
sdk 14.0
(後略)
すると,正しく MacOS X 10.5 Leopard を最低動作要件とするバイナリが生成されていることが分かります。
このバイナリを別名で退避しておきます。
$ cp jq jq-x86_64
Universal Binary として結合する
最後に,こうして得られた
- macOS 11.0 Big Sur 以上で動作する Apple Silicon 向けバイナリ
jq-arm64
- MacOS X 10.5 Leopard 以上で動作する Intel CPU 向けバイナリ
jq-x86_64
を Universal Binary として結合しましょう。バイナリの結合や分解には lipo
コマンドを使います。
$ lipo -create -output jq jq-arm64 jq-x86_64
こうして得られた jq
バイナリを確認します。
$ file jq
jq: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64]
jq (for architecture x86_64): Mach-O 64-bit executable x86_64
jq (for architecture arm64): Mach-O 64-bit executable arm64
確かに Universal Binary となっています!
これで,めでたくできるだけ広範囲の macOS 環境でネイティブ実行できる jq
バイナリが得られました!これならば,どこの環境に持っていっても確実に動作するので,安心して頒布可能です。以上のような,クロスコンパイルも用いた Universal Binary のビルド方法は,jq
に限らず,広く活用できることでしょう。
スクリプト化
ここまでの流れで確立した,jq
をソースコードからビルドし,できるだけ広範囲の macOS 環境でネイティブ実行できる Universal Binary を得るという流れを,1つのシェルスクリプトにまとめておきましょう。(autoconf
, autoreconf
が使える状態になっていることが前提t。)
#!/bin/bash -x
CONFIGURE_FLAGS=(
--with-oniguruma=builtin
--disable-shared
--enable-static
--host=$(uname -m)
)
export CC="/usr/bin/clang"
export CXX="/usr/bin/clang++"
export LD="/usr/bin/clang"
WORKDIR="$(mktemp -d)"
OUTDIR=$WORKDIR/output
rm -rf $WORKDIR
mkdir -p $OUTDIR
echo "Building in: $WORKDIR"
git clone --recursive https://github.com/jqlang/jq.git "$WORKDIR/jq"
cd "$WORKDIR/jq"
# 1. autoreconf の実行
autoreconf -i
# 2. arm64 ビルド (macOS 11 以上)
echo "Building arm64..."
export CFLAGS="-arch arm64 -mmacosx-version-min=11.0"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="$CFLAGS"
./configure "${CONFIGURE_FLAGS[@]}"
make clean
make
if [ $? -ne 0 ]; then
echo "Build failed: jq (arm64)"
exit 1
fi
cp jq "$WORKDIR/jq-arm64"
# 3. x86_64 ビルド (MacOS X 10.5 以上)
echo "Building x86_64..."
export CFLAGS="-arch x86_64 -mmacosx-version-min=10.5"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="$CFLAGS"
./configure "${CONFIGURE_FLAGS[@]}"
make clean
make
if [ $? -ne 0 ]; then
echo "Build failed: jq (x86_64)"
exit 1
fi
cp jq "$WORKDIR/jq-x86_64"
# 4. Universal Binary を作成
echo "Creating universal binary..."
cd $WORKDIR
lipo -create -output $OUTDIR/jq jq-arm64 jq-x86_64
strip $OUTDIR/jq
echo "Done! jq is now a Universal Binary."
open -R $OUTDIR/jq