2019年12月15日 (日)

俺氏、ついにWindowsでgo-oci8を使うことに成功する

0. 結果的にいろいろ理解不足だった

前回記事では進捗ダメです状態で終わっていたのだけど、あれからいろいろいじり倒してどうにか形にすることができた。

まず前回記事の最後にある

  > go get github.com/mattn/go-oci8
  # github.com/mattn/go-oci8
  In file included from GOPATH\src\github.com\mattn\go-oci8\cHelpers.go:3:0:
  ./oci8.go.h:1:17: fatal error: oci.h: No such file or directory
  compilation terminated.

について。これはoci8.pcのインクルードパスの書き方に問題があったようで、InstantClient側の階層を一つ下げるとともに、WindowsのパスセパレータであるバックスラッシュをUnix系OSで使われるスラッシュに置き換えることで事なきを得た。

  orasdk=C:/bin/instantclient_19_3/sdk
  gcc=C:/bin/TDM-GCC-64

  oralib=${orasdk}/lib/msvc
  orainclude=${orasdk}/include

  gcclib=${gcc}/lib
  gccinclude=${gcc}/include

  glib_genmarshal=glib-genmarshal
  gobject_query=gobject-query
  glib_mkenums=glib-mkenums

  Name: oci8
  Description: oci8 library
  Libs: -L${gcclib} -L${oralib} -loci
  Libs.private:
  Cflags: -I${orainclude} -I${gccinclude}
  Version: 19.3.0

1. 最終的にどうしたのか

どうやら、最初から以下のようにしておけばよかった、ということらしい。

  1. GCCをMinGW GCCの最新版にする
  2. MinGWと同じツールチェインで作られているGTK+由来のpkg-configを追加する
  3. oci8.pcをUnix形式のパスセパレータ表記で作成する

1-1. GCCをMinGW GCCの最新版にする

推奨品という触れ込みのTDM-GCCだが、上記のoci8.pcgo buildしてみると、

  C:\bin\instantclient_19_3\sdk\lib\msvc\oci.lib
  error adding symbols: File in wrong format
  collect2.exe: error: ld returned 1 exit status

と出る。

ググると同じところで躓いていた人がおり、スレッドを追っていくことで原因が「GCCのバージョンが古いこと」だということにようやく気が付いた。

なので、SourceForgeのMinGW-w64 - for 32 and 64 bit WindowsにあるMinGW-w64のインストーラを使ってMinGWのツールチェインをインストールした。
今回はversion 8.1.0を使い、フレーバーはインストーラのデフォルトと思しきPOSIX/sehにした。

1-2. MinGWと同じツールチェインで作られているGTK+由来のpkg-configを追加する

MinGWにはokg-configは含まれないので、前回記事で入れたgettext、glib、pkg-configをMinGWのインストール先に上書きで統合するだけ。

1-3. oci8.pcをUnix形式のパスセパレータ表記で作成する

パスセパレータについては前述のとおり変更済みなので、GCCを変更したことに対応させる。
最終的にoci8.pcはこうなった。

  orasdk=C:/bin/instantclient_19_3/sdk
  gcc="C:/Program Files/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64"

  oralib=${orasdk}/lib/msvc
  orainclude=${orasdk}/include

  gcclib=${gcc}/lib
  gccinclude=${gcc}/include

  glib_genmarshal=glib-genmarshal
  gobject_query=gobject-query
  glib_mkenums=glib-mkenums

  Name: oci8
  Description: oci8 library
  Libs: -L${gcclib} -L${oralib} -loci
  Libs.private:
  Cflags: -I${orainclude} -I${gccinclude}
  Version: 19.3.0

この状態でgo installしてみると、特にエラーアウトプットはなし。どうやらいけたらしい。
Elwhjakuyaaahoa

