diff --git a/go.mod b/go.mod index 0dae3bd..38fcfb1 100644 --- a/go.mod +++ b/go.mod @@ -26,18 +26,43 @@ require ( github.com/BurntSushi/toml v1.2.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcutil v1.1.6 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gofrs/flock v0.12.1 // indirect github.com/google/safehtml v0.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/sig-0/insertion-queue v0.0.0-20241004125609-6b3ca841346b // indirect + github.com/stretchr/testify v1.10.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.40.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 // indirect + golang.org/x/net v0.42.0 // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.69.4 // indirect google.golang.org/protobuf v1.36.3 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) + +replace github.com/gnolang/gno => github.com/n0izn0iz/gno v0.1.2-0.20250920103332-3549e304804d diff --git a/go.sum b/go.sum index 46c708e..a76458b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= @@ -61,12 +63,13 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/gnolang/gno v0.0.0-20250911110843-956c0ddde9af h1:kJqIlHX1gdSwa6cT4Hm24+tAv5RTI/j/Gc9sxEeFxns= -github.com/gnolang/gno v0.0.0-20250911110843-956c0ddde9af/go.mod h1:j9wKq29meqwktEj2ReqPbSkeYUwoisfxHHVeV20lhtw= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -113,11 +116,8 @@ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlT github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= @@ -126,6 +126,8 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/n0izn0iz/gno v0.1.2-0.20250920103332-3549e304804d h1:L9rznI/rbHWmMMKem+Yz+okxmq2HSu++c/8Ec/csKo0= +github.com/n0izn0iz/gno v0.1.2-0.20250920103332-3549e304804d/go.mod h1:j9wKq29meqwktEj2ReqPbSkeYUwoisfxHHVeV20lhtw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/pkg/resolver/driver.go b/pkg/resolver/driver.go index 022fbaa..461b137 100644 --- a/pkg/resolver/driver.go +++ b/pkg/resolver/driver.go @@ -9,6 +9,8 @@ import ( "strings" "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnolang" + gnopackages "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnoverse/gnopls/internal/packages" "github.com/gnoverse/gnopls/pkg/eventlogger" ) @@ -21,29 +23,51 @@ func Resolve(req *packages.DriverRequest, patterns ...string) (*packages.DriverR "tests", req.Tests, "build-flags", req.BuildFlags, "overlay", req.Overlay, + "patterns", patterns, ) - // Inject examples + requireBuiltin := false + loaderPatterns := []string{} + for _, pattern := range patterns { + // XXX: better support file pattern + if strings.HasPrefix(pattern, "file=") { + dir, _ := filepath.Split(pattern) + dir = strings.TrimPrefix(dir, "file=") + gnomodsRes, err := listPackagesPath(dir) + if err != nil { + logger.Error("failed to get pkg", slog.String("error", err.Error())) + return nil, err + } + if len(gnomodsRes) != 1 { + logger.Warn("unexpected number of packages", + slog.String("arg", pattern), + slog.Int("count", len(gnomodsRes)), + ) + } + loaderPatterns = append(loaderPatterns, gnomodsRes...) + continue + } else if pattern == "builtin" { + requireBuiltin = true + continue + } - gnoRoot, err := gnoenv.GuessRootDir() - if err != nil { - logger.Warn("can't find gno root, examples and std packages are ignored", slog.String("error", err.Error())) + loaderPatterns = append(loaderPatterns, pattern) } - targets := patterns + res := packages.DriverResponse{} - if gnoRoot != "" { - targets = append(targets, filepath.Join(gnoRoot, "examples", "...")) + findGoPkg := func(pkgpath string) *packages.Package { + for _, pkg := range res.Packages { + if pkg != nil && pkg.PkgPath == pkgpath { + return pkg + } + } + return nil } - pkgsCache := map[string]*packages.Package{} - res := packages.DriverResponse{} - - // Inject gnobuiltin - if pkg, err := getBuiltinPkg(); err == nil { - pkgsCache[pkg.Name] = pkg - res.Packages = append(res.Packages, pkg) - res.Roots = append(res.Roots, pkg.ID) + gnoRoot, err := gnoenv.GuessRootDir() + if err != nil { + logger.Warn("can't find gno root, examples and std packages are ignored", slog.String("error", err.Error())) } // Inject stdlibs @@ -59,6 +83,10 @@ func Resolve(req *packages.DriverRequest, patterns ...string) (*packages.DriverR return nil } + if path == "." { + return nil + } + pkgDir := filepath.Join(libsRoot, path) pkgs := readPkg(req, pkgDir, path, logger) @@ -68,9 +96,6 @@ func Resolve(req *packages.DriverRequest, patterns ...string) (*packages.DriverR } res.Packages = append(res.Packages, pkg) - if !strings.HasSuffix(pkg.Name, "_test") { - pkgsCache[path] = pkg - } testLibDir := filepath.Join(testLibsRoot, path) testsDir, err := os.ReadDir(testLibDir) @@ -106,62 +131,82 @@ func Resolve(req *packages.DriverRequest, patterns ...string) (*packages.DriverR }); err != nil { logger.Warn("failed to inject all stdlibs", slog.String("error", err.Error())) } - } - // Discover packages - - pkgpaths := []string{} - for _, target := range targets { - dir, file := filepath.Split(target) - if file == "..." { - gnomodsRes, err := listPackagesPath(dir) + // inject tests libs + if err := fs.WalkDir(os.DirFS(testLibsRoot), ".", func(path string, d fs.DirEntry, err error) error { if err != nil { - logger.Error("failed to get pkg list", slog.String("error", err.Error())) - return nil, err - } - pkgpaths = append(pkgpaths, gnomodsRes...) - } else if strings.HasPrefix(target, "file=") { - dir = strings.TrimPrefix(dir, "file=") - gnomodsRes, err := listPackagesPath(dir) - if err != nil { - logger.Error("failed to get pkg", slog.String("error", err.Error())) - return nil, err + return nil } - if len(gnomodsRes) != 1 { - logger.Warn("unexpected number of packages", - slog.String("arg", target), - slog.Int("count", len(gnomodsRes)), - ) + + if !d.IsDir() { + return nil } - pkgpaths = append(pkgpaths, gnomodsRes...) - } else { - logger.Warn("unknown arg shape", slog.String("value", target)) - } - } - logger.Info("discovered packages", slog.Int("count", len(pkgpaths)+len(res.Packages))) - // Convert packages + if path == "." { + return nil + } - for _, gnomodPath := range pkgpaths { - pkgs := gnoPkgToGo(req, gnomodPath, logger) - for _, pkg := range pkgs { - if pkg == nil { - logger.Error("failed to convert gno pkg to go pkg", slog.String("gnomod", gnomodPath)) - continue + if slices.ContainsFunc(res.Packages, func(p *packages.Package) bool { return p.PkgPath == path }) { + return nil } - if _, ok := pkgsCache[pkg.PkgPath]; ok { - // ignore duplicates in later targets, mostly useful to ignore examples present in explicit targets - logger.Debug("ignored duplicate", slog.String("pkg-path", pkg.PkgPath), slog.String("new", gnomodPath)) - continue + + pkgDir := filepath.Join(testLibsRoot, path) + + pkgs := readPkg(req, pkgDir, path, logger) + for _, pkg := range pkgs { + if len(pkg.GoFiles) == 0 { + continue + } + + res.Packages = append(res.Packages, pkg) + } + + // logger.Info("injected stdlib", slog.String("path", pkg.PkgPath), slog.String("name", pkg.Name)) + + return nil + }); err != nil { + logger.Warn("failed to inject all tests stdlibs", slog.String("error", err.Error())) + } + } + + if requireBuiltin { + // Inject gnobuiltin + if pkg, err := getBuiltinPkg(); err == nil { res.Packages = append(res.Packages, pkg) res.Roots = append(res.Roots, pkg.ID) - if !strings.HasSuffix(pkg.Name, "_test") { - pkgsCache[pkg.PkgPath] = pkg + } + } + + loadCfg := gnopackages.LoadConfig{ + Test: req.Tests, + Out: os.Stderr, + Deps: true, + GnoRoot: gnoRoot, + // Overlay: req.Overlay, + } + loadedPkgs, err := gnopackages.Load(loadCfg, loaderPatterns...) + if err != nil { + return nil, err + } + + // Convert packages + + for _, gnopkg := range loadedPkgs { + if gnolang.IsStdlib(gnopkg.ImportPath) { + continue + } + pkgs := gnoPkgToGo(req, gnopkg, logger) + res.Packages = append(res.Packages, pkgs...) + if len(gnopkg.Match) != 0 { + for _, pkg := range pkgs { + res.Roots = append(res.Roots, pkg.ID) } } } + logger.Info("discovered packages", slog.Int("count", len(res.Packages))) + // Resolve imports for _, pkg := range res.Packages { @@ -171,8 +216,8 @@ func Resolve(req *packages.DriverRequest, patterns ...string) (*packages.DriverR toDelete := []string{} for importPath := range pkg.Imports { - imp, ok := pkgsCache[importPath] - if ok { + imp := findGoPkg(importPath) + if imp != nil { pkg.Imports[importPath] = imp // logger.Info("found import", slog.String("path", importPath)) continue diff --git a/pkg/resolver/resolver.go b/pkg/resolver/resolver.go index 82d9ecf..12f5c3a 100644 --- a/pkg/resolver/resolver.go +++ b/pkg/resolver/resolver.go @@ -13,20 +13,20 @@ import ( "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/pkg/gnomod" + gnopackages "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnoverse/gnopls/internal/packages" "github.com/gnoverse/gnopls/pkg/gnotypes" ) -func gnoPkgToGo(req *packages.DriverRequest, path string, logger *slog.Logger) []*packages.Package { - mod, err := gnomod.ParseDir(path) +func gnoPkgToGo(req *packages.DriverRequest, pkg *gnopackages.Package, logger *slog.Logger) []*packages.Package { + mod, err := gnomod.ParseDir(pkg.Dir) if err != nil { - logger.Error("failed to read mod file", "path", path, "err", err) + logger.Error("failed to read mod file", "path", pkg.Dir, "err", err) return nil } - // TODO: support subpkgs module := mod.Module - return readPkg(req, path, module, logger) + return readPkg(req, pkg.Dir, module, logger) } // listPackagesPath recursively finds all gnomods at root