// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The export command writes type information of Go programs. // It should not be relied upon except by go list -export. // // It supports the following command-line protocol: // // -V=full print tool version // unit.cfg description of unit to export package main import ( "cmd/internal/objabi" "encoding/json" "flag" "fmt" "go/ast" "go/build" "go/parser" "go/token" "go/types" "log" "os" "strings" "golang.org/x/tools/go/gcexportdata" ) const message = `export is a tool to write type information of Go programs. Note that this tool is experimental and its interface is subject to change. It should not be depended upon directly. Usage of 'go tool export': Export type information specified by a config file: go tool export unit.cfg ` func usage() { fmt.Fprint(os.Stderr, message) fmt.Fprintln(os.Stderr, "\nFlags:") flag.PrintDefaults() os.Exit(2) } // config defines the JSON schema of the file passed by 'go list -export'. type config struct { ImportPath string // package path Compiler string // gc or gccgo, provided to makeTypesImporter GoVersion string // minimum required Go version, such as "go1.21.0" GoFiles []string // absolute paths to package source files ImportMap map[string]string // maps import path to package path PackageFile map[string]string // maps package path to file of type information Output string // where to write file of type information } func main() { log.SetFlags(0) log.SetPrefix("export: ") objabi.AddVersionFlag() objabi.Flagparse(usage) args := flag.Args() if len(args) != 1 || !strings.HasSuffix(args[0], ".cfg") { flag.Usage() } if err := export(args[0]); err != nil { log.Fatal(err) } } func export(configFile string) error { cfg, err := readConfig(configFile) if err != nil { return err } // TODO: Determine if it's useful for go list -export to return partial type // information, perhaps with the -e flag. // Load, parse, type check. fset := token.NewFileSet() var files []*ast.File for _, name := range cfg.GoFiles { f, err := parser.ParseFile(fset, name, nil, parser.SkipObjectResolution) if err != nil { return err } files = append(files, f) } tc := &types.Config{ Importer: makeTypesImporter(cfg, fset), Sizes: types.SizesFor(cfg.Compiler, build.Default.GOARCH), GoVersion: cfg.GoVersion, } pkg, err := tc.Check(cfg.ImportPath, fset, files, nil) if err != nil { return err } // Write export data. f, err := os.Create(cfg.Output) if err != nil { return err } if err := gcexportdata.Write(f, fset, pkg); err != nil { f.Close() // ignore error return err } return f.Close() } func readConfig(filename string) (*config, error) { data, err := os.ReadFile(filename) if err != nil { return nil, err } cfg := new(config) if err := json.Unmarshal(data, cfg); err != nil { return nil, fmt.Errorf("can't decode input %s: %v", filename, err) } if len(cfg.GoFiles) == 0 { return nil, fmt.Errorf("no files in package %s", cfg.ImportPath) } return cfg, nil } func makeTypesImporter(cfg *config, fset *token.FileSet) types.Importer { imports := make(map[string]*types.Package) imports["unsafe"] = types.Unsafe return importerFunc(func(importPath string) (*types.Package, error) { pkgPath, ok := cfg.ImportMap[importPath] if !ok { return nil, fmt.Errorf("can't resolve import %s", importPath) } // Check for cache hit. if pkg, ok := imports[pkgPath]; ok && pkg.Complete() { return pkg, nil } // Miss. cfgPath, ok := cfg.PackageFile[pkgPath] if !ok { return nil, fmt.Errorf("can't resolve export %s", pkgPath) } f, err := os.Open(cfgPath) if err != nil { return nil, err } defer f.Close() return gcexportdata.Read(f, fset, imports, pkgPath) }) } type importerFunc func(string) (*types.Package, error) func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }