1
2
3
4
5
6
7
8
9
10
11
12 package main
13
14 import (
15 "cmd/internal/objabi"
16 "encoding/json"
17 "flag"
18 "fmt"
19 "go/ast"
20 "go/build"
21 "go/parser"
22 "go/token"
23 "go/types"
24 "log"
25 "os"
26 "strings"
27
28 "golang.org/x/tools/go/gcexportdata"
29 )
30
31 const message = `export is a tool to write type information of Go programs.
32
33 Note that this tool is experimental and its interface is subject to change.
34 It should not be depended upon directly.
35
36 Usage of 'go tool export':
37 Export type information specified by a config file:
38 go tool export unit.cfg
39 `
40
41 func usage() {
42 fmt.Fprint(os.Stderr, message)
43 fmt.Fprintln(os.Stderr, "\nFlags:")
44 flag.PrintDefaults()
45 os.Exit(2)
46 }
47
48
49 type config struct {
50 ImportPath string
51 Compiler string
52 GoVersion string
53 GoFiles []string
54 ImportMap map[string]string
55 PackageFile map[string]string
56 Output string
57 }
58
59 func main() {
60 log.SetFlags(0)
61 log.SetPrefix("export: ")
62
63 objabi.AddVersionFlag()
64 objabi.Flagparse(usage)
65
66 args := flag.Args()
67 if len(args) != 1 || !strings.HasSuffix(args[0], ".cfg") {
68 flag.Usage()
69 }
70 if err := export(args[0]); err != nil {
71 log.Fatal(err)
72 }
73 }
74
75 func export(configFile string) error {
76 cfg, err := readConfig(configFile)
77 if err != nil {
78 return err
79 }
80
81
82
83
84 fset := token.NewFileSet()
85 var files []*ast.File
86 for _, name := range cfg.GoFiles {
87 f, err := parser.ParseFile(fset, name, nil, parser.SkipObjectResolution)
88 if err != nil {
89 return err
90 }
91 files = append(files, f)
92 }
93 tc := &types.Config{
94 Importer: makeTypesImporter(cfg, fset),
95 Sizes: types.SizesFor(cfg.Compiler, build.Default.GOARCH),
96 GoVersion: cfg.GoVersion,
97 }
98 pkg, err := tc.Check(cfg.ImportPath, fset, files, nil)
99 if err != nil {
100 return err
101 }
102
103
104 f, err := os.Create(cfg.Output)
105 if err != nil {
106 return err
107 }
108 if err := gcexportdata.Write(f, fset, pkg); err != nil {
109 f.Close()
110 return err
111 }
112 return f.Close()
113 }
114
115 func readConfig(filename string) (*config, error) {
116 data, err := os.ReadFile(filename)
117 if err != nil {
118 return nil, err
119 }
120 cfg := new(config)
121 if err := json.Unmarshal(data, cfg); err != nil {
122 return nil, fmt.Errorf("can't decode input %s: %v", filename, err)
123 }
124 if len(cfg.GoFiles) == 0 {
125 return nil, fmt.Errorf("no files in package %s", cfg.ImportPath)
126 }
127 return cfg, nil
128 }
129
130 func makeTypesImporter(cfg *config, fset *token.FileSet) types.Importer {
131 imports := make(map[string]*types.Package)
132 imports["unsafe"] = types.Unsafe
133 return importerFunc(func(importPath string) (*types.Package, error) {
134 pkgPath, ok := cfg.ImportMap[importPath]
135 if !ok {
136 return nil, fmt.Errorf("can't resolve import %s", importPath)
137 }
138
139 if pkg, ok := imports[pkgPath]; ok && pkg.Complete() {
140 return pkg, nil
141 }
142
143 cfgPath, ok := cfg.PackageFile[pkgPath]
144 if !ok {
145 return nil, fmt.Errorf("can't resolve export %s", pkgPath)
146 }
147 f, err := os.Open(cfgPath)
148 if err != nil {
149 return nil, err
150 }
151 defer f.Close()
152 return gcexportdata.Read(f, fset, imports, pkgPath)
153 })
154 }
155
156 type importerFunc func(string) (*types.Package, error)
157
158 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
159
View as plain text