This repository has been archived by the owner on Oct 27, 2021. It is now read-only.
forked from nsf/gocode
-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds a types.Importer that caches packages. The uses the filepath of the object/archive file associated with a package as the key so that vendoring is supported (it also makes the cache relatively simple). The timestamp of the object/archive file is used to determine when a package should be re-imported.
- Loading branch information
Charlie Vieth
committed
Sep 8, 2018
1 parent
00e7f5a
commit ba75703
Showing
2 changed files
with
104 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package goimporter | ||
|
||
import ( | ||
"fmt" | ||
"go/importer" | ||
"go/types" | ||
"os" | ||
"runtime" | ||
"sync" | ||
"time" | ||
|
||
"golang.org/x/tools/go/gcexportdata" | ||
) | ||
|
||
type entry struct { | ||
Filename string // object (.o) or archive (.a) filename | ||
ModTime time.Time // binary package modified time | ||
Package *types.Package | ||
} | ||
|
||
type goimporter struct { | ||
pkgs map[string]*entry | ||
mu sync.RWMutex | ||
compiler string | ||
} | ||
|
||
func For(compiler string) types.ImporterFrom { | ||
return &goimporter{ | ||
pkgs: make(map[string]*entry), | ||
compiler: compiler, | ||
} | ||
} | ||
|
||
func Default() types.ImporterFrom { | ||
return For(runtime.Compiler) | ||
} | ||
|
||
func (m *goimporter) load(filename string) (*entry, bool) { | ||
m.mu.RLock() | ||
pkg, ok := m.pkgs[filename] | ||
m.mu.RUnlock() | ||
return pkg, ok | ||
} | ||
|
||
func (m *goimporter) store(pkg *entry) *entry { | ||
m.mu.Lock() | ||
found, ok := m.pkgs[pkg.Filename] | ||
if !ok || found.ModTime.Before(pkg.ModTime) { | ||
m.pkgs[pkg.Filename] = pkg | ||
} else { | ||
pkg = found | ||
} | ||
m.mu.Unlock() | ||
return pkg | ||
} | ||
|
||
func (m *goimporter) Import(path string) (*types.Package, error) { | ||
return m.ImportFrom(path, "", 0) | ||
} | ||
|
||
func (m *goimporter) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) { | ||
filename, id := gcexportdata.Find(path, srcDir) | ||
if filename == "" { | ||
if path == "unsafe" { | ||
return types.Unsafe, nil | ||
} | ||
return nil, fmt.Errorf("can't find import: %q", id) | ||
} | ||
fi, err := os.Stat(filename) | ||
if err != nil { | ||
return nil, fmt.Errorf("can't stat package file (%s) for import (%s): %s", | ||
filename, id, err) | ||
} | ||
modtime := fi.ModTime() | ||
|
||
// TODO: instrument cache hits/misses | ||
if pkg, ok := m.load(filename); ok && pkg.ModTime.Equal(modtime) { | ||
return pkg.Package, nil | ||
} | ||
|
||
// TODO: consider reusing importer for packages (stdlib) ??? | ||
imp := importer.Default().(types.ImporterFrom) | ||
bpkg, err := imp.ImportFrom(path, srcDir, 0) | ||
if err != nil { | ||
return nil, fmt.Errorf("importing package (%s) from dir (%s): %s", | ||
path, srcDir, err) | ||
} | ||
|
||
// store package and return result (in case a newer version was added) | ||
pkg := m.store(&entry{ | ||
Filename: filename, | ||
ModTime: modtime, | ||
Package: bpkg, | ||
}) | ||
return pkg.Package, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters