背景
自分の開発PC(Windows)上でGoCVを使ったプログラムを書いて「よっしゃ!ビルドして.exe
を作って、他のWindowsユーザにも配布や!」と思ったとき、うまくいかなくてハマりました。
現象
開発PCには下記の記事の作業手順に従って環境構築を行いました。
ビルド対象のGoのプログラム(プログラム中でGoCVを使用している)があるフォルダで下記コマンドでビルドすると、同じフォルダ内に.exe
ファイルが出力されます。
> go build .
ここで生成された.exe
ファイルをそのまま他のWindows環境 (GoCVの環境未構築) に配布してその上で実行しても、途中でエラーが発生してアプリケーションが落ちます。具体的には GoCVを使用した処理が走った時点で落ちます。
理由
どうやら下記の二つが原因だったようです.
- OpenCVのビルド時に静的ライブラリ(.a)ではなく動的ライブラリ(.so)を生成してしまっていたこと
- goプログラムのビルド時に動的リンクでビルドしていたこと
OpenCVのビルド時には何も考えずにビルドすると動的ライブラリ(.so)が生成されます。今回の用途では静的リンクのために静的ライブラリ(.a)が必要です。
またGoCVはGoからC言語コードを呼び出すcgo
という仕組みを使っていますが、cgo
ではデフォルトでは動的リンクされるらしいです。
つまり、何も考えずにOpenCVとGoソースコードをビルドすると出力される.exe
は動的リンクモードで生成されます。生成された.exe
にはOpenCVのライブラリが含まれておらず、.exe
の実行時に動的に必要なライブラリ群がリンクされます。そのためOpenCVのライブラリが配置されているWindows環境では問題なく動きますが、当然何も環境構築を行っていない他のWindows環境に.exe
を配置して実行した場合、ライブラリの動的リンク時にライブラリが発見できないことでエラーとなります。
すなわちこの解決方法はOpenCVを静的ライブラリを生成するようビルドし、Goのソースコードを静的リンクモードでビルドして実行ファイルを生成することです。
解決策
下記の記事の手順でOpenCVのビルドを実施する際、バッチファイルであるwin_build_opencv.cmd
を使用しました。
当該バッチファイルの中ではcmakeを使用したビルドを行う前に下記の用な処理があります。これはつまり「第一引数に"static"と指定されていれば静的ライブラリを生成するようにビルドする」という挙動になっています。つまりstatic buildするためには、バッチファイル実行時に"static"引数を与えて静的ライブラリ(.a)を生成する必要があります。
if [%1]==[static] (
echo Build static opencv
set enable_shared=OFF
) else (
set enable_shared=ON
)
下記のIssueページでは以下の要領でOpenCVとプログラムをstatic buildするように報告されてます。
- static buildの実行
win_build_opencv.cmd static
- 実行ファイルの生成
go build -tags static -ldflags="-extldflags=-static" main.go
Linux上でのStatic build
Linux 上で static buildを試しましたが、どうやってもうまくいきません
下記Issueで本不具合は報告されていますが、未だ解決していないようです。
私の環境では下記のDockerfileでUbuntuをまず立て、
FROM ubuntu:20.04
# prepare
RUN apt update -y
RUN apt upgrade -y
RUN apt install -y build-essential sudo wget
# install go
ENV GOPATH /go
RUN wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
RUN rm -rf go1.21.5.linux-amd64.tar.gz
ENV PATH="/usr/local/go/bin:${PATH}"
# build opencv
RUN apt-get install -y git make sudo
RUN git clone https://github.com/hybridgroup/gocv.git
WORKDIR /gocv
ENV CGO_CPPFLAGS="-I/usr/local/include -I/usr/local/include/opencv4"
ENV CGO_LDFLAGS="-I/usr/local/include/opencv4 -L/usr/local/lib -L/usr/local/lib/opencv4/3rdparty -lopencv_gapi -lopencv_stitching -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dpm -lopencv_face -lopencv_fuzzy -lopencv_hfs -lopencv_img_hash -lopencv_line_descriptor -lopencv_quality -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_datasets -lopencv_text -lopencv_highgui -lopencv_dnn -lopencv_plot -lopencv_videostab -lopencv_video -lopencv_videoio -lopencv_xfeatures2d -lopencv_shape -lopencv_ml -lopencv_ximgproc -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core -littnotify -llibprotobuf -lIlmImf -lquirc -lippiw -lippicv -lade -lz -ljpeg -ldl -lm -lpthread -lrt -lopenblas -lquadmath -linference_engine_s -lpugixml -lngraph -lgfortran -ltbb"
その後コンテナにアタッチしてmake install BUILD_SHARED_LIBS=OFF
でOpenCVをビルド しました。その後 go build -tags static --ldflags '-extldflags "-static"' -o static_version ./cmd/version/main.go
でソースをstatic buildすると下記のようなエラーが発生しました。
一つ一つ足りないライブラリをインストールしようとしましたが一部(inference_engine_s
とtbb
)どうしてもインストール方法がわからず断念しました。。。
今のところLinux環境でstatic buildして配布は難しそうなので、Ansibleなどのプロビジョニングツールを使ってGoCV環境をLinux向けに構築するコードを書いた方が早そうです。
>go build -tags static --ldflags '-extldflags "-static"' -o static_version ./cmd/version/main.go
# command-line-arguments
/usr/local/go/pkg/tool/linux_amd64/link: running g++ failed: exit status 1
/usr/bin/ld: /usr/local/lib/libopencv_core.a(opencl_core.cpp.o): in function `opencl_check_fn(int)':
opencl_core.cpp:(.text._ZL15opencl_check_fni+0x11a): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/bin/ld: cannot find -lopenblas
/usr/bin/ld: cannot find -linference_engine_s
/usr/bin/ld: cannot find -lpugixml
/usr/bin/ld: cannot find -lngraph
/usr/bin/ld: cannot find -lgfortran
/usr/bin/ld: cannot find -ltbb
collect2: error: ld returned 1 exit status
その他参考