github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/list/list.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package list implements the ``go list'' command.
     6  package list
     7  
     8  import (
     9  	"bufio"
    10  	"bytes"
    11  	"encoding/json"
    12  	"io"
    13  	"os"
    14  	"sort"
    15  	"strings"
    16  	"text/template"
    17  
    18  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/base"
    19  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cache"
    20  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg"
    21  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/load"
    22  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/modload"
    23  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/str"
    24  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/work"
    25  )
    26  
    27  var CmdList = &base.Command{
    28  	// Note: -f -json -m are listed explicitly because they are the most common list flags.
    29  	// Do not send CLs removing them because they're covered by [list flags].
    30  	UsageLine: "go list [-f format] [-json] [-m] [list flags] [build flags] [packages]",
    31  	Short:     "list packages or modules",
    32  	Long: `
    33  List lists the named packages, one per line.
    34  The most commonly-used flags are -f and -json, which control the form
    35  of the output printed for each package. Other list flags, documented below,
    36  control more specific details.
    37  
    38  The default output shows the package import path:
    39  
    40      bytes
    41      encoding/json
    42      github.com/gorilla/mux
    43      golang.org/x/net/html
    44  
    45  The -f flag specifies an alternate format for the list, using the
    46  syntax of package template. The default output is equivalent
    47  to -f '{{.ImportPath}}'. The struct being passed to the template is:
    48  
    49      type Package struct {
    50          Dir           string   // directory containing package sources
    51          ImportPath    string   // import path of package in dir
    52          ImportComment string   // path in import comment on package statement
    53          Name          string   // package name
    54          Doc           string   // package documentation string
    55          Target        string   // install path
    56          Shlib         string   // the shared library that contains this package (only set when -linkshared)
    57          Goroot        bool     // is this package in the Go root?
    58          Standard      bool     // is this package part of the standard Go library?
    59          Stale         bool     // would 'go install' do anything for this package?
    60          StaleReason   string   // explanation for Stale==true
    61          Root          string   // Go root or Go path dir containing this package
    62          ConflictDir   string   // this directory shadows Dir in $GOPATH
    63          BinaryOnly    bool     // binary-only package (no longer supported)
    64          ForTest       string   // package is only for use in named test
    65          Export        string   // file containing export data (when using -export)
    66          Module        *Module  // info about package's containing module, if any (can be nil)
    67          Match         []string // command-line patterns matching this package
    68          DepOnly       bool     // package is only a dependency, not explicitly listed
    69  
    70          // Source files
    71          GoFiles         []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
    72          CgoFiles        []string // .go source files that import "C"
    73          CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
    74          IgnoredGoFiles  []string // .go source files ignored due to build constraints
    75          CFiles          []string // .c source files
    76          CXXFiles        []string // .cc, .cxx and .cpp source files
    77          MFiles          []string // .m source files
    78          HFiles          []string // .h, .hh, .hpp and .hxx source files
    79          FFiles          []string // .f, .F, .for and .f90 Fortran source files
    80          SFiles          []string // .s source files
    81          SwigFiles       []string // .swig files
    82          SwigCXXFiles    []string // .swigcxx files
    83          SysoFiles       []string // .syso object files to add to archive
    84          TestGoFiles     []string // _test.go files in package
    85          XTestGoFiles    []string // _test.go files outside package
    86  
    87          // Cgo directives
    88          CgoCFLAGS    []string // cgo: flags for C compiler
    89          CgoCPPFLAGS  []string // cgo: flags for C preprocessor
    90          CgoCXXFLAGS  []string // cgo: flags for C++ compiler
    91          CgoFFLAGS    []string // cgo: flags for Fortran compiler
    92          CgoLDFLAGS   []string // cgo: flags for linker
    93          CgoPkgConfig []string // cgo: pkg-config names
    94  
    95          // Dependency information
    96          Imports      []string          // import paths used by this package
    97          ImportMap    map[string]string // map from source import to ImportPath (identity entries omitted)
    98          Deps         []string          // all (recursively) imported dependencies
    99          TestImports  []string          // imports from TestGoFiles
   100          XTestImports []string          // imports from XTestGoFiles
   101  
   102          // Error information
   103          Incomplete bool            // this package or a dependency has an error
   104          Error      *PackageError   // error loading package
   105          DepsErrors []*PackageError // errors loading dependencies
   106      }
   107  
   108  Packages stored in vendor directories report an ImportPath that includes the
   109  path to the vendor directory (for example, "d/vendor/p" instead of "p"),
   110  so that the ImportPath uniquely identifies a given copy of a package.
   111  The Imports, Deps, TestImports, and XTestImports lists also contain these
   112  expanded import paths. See golang.org/s/go15vendor for more about vendoring.
   113  
   114  The error information, if any, is
   115  
   116      type PackageError struct {
   117          ImportStack   []string // shortest path from package named on command line to this one
   118          Pos           string   // position of error (if present, file:line:col)
   119          Err           string   // the error itself
   120      }
   121  
   122  The module information is a Module struct, defined in the discussion
   123  of list -m below.
   124  
   125  The template function "join" calls strings.Join.
   126  
   127  The template function "context" returns the build context, defined as:
   128  
   129      type Context struct {
   130          GOARCH        string   // target architecture
   131          GOOS          string   // target operating system
   132          GOROOT        string   // Go root
   133          GOPATH        string   // Go path
   134          CgoEnabled    bool     // whether cgo can be used
   135          UseAllFiles   bool     // use files regardless of +build lines, file names
   136          Compiler      string   // compiler to assume when computing target paths
   137          BuildTags     []string // build constraints to match in +build lines
   138          ReleaseTags   []string // releases the current release is compatible with
   139          InstallSuffix string   // suffix to use in the name of the install dir
   140      }
   141  
   142  For more information about the meaning of these fields see the documentation
   143  for the go/build package's Context type.
   144  
   145  The -json flag causes the package data to be printed in JSON format
   146  instead of using the template format.
   147  
   148  The -compiled flag causes list to set CompiledGoFiles to the Go source
   149  files presented to the compiler. Typically this means that it repeats
   150  the files listed in GoFiles and then also adds the Go code generated
   151  by processing CgoFiles and SwigFiles. The Imports list contains the
   152  union of all imports from both GoFiles and CompiledGoFiles.
   153  
   154  The -deps flag causes list to iterate over not just the named packages
   155  but also all their dependencies. It visits them in a depth-first post-order
   156  traversal, so that a package is listed only after all its dependencies.
   157  Packages not explicitly listed on the command line will have the DepOnly
   158  field set to true.
   159  
   160  The -e flag changes the handling of erroneous packages, those that
   161  cannot be found or are malformed. By default, the list command
   162  prints an error to standard error for each erroneous package and
   163  omits the packages from consideration during the usual printing.
   164  With the -e flag, the list command never prints errors to standard
   165  error and instead processes the erroneous packages with the usual
   166  printing. Erroneous packages will have a non-empty ImportPath and
   167  a non-nil Error field; other information may or may not be missing
   168  (zeroed).
   169  
   170  The -export flag causes list to set the Export field to the name of a
   171  file containing up-to-date export information for the given package.
   172  
   173  The -find flag causes list to identify the named packages but not
   174  resolve their dependencies: the Imports and Deps lists will be empty.
   175  
   176  The -test flag causes list to report not only the named packages
   177  but also their test binaries (for packages with tests), to convey to
   178  source code analysis tools exactly how test binaries are constructed.
   179  The reported import path for a test binary is the import path of
   180  the package followed by a ".test" suffix, as in "math/rand.test".
   181  When building a test, it is sometimes necessary to rebuild certain
   182  dependencies specially for that test (most commonly the tested
   183  package itself). The reported import path of a package recompiled
   184  for a particular test binary is followed by a space and the name of
   185  the test binary in brackets, as in "math/rand [math/rand.test]"
   186  or "regexp [sort.test]". The ForTest field is also set to the name
   187  of the package being tested ("math/rand" or "sort" in the previous
   188  examples).
   189  
   190  The Dir, Target, Shlib, Root, ConflictDir, and Export file paths
   191  are all absolute paths.
   192  
   193  By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir
   194  (that is, paths relative to Dir, not absolute paths).
   195  The generated files added when using the -compiled and -test flags
   196  are absolute paths referring to cached copies of generated Go source files.
   197  Although they are Go source files, the paths may not end in ".go".
   198  
   199  The -m flag causes list to list modules instead of packages.
   200  
   201  When listing modules, the -f flag still specifies a format template
   202  applied to a Go struct, but now a Module struct:
   203  
   204      type Module struct {
   205          Path      string       // module path
   206          Version   string       // module version
   207          Versions  []string     // available module versions (with -versions)
   208          Replace   *Module      // replaced by this module
   209          Time      *time.Time   // time version was created
   210          Update    *Module      // available update, if any (with -u)
   211          Main      bool         // is this the main module?
   212          Indirect  bool         // is this module only an indirect dependency of main module?
   213          Dir       string       // directory holding files for this module, if any
   214          GoMod     string       // path to go.mod file used when loading this module, if any
   215          GoVersion string       // go version used in module
   216          Error     *ModuleError // error loading module
   217      }
   218  
   219      type ModuleError struct {
   220          Err string // the error itself
   221      }
   222  
   223  The file GoMod refers to may be outside the module directory if the
   224  module is in the module cache or if the -modfile flag is used.
   225  
   226  The default output is to print the module path and then
   227  information about the version and replacement if any.
   228  For example, 'go list -m all' might print:
   229  
   230      my/main/module
   231      golang.org/x/text v0.3.0 => /tmp/text
   232      rsc.io/pdf v0.1.1
   233  
   234  The Module struct has a String method that formats this
   235  line of output, so that the default format is equivalent
   236  to -f '{{.String}}'.
   237  
   238  Note that when a module has been replaced, its Replace field
   239  describes the replacement module, and its Dir field is set to
   240  the replacement's source code, if present. (That is, if Replace
   241  is non-nil, then Dir is set to Replace.Dir, with no access to
   242  the replaced source code.)
   243  
   244  The -u flag adds information about available upgrades.
   245  When the latest version of a given module is newer than
   246  the current one, list -u sets the Module's Update field
   247  to information about the newer module.
   248  The Module's String method indicates an available upgrade by
   249  formatting the newer version in brackets after the current version.
   250  For example, 'go list -m -u all' might print:
   251  
   252      my/main/module
   253      golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
   254      rsc.io/pdf v0.1.1 [v0.1.2]
   255  
   256  (For tools, 'go list -m -u -json all' may be more convenient to parse.)
   257  
   258  The -versions flag causes list to set the Module's Versions field
   259  to a list of all known versions of that module, ordered according
   260  to semantic versioning, earliest to latest. The flag also changes
   261  the default output format to display the module path followed by the
   262  space-separated version list.
   263  
   264  The arguments to list -m are interpreted as a list of modules, not packages.
   265  The main module is the module containing the current directory.
   266  The active modules are the main module and its dependencies.
   267  With no arguments, list -m shows the main module.
   268  With arguments, list -m shows the modules specified by the arguments.
   269  Any of the active modules can be specified by its module path.
   270  The special pattern "all" specifies all the active modules, first the main
   271  module and then dependencies sorted by module path.
   272  A pattern containing "..." specifies the active modules whose
   273  module paths match the pattern.
   274  A query of the form path@version specifies the result of that query,
   275  which is not limited to active modules.
   276  See 'go help modules' for more about module queries.
   277  
   278  The template function "module" takes a single string argument
   279  that must be a module path or query and returns the specified
   280  module as a Module struct. If an error occurs, the result will
   281  be a Module struct with a non-nil Error field.
   282  
   283  For more about build flags, see 'go help build'.
   284  
   285  For more about specifying packages, see 'go help packages'.
   286  
   287  For more about modules, see 'go help modules'.
   288  	`,
   289  }
   290  
   291  func init() {
   292  	CmdList.Run = runList // break init cycle
   293  	work.AddBuildFlags(CmdList, work.DefaultBuildFlags)
   294  }
   295  
   296  var (
   297  	listCompiled = CmdList.Flag.Bool("compiled", false, "")
   298  	listDeps     = CmdList.Flag.Bool("deps", false, "")
   299  	listE        = CmdList.Flag.Bool("e", false, "")
   300  	listExport   = CmdList.Flag.Bool("export", false, "")
   301  	listFmt      = CmdList.Flag.String("f", "", "")
   302  	listFind     = CmdList.Flag.Bool("find", false, "")
   303  	listJson     = CmdList.Flag.Bool("json", false, "")
   304  	listM        = CmdList.Flag.Bool("m", false, "")
   305  	listU        = CmdList.Flag.Bool("u", false, "")
   306  	listTest     = CmdList.Flag.Bool("test", false, "")
   307  	listVersions = CmdList.Flag.Bool("versions", false, "")
   308  )
   309  
   310  var nl = []byte{'\n'}
   311  
   312  func runList(cmd *base.Command, args []string) {
   313  	modload.LoadTests = *listTest
   314  	work.BuildInit()
   315  	out := newTrackingWriter(os.Stdout)
   316  	defer out.w.Flush()
   317  
   318  	if *listFmt == "" {
   319  		if *listM {
   320  			*listFmt = "{{.String}}"
   321  			if *listVersions {
   322  				*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}`
   323  			}
   324  		} else {
   325  			*listFmt = "{{.ImportPath}}"
   326  		}
   327  	}
   328  
   329  	var do func(interface{})
   330  	if *listJson {
   331  		do = func(x interface{}) {
   332  			b, err := json.MarshalIndent(x, "", "\t")
   333  			if err != nil {
   334  				out.Flush()
   335  				base.Fatalf("%s", err)
   336  			}
   337  			out.Write(b)
   338  			out.Write(nl)
   339  		}
   340  	} else {
   341  		var cachedCtxt *Context
   342  		context := func() *Context {
   343  			if cachedCtxt == nil {
   344  				cachedCtxt = newContext(&cfg.BuildContext)
   345  			}
   346  			return cachedCtxt
   347  		}
   348  		fm := template.FuncMap{
   349  			"join":    strings.Join,
   350  			"context": context,
   351  			"module":  modload.ModuleInfo,
   352  		}
   353  		tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt)
   354  		if err != nil {
   355  			base.Fatalf("%s", err)
   356  		}
   357  		do = func(x interface{}) {
   358  			if err := tmpl.Execute(out, x); err != nil {
   359  				out.Flush()
   360  				base.Fatalf("%s", err)
   361  			}
   362  			if out.NeedNL() {
   363  				out.Write(nl)
   364  			}
   365  		}
   366  	}
   367  
   368  	if *listM {
   369  		// Module mode.
   370  		if *listCompiled {
   371  			base.Fatalf("go list -compiled cannot be used with -m")
   372  		}
   373  		if *listDeps {
   374  			// TODO(rsc): Could make this mean something with -m.
   375  			base.Fatalf("go list -deps cannot be used with -m")
   376  		}
   377  		if *listExport {
   378  			base.Fatalf("go list -export cannot be used with -m")
   379  		}
   380  		if *listFind {
   381  			base.Fatalf("go list -find cannot be used with -m")
   382  		}
   383  		if *listTest {
   384  			base.Fatalf("go list -test cannot be used with -m")
   385  		}
   386  
   387  		if modload.Init(); !modload.Enabled() {
   388  			base.Fatalf("go list -m: not using modules")
   389  		}
   390  
   391  		modload.InitMod() // Parses go.mod and sets cfg.BuildMod.
   392  		if cfg.BuildMod == "vendor" {
   393  			const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"
   394  
   395  			if *listVersions {
   396  				base.Fatalf(actionDisabledFormat, "determine available versions")
   397  			}
   398  			if *listU {
   399  				base.Fatalf(actionDisabledFormat, "determine available upgrades")
   400  			}
   401  
   402  			for _, arg := range args {
   403  				// In vendor mode, the module graph is incomplete: it contains only the
   404  				// explicit module dependencies and the modules that supply packages in
   405  				// the import graph. Reject queries that imply more information than that.
   406  				if arg == "all" {
   407  					base.Fatalf(actionDisabledFormat, "compute 'all'")
   408  				}
   409  				if strings.Contains(arg, "...") {
   410  					base.Fatalf(actionDisabledFormat, "match module patterns")
   411  				}
   412  			}
   413  		}
   414  
   415  		modload.LoadBuildList()
   416  
   417  		mods := modload.ListModules(args, *listU, *listVersions)
   418  		if !*listE {
   419  			for _, m := range mods {
   420  				if m.Error != nil {
   421  					base.Errorf("go list -m: %v", m.Error.Err)
   422  				}
   423  			}
   424  			base.ExitIfErrors()
   425  		}
   426  		for _, m := range mods {
   427  			do(m)
   428  		}
   429  		return
   430  	}
   431  
   432  	// Package mode (not -m).
   433  	if *listU {
   434  		base.Fatalf("go list -u can only be used with -m")
   435  	}
   436  	if *listVersions {
   437  		base.Fatalf("go list -versions can only be used with -m")
   438  	}
   439  
   440  	// These pairings make no sense.
   441  	if *listFind && *listDeps {
   442  		base.Fatalf("go list -deps cannot be used with -find")
   443  	}
   444  	if *listFind && *listTest {
   445  		base.Fatalf("go list -test cannot be used with -find")
   446  	}
   447  
   448  	load.IgnoreImports = *listFind
   449  	var pkgs []*load.Package
   450  	if *listE {
   451  		pkgs = load.PackagesAndErrors(args)
   452  	} else {
   453  		pkgs = load.Packages(args)
   454  	}
   455  
   456  	if cache.Default() == nil {
   457  		// These flags return file names pointing into the build cache,
   458  		// so the build cache must exist.
   459  		if *listCompiled {
   460  			base.Fatalf("go list -compiled requires build cache")
   461  		}
   462  		if *listExport {
   463  			base.Fatalf("go list -export requires build cache")
   464  		}
   465  		if *listTest {
   466  			base.Fatalf("go list -test requires build cache")
   467  		}
   468  	}
   469  
   470  	if *listTest {
   471  		c := cache.Default()
   472  		// Add test binaries to packages to be listed.
   473  		for _, p := range pkgs {
   474  			if p.Error != nil {
   475  				continue
   476  			}
   477  			if len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 {
   478  				var pmain, ptest, pxtest *load.Package
   479  				var err error
   480  				if *listE {
   481  					pmain, ptest, pxtest = load.TestPackagesAndErrors(p, nil)
   482  				} else {
   483  					pmain, ptest, pxtest, err = load.TestPackagesFor(p, nil)
   484  					if err != nil {
   485  						base.Errorf("can't load test package: %s", err)
   486  					}
   487  				}
   488  				if pmain != nil {
   489  					pkgs = append(pkgs, pmain)
   490  					data := *pmain.Internal.TestmainGo
   491  					h := cache.NewHash("testmain")
   492  					h.Write([]byte("testmain\n"))
   493  					h.Write(data)
   494  					out, _, err := c.Put(h.Sum(), bytes.NewReader(data))
   495  					if err != nil {
   496  						base.Fatalf("%s", err)
   497  					}
   498  					pmain.GoFiles[0] = c.OutputFile(out)
   499  				}
   500  				if ptest != nil && ptest != p {
   501  					pkgs = append(pkgs, ptest)
   502  				}
   503  				if pxtest != nil {
   504  					pkgs = append(pkgs, pxtest)
   505  				}
   506  			}
   507  		}
   508  	}
   509  
   510  	// Remember which packages are named on the command line.
   511  	cmdline := make(map[*load.Package]bool)
   512  	for _, p := range pkgs {
   513  		cmdline[p] = true
   514  	}
   515  
   516  	if *listDeps {
   517  		// Note: This changes the order of the listed packages
   518  		// from "as written on the command line" to
   519  		// "a depth-first post-order traversal".
   520  		// (The dependency exploration order for a given node
   521  		// is alphabetical, same as listed in .Deps.)
   522  		// Note that -deps is applied after -test,
   523  		// so that you only get descriptions of tests for the things named
   524  		// explicitly on the command line, not for all dependencies.
   525  		pkgs = load.PackageList(pkgs)
   526  	}
   527  
   528  	// Do we need to run a build to gather information?
   529  	needStale := *listJson || strings.Contains(*listFmt, ".Stale")
   530  	if needStale || *listExport || *listCompiled {
   531  		var b work.Builder
   532  		b.Init()
   533  		b.IsCmdList = true
   534  		b.NeedExport = *listExport
   535  		b.NeedCompiledGoFiles = *listCompiled
   536  		a := &work.Action{}
   537  		// TODO: Use pkgsFilter?
   538  		for _, p := range pkgs {
   539  			if len(p.GoFiles)+len(p.CgoFiles) > 0 {
   540  				a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p))
   541  			}
   542  		}
   543  		b.Do(a)
   544  	}
   545  
   546  	for _, p := range pkgs {
   547  		// Show vendor-expanded paths in listing
   548  		p.TestImports = p.Resolve(p.TestImports)
   549  		p.XTestImports = p.Resolve(p.XTestImports)
   550  		p.DepOnly = !cmdline[p]
   551  
   552  		if *listCompiled {
   553  			p.Imports = str.StringList(p.Imports, p.Internal.CompiledImports)
   554  		}
   555  	}
   556  
   557  	if *listTest {
   558  		all := pkgs
   559  		if !*listDeps {
   560  			all = load.PackageList(pkgs)
   561  		}
   562  		// Update import paths to distinguish the real package p
   563  		// from p recompiled for q.test.
   564  		// This must happen only once the build code is done
   565  		// looking at import paths, because it will get very confused
   566  		// if it sees these.
   567  		old := make(map[string]string)
   568  		for _, p := range all {
   569  			if p.ForTest != "" {
   570  				new := p.ImportPath + " [" + p.ForTest + ".test]"
   571  				old[new] = p.ImportPath
   572  				p.ImportPath = new
   573  			}
   574  			p.DepOnly = !cmdline[p]
   575  		}
   576  		// Update import path lists to use new strings.
   577  		m := make(map[string]string)
   578  		for _, p := range all {
   579  			for _, p1 := range p.Internal.Imports {
   580  				if p1.ForTest != "" {
   581  					m[old[p1.ImportPath]] = p1.ImportPath
   582  				}
   583  			}
   584  			for i, old := range p.Imports {
   585  				if new := m[old]; new != "" {
   586  					p.Imports[i] = new
   587  				}
   588  			}
   589  			for old := range m {
   590  				delete(m, old)
   591  			}
   592  		}
   593  		// Recompute deps lists using new strings, from the leaves up.
   594  		for _, p := range all {
   595  			deps := make(map[string]bool)
   596  			for _, p1 := range p.Internal.Imports {
   597  				deps[p1.ImportPath] = true
   598  				for _, d := range p1.Deps {
   599  					deps[d] = true
   600  				}
   601  			}
   602  			p.Deps = make([]string, 0, len(deps))
   603  			for d := range deps {
   604  				p.Deps = append(p.Deps, d)
   605  			}
   606  			sort.Strings(p.Deps)
   607  		}
   608  	}
   609  
   610  	// Record non-identity import mappings in p.ImportMap.
   611  	for _, p := range pkgs {
   612  		for i, srcPath := range p.Internal.RawImports {
   613  			path := p.Imports[i]
   614  			if path != srcPath {
   615  				if p.ImportMap == nil {
   616  					p.ImportMap = make(map[string]string)
   617  				}
   618  				p.ImportMap[srcPath] = path
   619  			}
   620  		}
   621  	}
   622  
   623  	for _, p := range pkgs {
   624  		do(&p.PackagePublic)
   625  	}
   626  }
   627  
   628  // TrackingWriter tracks the last byte written on every write so
   629  // we can avoid printing a newline if one was already written or
   630  // if there is no output at all.
   631  type TrackingWriter struct {
   632  	w    *bufio.Writer
   633  	last byte
   634  }
   635  
   636  func newTrackingWriter(w io.Writer) *TrackingWriter {
   637  	return &TrackingWriter{
   638  		w:    bufio.NewWriter(w),
   639  		last: '\n',
   640  	}
   641  }
   642  
   643  func (t *TrackingWriter) Write(p []byte) (n int, err error) {
   644  	n, err = t.w.Write(p)
   645  	if n > 0 {
   646  		t.last = p[n-1]
   647  	}
   648  	return
   649  }
   650  
   651  func (t *TrackingWriter) Flush() {
   652  	t.w.Flush()
   653  }
   654  
   655  func (t *TrackingWriter) NeedNL() bool {
   656  	return t.last != '\n'
   657  }