xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/gopackagesdriver/flatpackage.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1// Copyright 2021 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18	"encoding/json"
19	"fmt"
20	"go/parser"
21	"go/token"
22	"os"
23	"strconv"
24	"strings"
25)
26
27type ResolvePkgFunc func(importPath string) string
28
29// Copy and pasted from golang.org/x/tools/go/packages
30type FlatPackagesError struct {
31	Pos  string // "file:line:col" or "file:line" or "" or "-"
32	Msg  string
33	Kind FlatPackagesErrorKind
34}
35
36type FlatPackagesErrorKind int
37
38const (
39	UnknownError FlatPackagesErrorKind = iota
40	ListError
41	ParseError
42	TypeError
43)
44
45func (err FlatPackagesError) Error() string {
46	pos := err.Pos
47	if pos == "" {
48		pos = "-" // like token.Position{}.String()
49	}
50	return pos + ": " + err.Msg
51}
52
53// FlatPackage is the JSON form of Package
54// It drops all the type and syntax fields, and transforms the Imports
55type FlatPackage struct {
56	ID              string
57	Name            string              `json:",omitempty"`
58	PkgPath         string              `json:",omitempty"`
59	Errors          []FlatPackagesError `json:",omitempty"`
60	GoFiles         []string            `json:",omitempty"`
61	CompiledGoFiles []string            `json:",omitempty"`
62	OtherFiles      []string            `json:",omitempty"`
63	ExportFile      string              `json:",omitempty"`
64	Imports         map[string]string   `json:",omitempty"`
65	Standard        bool                `json:",omitempty"`
66}
67
68type (
69	PackageFunc      func(pkg *FlatPackage)
70	PathResolverFunc func(path string) string
71)
72
73func resolvePathsInPlace(prf PathResolverFunc, paths []string) {
74	for i, path := range paths {
75		paths[i] = prf(path)
76	}
77}
78
79func WalkFlatPackagesFromJSON(jsonFile string, onPkg PackageFunc) error {
80	f, err := os.Open(jsonFile)
81	if err != nil {
82		return fmt.Errorf("unable to open package JSON file: %w", err)
83	}
84	defer f.Close()
85
86	decoder := json.NewDecoder(f)
87	for decoder.More() {
88		pkg := &FlatPackage{}
89		if err := decoder.Decode(&pkg); err != nil {
90			return fmt.Errorf("unable to decode package in %s: %w", f.Name(), err)
91		}
92
93		onPkg(pkg)
94	}
95	return nil
96}
97
98func (fp *FlatPackage) ResolvePaths(prf PathResolverFunc) error {
99	resolvePathsInPlace(prf, fp.CompiledGoFiles)
100	resolvePathsInPlace(prf, fp.GoFiles)
101	resolvePathsInPlace(prf, fp.OtherFiles)
102	fp.ExportFile = prf(fp.ExportFile)
103	return nil
104}
105
106// FilterFilesForBuildTags filters the source files given the current build
107// tags.
108func (fp *FlatPackage) FilterFilesForBuildTags() {
109	fp.GoFiles = filterSourceFilesForTags(fp.GoFiles)
110	fp.CompiledGoFiles = filterSourceFilesForTags(fp.CompiledGoFiles)
111}
112
113func (fp *FlatPackage) IsStdlib() bool {
114	return fp.Standard
115}
116
117func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) error {
118	// Stdlib packages are already complete import wise
119	if fp.IsStdlib() {
120		return nil
121	}
122
123	fset := token.NewFileSet()
124
125	for _, file := range fp.CompiledGoFiles {
126		f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
127		if err != nil {
128			return err
129		}
130		// If the name is not provided, fetch it from the sources
131		if fp.Name == "" {
132			fp.Name = f.Name.Name
133		}
134
135		for _, rawImport := range f.Imports {
136			imp, err := strconv.Unquote(rawImport.Path.Value)
137			if err != nil {
138				continue
139			}
140			// We don't handle CGo for now
141			if imp == "C" {
142				continue
143			}
144			if _, ok := fp.Imports[imp]; ok {
145				continue
146			}
147
148			if pkgID := resolve(imp); pkgID != "" {
149				fp.Imports[imp] = pkgID
150			}
151		}
152	}
153
154	return nil
155}
156
157func (fp *FlatPackage) IsRoot() bool {
158	return strings.HasPrefix(fp.ID, "//")
159}
160