mirror of
https://github.com/golang/go
synced 2024-11-18 20:44:45 -07:00
5fb17a1e7b
For tests (and perhaps later, for daemon discovery), unix domain sockets offer advantages over TCP: we can know the exact socket address that will be used when starting a server subprocess. They also offer performance and security advantages over TCP, and were specifically requested on golang.org/issues/34111. This CL adds support for listening on UDS, and uses this to implement an additional regtest environment mode that starts up an external process. This mode is disabled by default, but may be enabled by the -enable_gopls_subprocess_tests. The regtest TestMain may be hijacked to instead run as gopls, if a special environment variable is set. This allows the the test runner to start a separate process by using os.Argv[0]. The -gopls_test_binary flag may be used to point tests at a separate gopls binary. Updates golang/go#36879 Updates golang/go#34111 Change-Id: I1cfdf55040e81ffa69a6726878a96529e5522e82 Reviewed-on: https://go-review.googlesource.com/c/tools/+/218839 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
|
"golang.org/x/tools/internal/lsp/cache"
|
|
"golang.org/x/tools/internal/lsp/lsprpc"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/tool"
|
|
)
|
|
|
|
// Serve is a struct that exposes the configurable parts of the LSP server as
|
|
// flags, in the right form for tool.Main to consume.
|
|
type Serve struct {
|
|
Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
|
|
Mode string `flag:"mode" help:"no effect"`
|
|
Port int `flag:"port" help:"port on which to run gopls for debugging purposes"`
|
|
Address string `flag:"listen" help:"address on which to listen for remote connections. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. Otherwise, TCP is used."`
|
|
Trace bool `flag:"rpc.trace" help:"print the full rpc trace in lsp inspector format"`
|
|
Debug string `flag:"debug" help:"serve debug information on the supplied address"`
|
|
|
|
app *Application
|
|
}
|
|
|
|
func (s *Serve) Name() string { return "serve" }
|
|
func (s *Serve) Usage() string { return "" }
|
|
func (s *Serve) ShortHelp() string {
|
|
return "run a server for Go code using the Language Server Protocol"
|
|
}
|
|
func (s *Serve) DetailedHelp(f *flag.FlagSet) {
|
|
fmt.Fprint(f.Output(), `
|
|
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
|
|
a child of an editor process.
|
|
|
|
gopls server flags are:
|
|
`)
|
|
f.PrintDefaults()
|
|
}
|
|
|
|
// Run configures a server based on the flags, and then runs it.
|
|
// It blocks until the server shuts down.
|
|
func (s *Serve) Run(ctx context.Context, args ...string) error {
|
|
if len(args) > 0 {
|
|
return tool.CommandLineErrorf("server does not take arguments, got %v", args)
|
|
}
|
|
|
|
err, closeLog := s.app.debug.SetLogFile(s.Logfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer closeLog()
|
|
s.app.debug.ServerAddress = s.Address
|
|
s.app.debug.DebugAddress = s.Debug
|
|
s.app.debug.Serve(ctx)
|
|
s.app.debug.MonitorMemory(ctx)
|
|
|
|
var ss jsonrpc2.StreamServer
|
|
if s.app.Remote != "" {
|
|
network, addr := parseAddr(s.app.Remote)
|
|
ss = lsprpc.NewForwarder(network, addr, true)
|
|
} else {
|
|
ss = lsprpc.NewStreamServer(cache.New(s.app.options), true)
|
|
}
|
|
|
|
if s.Address != "" {
|
|
network, addr := parseAddr(s.Address)
|
|
return jsonrpc2.ListenAndServe(ctx, network, addr, ss)
|
|
}
|
|
if s.Port != 0 {
|
|
addr := fmt.Sprintf(":%v", s.Port)
|
|
return jsonrpc2.ListenAndServe(ctx, "tcp", addr, ss)
|
|
}
|
|
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
|
|
if s.Trace {
|
|
stream = protocol.LoggingStream(stream, s.app.debug.LogWriter)
|
|
}
|
|
return ss.ServeStream(ctx, stream)
|
|
}
|
|
|
|
// parseAddr parses the -listen flag in to a network, and address.
|
|
func parseAddr(listen string) (network string, address string) {
|
|
if parts := strings.SplitN(listen, ";", 2); len(parts) == 2 {
|
|
return parts[0], parts[1]
|
|
}
|
|
return "tcp", listen
|
|
}
|