diff --git a/gopmod/gopmod_test.go b/gopmod/gopmod_test.go new file mode 100644 index 0000000..4319a7a --- /dev/null +++ b/gopmod/gopmod_test.go @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gopmod + +import ( + "log" + "runtime" + "testing" + + "github.com/goplus/mod/modload/modtest" +) + +func TestLookup(t *testing.T) { + mod := New(modtest.GopClass(t)) + if modv, ok := mod.LookupDepMod("github.com/qiniu/x"); !ok || modv.Version != "v1.13.2" { + t.Fatal("mod.LookupDepMod:", modv) + } + if pkg, err := mod.Lookup("fmt"); err != nil || pkg.ModPath != "" || pkg.ModDir != runtime.GOROOT()+"/src" { + t.Fatal("mod.Lookup fmt:", pkg.ModPath, pkg.ModDir, "err:", err) + } + if pkg, err := mod.Lookup("github.com/goplus/community/foo"); err != nil || pkg.ModPath != "github.com/goplus/community" { + t.Fatal("mod.Lookup github.com/goplus/community/foo:", pkg.ModPath, pkg.ModDir, "err:", err) + } + if _, err := mod.Lookup("github.com/qiniu/y/mockhttp"); err == nil || err.Error() != `no required module provides package github.com/qiniu/y/mockhttp; to add it: + gop get github.com/qiniu/y/mockhttp` { + t.Fatal("mod.Lookup github.com/qiniu/y/mockhttp:", err) + } + if pkg, err := mod.Lookup("github.com/qiniu/x/mockhttp"); err != nil || pkg.ModPath != "github.com/qiniu/x" { + t.Fatal("mod.Lookup github.com/qiniu/x/mockhttp:", pkg.ModPath, pkg.ModDir, "err:", err) + } + defer func() { + if e := recover(); e == nil { + log.Fatal("mod.Lookup: no panic?") + } + }() + mod.Lookup("") +} + +func TestPkgType(t *testing.T) { + mod := New(modtest.GopClass(t)) + if mod.IsPkgtStandard("github.com/qiniu/x") { + t.Fatal("mod.IsPkgtStandard: true?") + } + if !mod.IsPkgtStandard("fmt") { + t.Fatal("mod.IsPkgtStandard: false?") + } + if pt := mod.PkgType(""); pt != PkgtInvalid { + t.Fatal("mod.PkgType:", pt) + } + if pt := mod.PkgType("./fmt"); pt != PkgtLocal { + t.Fatal("mod.PkgType ./fmt:", pt) + } + if pt := mod.PkgType("github.com/goplus/community/foo"); pt != PkgtModule { + t.Fatal("mod.PkgType github.com/goplus/community/foo:", pt) + } +} diff --git a/gopmod/module.go b/gopmod/module.go index 88e622e..a298b87 100644 --- a/gopmod/module.go +++ b/gopmod/module.go @@ -21,7 +21,6 @@ import ( "log" "path/filepath" "runtime" - "sort" "strings" "syscall" @@ -34,15 +33,19 @@ import ( // ----------------------------------------------------------------------------- -type depmodInfo struct { - path string - real module.Version -} - type Module struct { modload.Module projects map[string]*Project // ext -> project - depmods []depmodInfo + depmods_ map[string]module.Version +} + +// DepMods returns all depended modules. +// If a depended module path is replace to be a local path, it will be canonical to an absolute path. +func (p *Module) DepMods() map[string]module.Version { + if p.depmods_ == nil { + p.depmods_ = p.Module.DepMods() + } + return p.depmods_ } // PkgType specifies a package type. @@ -119,12 +122,12 @@ func (p *Module) Lookup(pkgPath string) (pkg *Package, err error) { // lookupExternPkg lookups a external package from depended modules. // If modVer.Path is replace to be a local path, it will be canonical to an absolute path. func (p *Module) lookupExternPkg(pkgPath string) (pkg *Package, err error) { - for _, m := range p.depmods { - if isPkgInMod(pkgPath, m.path) { - if modDir, e := modcache.Path(m.real); e == nil { - modPath := m.path + for path, real := range p.DepMods() { + if isPkgInMod(pkgPath, path) { + if modDir, e := modcache.Path(real); e == nil { + modPath := path dir := modDir + pkgPath[len(modPath):] - pkg = &Package{Type: PkgtExtern, Real: m.real, ModPath: modPath, ModDir: modDir, Dir: dir} + pkg = &Package{Type: PkgtExtern, Real: real, ModPath: modPath, ModDir: modDir, Dir: dir} } else { err = e } @@ -138,45 +141,15 @@ func (p *Module) lookupExternPkg(pkgPath string) (pkg *Package, err error) { // LookupDepMod lookups a depended module. // If modVer.Path is replace to be a local path, it will be canonical to an absolute path. func (p *Module) LookupDepMod(modPath string) (modVer module.Version, ok bool) { - for _, m := range p.depmods { - if m.path == modPath { - modVer, ok = m.real, true - break - } - } + deps := p.DepMods() + modVer, ok = deps[modPath] return } -// IsGopMod returns if this module is a Go+ module or not. -func (p *Module) IsGopMod() bool { - const gopPkgPath = "github.com/goplus/gop" - _, file := filepath.Split(p.Modfile()) - if file == "gop.mod" { - return true - } - if _, ok := p.LookupDepMod(gopPkgPath); ok { - return true - } - return p.Path() == gopPkgPath -} - -func getDepMods(mod modload.Module) []depmodInfo { - depmods := mod.DepMods() - ret := make([]depmodInfo, 0, len(depmods)) - for path, m := range depmods { - ret = append(ret, depmodInfo{path: path, real: m}) - } - sort.Slice(ret, func(i, j int) bool { - return ret[i].path > ret[j].path - }) - return ret -} - // New creates a module from a modload.Module instance. func New(mod modload.Module) *Module { projects := make(map[string]*Project) - depmods := getDepMods(mod) - return &Module{projects: projects, depmods: depmods, Module: mod} + return &Module{projects: projects, Module: mod} } // Load loads a module from a local directory. diff --git a/modload/modtest/modtest.go b/modload/modtest/modtest.go index c3c74c6..ea8cde0 100644 --- a/modload/modtest/modtest.go +++ b/modload/modtest/modtest.go @@ -44,7 +44,7 @@ func Load(t *testing.T, gomodText, gopmodText string, errMsg string) modload.Mod return mod } -func GopClass(t *testing.T) { +func GopClass(t *testing.T) modload.Module { const gomodText = ` module github.com/goplus/community @@ -60,9 +60,10 @@ require ( if n := len(mod.Opt.ClassMods); n != 2 { t.Fatal("len(mod.Opt.Import):", n) } + return mod } -func Import(t *testing.T) { +func Import(t *testing.T) modload.Module { const gomodText = ` module github.com/goplus/yap @@ -82,4 +83,5 @@ import yauth github.com/goplus/yap/ytest/auth if n := len(mod.Opt.Projects); n != 2 { t.Fatal("len(mod.Opt.Projects):", n) } + return mod }