Skip to content

Commit 1443442

Browse files
committed
cmd/apidiff: stop using gcexportdata.{Read,Write}Bundle
This CL changes apidiff to stop using the bundle read/write operations, which were always experimental and are in the process of being deprecated/dropped (#69573). Instead, we use the ordinary gcexportdata.Write operation to save each package into a section of a zip file named after the package, in topological order. (This could perhaps be simplified further by simply retaining the export data files originally produced by the compiler without even parsing them; see go/packages.NeedExportFile.) Also: - plumb token.FileSet down from main, addressing a TODO. - remove unnecessary packages.LoadMode bits. Updates golang/go#69573 Change-Id: I6347097b480853d7bf21047595282f2123ee50b1 Reviewed-on: https://go-review.googlesource.com/c/exp/+/634600 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent 43b7b7c commit 1443442

File tree

1 file changed

+124
-53
lines changed

1 file changed

+124
-53
lines changed

cmd/apidiff/main.go

Lines changed: 124 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
package main
33

44
import (
5+
"archive/zip"
56
"bufio"
7+
"errors"
68
"flag"
79
"fmt"
810
"go/token"
911
"go/types"
12+
"io"
1013
"os"
1114
"strings"
1215

@@ -45,14 +48,16 @@ func main() {
4548
fmt.Fprintf(w, " Same NOTE for packages applies to modules.\n")
4649
flag.PrintDefaults()
4750
}
48-
4951
flag.Parse()
52+
53+
fset := token.NewFileSet()
54+
5055
if *exportDataOutfile != "" {
5156
if len(flag.Args()) != 1 {
5257
flag.Usage()
5358
os.Exit(2)
5459
}
55-
if err := loadAndWrite(flag.Arg(0)); err != nil {
60+
if err := loadAndWrite(fset, flag.Arg(0)); err != nil {
5661
die("writing export data: %v", err)
5762
}
5863
os.Exit(0)
@@ -65,13 +70,13 @@ func main() {
6570

6671
var report apidiff.Report
6772
if *moduleMode {
68-
oldmod := mustLoadOrReadModule(flag.Arg(0))
69-
newmod := mustLoadOrReadModule(flag.Arg(1))
73+
oldmod := mustLoadOrReadModule(fset, flag.Arg(0))
74+
newmod := mustLoadOrReadModule(fset, flag.Arg(1))
7075

7176
report = apidiff.ModuleChanges(oldmod, newmod)
7277
} else {
73-
oldpkg := mustLoadOrReadPackage(flag.Arg(0))
74-
newpkg := mustLoadOrReadPackage(flag.Arg(1))
78+
oldpkg := mustLoadOrReadPackage(fset, flag.Arg(0))
79+
newpkg := mustLoadOrReadPackage(fset, flag.Arg(1))
7580
if !*allowInternal {
7681
if isInternalPackage(oldpkg.Path(), "") && isInternalPackage(newpkg.Path(), "") {
7782
fmt.Fprintf(os.Stderr, "Ignoring internal package %s\n", oldpkg.Path())
@@ -92,41 +97,42 @@ func main() {
9297
}
9398
}
9499

95-
func loadAndWrite(path string) error {
100+
func loadAndWrite(fset *token.FileSet, path string) error {
96101
if *moduleMode {
97-
module := mustLoadModule(path)
98-
return writeModuleExportData(module, *exportDataOutfile)
102+
module := mustLoadModule(fset, path)
103+
return writeModuleExportData(fset, module, *exportDataOutfile)
99104
}
100105

101106
// Loading and writing data for only a single package.
102-
pkg := mustLoadPackage(path)
107+
pkg := mustLoadPackage(fset, path)
103108
return writePackageExportData(pkg, *exportDataOutfile)
104109
}
105110

106-
func mustLoadOrReadPackage(importPathOrFile string) *types.Package {
111+
func mustLoadOrReadPackage(fset *token.FileSet, importPathOrFile string) *types.Package {
107112
fileInfo, err := os.Stat(importPathOrFile)
108113
if err == nil && fileInfo.Mode().IsRegular() {
109-
pkg, err := readPackageExportData(importPathOrFile)
114+
pkg, err := readPackageExportData(fset, importPathOrFile)
110115
if err != nil {
111116
die("reading export data from %s: %v", importPathOrFile, err)
112117
}
113118
return pkg
114119
} else {
115-
return mustLoadPackage(importPathOrFile).Types
120+
return mustLoadPackage(fset, importPathOrFile).Types
116121
}
117122
}
118123

119-
func mustLoadPackage(importPath string) *packages.Package {
120-
pkg, err := loadPackage(importPath)
124+
func mustLoadPackage(fset *token.FileSet, importPath string) *packages.Package {
125+
pkg, err := loadPackage(fset, importPath)
121126
if err != nil {
122127
die("loading %s: %v", importPath, err)
123128
}
124129
return pkg
125130
}
126131

127-
func loadPackage(importPath string) (*packages.Package, error) {
128-
cfg := &packages.Config{Mode: packages.LoadTypes |
129-
packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps,
132+
func loadPackage(fset *token.FileSet, importPath string) (*packages.Package, error) {
133+
cfg := &packages.Config{
134+
Fset: fset,
135+
Mode: packages.NeedName | packages.NeedTypes,
130136
}
131137
pkgs, err := packages.Load(cfg, importPath)
132138
if err != nil {
@@ -142,34 +148,35 @@ func loadPackage(importPath string) (*packages.Package, error) {
142148
return pkgs[0], nil
143149
}
144150

145-
func mustLoadOrReadModule(modulePathOrFile string) *apidiff.Module {
151+
func mustLoadOrReadModule(fset *token.FileSet, modulePathOrFile string) *apidiff.Module {
146152
var module *apidiff.Module
147153
fileInfo, err := os.Stat(modulePathOrFile)
148154
if err == nil && fileInfo.Mode().IsRegular() {
149-
module, err = readModuleExportData(modulePathOrFile)
155+
module, err = readModuleExportData(fset, modulePathOrFile)
150156
if err != nil {
151157
die("reading export data from %s: %v", modulePathOrFile, err)
152158
}
153159
} else {
154-
module = mustLoadModule(modulePathOrFile)
160+
module = mustLoadModule(fset, modulePathOrFile)
155161
}
156162

157163
filterInternal(module, *allowInternal)
158164

159165
return module
160166
}
161167

162-
func mustLoadModule(modulepath string) *apidiff.Module {
163-
module, err := loadModule(modulepath)
168+
func mustLoadModule(fset *token.FileSet, modulepath string) *apidiff.Module {
169+
module, err := loadModule(fset, modulepath)
164170
if err != nil {
165171
die("loading %s: %v", modulepath, err)
166172
}
167173
return module
168174
}
169175

170-
func loadModule(modulepath string) (*apidiff.Module, error) {
171-
cfg := &packages.Config{Mode: packages.LoadTypes |
172-
packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps | packages.NeedModule,
176+
func loadModule(fset *token.FileSet, modulepath string) (*apidiff.Module, error) {
177+
cfg := &packages.Config{
178+
Fset: fset,
179+
Mode: packages.NeedName | packages.NeedTypes | packages.NeedModule,
173180
}
174181
loaded, err := packages.Load(cfg, fmt.Sprintf("%s/...", modulepath))
175182
if err != nil {
@@ -190,70 +197,134 @@ func loadModule(modulepath string) (*apidiff.Module, error) {
190197
return &apidiff.Module{Path: loaded[0].Module.Path, Packages: tpkgs}, nil
191198
}
192199

193-
func readModuleExportData(filename string) (*apidiff.Module, error) {
194-
f, err := os.Open(filename)
200+
func readModuleExportData(fset *token.FileSet, filename string) (*apidiff.Module, error) {
201+
f, err := zip.OpenReader(filename)
195202
if err != nil {
196203
return nil, err
197204
}
198205
defer f.Close()
199-
r := bufio.NewReader(f)
200-
modPath, err := r.ReadString('\n')
201-
if err != nil {
202-
return nil, err
203-
}
204-
modPath = modPath[:len(modPath)-1] // remove delimiter
205-
m := map[string]*types.Package{}
206-
pkgs, err := gcexportdata.ReadBundle(r, token.NewFileSet(), m)
207-
if err != nil {
208-
return nil, err
209-
}
210206

207+
imports := make(map[string]*types.Package)
208+
209+
var modPath string
210+
var pkgs []*types.Package
211+
for _, entry := range f.File {
212+
if err := func() error {
213+
r, err := entry.Open()
214+
if err != nil {
215+
return err
216+
}
217+
defer r.Close()
218+
if entry.Name == "module" {
219+
data, err := io.ReadAll(r)
220+
if err != nil {
221+
return err
222+
}
223+
modPath = string(data)
224+
} else {
225+
pkg, err := gcexportdata.Read(r, fset, imports, strings.TrimSuffix(entry.Name, ".x"))
226+
if err != nil {
227+
return err
228+
}
229+
if imports[entry.Name] != nil {
230+
panic("not in topological order")
231+
}
232+
imports[entry.Name] = pkg
233+
pkgs = append(pkgs, pkg)
234+
}
235+
return nil
236+
}(); err != nil {
237+
return nil, err
238+
}
239+
}
211240
return &apidiff.Module{Path: modPath, Packages: pkgs}, nil
212241
}
213242

214-
func writeModuleExportData(module *apidiff.Module, filename string) error {
243+
func writeModuleExportData(fset *token.FileSet, module *apidiff.Module, filename string) (err error) {
215244
f, err := os.Create(filename)
216245
if err != nil {
217246
return err
218247
}
248+
defer func() {
249+
err = errors.Join(err, f.Close())
250+
}()
251+
219252
fmt.Fprintln(f, module.Path)
220-
// TODO: Determine if token.NewFileSet is appropriate here.
221-
if err := gcexportdata.WriteBundle(f, token.NewFileSet(), module.Packages); err != nil {
253+
254+
// Write types for each package into a zip archive.
255+
256+
// First write the module path.
257+
w := zip.NewWriter(f)
258+
entry, err := w.Create("module")
259+
if err != nil {
260+
return err
261+
}
262+
if _, err := io.WriteString(entry, module.Path); err != nil {
222263
return err
223264
}
224-
return f.Close()
265+
266+
// Then emit packages, dependencies first.
267+
seen := map[*types.Package]bool{types.Unsafe: true}
268+
var emit func(pkg *types.Package) error
269+
emit = func(pkg *types.Package) error {
270+
if pkg.Name() == "main" {
271+
return nil // nonimportable
272+
}
273+
if !seen[pkg] {
274+
seen[pkg] = true
275+
for _, dep := range pkg.Imports() {
276+
emit(dep)
277+
}
278+
entry, err := w.Create(pkg.Path() + ".x")
279+
if err != nil {
280+
return err
281+
}
282+
if err := gcexportdata.Write(entry, fset, pkg); err != nil {
283+
return err
284+
}
285+
}
286+
return nil
287+
}
288+
for _, pkg := range module.Packages {
289+
if err := emit(pkg); err != nil {
290+
return err
291+
}
292+
}
293+
294+
return w.Close()
225295
}
226296

227-
func readPackageExportData(filename string) (*types.Package, error) {
297+
func readPackageExportData(fset *token.FileSet, filename string) (*types.Package, error) {
228298
f, err := os.Open(filename)
229299
if err != nil {
230300
return nil, err
231301
}
232302
defer f.Close()
233303
r := bufio.NewReader(f)
234-
m := map[string]*types.Package{}
235304
pkgPath, err := r.ReadString('\n')
236305
if err != nil {
237306
return nil, err
238307
}
239308
pkgPath = pkgPath[:len(pkgPath)-1] // remove delimiter
240-
return gcexportdata.Read(r, token.NewFileSet(), m, pkgPath)
309+
imports := make(map[string]*types.Package)
310+
return gcexportdata.Read(r, fset, imports, pkgPath)
241311
}
242312

243-
func writePackageExportData(pkg *packages.Package, filename string) error {
313+
func writePackageExportData(pkg *packages.Package, filename string) (err error) {
244314
f, err := os.Create(filename)
245315
if err != nil {
246316
return err
247317
}
318+
defer func() {
319+
err = errors.Join(err, f.Close())
320+
}()
321+
248322
// Include the package path in the file. The exportdata format does
249323
// not record the path of the package being written.
250-
fmt.Fprintln(f, pkg.PkgPath)
251-
err1 := gcexportdata.Write(f, pkg.Fset, pkg.Types)
252-
err2 := f.Close()
253-
if err1 != nil {
254-
return err1
324+
if _, err := fmt.Fprintln(f, pkg.PkgPath); err != nil {
325+
return err
255326
}
256-
return err2
327+
return gcexportdata.Write(f, pkg.Fset, pkg.Types)
257328
}
258329

259330
func die(format string, args ...interface{}) {

0 commit comments

Comments
 (0)