2022/09/06 追記
Vim / Neovim を使っている場合は sago35/tinyg.vim
を使うと TinyGo の LSP 環境が立ち上がります。
今の所サポートしているのは以下の 4 パターンです。
- Vim8 + vim-lsp
- Vim8 + coc.nvim
- Neovim (native LSP client)
- Neovim + coc.nvim
インストール後は、 :TinygoTarget wioterminal
のようにターゲットを指定することで vim-lsp が立ち上げ直します。
:TinygoTarget -
で元の状態に戻ります。
2021/06/14 追記
sago35/tinygo.vim
という Vim script を作成しました。
TinyGo + Vim (+ vim-lsp) の環境を作るなら、 sago35/tinygo.vim
が良いはず。
インストール後は、 :TinygoTarget wioterminal
のようにターゲットを指定することで vim-lsp が立ち上げ直します。
インストール方法は以下のような形でインストールしてください。
Plug 'sago35/tinygo.vim'
2020/09/23 追記
TinyGo 0.15 以降は以下のやり方を使うのが簡単です。
2020/08/10 追記
以下に記載している go.mod で replace を書く方法でも動かすことはできますが、ページ下部に記載のコメント 1 および 2 の方法/情報で実施するのが簡単で良いです。
以下の GIF のように、 PyPortal (atsamd51j20a) の設定で gopls を動かせるようになりました。
正しく board_pyportal.go 等に飛べているのが確認できます。
もちろん、メソッド等の補完も動きます。
はじめに
TinyGo は、以下の理由により gopls がほとんど動きませんでした。
- 独自のビルトインパッケージを持っている
- 例: machine や device など
- buildtag による分岐
buildtag による分岐については、 gopls でも issue として上がっている内容になりますが、現時点でも gopls 全体で ある単一の buildtag を持つこと
は可能なので何とかなりました。
ということで、 LSP を動かすために以下の 3 つを実施していきます。
- 開発しているフォルダの go.mod に replace を書く
- tinygo ディレクトリの各パッケージフォルダに go.mod (空で良い) を置く
- 環境変数 GOOS + GOARCH + GOFLAGS を設定する
環境
この記事を作るにあたり、以下の環境で確認しました。
- Windows 10
- Go version go1.14.1 windows/amd64
- tinygo version 0.13.1 windows/amd64 (using go version go1.14.1 and LLVM version 10.0.1)
- gopls ead0a569305d87def8dc4ad3899a7d78432b12c6
- Vim 8.2.147
- vim-go 13af5df6a1b3bc4bdfd03e3c05fa600d1dd16de6
多分、 Windows じゃなくても Vim じゃなくても同じように設定可能だと思います。
やり方
1. 開発しているフォルダの go.mod に replace を書く
背景
TinyGo では、以下のパッケージ以外のものは GOROOT にあるものを使っています。
以下のパッケージは tinygo をインストールしたディレクトリ直下のものが使われます。
- machine
- os
- reflect
- runtime
- runtime/interrupt
- runtime/volatile
- sync
- testing
- internal/reflectlite
- internal/task
- device/*
- examples/*
例えば fmt パッケージは GOROOT にあるものが使われるので、インストールしている Go の fmt パッケージが使われます。
fmt.Printf()
はほとんどのマイコンボードにおいて、 USBCDC (USB の SerialPort) への書き込みに使われますが、 fmt パッケージ自体は Go (not TinyGo) の物が使われます。
Go の fmt パッケージを使っているのにどうやって USBCDC に出力するかというと、 TinyGo 内で os.Stdout
を USBCDC への書き込みにマッピングして実現しています。
この 特別扱い
は以下のコードにより実現しています。
そして、この 特別扱い
が gopls がうまく動かない状態を作っています。
// tinygo/compiler/compiler.go
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
// ...
lprogram := &loader.Program{
// ...
OverlayPath: func(path string) string {
// Return the (overlay) import path when it should be overlaid, and
// "" if it should not.
if strings.HasPrefix(path, tinygoPath+"/src/") {
// Avoid issues with packages that are imported twice, one from
// GOPATH and one from TINYGOPATH.
path = path[len(tinygoPath+"/src/"):]
}
switch path {
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/task":
return path
default:
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
return path
} else if path == "syscall" {
for _, tag := range c.BuildTags() {
if tag == "baremetal" || tag == "darwin" {
return path
}
}
}
}
return ""
},
// ...
}
// ...
}
対策
ということで、上記の特別扱いされたフォルダに対して go.mod で replace ディレクティブを書くことにより解決できます。
Windows での標準的なインストール場所に合わせた設定は以下になります。
module tinygo.org/x/drivers
go 1.14
replace (
device/sam => C:\tinygo\src\device/sam
internal/reflectlite => C:\tinygo\src\internal/reflectlite
internal/task => C:\tinygo\src\internal/task
machine => C:\tinygo\src\machine
os => C:\tinygo\src\os
reflect => C:\tinygo\src\reflect
runtime => C:\tinygo\src\runtime
runtime/interrupt => C:\tinygo\src\runtime/interrupt
runtime/volatile => C:\tinygo\src\runtime/volatile
sync => C:\tinygo\src\sync
testing => C:\tinygo\src\testing
)
なお、 replace ディレクティブで指し示す先は、空でも良いので go.mod ファイルが必要になります。
後述の 各パッケージフォルダに go.mod を置く
を実行しないと、この時点ではうまく動作しません。
2. tinygo ディレクトリの各パッケージフォルダに go.mod (空で良い) を置く
背景
以下に記載の通り、 replace される側については go.mod ファイルが必要となります。
空でも良いので作成しておく必要があります。
Note: if the right-hand side of a replace directive is a filesystem path, then the target must have a go.mod file at that location. If the go.mod file is not present, you can create one with go mod init.
https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive
対策
touch 等を使ってからファイルを置いてください。
git-bash 等が使える場合は以下を実行することで go.mod を簡単に作成することができます。
touch C:/tinygo/src/device/sam/go.mod
touch C:/tinygo/src/internal/reflectlite/go.mod
touch C:/tinygo/src/internal/task/go.mod
touch C:/tinygo/src/machine/go.mod
touch C:/tinygo/src/os/go.mod
touch C:/tinygo/src/reflect/go.mod
touch C:/tinygo/src/runtime/go.mod
touch C:/tinygo/src/runtime/interrupt/go.mod
touch C:/tinygo/src/runtime/volatile/go.mod
touch C:/tinygo/src/sync/go.mod
touch C:/tinygo/src/testing/go.mod
3. 環境変数 GOOS + GOARCH + GOFLAGS を設定する
背景
TinyGo では以下を使用しています。
これらを gopls に伝える必要があります。
- GOOS
- GOARCH
- buildtag (マイコンやボードなどの分岐)
対策
buildtag については、環境変数 GOFLAGS で設定できます。
それぞれの値をどう設定すべきかは tinygo info
で調べることができます。
例えば、 PyPortal というターゲットに対しては tinygo flash -target pyportal .
というようなコマンドでビルドしますが、その際の -target pyportal
の部分を tinygo info
に設定します。
$ C:\tinygo\bin\tinygo.exe info -target pyportal
LLVM triple: armv7em-none-eabi
GOOS: linux
GOARCH: arm
build tags: cortexm baremetal linux arm sam atsamd51 atsamd51j20 atsamd51j20a pyportal tinygo gc.conservative scheduler.tasks
garbage collector: conservative
scheduler: tasks
上記を調べることができたので、後は環境変数を設定します。
GOFLAGS はカンマ区切りなので注意が必要です。
set GOOS=linux
set GOARCH=arm
set GOFLAGS=-tags=cortexm,baremetal,linux,arm,sam,atsamd51,atsamd51j20,atsamd51j20a,pyportal,tinygo,gc.conservative,scheduler.tasks
bash 等では以下のように設定します。
export GOOS=linux
export GOARCH=arm
export GOFLAGS=-tags=cortexm,baremetal,linux,arm,sam,atsamd51,atsamd51j20,atsamd51j20a,pyportal,tinygo,gc.conservative,scheduler.tasks
おまけ
3. 環境変数 GOOS + GOARCH + GOFLAGS を設定する
の部分だけをヘルプする小さな自分用 CLI ツールとして tinygo-edit を作りました。
GOOS + GOARCH + GOFLAGS を設定しつつ --editor で指定した editor で開きます。
以下でインストールできます。
$ go get github.com/sago35/tinygo-edit
以下のように使用できます。
$ cd ./examples/blinky1
# feather-m4 の設定で gvim を立ち上げ
$ tinygo-edit --editor gvim --target feather-m4
# pyportal の設定で vim を立ち上げ
$ tinygo-edit --editor vim --target pyportal
まとめ
以下を実施することで、快適な TinyGo 環境になりました。
今後、 (主に gopls 側の version up により) 以下の設定は不要になるかと思いますが、しばらくはこの方法を使っていくことになりそうです。
- 開発しているフォルダの go.mod に replace を書く
- tinygo ディレクトリの各パッケージフォルダに go.mod (空で良い) を置く
- 環境変数 GOOS + GOARCH + GOFLAGS を設定する
リンク
- sago35/tinygo-edit: Add an environment variable for tinygo and open the editor
- x/tools/gopls: improve handling for build tags · Issue #29202 · golang/go