で、動作確認をしてみる。
_example/dbms_outputは、発行したSQLの戻りに入っている文字列「hello」を表示するものなので、最終的なアウトプットに「hello」と出ていればいいはず。

  C:\Users\f97one\GOPATH\src\github.com\mattn\go-oci8\_example\dbms_output>set GO_OCI8_CONNECT_STRING=system/Orcl19cAdmin@//localhost:1521/ORCLCDB
  C:\Users\f97one\GOPATH\src\github.com\mattn\go-oci8\_example\dbms_output>go run -x main.go
  WORK=C:\Users\f97one\AppData\Local\Temp\go-build013517802
  mkdir -p $WORK\b001\
  cat >$WORK\b001\importcfg.link << 'EOF' # internal
  packagefile command-line-arguments=C:\Users\f97one\AppData\Local\go-build\58\5863c3505d965e6d20b9f8f592c519e220e5eed4b41183b13033d2e2374a1571-d
  packagefile database/sql=C:\Users\f97one\AppData\Local\go-build\62\628feb6160d48c5ea359bb61135be38aa5057fe7edbe4e99838bfb2c6089bbde-d
  packagefile fmt=c:\go\pkg\windows_amd64\fmt.a
  packagefile github.com/mattn/go-oci8=C:\Users\f97one\GOPATH\pkg\windows_amd64\github.com\mattn\go-oci8.a
  packagefile log=c:\go\pkg\windows_amd64\log.a
  packagefile os=c:\go\pkg\windows_amd64\os.a
  packagefile runtime=c:\go\pkg\windows_amd64\runtime.a
  packagefile context=c:\go\pkg\windows_amd64\context.a
  packagefile database/sql/driver=C:\Users\f97one\AppData\Local\go-build\bb\bbc0505de003812bb8e208b6f9c39c7ee40a4e3ade95c0d9d2db378042112a86-d
  packagefile errors=c:\go\pkg\windows_amd64\errors.a
  packagefile io=c:\go\pkg\windows_amd64\io.a
  packagefile reflect=c:\go\pkg\windows_amd64\reflect.a
  packagefile sort=c:\go\pkg\windows_amd64\sort.a
  packagefile strconv=c:\go\pkg\windows_amd64\strconv.a
  packagefile sync=c:\go\pkg\windows_amd64\sync.a
  packagefile sync/atomic=c:\go\pkg\windows_amd64\sync\atomic.a
  packagefile time=c:\go\pkg\windows_amd64\time.a
  packagefile unicode=c:\go\pkg\windows_amd64\unicode.a
  packagefile unicode/utf8=c:\go\pkg\windows_amd64\unicode\utf8.a
  packagefile internal/fmtsort=c:\go\pkg\windows_amd64\internal\fmtsort.a
  packagefile math=c:\go\pkg\windows_amd64\math.a
  packagefile bytes=c:\go\pkg\windows_amd64\bytes.a
  packagefile encoding/binary=c:\go\pkg\windows_amd64\encoding\binary.a
  packagefile io/ioutil=c:\go\pkg\windows_amd64\io\ioutil.a
  packagefile regexp=C:\Users\f97one\AppData\Local\go-build\d2\d2c97385147b6b2df852f8314ea0bc365ea5fd428c22cc28a7751caff46481e2-d
  packagefile strings=c:\go\pkg\windows_amd64\strings.a
  packagefile runtime/cgo=C:\Users\f97one\AppData\Local\go-build\05\050b0c52184a06b9e406b4908e746f8690cbeab9c2d8c629ec5358b2648f5d6f-d
  packagefile syscall=c:\go\pkg\windows_amd64\syscall.a
  packagefile internal/oserror=c:\go\pkg\windows_amd64\internal\oserror.a
  packagefile internal/poll=c:\go\pkg\windows_amd64\internal\poll.a
  packagefile internal/syscall/windows=c:\go\pkg\windows_amd64\internal\syscall\windows.a
  packagefile internal/testlog=c:\go\pkg\windows_amd64\internal\testlog.a
  packagefile unicode/utf16=c:\go\pkg\windows_amd64\unicode\utf16.a
  packagefile internal/bytealg=c:\go\pkg\windows_amd64\internal\bytealg.a
  packagefile internal/cpu=c:\go\pkg\windows_amd64\internal\cpu.a
  packagefile runtime/internal/atomic=c:\go\pkg\windows_amd64\runtime\internal\atomic.a
  packagefile runtime/internal/math=c:\go\pkg\windows_amd64\runtime\internal\math.a
  packagefile runtime/internal/sys=c:\go\pkg\windows_amd64\runtime\internal\sys.a
  packagefile internal/reflectlite=c:\go\pkg\windows_amd64\internal\reflectlite.a
  packagefile math/bits=c:\go\pkg\windows_amd64\math\bits.a
  packagefile internal/race=c:\go\pkg\windows_amd64\internal\race.a
  packagefile internal/syscall/windows/registry=c:\go\pkg\windows_amd64\internal\syscall\windows\registry.a
  packagefile path/filepath=c:\go\pkg\windows_amd64\path\filepath.a
  packagefile regexp/syntax=C:\Users\f97one\AppData\Local\go-build\09\09ed04a320a2025b1e0a177d0508fba26180bcc7e9a93f5b6b9a0215a3250c5a-d
  packagefile internal/syscall/windows/sysdll=c:\go\pkg\windows_amd64\internal\syscall\windows\sysdll.a
  EOF
  mkdir -p $WORK\b001\exe\
  cd .
  "c:\\go\\pkg\\tool\\windows_amd64\\link.exe" -o "C:\\Users\\f97one\\AppData\\Local\\Temp\\go-build013517802\\b001\\exe\\main.exe" -importcfg "C:\\Users\\f97one\\AppData\\Local\\Temp\\go-build013517802\\b001\\importcfg.link" -s -w -buildmode=exe -buildid=ZWzZXxPk1jGh5zBAmzMZ/JNrpGUoogY-8JjSab9MF/eSW2eCfLhSZIeYr8x4zF/ZWzZXxPk1jGh5zBAmzMZ -extld=gcc "C:\\Users\\f97one\\AppData\\Local\\go-build\\58\\5863c3505d965e6d20b9f8f592c519e220e5eed4b41183b13033d2e2374a1571-d"
  $WORK\b001\exe\main.exe
  hello

  C:\Users\f97one\GOPATH\src\github.com\mattn\go-oci8\_example\dbms_output>

今度は大丈夫そうである。

2. 終わりに

蓋を開けてみれば、GCCさえちゃんとしていれば何の苦労もなかった、ということなんだが、MSYS2のpacmanが挙動不審になるのを皮切りに、いろいろ大周りをする羽目になった。

| | コメント (0)

2019年12月14日 (土)

Windowsでmatn/go-oci8を使おうとしてとても苦労している話

0. 唐突にOracle Databaseを使おうと思い立った

Oracle Databaseを使ったシステムなど、お仕事くらいでしか使うことはなかったんだけど、Oracle Cloud Free Tierとして使えるDBのPaaSがOracle Databaseだけ、というので、かなり仕方なく使う、というネガティブな話だったりする。

Javaや.NETには公式のデータベースドライバが出ているので、それらを使えば簡単にDBを利用できるけど、せっかくなので今回はGoでやってみることにした。

なお、結論から書くとこの話、実現できてないorz

1. 環境

ビルドには以下が必要。

  • C/C++コンパイラ
  • pkg-config
  • プラットフォームとバージョンに応じたOracle InstantClient

このほか、当然のことながらGitやGoが必要。

1-1. MSYS2

僕のWindows PC(Windows 10 pro version 1909)にはSphinxでPDFをビルドするため、MSYS由来のGNU Makeとshが入っていたので、GCCを入れるためpacmanを起動。
しかしながら、なぜか何もフィードバックを出さずpacmanが途中で終了してしまい、パッケージのインストールもアップデートもできずハマる。

