1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package main
16
17 import (
18 "encoding/json"
19 "fmt"
20 "go/parser"
21 "go/token"
22 "os"
23 "strconv"
24 "strings"
25 )
26
27 type ResolvePkgFunc func(importPath string) string
28
29
30 type FlatPackagesError struct {
31 Pos string
32 Msg string
33 Kind FlatPackagesErrorKind
34 }
35
36 type FlatPackagesErrorKind int
37
38 const (
39 UnknownError FlatPackagesErrorKind = iota
40 ListError
41 ParseError
42 TypeError
43 )
44
45 func (err FlatPackagesError) Error() string {
46 pos := err.Pos
47 if pos == "" {
48 pos = "-"
49 }
50 return pos + ": " + err.Msg
51 }
52
53
54
55 type 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
68 type (
69 PackageFunc func(pkg *FlatPackage)
70 PathResolverFunc func(path string) string
71 )
72
73 func resolvePathsInPlace(prf PathResolverFunc, paths []string) {
74 for i, path := range paths {
75 paths[i] = prf(path)
76 }
77 }
78
79 func 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
98 func (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
107
108 func (fp *FlatPackage) FilterFilesForBuildTags() {
109 fp.GoFiles = filterSourceFilesForTags(fp.GoFiles)
110 fp.CompiledGoFiles = filterSourceFilesForTags(fp.CompiledGoFiles)
111 }
112
113 func (fp *FlatPackage) filterTestSuffix(files []string) (err error, testFiles []string, xTestFiles, nonTestFiles []string) {
114 for _, filename := range files {
115 if strings.HasSuffix(filename, "_test.go") {
116 fset := token.NewFileSet()
117 f, err := parser.ParseFile(fset, filename, nil, parser.PackageClauseOnly)
118 if err != nil {
119 return err, nil, nil, nil
120 }
121 if f.Name.Name == fp.Name {
122 testFiles = append(testFiles, filename)
123 } else {
124 xTestFiles = append(xTestFiles, filename)
125 }
126 } else {
127 nonTestFiles = append(nonTestFiles, filename)
128 }
129 }
130 return
131 }
132
133 func (fp *FlatPackage) MoveTestFiles() *FlatPackage {
134 err, tgf, xtgf, gf := fp.filterTestSuffix(fp.GoFiles)
135
136 if err != nil {
137 return nil
138 }
139 fp.GoFiles = append(gf, tgf...)
140 fp.CompiledGoFiles = append(gf, tgf...)
141
142 if len(xtgf) == 0 {
143 return nil
144 }
145
146 newImports := make(map[string]string, len(fp.Imports))
147 for k, v := range fp.Imports {
148 newImports[k] = v
149 }
150
151 newImports[fp.PkgPath] = fp.ID
152
153
154 return &FlatPackage{
155 ID: fp.ID + "_xtest",
156 Name: fp.Name + "_test",
157 PkgPath: fp.PkgPath + "_test",
158 Imports: newImports,
159 Errors: fp.Errors,
160 GoFiles: append([]string{}, xtgf...),
161 CompiledGoFiles: append([]string{}, xtgf...),
162 OtherFiles: fp.OtherFiles,
163 ExportFile: fp.ExportFile,
164 Standard: fp.Standard,
165 }
166 }
167
168 func (fp *FlatPackage) IsStdlib() bool {
169 return fp.Standard
170 }
171
172 func (fp *FlatPackage) ResolveImports(resolve ResolvePkgFunc) error {
173
174 if fp.IsStdlib() {
175 return nil
176 }
177
178 fset := token.NewFileSet()
179
180 for _, file := range fp.CompiledGoFiles {
181 f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
182 if err != nil {
183 return err
184 }
185
186 if fp.Name == "" {
187 fp.Name = f.Name.Name
188 }
189
190 for _, rawImport := range f.Imports {
191 imp, err := strconv.Unquote(rawImport.Path.Value)
192 if err != nil {
193 continue
194 }
195
196 if imp == "C" {
197 continue
198 }
199 if _, ok := fp.Imports[imp]; ok {
200 continue
201 }
202
203 if pkgID := resolve(imp); pkgID != "" {
204 fp.Imports[imp] = pkgID
205 }
206 }
207 }
208
209 return nil
210 }
211
212 func (fp *FlatPackage) IsRoot() bool {
213 return strings.HasPrefix(fp.ID, "//")
214 }
215
View as plain text