Golang って Solaris でビルドできるのか調べてみた(気にすんなよ。くよくよすんなよ)


[Golang って Solaris ではどうなのか調べてみた](http://qiita.com/satokaz/items/e0ef0394da80752b1cbd)の続きになります。
ブートストラップに利用する Go 1.4.2 を Solaris 11.3 でビルドし、Go 1.5.3 を手に入れることがゴールになります。
(Solaris x86 環境が対象です)

# **作業ディレクトリ**
$HOME/go ディレクトリを作業などもろもろ利用するディレクトリとします。

```
$ mkdir $HOME/go
$ cd $HOME/go
```

# **ビルドの準備**

必要なパッケージをインストール。

```
# pkg install developer/versioning/git developer/gnu-binutils \
    developer/build/gnu-make developer/gcc-48
```

# **ソースコードの入手**

Go 1.4.3 と Go 1.5.2 を下記から入手し、$HOME/go ディレクトリに配置。

<https://golang.org/dl/>

```
$ wget https://storage.googleapis.com/golang/go1.4.3.src.tar.gz
$ wget https://storage.googleapis.com/golang/go1.5.2.src.tar.gz
```

# **まずは Go 1.4.3 のビルド**

アーカイブを展開し、go1.4.3 という名前に変更。

```
$ gtar xvzf go1.4.3.src.tar.gz
$ mv go go1.4.3
$ cd go1.4.3/src
$ PATH=/usr/gnu/bin:$PATH
$ export PATH
```
make.bash を実行

```
$ ./make.bash
# Building C bootstrap tool.
cmd/dist

# Building compilers and Go bootstrap tool for host, solaris/amd64.
lib9
libbio
liblink
:
testing/iotest
testing/quick
text/scanner


---
Installed Go for solaris/amd64 in /export/home/kazus/go/go1.4.3
Installed commands in /export/home/kazus/go/go1.4.3/bin
```

以上で、Go 1.4.3 のビルドは完了。

```
$ $HOME/go/go1.4.3/bin/go version
go version go1.4.3 solaris/amd64
```

このディレクトリをそのまま利用します。


# **Go 1.5.2 のビルド**

Go 1.5 のビルドには、Go 1.4.3 が必要になるので、展開した Go 1.4.3 のディレクトリを $GOROOT_BOOTSTRAP に設定します。

```
$ GOROOT_BOOTSTRAP=$HOME/go/go1.4.3; export GOROOT_BOOTSTRAP
```

アーカイブを展開し、go1.5.2 という名前に変更。

```
$ gtar xvzf go1.5.2.src.tar.gz
$ mv go go1.5.2
```

go1.5.2/src に移動して make.bash を実行

```
$ cd go1.5.2/src
$ ./make.bash

##### Building Go bootstrap tool.
cmd/dist

##### Building Go toolchain using /export/home/kazus/go/go1.4.3.
bootstrap/internal/obj
bootstrap/asm/internal/flags
:
:
---
Installed Go for solaris/amd64 in /export/home/kazus/go/go1.5.2
Installed commands in /export/home/kazus/go/go1.5.2/bin
```

以上で、Go 1.5.2 のビルドは完了。

```
$ $HOME/go/go1.5.2/bin/go version
go version go1.5.2 solaris/amd64
```


# **GOPATH と GOROOT**

ひとまず、出来上がった go1.5.2 の go コマンドに PATH を通しておきます。
これで、何も考えずに go コマンドを実行できるようになりました。

```
$ PATH=$HOME/go/go1.5.2/bin:$PATH; export PATH
```

続いて、ビルドした go コマンドが持っている環境変数を確認してみます。

```
$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="solaris"
GOOS="solaris"
GOPATH=""
GORACE=""
GOROOT="/export/home/kazus/go/go1.5.2"
GOTOOLDIR="/export/home/kazus/go/go1.5.2/pkg/tool/solaris_amd64"
GO15VENDOREXPERIMENT=""
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
```

$GOROOT が埋め込まれているをことを確認できるので、$GOPATH のみを設定します。
$GOPATH に指定されたディレクトリはワーキングディレクトリとして利用され、$GOPATH/src にパッケージ、pkg にビルドされたライブラリ、bin に実行可能バイナリなどが格納されます。
このあたりは、特にココ！という場所は無いようなのでお好きなようにって感じでしょうか。
自身で作成したパッケージなどの構成にも関わるので、じっくり調べてみるのも良いかと思います。

* [そろそろ真面目に Golang 開発環境について考える — GOPATH 汚染問題](http://qiita.com/spiegel-im-spiegel/items/73ebc684b5807277b7e2)


```
$ GOPATH=$HOME/go; export GOPATH
```

もう一度、`go env` を実行してみると、GOPATH に先ほど設定した $GOPATH の値が入っていることがわかります。

```
kazus@solaris:~/go$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="solaris"
GOOS="solaris"
GOPATH="/export/home/kazus/go"
GORACE=""
GOROOT="/export/home/kazus/go/go1.5.2"
GOTOOLDIR="/export/home/kazus/go/go1.5.2/pkg/tool/solaris_amd64"
GO15VENDOREXPERIMENT=""
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
```

# **Hello, World!**

[How to Write Go Code](https://golang.org/doc/code.html) などを参考に Hello,World! を実行してみます。
ソースはコピペで。

```
package main

import "fmt"

func main() {
	fmt.Printf("Hello, world.\n")
}
```

go run で実行できます。
(一時的にワーキングディレクトリに実行可能ファイルを作成し、実行後は消去します。)

```
$ go run hello.go
hello, world
```

実行可能なバイナリを作成するには、go build を利用します。

```
$ go build hello.go
$ ls -al
total 2312
drwxr-xr-x  2 kazus staff       4 Jan 11 16:24 .
drwxr-xr-x 14 kazus staff      23 Jan 11 16:21 ..
-rwxr-xr-x  1 kazus staff 2349856 Jan 11 16:24 hello
-rw-r--r--  1 kazus staff      77 Jan 11 16:22 hello.go

$ ./hello
hello, world

```

go build 時に -x オプションをつけることで、何が行われているか詳細を知ることができます。


```
$ go build -x hello.go
WORK=/tmp/go-build595923179
mkdir -p $WORK/command-line-arguments/_obj/
mkdir -p $WORK/command-line-arguments/_obj/exe/
cd /export/home/kazus/go/test
/export/home/kazus/go/go1.5.2/pkg/tool/solaris_amd64/compile -o $WORK/command-line-arguments.a -trimpath $WORK -p main -complete -buildid f68da26c4308377ead5ac41ebd2a70f05db675c6 -D _/export/home/kazus/go/test -I $WORK -pack ./hello.go
cd .
/export/home/kazus/go/go1.5.2/pkg/tool/solaris_amd64/link -o $WORK/command-line-arguments/_obj/exe/a.out -L $WORK -extld=gcc -buildmode=exe -buildid=f68da26c4308377ead5ac41ebd2a70f05db675c6 $WORK/command-line-arguments.a
cp $WORK/command-line-arguments/_obj/exe/a.out hello
```

# まとめ
以上で Go 1.5 の環境を手に入れることができました。
あとは、もう好きにやっちゃってください。

下記は、お時間あればどうぞ。

# **ポータビリティ問題?**

platform 依存の low-level interactions を利用した実装が多く見られるので、ポータビリティが損なわれているんじゃないか問題を Solaris 観点で。
確かに、Terminal 系のプログラムで ioctl 使っているものに遭遇して乗り越えられないもの多数

* [Proposal: add Ioctl function to x/sys/unix for solaris and add TCGET*, TCSET*, TCSBRK, and TCXONC constants #12574](https://github.com/golang/go/issues/12574)
* [x/crypto/ssh/terminal ReadPassword() not supported on Solaris #13085](https://github.com/golang/go/issues/13085)
* [On Go, Portability, and System Interfaces](http://garrett.damore.org/2015/09/on-go-portability-and-system-interfaces.html)  
  ioctl などプラットフォーム固有の API を直接呼び出しすることによるポータビリティ問題について。
  CGO を利用すれば幸せになる人は増えるが、オーバーヘッドなどの問題も絡んでくる
* [Tcell - Terminal functionality for Pure Go apps](http://garrett.damore.org/2015/10/tcell-terminal-functionality-for-pure.html)  
  termbox-go でも Solaris/illumos をサポートしてもらおうと[Improve portability for POSIX systems #99](https://github.com/nsf/termbox-go/pull/99)頑張ってみたが、中々うまくいかなかったよう。
  結局、termbox-go を書き直した Tcell を公開

# **何かインストールしてみよう**

この記事を参考に ccat, pt, peco にチャレンジしてみた
* [あるとちょっと便利なコマンドラインツール3つ](http://qiita.com/maeda1991/items/896f344921a6dab393d0)  

## **pt** - The Platinum Searcher:
<https://github.com/monochromegane/the_platinum_searcher>
Go言語で作られたパターン検索ツール。

* [Go言語でag(The Silver Searcher)ライクな高速検索ツールをつくった。EUC-JP/Shift-JISも検索できマス。](http://blog.monochromegane.com/blog/2014/01/16/the-platinum-searcher/)

pt が依存している Terminal パッケージ が、Solaris に対応していないためインストールできず。
が、FreeBSD や　Darwin (OS X) では [CGO](https://golang.org/cmd/cgo/) (Go から C の関数を呼び出すことができる)という仕組みを使ったものも提供されていたため、見よう見まねでごにょごにょしたら Solaris でも動くものが出来上がったので [Pull Request](https://github.com/monochromegane/terminal/pull/3) してみた。
1.5 のリリースノートに書かれていた `The Solaris port now has full support for cgo ` を覚えていたので、そこからたどってみた。
というわけで、今日から Solaris でも pt 使えます。


```
$ go get github.com/monochromegane/the_platinum_searcher/...
$ $GOPATH/bin/pt --help
```

## **ccat** - ccat is the colorizing cat:
<https://github.com/jingweno/ccat>
syntax highlighting をサポートした cat コマンド 

やってみる。

```
$ go get -u github.com/jingweno/ccat
# github.com/jingweno/ccat
jingweno/ccat/ccat.go:22: undefined: isatty.IsTerminal
```

ccat パッケージに含まれ、かつ depend している go-isatty パッケージが Solaris をサポートしていないため、`undefined: isatty.IsTerminal` でエラーとなる。
本家では Solaris をサポートしたパッケージを配布しているので、ccat.go を下記のように書き換える、もう一度、`go get -u github.com/jingweno/ccat` すればインストールできる。

```
diff --git a/ccat.go b/ccat.go
index eae8a8e..b4a67b7 100644
--- a/ccat.go
+++ b/ccat.go
@@ -7,7 +7,7 @@ import (
        "os"
        "syscall"

-       "github.com/jingweno/ccat/Godeps/_workspace/src/github.com/mattn/go-isatty"
+       "github.com/mattn/go-isatty"
 )

 type CCatPrinter interface {
```

もう一回

```
$ go get -fix -v -u github.com/jingweno/ccat
github.com/jingweno/ccat (download)
github.com/mattn/go-isatty (download)
Fetching https://golang.org/x/sys/unix?go-get=1
Parsing meta tags from https://golang.org/x/sys/unix?go-get=1 (status code 200)
get "golang.org/x/sys/unix": found meta tag main.metaImport{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} at https://golang.org/x/sys/unix?go-get=1
get "golang.org/x/sys/unix": verifying non-authoritative meta tag
Fetching https://golang.org/x/sys?go-get=1
Parsing meta tags from https://golang.org/x/sys?go-get=1 (status code 200)
golang.org/x/sys (download)
github.com/jingweno/ccat
```

ccat コマンドが $GOPATH/bin に作成されている。

```
$ ls $GOPATH/bin/ccat
/export/home/kazus/go/bin/ccat*
```

## **peco** - Simplistic interactive filtering tool
<https://github.com/peco/peco>

これが、超絶便利そうだったのでインストールしてみたかったが Solaris をサポートしていない termbox への依存が激しくくじけました。
かわりに Python で実装された [percol](https://github.com/mooz/percol) 置いときますね。


## **boom** - HTTP(S) load generator, ApacheBench (ab) replacement, written in Go:
<https://github.com/rakyll/boom>

```
$ go get github.com/rakyll/boom
```

下記のエラーが出力されるので、pb/pb.go に patch を適用します。

```
# github.com/rakyll/pb
../src/github.com/rakyll/pb/pb.go:168: undefined: terminalWidth
```

パッチ:

```
$ git diff
diff --git a/pb_nix.go b/pb_nix.go
index 1f606aa..45bcfb0 100644
--- a/pb_nix.go
+++ b/pb_nix.go
@@ -1,4 +1,4 @@
-// +build linux darwin freebsd
+// +build linux darwin freebsd solaris

 package pb
```

もう、一回。
今度は `undefined: syscall.SYS_IOCTL` が出ました。
ちょっとこれを CGO 対応させるスキルがありません。

```
$ go get github.com/rakyll/boom
# github.com/rakyll/pb
./pb_nix.go:26: undefined: syscall.SYS_IOCTL
```

正しい手法ではないですが、go1.5.2 のソースを修正します。
書き換えたら go 1.5.2 のビルド手順と同じ make.bash を実行してください。

```
diff -cNr go/src/syscall/zsysnum_solaris_amd64.go go1.5.2/src/syscall/zsysnum_solaris_amd64.go
*** go/src/syscall/zsysnum_solaris_amd64.go     Thu Dec  3 09:53:02 2015
--- go1.5.2/src/syscall/zsysnum_solaris_amd64.go        Mon Jan 11 23:14:32 2016
***************
*** 8,13 ****
--- 8,14 ----

  // TODO(aram): remove these before Go 1.3.
  const (
+       SYS_IOCTL  = 54
        SYS_EXECVE = 59
        SYS_FCNTL  = 62
  )
```

修正を加えたビルドが完了したら、また go get を実行します。

```
$ go get github.com/rakyll/boom
```
これで、$GOROOT/bin に boom バイナリが作成されているはず。

動作しました。

```
$ $GOPATH/bin/boom -n 10000 -c 100 -m POST http://192.168.100.15:8080
10000 / 10000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 %

Summary:
  Total:        5.1659 secs.
  Slowest:      1.2772 secs.
  Fastest:      0.0025 secs.
  Average:      0.0428 secs.
  Requests/sec: 1935.7703
  Total Data Received:  12670000 bytes.
  Response Size per Request:    1267 bytes.

Status code distribution:
  [404] 10000 responses

Response time histogram:
  0.003 [1]     |
  0.130 [9902]  |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  0.257 [18]    |
  0.385 [2]     |
  0.512 [0]     |
  0.640 [0]     |
  0.767 [0]     |
  0.895 [0]     |
  1.022 [0]     |
  1.150 [26]    |
  1.277 [51]    |

Latency distribution:
  10% in 0.0170 secs.
  25% in 0.0232 secs.
  50% in 0.0310 secs.
  75% in 0.0425 secs.
  90% in 0.0543 secs.
  95% in 0.0650 secs.
  99% in 0.1263 secs.
```

* [CentOS に Go 言語をインストールして boom で負荷テストをやってみた](http://kakakikikeke.blogspot.jp/2015/03/centos-go-boom.html)




# 参考
* [ʕ　 ﾟ皿ﾟ ʔ cgoやってみた](http://yuroyoro.net/trying_cgo/#/)