in internal/fetch/load.go [53:184]
func loadPackage(ctx context.Context, contentDir fs.FS, goFilePaths []string, innerPath string,
sourceInfo *source.Info, modInfo *godoc.ModuleInfo) (_ *goPackage, err error) {
defer derrors.Wrap(&err, "loadPackage(ctx, zipGoFiles, %q, sourceInfo, modInfo)", innerPath)
ctx, span := trace.StartSpan(ctx, "fetch.loadPackage")
defer span.End()
// Make a map with all the zip file contents.
files := make(map[string][]byte)
for _, p := range goFilePaths {
_, name := path.Split(p)
b, err := readFSFile(contentDir, p, MaxFileSize)
if err != nil {
return nil, err
}
files[name] = b
}
modulePath := modInfo.ModulePath
importPath := path.Join(modulePath, innerPath)
if modulePath == stdlib.ModulePath {
importPath = innerPath
}
v1path := internal.V1Path(importPath, modulePath)
var pkg *goPackage
// Parse the package for each build context.
// The documentation is determined by the set of matching files, so keep
// track of those to avoid duplication.
docsByFiles := map[string]*internal.Documentation{}
for _, bc := range internal.BuildContexts {
mfiles, err := matchingFiles(bc.GOOS, bc.GOARCH, files)
if err != nil {
return nil, err
}
filesKey := mapKeyForFiles(mfiles)
if doc := docsByFiles[filesKey]; doc != nil {
// We have seen this set of files before.
// loadPackageWithBuildContext will produce the same outputs,
// so don't bother calling it. Just copy the doc.
doc2 := *doc
doc2.GOOS = bc.GOOS
doc2.GOARCH = bc.GOARCH
doc2.API = nil
for _, s := range doc.API {
s2 := *s
s2.Children = nil
s2.GOOS = bc.GOOS
s2.GOARCH = bc.GOARCH
s2.Children = append(s2.Children, s.Children...)
doc2.API = append(doc2.API, &s2)
}
pkg.docs = append(pkg.docs, &doc2)
continue
}
name, imports, synopsis, source, api, err := loadPackageForBuildContext(ctx,
mfiles, innerPath, sourceInfo, modInfo)
for _, s := range api {
s.GOOS = bc.GOOS
s.GOARCH = bc.GOARCH
}
switch {
case errors.Is(err, derrors.NotFound):
// No package for this build context.
continue
case errors.As(err, new(*BadPackageError)):
// This build context was bad, but maybe others aren't.
continue
case errors.Is(err, godoc.ErrTooLarge):
// The doc for this build context is too large. To keep things
// simple, return a single package with this error that will be used
// for all build contexts, and ignore the others.
return &goPackage{
err: err,
path: importPath,
v1path: v1path,
name: name,
imports: imports,
docs: []*internal.Documentation{{
GOOS: internal.All,
GOARCH: internal.All,
Synopsis: synopsis,
Source: source,
API: api,
}},
}, nil
case err != nil:
// Serious error. Fail.
return nil, err
default:
// No error.
if pkg == nil {
pkg = &goPackage{
path: importPath,
v1path: v1path,
name: name,
imports: imports, // Use the imports from the first successful build context.
}
}
// All the build contexts should use the same package name. Although
// it's technically legal for different build tags to result in different
// package names, it's not something we support.
if name != pkg.name {
return nil, &BadPackageError{
Err: fmt.Errorf("more than one package name (%q and %q)", pkg.name, name),
}
}
doc := &internal.Documentation{
GOOS: bc.GOOS,
GOARCH: bc.GOARCH,
Synopsis: synopsis,
Source: source,
API: api,
}
docsByFiles[filesKey] = doc
pkg.docs = append(pkg.docs, doc)
}
}
// If all the build contexts succeeded and had the same set of files, then
// assume that the package doc is valid for all build contexts. Represent
// this with a single Documentation whose GOOS and GOARCH are both "all".
if len(docsByFiles) == 1 && len(pkg.docs) == len(internal.BuildContexts) {
pkg.docs = pkg.docs[:1]
pkg.docs[0].GOOS = internal.All
pkg.docs[0].GOARCH = internal.All
for _, s := range pkg.docs[0].API {
s.GOOS = internal.All
s.GOARCH = internal.All
}
}
return pkg, nil
}