おかしい、version 1809のころはふつうにpacmanが動いていたはずなのに。

これでは何もできないので、MSYS2はあきらめることに。

1-2. Cygwin

ならば、ということでMSYS2をアンインストールしてCygwin x64を入れてみた。

Baseパッケージグループのほか、makeやGCCをCygwin Installerで一とおりインストールし、C:\cygwin64\binなどをWindowsのPath環境変数に追加していった。

Cygwin shellではなくDOS窓からgo get github.com/mattn/go-oci8とやったところ、

  # runtime/cgo
  gcc_libinit_windows.c: 関数 ‘x_cgo_sys_thread_create’ 内:
  gcc_libinit_windows.c:57:12: エラー: implicit declaration of function ‘_beginthread’; did you mean ‘OpenThread’? [-Werror=implicit-function-declaration]
    thandle = _beginthread(func, 0, arg);
              ^~~~~~~~~~~~
              OpenThread
  cc1: all warnings being treated as errors
  go: failed to remove work dir: GetFileInformationByHandle C:\cygwin64\tmp\go-build601027150\NUL: Incorrect function.

と出てビルドできず。後でググると本体のGitHub WikiInstallFromSource

Go does not support the Cygwin toolchain.

などど書かれていた。なん....だと....?

1-3. WSL

最近のエディションにはWSL(Windows Subsystem for Linux)という、読んで字のごとくLinux互換環境を提供するサブシステムが標準搭載されている。デーモンを常駐させられない以外はほぼLinuxそのままなので、こいつで頑張ってみることに。

僕は普段UbuntuのWSLを使っているけど、GCCもpkg-configもapt installで導入できるのでお手軽だ。

ということでapt installでGCCやらpkg-configやらをインストールしてgo getしてみる。
結果、エラー表示は出ず。

いけた、のか? ということで付属のexampleで動作確認。

  $ cd $GOPATH/src/github.com/mattn/go-oci8/_example/nls
  $ GO_OCI8_CONNECT_STRING=system/Orcl19cAdmin@//localhost:1521/ORCLCDB go run main.go
  ORA-12571: TNS:packet writer failure

パケット到達不能、だと....? まぁ、Oracle Databaseは親環境のDockerで動かしているとはいえ、WSLからDockerを制御できているので、親環境とは通信できているはずだが。

てなわけで、この方法もダメということに。

1-4. TDM-GCC

Goの公式としてはTDM-GCCを推奨しているようなので、Cygwin GCCをアンインストールしてこちらに切り替えてみた。

パッケージマネージャは付属していないので、pkg-configを自力でセットアップしなければならない。やり方自体はStackOverflowの記事を参考にやってみた。

一応、直リンクを置いておく。

準備自体はGistを残してくれている方がいたので、それを使わせてもらうことに。

  > go get github.com/mattn/go-oci8
  # pkg-config --cflags  -- oci8
  pkg-config: exit status 3221225595

3221225595をWindowsの電卓に通すと0xC000007Bになるのだが、pkg-configが正常起動できていない、ということのようだ。どうやらABIが違うようなので、win64ではなくwin32に変えることで正常起動するようになった。

で、再挑戦。

  > go get github.com/mattn/go-oci8
  # github.com/mattn/go-oci8
  In file included from GOPATH\src\github.com\mattn\go-oci8\cHelpers.go:3:0:
  ./oci8.go.h:1:17: fatal error: oci.h: No such file or directory
  compilation terminated.

....pkg-configがインクルードパスを正常に処理できていないようだ。もちろん、oci.hの場所は-Iオプションの行で指定しているんだが。

おわりに

最後のTDM-GCCパターンではあと一歩なような気もする。

そんな気はするんだが、MySQLやPostgreSQLのドライバだったり、GoではなくJavaを選択していた場合だと、もっと簡単にデータアクセスを実装できていたであろう、と考えると、「なぜ僕はこんな苦行を....?」などと考えてしまう。

| | コメント (0)