22package main
33
44import (
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
259330func die (format string , args ... interface {}) {
0 commit comments