Go Tooling

Notes based on:

go build

/tmp/demo via 🐹 v1.15.6
$ cat main.go
package main

import (
	"fmt"
	"log"
)

func main() {
	fmt.Println("Hello, YouTube")
	log.Println("Hello, log")
}

$ go build

$ ls
demo*  main.go

$ file demo
demo: Mach-O 64-bit executable x86_64

$ GOOS=windows go build

$ ls
demo*  demo.exe*  main.go

$ file demo.exe
demo.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

go install

/tmp/demo via 🐹 v1.15.6
$ go install

$ $GOPATH/bin/demo
Hello, YouTube
2021/01/01 13:04:42 Hello, log

go get

  • go get -d: download the code, but do not compile anything.
  • go get -u: even if the code is already stored in GOPATH, download the latest version.
  • go get -v: enable verbose mode.

See: github.com/campoy/go-tooling-workshop/blob/master/1-source-code/1-workspace/1-intro.md

/tmp/demo via 🐹 v1.15.6
$ go get github.com/golang/example/hello

$ $GOPATH/bin/hello
Hello, Go examples!

$ tail $GOPATH/src/github.com/golang/example/hello/hello.go

import (
	"fmt"

	"github.com/golang/example/stringutil"
)

func main() {
	fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))
}

The Go tool will first try to find a match to the import path in the standard library and your GOPATH. If it doesn’t find the package, it considers the path to be a URL pointing to a repository where the code can be found.

The go tool is able to fetch code from most repositories, as long as they support one of the following: git, svn, hg, bzr.

Note that "github.com/golang/example/stringutil" is not technically pointing to any code on GitHub, as the actual URL to the code would also include https and the branch (probably master). The go tool is able to figure these differences out, and it will store the code under $GOPATH/src/{import_path}.

go list

go list allows you to obtain information about your workspace and the packages stored in it.

NOTE: in order to list all the packages in the standard library you can simply run go list std.

See: github.com/campoy/go-tooling-workshop/blob/master/1-source-code/1-workspace/2-tooling.md

/tmp/demo via 🐹 v1.15.6
$ go list
_/tmp/demo

# Same as above
$ go list -f '{{.ImportPath}}'
_/tmp/demo

# List name of the package
$ go list -f '{{ .Name }}'
main

# And documentation
$ go list -f '{{ .Name }}: {{ .Doc }}'
main:

# add comment to the beginning of file
$ head -n7 main.go
// awesome demo
package main

import (
	"fmt"
	"log"
)

$ go list -f '{{ .Name }}: {{ .Doc }}'
main: awesome demo

# List imports of main package
$ go list -f '{{ .Imports }}'
[fmt log]

# List dependency packages of log
$ go list -f '{{ .Imports }}' log
[fmt io os runtime sync time]

$ go list -f '{{ join .Imports "\n" }}' log
fmt
io
os
runtime
sync
time

In a similar way to how the PATH environment variable can contain multiple directories, which are used to find binaries in your system, GOPATH can contain multiple directories which will be used consecutively to find the packages you import.

The go tool will try to find a package in the first component of GOPATH, and fallback to the second one only if the package wasn’t found yet. Whenever you run go get the source code will be stored under the first GOPATH component.

$ go list -f '{{ join .Imports "\n" }}' io
errors
sync

$ go list -f '{{join .Imports "\n"}}' github.com/golang/example/hello 
fmt 
github.com/golang/example/stringutil


$ go list github.com/golang/example/...    # remember that ... means "and everything below"
github.com/golang/example/appengine-hello
github.com/golang/example/gotypes
...


$ go list -json github.com/golang/example/hello
{
	"Dir": "/Users/aung/go/src/github.com/golang/example/hello",
	"ImportPath": "github.com/golang/example/hello",
	"Name": "main",
...


$ go list -f '{{join .GoFiles "\n"}}' github.com/golang/example/... 
app.go
weave.go
main.go
...

$ go list -f '{{.ImportPath}}: {{.Imports}}' image/… | grep "internal"
image/draw: [image image/color image/internal/imageutil]
image/internal/imageutil: [image]
image/jpeg: [bufio errors image image/color image/internal/imageutil io]

The vendor directory

The vendor directory is used to keep third party dependencies to your project.

Whenever an import path "example.com/foo" is found in Go code, the go tool will navigate the directory tree towards the root, looking for a directory containing a vendor directory.

When a vendor directory is found, the go tool will look into vendor/example.com/foo, if that directory exists it will be used as the package imported with the path "example.com/foo". If it doesn’t exist the go tool will continue upwards looking for other vendor directories, and eventually in GOPATH.

https://golang.org/s/go15vendor

The internal directory

Go visibility rules for types are quite straight forward:

  • Identifiers that start with a capital letter are visible to everyone.
  • All other identifiers are visible only from the package that defines them.

With this two rules it is impossible to provide an identifier that is visible from only a subset of other packages, that is why the go tool supports internal directories.

The packages inside of an internal directory are only accessible to those packages that are siblings of that internal package or are contained by one of those siblings.

https://golang.org/s/go14internal

go doc

  • go doc fmt
  • go doc fmt Printf
/tmp/demo via 🐹 v1.15.6 took 12s
$ go doc
awesome demo

$ go doc fmt | head -n20
package fmt // import "fmt"

Package fmt implements formatted I/O with functions analogous to C's printf
and scanf. The format 'verbs' are derived from C's but are simpler.


Printing

The verbs:

General:

    %v	the value in a default format
    	when printing structs, the plus flag (%+v) adds field names
    %#v	a Go-syntax representation of the value
    %T	a Go-syntax representation of the type of the value
    %%	a literal percent sign; consumes no value
...


$ go doc fmt | tail -n20
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)
func Scan(a ...interface{}) (n int, err error)
...
type Scanner interface{ ... }
type State interface{ ... }
type Stringer interface{ ... }



