WidowsやLinuxでGoCVを使用したGOプログラムをstatic buildしたい話

# 背景

自分の開発PC(Windows)上でGoCVを使ったプログラムを書いて「よっしゃ！ビルドして`.exe`を作って、他のWindowsユーザにも配布や！」と思ったとき、うまくいかなくてハマりました。


# 現象

開発PCには下記の記事の作業手順に従って環境構築を行いました。

https://qiita.com/Nekonun


ビルド対象の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`を使用しました。

https://qiita.com/Nekonun

当該バッチファイルの中では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
```

https://github.com/hybridgroup/gocv/issues/601



# Linux上でのStatic build

Linux 上で static buildを試しましたが、どうやってもうまくいきません

下記Issueで本不具合は報告されていますが、未だ解決していないようです。

https://github.com/hybridgroup/gocv/issues/601

私の環境では下記の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
```

# その他参考

https://github.com/hybridgroup/gocv/issues/728