./configure --prefix=/usr/local
make -j 4
sudo make install
とする事で --prefix に指定したディレクトリにライブラリをインストールする事ができる。make -j は並列度のオプションである。
ところが一部のライブラリは automake/autoconf や CMake、meson/ninja によるビルドを前提としたものが存在する。
そこでライブラリを円滑に導入する為のいくつかの注意点について簡単に纏めておく。
実際の導入は FFmpeg をビルドする際に自動化したスクリプトで処理する事にするのでここでは省略する。
automake/autoconf によりビルドする環境の特徴は source code 中に Makefile.am や configure.ac が存在するという点である。 このビルド環境では Makefile や configure を自動的に生成することを目的としており、一般的には次のように行う。
autoreconf --install
automake
./configure --prefix=/usr/local
make -j 4
sudo make install
このようにして Makefile や configure を作成した上で ./configure や make を実行する事になる。 automake/autoconf を導入する際には、libtool を必要とするので予めインストールが必要である。
CMake によりビルドする環境の特徴は source code 中に CMakeLists.txt が存在する点である。 LLVM/Clang などのプロジェクトでも用いられているが、Makefile や configure が用意されている場合も多い。 CMake 環境の特徴の一つは configure は実行せず make を呼び出す点で、一般的には次のように行う。
cmake -DCMAKE_INSTALL_PREFIX=/usr/local
make -j 4
sudo make install
D で始まる独特のオプションを伴う事が一般的であり、vid.stab ライブラリで示した様に CMakeLists.txt に 独自に定義されたオプションによって ./configure で行われていた多彩なカスタマイズを可能としている。インストールにあたって特別必要な外部ライブラリはない。 また CMake においては Out-of-Tree build と呼ばれる CMakeLists.txt と異なるディレクトリで cmake/make の実行が要請されることも多く、 例えば CMakeLists.txt ファイルから見て ../build ディレクトリでビルドしたい場合は cmake ../build/ -DCMAKE_INSTALL_PREFIX=/usr/local などと指定する。
meson/ninja は比較的新しい Python3 を基盤としたビルド環境であり、その特徴は source code 中に meson.build が存在し、 なによりも CMake においても用いられた make に変わって ninja を呼び出す点である。一般的には次のように行う。
meson --prefix=/usr/local . ../build/
ninja
ninja install
meson においては Out-of-Tree build が標準的であり、上記の例は meson.build ファイルから見て ../build ディレクトリでビルドしたい場合のものである。 macOS では Python2.x しか標準でインストールされていないため、一般には Python3 を用意するところから行なわねばならない。 その際に Python3 は zlib がなければ zlib を必要とする標準ライブラリをインストールしてくれないため、明示的に zlib を示しておかねばならない。 故に zlib, Python3, setuptools, meson, re2c, ninja の順にインストールせねばならない。 setuptools は meson をビルドする際に必要となる Python3 で非常に一般的な拡張ライブラリであり、re2c は ninja が必要としている。 setuptools, re2c は python3 ./setup.py install とするだけでインストールが完了する。インストール先は当然ながら Python3 をインストールした --prefix 環境である。
#!/bin/sh
SDK_DIR="ffmpeg-20190529-d903c09-macos64-shared"
INSTALL_DIR="include"
SOURCE_CODE_DIR="ffmpeg-02333fe"
BUILD_DIR="build"
THREAD_NUM=8
OPTINONAL_CONFIG=""
CURRENT_DIR=`pwd`
export SDK_DIR
CONFIG=`ruby <<-EOF
dir = ENV["SDK_DIR"]
options = []
if File.exists?("#{dir}/README.txt")
fileHandle = File.open("#{dir}/README.txt", "r")
isConfig = false
while ! fileHandle.eof?
string = fileHandle.readline
if isConfig
if string[0, " --".length] == " --"
options.push(string[2, string.length - 4])
end
break if string[0, "Libraries:".length] == "Libraries:"
else
isConfig = true if string[0, "Configuration:".length] == "Configuration:"
end
end
end
print options.join(" ")
EOF`
TEMPORARY_INSTALL_DIR=/tmp
while [ -e ${TEMPORARY_INSTALL_DIR} ]
do
TEMPORARY_INSTALL_DIR=/tmp/`cat /dev/urandom | base64 | fold -w 10 | head -n 1`
done
mkdir ${TEMPORARY_INSTALL_DIR}
PATH=${TEMPORARY_INSTALL_DIR}/bin:${PATH}
export LDFLAGS=-L${TEMPORARY_INSTALL_DIR}/lib
export CFLAGS=-I${TEMPORARY_INSTALL_DIR}/include
export PKG_CONFIG_PATH=${TEMPORARY_INSTALL_DIR}/lib/pkgconfig
function CreateSDK ()
{
LIBS_LIST=(avcodec avdevice avfilter avformat avutil postproc swresample swscale)
if [ ! -e ${SDK_DIR}/${INSTALL_DIR} ]; then
mkdir ${SDK_DIR}/${INSTALL_DIR}
fi
for libName in "${LIBS_LIST[@]}"
do
mv ${TEMPORARY_INSTALL_DIR}/include/lib$libName ${SDK_DIR}/${INSTALL_DIR}/lib$libName
done
}
function ConfigFFmpeg ()
{
cd "${SOURCE_CODE_DIR}"
./configure ${CONFIG} --prefix=${TEMPORARY_INSTALL_DIR} ${OPTINONAL_CONFIG} #--extra-ldflags=-L${TEMPORARY_INSTALL_DIR}/lib --extra-cflags=-I/TEMPORARY_INSTALL_DIR}/include
make -j ${THREAD_NUM}
make install
cd "${CURRENT_DIR}"
}
function ProjectExtend ()
{
fileName=$1
fileExt=$2
fileExtender=$3
optExtender=$4
mkdir extended
if [ -z "${optExtender}" ]; then
${fileExtender} ${fileName}.${fileExt} -C extended
else
${optExtender} ${fileName}.${fileExt} | ${fileExtender} -C extended
fi
if [ -e extended/${fileName} ]; then
mv extended/${fileName} ${fileName}
rm -r extended
else
mv extended ${fileName}
fi
}
function ProjectBuild ()
{
fileName=$1
envOpt=$5
buildOpt=$6
makeType="make"
cd "${BUILD_DIR}"
ProjectExtend "$1" "$2" "$3" "$4"
cd "${fileName}"
if [ -n "${envOpt}" ]; then
eval ${envOpt}
fi
if [ ! -e configure ]; then
if [ -e Makefile.am ]; then
autoreconf --install
automake
fi
fi
if [ -e configure ]; then
./configure --prefix=${TEMPORARY_INSTALL_DIR} ${buildOpt}
elif [ -e CMakeLists.txt ]; then
cmake -DCMAKE_INSTALL_PREFIX=${TEMPORARY_INSTALL_DIR} ${buildOpt}
elif [ -e setup.py ]; then
python3 ./setup.py install
makeType=""
else
makeType=""
fi
if [ -n "${makeType}" ]; then
make -j ${THREAD_NUM}
make install
fi
cd ../
rm -r "${fileName}"
cd "${CURRENT_DIR}"
}
function ProjectTreeBuild ()
{
fileName=$1
envOpt=$5
buildOpt=$6
makeType="make"
cd "${BUILD_DIR}"
ProjectExtend "$1" "$2" "$3" "$4"
mkdir work
cd work
if [ -n "${envOpt}" ]; then
eval ${envOpt}
fi
if [ -e ../${fileName}/configure ]; then
../${fileName}/configure --prefix=${TEMPORARY_INSTALL_DIR} ${buildOpt}
elif [ -e ../${fileName}/CMakeLists.txt ]; then
cmake ../${fileName}/ -DCMAKE_INSTALL_PREFIX=${TEMPORARY_INSTALL_DIR} ${buildOpt}
elif [ -e ../${fileName}/meson.build ]; then
meson --prefix=${TEMPORARY_INSTALL_DIR} ../${fileName}/ .
ninja
ninja install
makeType=""
fi
if [ -n "${makeType}" ]; then
make -j ${THREAD_NUM}
make install
fi
cd ../
rm -r work
rm -r "${fileName}"
cd "${CURRENT_DIR}"
}
function CreateLibrary ()
{
ProjectBuild "pkg-config-0.29.2" "tar.gz" "tar -xzvf" "" "" "--with-internal-glib"
ProjectBuild "libtool-2.4.6" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "automake-1.16.1" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "autoconf-2.69" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "cmake-3.14.4" "tar.gz" "tar -xzvf"
ProjectBuild "zlib-1.2.11" "tar.gz" "tar -xzvf"
ProjectBuild "Python-3.7.3" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "setuptools-41.0.1" "zip" "echo" "" "cd ../; rm -r setuptools-41.0.1; unzip setuptools-41.0.1.zip; cd setuptools-41.0.1"
ProjectBuild "meson-0.50.1" "tar.gz" "tar -xzvf"
ProjectBuild "re2c-1.1.1" "tar.gz" "tar -xzvf"
ProjectBuild "ninja-master" "zip" "echo" "" "cd ../; rm -r ninja-master; unzip ninja-master.zip; cd ninja-master; python3 configure.py --bootstrap; mv ninja ${TEMPORARY_INSTALL_DIR}/bin/ninja"
ProjectBuild "gmp-6.1.2" "tar.bz2" "tar -xjvf"
ProjectBuild "nasm-2.14.02" "tar.gz" "tar -xzvf"
ProjectBuild "yasm-1.3.0" "tar.gz" "tar -xzvf"
ProjectBuild "nettle-3.4.1" "tar.gz" "tar -xzvf"
ProjectBuild "libtasn1-4.9" "tar.gz" "tar -xzvf" "" "CFLAGS=\"${CFLAGS} -Wno-sign-compare\"" ""
ProjectBuild "gnutls-3.6.8" "tar.xz" "tar xfv -" "xz -dckv" "" "--with-included-unistring --with-included-libtasn1 --without-p11-kit"
ProjectTreeBuild "aom-1.0.0" "tar.gz" "tar -xzvf"
ProjectBuild "freetype-2.10.0" "tar.bz2" "tar -xjvf"
ProjectBuild "fribidi-1.0.5" "tar.bz2" "tar -xjvf"
ProjectBuild "libass-0.14.0" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "libxml2-2.9.7" "tar.gz" "tar -xzvf"
ProjectBuild "fontconfig-2.13.1" "tar.bz2" "tar -xjvf"
ProjectBuild "libbluray-1.1.1" "tar.bz2" "tar -xjvf" "" "" "--disable-bdjava-jar"
ProjectTreeBuild "dav1d-master" "tar.bz2" "tar -xjvf"
ProjectBuild "lame-3.99.5" "tar.gz" "tar -xzvf" # https://forums.slimdevices.com/showthread.php?108552-Installation-of-LAME-under-MacOS-10-13-2-(High-Sierra)-for-use-with-Squeezebox
ProjectBuild "CUnit-2.1-2" "tar.bz2" "tar -xjvf" # https://qiita.com/from_chc/items/db771bef1e83fc00783a
ProjectTreeBuild "libmysofa-master" "zip" "echo" "" "cd ../; rm -r libmysofa-master; unzip libmysofa-master.zip; cd libmysofa-master"
ProjectBuild "opencore-amr-0.1.5" "tar.gz" "tar -xzvf"
ProjectBuild "openjpeg-2.3.1" "tar.gz" "tar -xzvf"
ProjectBuild "opus-1.3.1" "tar.gz" "tar -xzvf"
ProjectBuild "shine-master" "zip" "echo" "" "cd ../; rm -r shine-master; unzip shine-master.zip; cd shine-master"
ProjectBuild "snappy-master" "zip" "echo" "" "cd ../; rm -r snappy-master; unzip snappy-master.zip; cd snappy-master"
ProjectBuild "soxr-0.1.3-Source" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "speex-1.2.0" "tar.gz" "tar -xzvf"
ProjectBuild "libogg-1.3.3" "tar.xz" "tar xfv -" "xz -dckv"
ProjectBuild "libvorbis-1.3.6" "tar.xz" "tar -xjvf"
ProjectBuild "libtheora-1.1.1" "tar.bz2" "tar -xjvf"
ProjectBuild "twolame-0.3.13" "tar.gz" "tar -xzvf"
ProjectBuild "vid.stab-master" "zip" "echo" "" "cd ../; rm -r vid.stab-master; unzip vid.stab-master.zip; cd vid.stab-master" "-DUSE_OMP=NO"
ProjectBuild "vo-amrwbenc-master" "zip" "echo" "" "cd ../; rm -r vo-amrwbenc-master; unzip vo-amrwbenc-master.zip; cd vo-amrwbenc-master"
ProjectBuild "libvpx-refs_heads_master" "tar.gz" "tar -xzvf"
ProjectBuild "wavpack-5.1.0" "tar.bz2" "tar -xjvf"
ProjectBuild "libwebp-master" "zip" "echo" "" "cd ../; rm -r libwebp-master; unzip libwebp-master.zip; cd libwebp-master"
ProjectBuild "x264-snapshot-20190530-2245" "tar.bz2" "tar -xjvf" "" "" "--enable-shared"
ProjectBuild "x265_3.0" "tar.gz" "tar -xzvf" "" "cd source"
ProjectBuild "xvidcore" "tar.gz" "tar -xzvf" "" "cd build/generic"
ProjectBuild "zimg-release-2.8" "tar.gz" "tar -xzvf"
ProjectBuild "SDL2-2.0.9" "tar.gz" "tar -xzvf"
}
CreateLibrary
ConfigFFmpeg
CreateSDK
rm -r ${TEMPORARY_INSTALL_DIR}
このスクリプトは慣習的に sh を指定した宣言になっているが bash 固有の機能を呼び出している点に注意する。基本的には次にようなディレクトリ構成を前提としている。
./script.sh ./SDK_DIR/ ./SOURCE_CODE_DIR/ ./BUILD_DIR/ ./INSTALL_DIR/
SDK_DIR とは Zeranoe FFmpeg で配布されている、動的な共有ライブラリ形式でビルドされた FFmpeg を展開したディレクトリである。 FFmpeg をビルドする際に README.txt を読み取る事で配布されたバイナリと同じコンパイルオプションになる様にするためのもので、そのオプションは CONFIG に格納されている。 Ruby をヒアドキュメントで実行しているが README.txt が CRLF で改行されていることを前提にしたコードとなっている。 なお OPTINONAL_CONFIG は FFmpeg をビルドする際に追加で加えておきたいオプションを指定する定数である。
SOURCE_CODE_DIR はビルドしたい FFmpeg のソースコードを展開したディレクトリである。Zeranoe FFmpeg で配布物毎にリポジトリのスナップショット URL が示されているのでそれを参考に作成する。
BUILD_DIR は FFmpeg をビルドする際に使用するライブラリの圧縮ファイルを設置するディレクトリである。 ビルドする際に使用するライブラリのバージョンは function CreateLibrary で圧縮ファイルの名前として指定しているので、適宜書き換える必要性がある。 またこの圧縮ファイル名は展開された際に生成されるディレクトリ名と一致している必要性がある。 殆どの場合は tar.gz, tar.bz2 を前提としたコードとなっているが、tar.xz を導入する場合はパイプで流し込んで処理する。 この xz を利用する為に XZ Utils を予めインストールする必要性がある。 zip の場合は展開作業を行うことなく環境変数定義用に用意した function ProjectBuild の第五引数で展開する。
INSTALL_DIR は FFmpeg をビルド後にヘッダを抽出して格納するディレクトリである。一部のヘッダでビルド時のみ参照されると思われる config.h が参照されているのでコメントアウトすべき点に注意したい。 因みに Xcode で利用する際に .tbd ファイルが必要な場合は Xcode toolchains の ld が含まれているディレクトリに存在する tapi に対して tapi stubify libavcodec.58.dylib などとすると作成できる。
THREAD_NUM は make する際に指定する並列度であり、TEMPORARY_INSTALL_DIR はスクリプトを実行すると自動的に決定される作業用のディレクトリである。 TEMPORARY_INSTALL_DIR はこのスクリプトがライブラリをインストールする先であり、作業終了後には自動的に削除されるディレクトリである。
ProjectBuild/ProjectTreeBuild はライブラリの圧縮ファイルからライブラリを TEMPORARY_INSTALL_DIR へインストールするルーチンである。 引数は、ファイル名、拡張子、展開コマンド文字列、パイプを伴う展開コマンド文字列、ビルド前実行スクリプト文字列、ビルドオプション、以上の六つとなっている。 ビルドオプションは configure か cmake に対して渡される。
ninja は python3 configure.py --bootstrap を実行した上で生成された ninja ファイルをコピーして使用する。
nasm は FFmpeg で必要とされているが macOS のインストールされているものでは古い場合があるので FFmpeg に対して明示的に与えられる様に、PATH 変数の書き換えは TEMPORARY_INSTALL_DIR の bin を優先する。
libtasn1 は 4.9 の場合に -Wno-sign-compare を与えなければ整数比較の箇所で Clang がエラーを出してビルドできない問題がある。 最新版では meson が使われているが gnutls では内蔵されているので絶対に同じバージョンでなければならないわけではない。
libbruray は 1.1.1 の場合には bdjava オプションを指定しないことでビルドを通す様にする。
lame は最新版には ./configure が存在しないなどの問題があるので古いバージョンを導入する。
vid.stab はスレッド並列を実現する OpenMP を標準で前提としているが Clang は標準では対応していないので CMakeLists.txt に記載された DUSE_OMP に NO を与えることで無効化する。 Mac OS X 10.5 頃の Xcode で gcc を採用していた際は OpenMP に対応していたが、Apple は GCD を用いたスレッド並列を推奨していて、OpenMP が使えない事情がある。 LLVM/Clang も最近は公式のリソースをビルドすれば Intel の OpenMP ライブラリを組み込むことができる様になってきたが、今尚その手続きは複雑なものである。
x264 は標準で共有ライブラリが作られないので明治的にビルドオプションを指定する。 x265 や xvidcore では make するディレクトリまで移動する必要性があるので、ビルド前実行スクリプトで cd を指定する。