$ go doc fmt Printf
 package fmt // import "fmt"
 func Printf(format string, a …interface{}) (n int, err error)
     Printf formats according to a format specifier and writes to standard
     output. It returns the number of bytes written and any write error
     encountered.
$ $GOPATH/godoc -http=:6060

Go to: http://localhost:6060

go help fmt

$ go help fmt
usage: go fmt [-n] [-x] [packages]

Fmt runs the command 'gofmt -l -w' on the packages named
by the import paths. It prints the names of the files that are modified.

For more about gofmt, see 'go doc cmd/gofmt'.
For more about specifying packages, see 'go help packages'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
$ gofmt --help
usage: gofmt [flags] [path ...]
  -cpuprofile string
    	write cpu profile to this file
  -d	display diffs instead of rewriting files
  -e	report all errors (not just the first 10 on different lines)
  -l	list files whose formatting differs from gofmt's
  -r string
    	rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')
  -s	simplify code
  -w	write result to (source) file instead of stdout
$ go doc cmd/gofmt
Gofmt formats Go programs. It uses tabs for indentation and blanks for
alignment. Alignment assumes that an editor is using a fixed-width font.

Without an explicit path, it processes the standard input. Given a file, it
operates on that file; given a directory, it operates on all .go files in
that directory, recursively. (Files starting with a period are ignored.) By
default, gofmt prints the reformatted sources to standard output.

Usage:

    gofmt [flags] [path ...]

...

Examples

To check files for unnecessary parentheses:

    gofmt -r '(a) -> a' -l *.go

To remove the parentheses:

    gofmt -r '(a) -> a' -w *.go

To convert the package tree from explicit slice upper bounds to implicit
ones:

    gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src


The simplify command

When invoked with -s gofmt will make the following source transformations
where possible.

    An array, slice, or map composite literal of the form:
    	[]T{T{}, T{}}
    will be simplified to:
    	[]T{{}, {}}

    A slice expression of the form:
    	s[a:len(s)]
    will be simplified to:
    	s[a:]

    A range of the form:
    	for x, _ = range v {...}
    will be simplified to:
    	for x = range v {...}

    A range of the form:
    	for _ = range v {...}
    will be simplified to:
    	for range v {...}

This may result in changes that are incompatible with earlier versions of
Go.

go help vet

$ go help vet
usage: go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]

Vet runs the Go vet command on the packages named by the import paths.

For more about vet and its flags, see 'go doc cmd/vet'.
For more about specifying packages, see 'go help packages'.
For a list of checkers and their flags, see 'go tool vet help'.
For details of a specific checker such as 'printf', see 'go tool vet help printf'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
...
$ go doc cmd/vet
Vet examines Go source code and reports suspicious constructs, such as
Printf calls whose arguments do not align with the format string. Vet uses
heuristics that do not guarantee all reports are genuine problems, but it
can find errors not caught by the compilers.

Vet is normally invoked through the go command. This command vets the
package in the current directory:

    go vet

whereas this one vets the packages whose path is provided:

    go vet my/project/...

Use "go help packages" to see other ways of specifying which packages to
vet.

Vet's exit code is non-zero for erroneous invocation of the tool or if a
problem was reported, and 0 otherwise. Note that the tool does not check
every possible problem and depends on unreliable heuristics, so it should be
used as guidance only, not as a firm indicator of program correctness.
...

go help fix

$ go help fix
usage: go fix [packages]

Fix runs the Go fix command on the packages named by the import paths.

For more about fix, see 'go doc cmd/fix'.
For more about specifying packages, see 'go help packages'.

To run fix with specific options, run 'go tool fix'.

See also: go fmt, go vet.
$ go doc cmd/fix
Fix finds Go programs that use old APIs and rewrites them to use newer ones.
After you update to a new Go release, fix helps make the necessary changes
to your programs.

Usage:

    go tool fix [-r name,...] [path ...]

Without an explicit path, fix reads standard input and writes the result to
standard output.

If the named path is a file, fix rewrites the named files in place. If the
named path is a directory, fix rewrites all .go files in that directory
tree. When fix rewrites a file, it prints a line to standard error giving
the name of the file and the rewrite applied.

If the -diff flag is set, no files are rewritten. Instead fix prints the
differences a rewrite would introduce.

The -r flag restricts the set of rewrites considered to those in the named
list. By default fix considers all known rewrites. Fix's rewrites are
idempotent, so that it is safe to apply fix to updated or partially updated
code even without using the -r flag.

Fix prints the full list of fixes it can apply in its help output; to see
them, run go tool fix -help.

Fix does not make backup copies of the files that it edits. Instead, use a
version control system's “diff” functionality to inspect the changes that
fix makes before committing them.

Leave a Comment

Your email address will not be published. Required fields are marked *