+++ /dev/null
-// Copyright (c) 2019, Daniel Martà <mvdan@mvdan.cc>
-// See LICENSE for licensing information
-
-// +build ignore
-
-package main
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "time"
-)
-
-func main() {
- pkgs, err := listPackages(context.TODO(), nil,
- "cmd/gofmt",
-
- // These are internal cmd dependencies. Copy them.
- "cmd/internal/diff",
-
- "golang.org/x/tools/cmd/goimports",
-
- // These are internal goimports dependencies. Copy them.
- "golang.org/x/tools/internal/event",
- "golang.org/x/tools/internal/event/core",
- "golang.org/x/tools/internal/event/keys",
- "golang.org/x/tools/internal/event/label",
- "golang.org/x/tools/internal/fastwalk",
- "golang.org/x/tools/internal/gocommand",
- "golang.org/x/tools/internal/gopathwalk",
- "golang.org/x/tools/internal/imports",
- "golang.org/x/tools/internal/module",
- "golang.org/x/tools/internal/semver",
- "golang.org/x/tools/internal/telemetry/event",
- )
- if err != nil {
- panic(err)
- }
- for _, pkg := range pkgs {
- switch pkg.ImportPath {
- case "cmd/gofmt":
- copyGofmt(pkg)
- case "golang.org/x/tools/cmd/goimports":
- copyGoimports(pkg)
- default:
- parts := strings.Split(pkg.ImportPath, "/")
- if parts[0] == "cmd" {
- copyInternal(pkg, filepath.Join(parts[1:]...))
- } else {
- dir := filepath.Join(append([]string{"gofumports"}, parts[3:]...)...)
- copyInternal(pkg, dir)
- }
- }
- }
-}
-
-type Module struct {
- Path string // module path
- Version string // module version
- Versions []string // available module versions (with -versions)
- Replace *Module // replaced by this module
- Time *time.Time // time version was created
- Update *Module // available update, if any (with -u)
- Main bool // is this the main module?
- Indirect bool // is this module only an indirect dependency of main module?
- Dir string // directory holding files for this module, if any
- GoMod string // path to go.mod file used when loading this module, if any
- GoVersion string // go version used in module
- Error *ModuleError // error loading module
-}
-
-type ModuleError struct {
- Err string // the error itself
-}
-
-type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- ImportComment string // path in import comment on package statement
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Shlib string // the shared library that contains this package (only set when -linkshared)
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- StaleReason string // explanation for Stale==true
- Root string // Go root or Go path dir containing this package
- ConflictDir string // this directory shadows Dir in $GOPATH
- BinaryOnly bool // binary-only package (no longer supported)
- ForTest string // package is only for use in named test
- Export string // file containing export data (when using -export)
- Module *Module // info about package's containing module, if any (can be nil)
- Match []string // command-line patterns matching this package
- DepOnly bool // package is only a dependency, not explicitly listed
-
- // Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go source files that import "C"
- CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
- IgnoredGoFiles []string // .go source files ignored due to build constraints
- CFiles []string // .c source files
- CXXFiles []string // .cc, .cxx and .cpp source files
- MFiles []string // .m source files
- HFiles []string // .h, .hh, .hpp and .hxx source files
- FFiles []string // .f, .F, .for and .f90 Fortran source files
- SFiles []string // .s source files
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
- SysoFiles []string // .syso object files to add to archive
- TestGoFiles []string // _test.go files in package
- XTestGoFiles []string // _test.go files outside package
-
- // Cgo directives
- CgoCFLAGS []string // cgo: flags for C compiler
- CgoCPPFLAGS []string // cgo: flags for C preprocessor
- CgoCXXFLAGS []string // cgo: flags for C++ compiler
- CgoFFLAGS []string // cgo: flags for Fortran compiler
- CgoLDFLAGS []string // cgo: flags for linker
- CgoPkgConfig []string // cgo: pkg-config names
-
- // Dependency information
- Imports []string // import paths used by this package
- ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
- Deps []string // all (recursively) imported dependencies
- TestImports []string // imports from TestGoFiles
- XTestImports []string // imports from XTestGoFiles
-
- // Error information
- Incomplete bool // this package or a dependency has an error
- Error *PackageError // error loading package
- DepsErrors []*PackageError // errors loading dependencies
-}
-
-type PackageError struct {
- ImportStack []string // shortest path from package named on command line to this one
- Pos string // position of error (if present, file:line:col)
- Err string // the error itself
-}
-
-func getEnv(env []string, name string) string {
- for _, kv := range env {
- if i := strings.IndexByte(kv, '='); i > 0 && name == kv[:i] {
- return kv[i+1:]
- }
- }
- return ""
-}
-
-// listPackages is a wrapper for 'go list -json -e', which can take arbitrary
-// environment variables and arguments as input. The working directory can be
-// fed by adding $PWD to env; otherwise, it will default to the current
-// directory.
-//
-// Since -e is used, the returned error will only be non-nil if a JSON result
-// could not be obtained. Such examples are if the Go command is not installed,
-// or if invalid flags are used as arguments.
-//
-// Errors encountered when loading packages will be returned for each package,
-// in the form of PackageError. See 'go help list'.
-func listPackages(ctx context.Context, env []string, args ...string) (pkgs []*Package, finalErr error) {
- goArgs := append([]string{"list", "-json", "-e"}, args...)
- cmd := exec.CommandContext(ctx, "go", goArgs...)
- cmd.Env = env
- cmd.Dir = getEnv(env, "PWD")
-
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
- var stderrBuf bytes.Buffer
- cmd.Stderr = &stderrBuf
- defer func() {
- if finalErr != nil && stderrBuf.Len() > 0 {
- // TODO: wrap? but the format is backwards, given that
- // stderr is likely multi-line
- finalErr = fmt.Errorf("%v\n%s", finalErr, stderrBuf.Bytes())
- }
- }()
-
- if err := cmd.Start(); err != nil {
- return nil, err
- }
- dec := json.NewDecoder(stdout)
- for dec.More() {
- var pkg Package
- if err := dec.Decode(&pkg); err != nil {
- return nil, err
- }
- pkgs = append(pkgs, &pkg)
- }
- if err := cmd.Wait(); err != nil {
- return nil, err
- }
- return pkgs, nil
-}
-
-func readFile(path string) string {
- body, err := ioutil.ReadFile(path)
- if err != nil {
- panic(err)
- }
- return string(body)
-}
-
-func writeFile(path, body string) {
- if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
- panic(err)
- }
- if err := ioutil.WriteFile(path, []byte(body), 0o644); err != nil {
- panic(err)
- }
-}
-
-func sourceFiles(pkg *Package) (paths []string) {
- var combined []string
- for _, list := range [...][]string{
- pkg.GoFiles,
- pkg.IgnoredGoFiles,
- } {
- for _, name := range list {
- if strings.HasSuffix(name, "_test.go") {
- // IgnoredGoFiles can contain test files too.
- continue
- }
- combined = append(combined, filepath.Join(pkg.Dir, name))
- }
- }
- return combined
-}
-
-const extraImport = `gformat "mvdan.cc/gofumpt/format"; `
-
-const extraSrcLangVersion = `` +
- `if *langVersion == "" {
- out, err := exec.Command("go", "list", "-m", "-f", "{{.GoVersion}}").Output()
- out = bytes.TrimSpace(out)
- if err == nil && len(out) > 0 {
- *langVersion = string(out)
- }
- }`
-
-func copyGofmt(pkg *Package) {
- const extraSrc = `
- // This is the only gofumpt change on gofmt's codebase, besides changing
- // the name in the usage text.
- ` + extraSrcLangVersion + `
- gformat.File(fileSet, file, gformat.Options{
- LangVersion: *langVersion,
- ExtraRules: *extraRules,
- })
- `
- for _, path := range sourceFiles(pkg) {
- body := readFile(path)
- body = fixImports(body)
- name := filepath.Base(path)
- switch name {
- case "doc.go":
- continue // we have our own
- case "gofmt.go":
- if i := strings.Index(body, "\t\"mvdan.cc/gofumpt"); i > 0 {
- body = body[:i] + "\n" + extraImport + "\n" + body[i:]
- }
- if i := strings.Index(body, "res, err := format("); i > 0 {
- body = body[:i] + "\n" + extraSrc + "\n" + body[i:]
- }
- }
- body = strings.Replace(body, "gofmt", "gofumpt", -1)
- writeFile(name, body)
- }
-}
-
-func copyGoimports(pkg *Package) {
- const extraSrc = `
- // This is the only gofumpt change on goimports's codebase, besides changing
- // the name in the usage text.
- ` + extraSrcLangVersion + `
- res, err = gformat.Source(res, gformat.Options{LangVersion: *langVersion})
- if err != nil {
- return err
- }
- `
- for _, path := range sourceFiles(pkg) {
- body := readFile(path)
- body = fixImports(body)
- name := filepath.Base(path)
- switch name {
- case "doc.go":
- continue // we have our own
- case "goimports.go":
- if i := strings.Index(body, "\t\"mvdan.cc/gofumpt"); i > 0 {
- body = body[:i] + "\n" + extraImport + "\n" + body[i:]
- }
- if i := strings.Index(body, "if !bytes.Equal"); i > 0 {
- body = body[:i] + "\n" + extraSrc + "\n" + body[i:]
- }
- }
- body = strings.Replace(body, "goimports", "gofumports", -1)
-
- writeFile(filepath.Join("gofumports", name), body)
- }
-}
-
-func copyInternal(pkg *Package, dir string) {
- for _, path := range sourceFiles(pkg) {
- body := readFile(path)
- body = fixImports(body)
- name := filepath.Base(path)
- writeFile(filepath.Join(dir, name), body)
- }
-}
-
-func fixImports(body string) string {
- body = strings.Replace(body,
- "golang.org/x/tools/internal/",
- "mvdan.cc/gofumpt/gofumports/internal/",
- -1)
- body = strings.Replace(body,
- "cmd/internal/",
- "mvdan.cc/gofumpt/internal/",
- -1)
- return body
-}