Google App Engine Goの開発環境dev_appserver.pyで最適化オプションをオフにする

## はじめに

Google App Engineではdev_appserver.pyというスクリプトを実行することで、開発環境が立ち上がります。

GAE/Goの環環境でデバッガを使いたいのですが、dev_appserver.pyでは、`go build`する際のオプションを渡すことができず、必ず最適化された状態でコンパイルされてしまいます。デバッグする際には最適化しない状態で正しくソースコードとの対応を取りながらデバッグをしたいものです。(最適化により命令順序が変更されてしまうとデバッグしづらい)

この記事では、最適化オプションをオフにしてビルドする方法とデバッガの対応状況について説明します。

結論だけを見たい人は「最適化オプションをオフにする」を見てください。


## 最適化オプション

`go build`のヘルプを確認してみると`--gcflags`というオプションがあることがわかります。

```
$ go build --help
略
-gcflags 'arg list'
        arguments to pass on each go tool compile invocation.
```

これだけだと詳細がわかりませんが、`go build`は実際にコンパイルする際には`go tool compile`コマンドを実行しているのでこちらの[compile - The Go Programming Language](https://golang.org/cmd/compile/)を確認します。

すると以下の2つがデバッグの際には必要になりそうなことがわかります。最適化とインライン展開をやめることでソースコードと実行バイナリが正しく対応をとることができるようになります。

```
-N
	Disable optimizations.
-l
	Disable inlining.
```

つまりdev_appserver.pyでビルドする際に、以下のようなことができればよさそうです。

```
$ go build -gcflags '-N -l'
```


## dev_appserver.pyによるビルド方法

続いては、どのようにdev_appserver.pyがビルドしているのかを確認したいと思います。

dev_appserver.pyを起動するとAPIサーバ、開発者が作成した各module、admin consoleのためののスレッドが立ち上がります。


ソースコードをビルドするのは、moduleのスレッドになります。またファイルの更新を監視しており、変更があった場合には自動的にビルドが実行されます。

ビルドはSDK内部にある`$(SDK_PATH)/goroot/bin/go-app-builder`というツールを使って実現しています。基本的には`go build`のようなものですが、GAE/Go専用にカスタマイズされています。

`go-app-builder`の内部実装は`$(SDK_PATH)/goroot/src/cmd/go-app-builder`に存在するgoのソースコードです。

go-app-builderのヘルプを読んでみましょう。

```
$ (SDK_PATH)/goroot/src/cmd/go-app-builder --help
略
-gcflags 'arg list'
        arguments to pass on each go tool compile invocation.

```

`go build`と同様に`-gcflags`を渡せるようです。つまり`go-app-builder`のソースコードを改変する必要がないことがわかりました。そこでdev_appserver.pyがどのようにgo-app-builderを実行しているかさえわかれば対応できそうです。

`go-app-builder`でgrepしてみると`$(SDK_PATH)/google/appengine/tools/devappserver2/go_application.py`でgo-app-builderを実行していることがわかります。

## 最適化オプションをオフにする

$(SDK_PATH)/google/appengine/tools/devappserver2/go_application.pyの86行目あたりにgo-app-builderのオプションがあるので`-gcflags`を追加します。

```diff
  gab_args = [
      _GAB_PATH,
      '-app_base', application_root,
      '-arch', arch,
      '-dynamic',
      '-goroot', GOROOT,
      '-gopath', os.environ.get('GOPATH', GOPATH),
      '-nobuild_files', '^' + str(nobuild_files),
      '-incremental_rebuild',
      '-unsafe',
+     '-gcflags', '-N -l',
  ]
```

これで実際に動かしてみると問題なくビルドできていることが確認できます。
またビルド時のログを出力してみるとコンパイル時にオプションが渡されていることまでは確認することができました。

しかし本当に最適化されずにビルドできたかどうかを確認するすべがわかりませんでした。バイナリが生成されればバイナリから最適化されているかどうか確認できるのですが・・・


## dev_appserver.pyのコマンドラインオプションで最適化のオンオフを切り替える

先程は無理やりソースコードを改変しましたが、理想としてはdev_appserver.pyにオプションを渡して最適化のオンオフを切り替えたいところです。
GAE/PHPではこのようなオプションが用意されています。

```
$ dev_appserver.py --help
  --php_remote_debugging [PHP_REMOTE_DEBUGGING]
                        enable XDebug remote debugging (default: False)
```

PHP同様の変更を加えればGoでもいけるのではないかと考えてソースコードを読み進めましたが、内部でProtocol Buffersが使われており、自動生成されたコードが存在するのですが、肝心の`.proto`ファイルがSDK内に含まれていないため頓挫してしまいました。

## おわりに

これらについてはGogland開発者がGoogleに提案しているのですが長い間動きがありません。もしかしたらGoogleでは専用の開発環境を持っていてdev_appserver.pyをあまり重要視していないのかもしれませんね・・・


## 参考

- [Support debugging Go projects via Delve](https://issuetracker.google.com/issues/35901233)
- [GO-1897 GAE support for delve](https://youtrack.jetbrains.com/issue/GO-1897)
- [GO-620 Add support for remote debugger](https://youtrack.jetbrains.com/issue/GO-620)