LoginSignup
4
3

More than 1 year has passed since last update.

TinyGo + Vim で gopls するための設定

Last updated at Posted at 2020-05-10

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 - で元の状態に戻ります。

tinygo-target-vim.gif

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-vim.gif

はじめに

TinyGo は、以下の理由により gopls がほとんど動きませんでした。

  • 独自のビルトインパッケージを持っている
    • 例: machine や device など
  • buildtag による分岐

buildtag による分岐については、 gopls でも issue として上がっている内容になりますが、現時点でも gopls 全体で ある単一の buildtag を持つこと は可能なので何とかなりました。

ということで、 LSP を動かすために以下の 3 つを実施していきます。

  1. 開発しているフォルダの go.mod に replace を書く
  2. tinygo ディレクトリの各パッケージフォルダに go.mod (空で良い) を置く
  3. 環境変数 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 を設定する

リンク

4
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3