Skip to content

Dòng lệnh

Các lệnh trong Go bao gồm một bộ công cụ hoàn chỉnh, các lệnh này涵盖 tài liệu, định dạng, kiểm tra mã, biên dịch, kiểm thử, quản lý phụ thuộc và nhiều khía cạnh khác của phát triển Go.

text
bug         Báo cáo lỗi
build       Biên dịch gói và các phụ thuộc
clean       Xóa các tệp đối tượng
doc         Hiển thị tài liệu trong mã nguồn
env         Xem thông tin biến môi trường Go
fix         Sửa các vấn đề tương thích API do thay đổi phiên bản go
fmt         Định dạng mã nguồn
generate    Tạo mã
get         Thêm phụ thuộc
install     Cài đặt và biên dịch gói
list        Lệnh liệt kê gói/mô-đun
mod         Lệnh bảo trì mô-đun
work        Lệnh bảo trì vùng làm việc
run         Biên dịch và chạy
test        Kiểm thử
tool        Chạy công cụ go được chỉ định
version     Hiển thị thông tin phiên bản go
vet         Quét và hiển thị các vấn đề tiềm ẩn trong mã nguồn

Bài viết này chỉ đơn giản trình bày và giới thiệu cách sử dụng của chúng, tất cả nội dung tham khảo từ tài liệu chính thức, nếu muốn tìm hiểu thêm chi tiết có thể truy cập cmd/go.

help

Lệnh đầu tiên cần biết là lệnh help, thông qua nó có thể đọc cách sử dụng của các lệnh. Có hai cách sử dụng, nếu muốn lấy thông tin sử dụng ngắn gọn, có thể thêm cờ -h sau lệnh được chỉ định, ví dụ

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

go sẽ hiển thị ngắn gọn cách sử dụng của lệnh đó, nó cũng提示 rằng muốn lấy thông tin chi tiết hơn cần sử dụng lệnh help

sh
$ go help env
usage: go env [-json] [-u] [-w] [var ...]

Env prints Go environment information.

By default env prints information as a shell script
(on Windows, a batch file). If one or more variable
names is given as arguments, env prints the value of
each named variable on its own line.

The -json flag prints the environment in JSON format
instead of as a shell script.

The -u flag requires one or more arguments and unsets
the default setting for the named environment variables,
if one has been set with 'go env -w'.

The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings
of the named environment variables to the given values.

For more about environment variables, see 'go help environment'.

Sử dụng thành thạo lệnh help, thông qua nó bạn có thể lấy nhiều thông tin liên quan đến lệnh.

doc

sh
$ go doc -h
Usage of [go] doc:
        go doc
        go doc <pkg>
        go doc <sym>[.<methodOrField>]
        go doc [<pkg>.]<sym>[.<methodOrField>]
        go doc [<pkg>.][<sym>.]<methodOrField>
        go doc <pkg> <sym>[.<methodOrField>]
For more information run
        go help doc

Flags:
  -C dir
        change to dir before running command
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -short
        one-line representation for each symbol
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported

Lệnh doc sẽ xuất tài liệu chú thích của gói, hằng số, hàm, kiểu, biến, phương thức và thậm chí cả trường cấu trúc được chỉ định. Khi không có tham số nào, nó sẽ xuất chú thích của gói hiện tại

sh
$ go doc

Cũng có thể chỉ định xem một gói nào đó, ví dụ xem tài liệu chú thích của gói runtime

sh
$ go doc runtime
package runtime // import "runtime"

Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type
information used by the reflect package; see reflect's documentation for the
programmable interface to the run-time type system.
......

Hoặc một kiểu nào đó

sh
$ go doc unsafe.Pointer
package unsafe // import "unsafe"

type Pointer *ArbitraryType
    Pointer represents a pointer to an arbitrary type. There are four special
    operations available for type Pointer that are not available for other
    types:
      - A pointer value of any type can be converted to a Pointer.
      - A Pointer can be converted to a pointer value of any type.
      - A uintptr can be converted to a Pointer.
      - A Pointer can be converted to a uintptr.
      ...

Hoặc một hàm nào đó

sh
$ go doc runtime.GC
package runtime // import "runtime"

func GC()
    GC runs a garbage collection and blocks the caller until the garbage
    collection is complete. It may also block the entire program.

Nó có các cờ常用 sau

  • -u: Xem các kiểu riêng tư
  • -all: Xem tất cả tài liệu của gói được chỉ định
  • -short: Chỉ hiển thị mô tả ngắn gọn một dòng
  • -src: Xuất mã nguồn
  • -cmd: Đối với một số gói thuộc lệnh go, cũng xuất tài liệu mã trong gói của chúng.

Ví dụ xem biến runtime.inf, đây là một biến không được export

sh
$ go doc -u runtime.inf
package runtime // import "runtime"

var inf = float64frombits(0x7FF0000000000000)

Sử dụng tốt lệnh doc có thể giúp bạn đọc tài liệu thuận tiện hơn.

Một cách khác để đọc tài liệu lệnh là đọc mã nguồn, bởi vì tài liệu của một số lệnh không được viết kỹ lưỡng, ngược lại trong mã nguồn sẽ có giải thích chi tiết hơn. Vì tất cả các lệnh này đều được viết bằng go, nên việc đọc cũng rất thuận tiện. Các lệnh này đều nằm trong gói src/cmd, mỗi gói con là một lệnh riêng, entry point nằm trong tệp cmd/go/main.go

go
func init() {
    base.Go.Commands = []*base.Command{
       bug.CmdBug,
       work.CmdBuild,
       clean.CmdClean,
       doc.CmdDoc,
       envcmd.CmdEnv,
       fix.CmdFix,
       fmtcmd.CmdFmt,
       generate.CmdGenerate,
       modget.CmdGet,
       work.CmdInstall,
       list.CmdList,
       modcmd.CmdMod,
       workcmd.CmdWork,
       run.CmdRun,
       test.CmdTest,
       tool.CmdTool,
       version.CmdVersion,
       vet.CmdVet,

       help.HelpBuildConstraint,
       help.HelpBuildmode,
       help.HelpC,
       help.HelpCache,
       help.HelpEnvironment,
       help.HelpFileType,
       modload.HelpGoMod,
       help.HelpGopath,
       get.HelpGopathGet,
       modfetch.HelpGoproxy,
       help.HelpImportPath,
       modload.HelpModules,
       modget.HelpModuleGet,
       modfetch.HelpModuleAuth,
       help.HelpPackages,
       modfetch.HelpPrivate,
       test.HelpTestflag,
       test.HelpTestfunc,
       modget.HelpVCS,
    }
}

Ở đây bạn sẽ tìm thấy tất cả các lệnh con của go, cũng như thông tin tài liệu trợ giúp của chúng.

bug

sh
$ go help bug
usage: go bug

Bug opens the default browser and starts a new bug report.
The report includes useful system information.

Lệnh này không có tham số và cờ nào, nó sẽ dùng trình duyệt mặc định của bạn để truy cập trang issue của kho lưu trữ github.com/golang/go, thuận tiện cho bạn phản hồi bug, ngoài ra không có chức năng nào khác.

version

Thông qua lệnh version có thể xem thông tin phiên bản go hiện tại.

go
$ go version -h
usage: go version [-m] [-v] [file ...]

Khi thực thi không có tham số nào, nó sẽ xuất phiên bản go hiện tại

sh
$ go version
go version go1.21.0 windows/amd64

Nó cũng nhận đường dẫn tệp làm tham số, nó sẽ xuất phiên bản go được sử dụng khi biên dịch tất cả các tệp nhị phân có thể nhận dạng được trong đường dẫn đó.

sh
$ go version -v ./
buf.exe: go1.20.2
cobra-cli.exe: go1.21.0
dlv.exe: go1.20.2
goctl.exe: go1.20.2
goimports.exe: go1.20.2
golangci-lint.exe: go1.20.2
gopls.exe: go1.19.3
kratos.exe: go1.20.2
main.exe: go1.19.1
protoc-gen-go-grpc.exe: go1.20.2
protoc-gen-go-http.exe: go1.20.2
protoc-gen-go.exe: go1.20.2
protoc-gen-openapi.exe: go1.20.2
swag.exe: go1.21.0
wire.exe: go1.21.0

Trong đó tham số -v chỉ định lệnh version thử xuất phiên bản go của các tệp không thể nhận dạng, tham số -m xuất thông tin mô-đun của tệp nhị phân, cũng như một số tham số biên dịch, dưới đây là một ví dụ đơn giản.

sh
$ go version -v -m wire.exe
wire.exe: go1.21.0
        path    github.com/google/wire/cmd/wire
        mod     github.com/google/wire  v0.5.0  h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
        dep     github.com/google/subcommands   v1.0.1  h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
        dep     github.com/pmezard/go-difflib   v1.0.0  h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
        dep     golang.org/x/tools      v0.0.0-20190422233926-fe54fb35175b      h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
        build   -buildmode=exe
        build   -compiler=gc
        build   DefaultGODEBUG=panicnil=1
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=amd64
        build   GOOS=windows
        build   GOAMD64=v1

Bản thân go cũng là một tệp nhị phân, thực ra trong trường hợp không có tham số nào, go version xuất chính phiên bản go của tệp nhị phân của nó, vì tất cả các công cụ của cmd/go đều được triển khai bằng chính ngôn ngữ go.

env

Thông qua lệnh env có thể xem tất cả thông tin biến môi trường go, việc thay đổi các biến môi trường này sẽ ảnh hưởng đến hành vi của công cụ go.

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

Thực thi lệnh này không có tham số nào sẽ xuất giá trị của tất cả biến môi trường go

sh
$ go env
set GO111MODULE=on
set GOARCH=amd64
...

Đưa tên một biến môi trường nào đó làm tham số có thể chỉ xuất giá trị của biến đó

sh
$ go env GO111MODULE
on

Thêm -json có thể xuất dưới dạng json

sh
$ go env -json
{
        "AR": "ar",
        "CC": "gcc",
    ......
}

Thông qua cờ -w, và dùng dạng var=value làm tham số, sẽ thay đổi vĩnh viễn giá trị của một biến nào đó

sh
$ go env -w GO111MODULE=on

Sử dụng cờ -u, có thể khôi phục một biến nào đó về giá trị mặc định

sh
$ go env -u GO111MODULE

Thực thi go help environment có thể xem giới thiệu của từng biến môi trường

sh
$ go help environment
The go command and the tools it invokes consult environment variables
for configuration. If an environment variable is unset or empty, the go
command uses a sensible default setting. To see the effective setting of
the variable <NAME>, run 'go env <NAME>'. To change the default setting,
run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
are recorded in a Go environment configuration file stored in the
per-user configuration directory, as reported by os.UserConfigDir.
The location of the configuration file can be changed by setting
the environment variable GOENV, and 'go env GOENV' prints the
effective location, but 'go env -w' cannot change the default location.
See 'go help env' for details.

General-purpose environment variables:

        GO111MODULE
                Controls whether the go command runs in module-aware mode or GOPATH mode.
                May be "off", "on", or "auto".
                See https://golang.org/ref/mod#mod-commands.
        GCCGO
                The gccgo command to run for 'go build -compiler=gccgo'.
        GOARCH
                The architecture, or processor, for which to compile code.
                Examples are amd64, 386, arm, ppc64.
        GOBIN
                The directory where 'go install' will install a command.
        GOCACHE
                The directory where the go command will store cached
                information for reuse in future builds.
    ......

Dưới đây giới thiệu một số biến môi trường常用

GOVERSION

Giá trị của biến môi trường này phụ thuộc vào phiên bản go, và số phiên bản đến từ tệp $GOROOT/VERSION, tệp này ghi lại phiên bản go hiện tại và thời gian build.

sh
$ cat $GOROOT/VERSION
go1.21.3
time 2023-10-09T17:04:35Z

Giá trị của biến runtime.Version giống với giá trị của GOVERSION, và biến môi trường này không thể bị thay đổi.

GOENV

Trong thư mục $GOROOT sẽ có một tệp cấu hình mặc định tên là go.env

sh
$ cat $GOROOT/go.env
# This file contains the initial defaults for go command configuration.
# Values set by 'go env -w' and written to the user's go/env file override these.
# The environment overrides everything else.

# Use the Go module mirror and checksum database by default.
# See https://proxy.golang.org for details.
GOPROXY=https://proxy.golang.org,direct
GOSUMDB=sum.golang.org

# Automatically download newer toolchains as directed by go.mod files.
# See https://go.dev/doc/toolchain for details.
GOTOOLCHAIN=auto

Định dạng của nó đơn giản là key=value, giá trị biến môi trường được thay đổi bằng lệnh go env -w key=value sẽ được ghi vào tệp cấu hình. Tuy nhiên cũng có thể không dùng tệp cấu hình mặc định, biến môi trường GOENV có thể chỉ định thủ công địa chỉ tệp cấu hình env, và giá trị của biến môi trường GOENV chỉ có thể bị ghi đè bởi biến môi trường hệ điều hành, không thể bị thay đổi bằng lệnh go env -w.

GOHOSTARCH

Đại diện cho kiến trúc CPU của máy này, chỉ dùng để hiển thị, giá trị của biến môi trường này không được đọc từ tệp cấu hình và cũng không thể bị thay đổi.

GOHOSTOS

Đại diện cho hệ điều hành của máy này, chỉ dùng để hiển thị, giá trị của biến môi trường này không được đọc từ tệp cấu hình và cũng không thể bị thay đổi.

GOOS

Khi biên dịch, giá trị của GOOS sẽ quyết định biên dịch mã nguồn thành tệp nhị phân của hệ thống mục tiêu nào, giá trị mặc định là GOHOSTOS, tức là hệ điều hành của máy này, nó có các tùy chọn sau

  • linux
  • darwin
  • windows
  • netbsd
  • aix
  • android

Thực tế các hệ điều hành được hỗ trợ không chỉ có这些, sử dụng lệnh go tool dist list, xem tất cả các giá trị được hỗ trợ

sh
$ go tool dist list | awk -F '/' '{print $1}' | awk '!seen[$0]++'
aix
android
darwin
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
wasip1
windows

GOARCH

Khi biên dịch, giá trị của GOARCH sẽ quyết định sử dụng kiến trúc CPU nào khi biên dịch, giá trị mặc định là GOHOSTARCH, tức là kiến trúc CPU của máy này, nó có các tùy chọn sau

  • amd64
  • 386
  • arm
  • ppc64

Thực tế các kiến trúc được hỗ trợ không chỉ có这些, sử dụng lệnh go tool dist list, xem tất cả các giá trị được hỗ trợ

sh
$ go tool dist list | awk -F '/' '{print $2}' | awk '!seen[$0]++'
ppc64
386
amd64
arm
arm64
riscv64
wasm
loong64
mips
mips64
mips64le
mipsle
ppc64le
s390x

Cần lưu ý rằng GOOSGOARCH không thể kết hợp tùy ý, một số hệ điều hành chỉ có thể hỗ trợ các kiến trúc CPU cụ thể.

GOROOT

GOROOT đại diện cho thư mục gốc của vị trí cài đặt go, giá trị của GOROOT không thể bị thay đổi trực tiếp và chỉ có thể bị ghi đè bởi biến môi trường hệ điều hành.

sh
$ ls $GOROOT -1
api
bin
codereview.cfg
CONTRIBUTING.md
doc
go.env
lib
LICENSE
misc
PATENTS
pkg
README.md
SECURITY.md
src
test
VERSION

Trong thư mục gốc có một số thư mục hoặc tệp quan trọng sau

  • lib, chứa một số phụ thuộc, hiện tại chỉ có một thư viện chứa thông tin múi giờ của các quốc gia trên thế giới, nằm trong $GOROOT/lib/time, các tệp nhị phân sau khi biên dịch sẽ không chứa thông tin múi giờ này.

  • pkg, chứa một số thư viện công cụ và tệp tiêu đề, ví dụ lệnh go tool sẽ tìm kiếm các tệp nhị phân của công cụ go trong thư mục $GOROOT/pkg/tool

  • bin, chứa các tệp nhị phân, mặc định chỉ có các tệp thực thi gogofmt, $GOROOT/bin nên được thêm vào biến hệ thống, nếu không sẽ không thể sử dụng các lệnh go.

  • src, chứa mã nguồn go

  • VERSION, tệp này chứa thông tin phiên bản của go

  • go.env, tệp này là tệp cấu hình env mặc định

GOPATH

Giá trị mặc định của GOPATH$HOME/go, giá trị của biến môi trường này chỉ định nơi tìm kiếm các tệp được import khi phân tích cú pháp câu lệnh import. Trong thời kỳ đầu không có gomod, GOPATH được dùng专门 để chứa các thư viện bên thứ ba, cấu trúc của nó như sau

sh
GOPATH=/home/user/go

/home/user/go/
    src/
        foo/
            bar/               (go code in package bar)
                x.go
            quux/              (go code in package main)
                y.go
    bin/
        quux                   (installed command)
    pkg/
        linux_amd64/
            foo/
                bar.a          (installed package object)

Sau khi gomod ra đời, GOPATH chỉ là nơi chứa các phụ thuộc được tải xuống bằng go get, cũng như dùng để chứa các tệp nhị phân được tải xuống và biên dịch bằng go install. Cần lưu ý rằng vị trí của GOPATH không thể giống với GOROOT, nếu không sẽ không có tác dụng gì.

sh
$ go env GOBIN
warning: GOPATH set to GOROOT (/home/user/go) has no effect

Tính đến thời điểm笔者 viết bài này, phiên bản go đã là go1.21.3, ngoài các dự án rất cũ, hầu như không ai còn dùng gopath để quản lý phụ thuộc nữa.

GOBIN

GOBIN dùng để chứa các tệp thực thi nhị phân bên thứ ba được tải xuống và biên dịch bằng go install, giá trị mặc định của nó là $GOPATH/bin. Giống như $GOROOT/bin, thư mục này nên được thêm vào biến môi trường hệ điều hành, nếu không cũng sẽ không thể sử dụng các tệp nhị phân trong thư mục GOBIN.

GOMODCACHE

GOMODCACHE biểu thị vị trí chứa các phụ thuộc được tải xuống bằng go get, giá trị mặc định là $GOPATH/pkg/mod. Định dạng lưu trữ của nó như sau

$GOMODCACHE/domain/username/project@verion

Trong cùng cấp thư mục sẽ có một thư mục tên là sumdb, dùng để chứa thông tin liên quan đến cơ sở dữ liệu checksum phụ thuộc.

GOCACHE

Chứa thông tin cache dùng cho biên dịch, giá trị mặc định của nó là $HOME/.cache/go-build, trong thư mục này sẽ tạo một tệp README.

sh
$ cat $(go env GOCACHE)/README
This directory holds cached build artifacts from the Go build system.
Run "go clean -cache" if the directory is getting too large.
Run "go clean -fuzzcache" to delete the fuzz cache.
See golang.org to learn more about Go.

Mỗi lần build sẽ tạo ra nhiều tệp, go sẽ cache các tệp này để tái sử dụng khi biên dịch lần sau.

GOTEMPDIR

Dùng cho các tệp tạm thời phát sinh khi biên dịch, ví dụ tệp nhị phân tạm thời cần chạy bằng go run. Giá trị mặc định của nó là thư mục tạm thời được chỉ định bởi hệ điều hành, trên mac hoặc linux là /tmp, trên windows là %TEMP%, cũng có thể thay đổi thành vị trí được chỉ định bởi người dùng.

GO111MODULE

Biến môi trường này biểu thị sử dụng cách nào để quản lý phụ thuộc của dự án go, có ba giá trị khả dụng sau

  • off, tắt gomod, dùng gopath, và sẽ bỏ qua tất cả các tệp go.mod
  • on, dùng gomod, không dùng gopath (mặc định).
  • auto, tự động nhận biết, nếu tệp dự án chứa go.mod sẽ dùng gomod để quản lý

TIP

Tại sao gọi là GO111MODULE, không gọi trực tiếp là GOMODULE, vì gomod được giới thiệu lần đầu trong phiên bản go1.11.

GOPROXY

Proxy mô-đun go, giá trị mặc định là https://proxy.golang.org,direct, url được phân tách bằng dấu phẩy, direct nghĩa là trực tiếp dùng VCS bỏ qua proxy mô-đun, chỉ khi cái trước không thể truy cập mới thực thi cái sau, còn một tùy chọn khả dụng khác là off, biểu thị cấm tải xuống bất kỳ mô-đun nào. Ngoài ra, GOPROXY cũng có thể là địa chỉ tệp, ví dụ

GOPROXY=file://$(go env GOMODCACHE)/cache/download

Thông qua go get -x có thể xem các lệnh được thực thi trong quá trình tải xuống phụ thuộc, từ đó biết có dùng proxy hay không.

sh
$ go get -x github.com/spf13/cast
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/spf13/cast/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list: 404 Not Found (0.118s)
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.197s)
# get https://goproxy.cn/github.com/spf13/cast/@v/list: 200 OK (0.257s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info: 200 OK (0.013s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod: 200 OK (0.015s)
# get https://goproxy.cn/sumdb/sum.golang.org/supported
# get https://goproxy.cn/sumdb/sum.golang.org/supported: 200 OK (0.064s)
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1: 200 OK (0.014s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736: 200 OK (0.016s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334: 200 OK (0.023s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266: 200 OK (0.028s)
go: downloading github.com/spf13/cast v1.5.1
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip: 200 OK (0.024s)
go: added github.com/spf13/cast v1.5.1

Sử dụng proxy mô-đun có thể nâng cao hiệu quả tải xuống mô-đun, người dùng trong nước về cơ bản nếu không dùng proxy sẽ không thể truy cập proxy mặc định của官方, hiện tại các proxy mô-đun bên thứ ba công khai và đáng tin cậy như sau

  • https://proxy.golang.com.cn, mã nguồn mở đồng thời cung cấp dịch vụ phiên bản doanh nghiệp
  • https://goproxy.cn, do Qiniu Cloud cung cấp và mã nguồn mở

Đương nhiên cũng có giải pháp tự xây dựng proxy mô-đun mã nguồn mở: goproxy

GOSUMDB

GOSUMDB dùng để thiết lập địa chỉ cơ sở dữ liệu phát hiện checksum của thư viện phụ thuộc, mặc định là sum.golang.org, khi bạn thiết lập proxy, go sẽ truy cập cơ sở dữ liệu checksum thông qua proxy.

GOPRIVATE

Biến môi trường GOPRIVATE dùng để thiết lập các kho riêng tư, các kho khớp sẽ không được xác minh qua sumdb và cũng không đi qua proxy, sẽ tải xuống phụ thuộc trực tiếp thông qua VCS. Hỗ trợ thiết lập ký tự đại diện, dùng dấu phẩy phân tách, như dưới đây, tất cả các phụ thuộc có hậu缀 là corp.example.com và tên là github.com/gohper/myproject sẽ không đi qua proxy và sumdb.

GOPRIVATE=*.corp.example.com,github.com/gohper/myproject

Cũng có thể thiết lập trực tiếp một người dùng hoặc tổ chức nào đó

GOPRIVATE=github.com/gopher,github.com/myorganization

GONOPROXY

Biểu thị những phụ thuộc nào không cần đi qua proxy, quy tắc giống với GOPRIVATE, và sẽ ghi đè lên GOPRIVATE.

GONOSUMDB

Biểu thị những phụ thuộc nào không cần đi qua cơ sở dữ liệu checksum, quy tắc giống với GOPRIVATE, và sẽ ghi đè lên GOPRIVATE.

GOINSECURE

Biểu thị những phụ thuộc nào trực tiếp dùng VCS để tải xuống, quy tắc giống với GOPRIVATE, và sẽ bị ghi đè bởi GONOPROXYGONOSUMDB.

GOVCS

Thiết lập hệ thống kiểm soát phiên bản của quản lý mô-đun, mặc định public:git|hg,private:all. Cũng có thể hạn chế VCS của tên miền được chỉ định, ví dụ

GOVCS=github.com:git,evil.com:off,*:git|hg

Trong hạn chế trên, github chỉ có thể dùng git, evil.com thì không được phép, dùng | để biểu thị nhiều VCS. Nếu không thiết lập hạn chế nào, có thể thiết lập như sau

GOVCS=*:all

Nếu không cho phép sử dụng bất kỳ VCS nào, có thể thiết lập như sau

GOVCS=*:off

GOWORK

Thiết lập xem có bật vùng làm việc hay không, mặc định là rỗng tức là bật, nếu thiết lập thành off, thì không bật, sẽ bỏ qua tất cả các tệp go.work.

GOTOOLDIR

Thiết lập vị trí của công cụ go cần sử dụng, mặc định là $GOROOT/pkg/tool, công cụ mặc định cũng được lưu trữ tại vị trí này.

GODEBUG

Thiết lập các tùy chọn gỡ lỗi, kiểm soát một phần hành vi thực thi của chương trình go dưới dạng cặp khóa-giá trị, ví dụ

GODEBUG=http2client=0,http2server=0

Những thiết lập này là để thuận tiện khi go lùi về hành vi cũ khi có thay đổi không tương thích trong quá trình cập nhật phiên bản, ví dụ trong 1.21 không còn cho phép xảy ra tình huống panic(nil), vì vậy,官方 go đã ghi lại GODEBUG History, truy cập GODEBUG để biết thêm chi tiết.

CGO_ENABLED

Biểu thị có bật cgo hay không, mặc định là 1, tức là bật, thiết lập thành 0 thì tắt.

Những biến môi trường trên đều là những biến常用, đối với một số biến không常用 lắm sẽ không giới thiệu quá nhiều, như CGO, WASM và những thứ khác, nếu quan tâm có thể tự tìm hiểu.

build

Go hỗ trợ hai loại trình biên dịch, gccgo và gc. gcc là một trình biên dịch c/c++ lâu đời, hỗ trợ nhiều ngôn ngữ bao gồm go, gc sau này không phải là viết tắt của garbage collection, mà là go compiler, ngôn ngữ go đã hoàn thành self-hosting trong go1.5, gc là trình biên dịch được viết hoàn toàn bằng ngôn ngữ go, mã nguồn của nó nằm trong gói cmd/compile, vì được triển khai hoàn toàn bằng go nên cũng rất thuận tiện cho việc tìm hiểu và học tập cơ chế nội bộ của nó. Trong trường hợp mặc định, trình biên dịch sử dụng gc để biên dịch. Tiện thể nhắc một chút, trình gỡ lỗi go cũng chia làm hai loại, gdb và dlv, gdb là trình gỡ lỗi c/c++ lâu đời, hỗ trợ nhiều ngôn ngữ bao gồm go, dlv là trình gỡ lỗi được viết bằng ngôn ngữ go, hỗ trợ go thân thiện hơn, nó cũng là mã nguồn mở, khuyến nghị sử dụng cái sau. Lệnh build có tác dụng biên dịch các tệp nguồn go thành các tệp nhị phân có thể thực thi, bạn sẽ trải nghiệm khả năng biên dịch rất nhanh chóng, đây cũng chính là một trong những đặc điểm của ngôn ngữ go.

sh
$ go build -h
usage: go build [-o output] [build flags] [packages]
Run 'go help build' for details.

Nó nhận ba tham số, một là đường dẫn xuất tệp được chỉ định bởi cờ -o, hai là các cờ build dùng để định nghĩa hành vi biên dịch build flags, cuối cùng là gói cần biên dịch, tham số này phải đặt ở cuối. Dưới đây là một ví dụ đơn giản, không dùng đến cờ build.

sh
# Windows
$ go build -o .\bin\golearn.exe golearn

# macOS / Linux
$ go build -o ./bin/golearn golearn

./bin/golearn.exe biểu thị đường dẫn xuất, golearn biểu thị mô-đun cần biên dịch, cũng có thể là một tệp entry, hoặc một thư mục. Ví dụ dưới đây là một ví dụ đơn giản dùng tệp entry main.go làm mục tiêu biên dịch.

sh
# Windows
$ go build -o .\bin\golearn.exe main.go

# macOS / Linux
$ go build -o ./bin/golearn main.go

Khi biên dịch nó sẽ bỏ qua tất cả các tệp có đuôi _test.go, vì theo quy ước những tệp này đều là tệp kiểm thử.

Ngoài ra, lệnh build còn hỗ trợ khá nhiều cờ build dùng để kiểm soát một số hành vi khi biên dịch.

  • -x: Xuất các lệnh chi tiết trong quá trình biên dịch
  • -n: Tương tự -x, nhưng khác biệt là nó chỉ xuất các lệnh này, nhưng thực tế sẽ không thực thi.
  • -v: Xuất các gói được biên dịch
  • -p: Số lượng đồng thời trong quá trình biên dịch
  • -a: Bắt buộc build lại, ngay cả khi đã là mới nhất.
  • -compiler: Chỉ định sử dụng trình biên dịch nào, gccgo hoặc gc, cái sau là trình biên dịch được viết bằng go.
  • -race: Bật phát hiện race condition
  • -msan: Bật phân tích bộ nhớ
  • -asan: Bật phân tích địa chỉ
  • -cover: Bật phát hiện độ phủ mã
  • -buildmode: Chỉ định chế độ biên dịch, có các tùy chọn archive, c-archive, c-shared, default, shared, exe, pie, plugin.
  • -pgo, chỉ định tệp pgo
  • -trimpath: Loại bỏ tiền tố đường dẫn tệp nguồn, ví dụ đường dẫn tương đối /var/lib/go/src/main.go, sau khi loại bỏ thì thông qua runtime获取 được khi chạy chỉ còn đường dẫn tương đối so với đường dẫn mô-đun /main.go, sau khi bật mục này, thời gian biên dịch sẽ tăng rõ rệt, khoảng 20-40%左右, tùy thuộc vào số lượng tệp.
  • -toolexec, một số lệnh go được thực thi trước khi biên dịch, định dạng là -toolexec 'cmd args'.
  • -gcflags: Chỉ định một số tag của trình biên dịch gc
  • -gccgoflags: Chỉ định một số tag của trình biên dịch gccgo
  • -ldflags: Chỉ định một số tag của công cụ link

Đối với một số tham số truyền như ldflags, có thể truyền các tham số như "-help" để获取 các giá trị có thể có của nó, ví dụ

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
......

Những điều trên là những thứ常用, đối với những thứ không常用 lắm có thể tự tìm hiểu.

gcflags

Thông qua gcflags có thể truyền một số tham số cho trình biên dịch gc để kiểm soát các hành vi cụ thể, định dạng sử dụng của nó là -gcflags="pattern=args list", args list là danh sách tham số, pattern là phạm vi tác dụng, có các giá trị khả dụng sau

  • main, đường dẫn gói cấp cao nhất nơi tệp entry nằm
  • all, tất cả các phụ thuộc của mô-đun hiện tại và chế độ hiện tại
  • std, thư viện chuẩn
  • cmd, tác dụng lên tất cả các tệp nguồn trong gói cmd
  • Ký tự đại diện, ví dụ ., ./..., cmd/....

Quy tắc pattern này áp dụng cho tất cả các cờ hỗ trợ định dạng này, ví dụ ldflags. Thông qua lệnh sau để xem các giá trị khả dụng của tham số

sh
$ go build -gcflags -help
用法:compile [选项] file.go...
  -%    调试非静态初始化器
  -+    编译运行时
  -B    禁用边界检查
  -C    禁用错误消息中的列号打印
  -D path
        设置本地导入的相对路径
  -E    调试符号导出
  -I directory
        添加目录到导入搜索路径
  -K    调试缺失的行号
  -L    对于受 //line 指令影响的错误位置,同时显示实际源文件名
  -N    禁用优化
  -S    打印汇编列表
  -V    打印版本并退出
  -W    类型检查后调试解析树
  ......

Dưới đây giới thiệu một số tham số常用

  • -S: Xuất dạng hợp ngữ của mã
  • -N: Tắt tối ưu hóa biên dịch
  • -m: Xuất quyết định tối ưu hóa
  • -l: Tắt inline hàm
  • -c: Số lượng đồng thời khi biên dịch
  • -dwarf: Tạo ký hiệu DWARF

Ví dụ nếu muốn xem dạng hợp ngữ của mã, có thể sử dụng tham số -S, và còn phải tắt tối ưu hóa và inline, như vậy mới khôi phục được dạng gốc của nó, như sau

sh
$ go build -trimpath -gcflags="-N -l -S" main.go
main.main STEXT size=171 args=0x0 locals=0x58 funcid=0x0 align=0x0
        0x0000 00000 (./main.go:9)      TEXT    main.main(SB), ABIInternal, $88-0
        0x0000 00000 (./main.go:9)      CMPQ    SP, 16(R14)
        0x0004 00004 (./main.go:9)      PCDATA  $0, $-2
        0x0004 00004 (./main.go:9)      JLS     161
        0x000a 00010 (./main.go:9)      PCDATA  $0, $-1
        0x000a 00010 (./main.go:9)      PUSHQ   BP
        0x000b 00011 (./main.go:9)      MOVQ    SP, BP
        0x000e 00014 (./main.go:9)      SUBQ    $80, SP
        0x0012 00018 (./main.go:9)      FUNCDATA        $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $1, gclocals·bDfKCdmtOiGIuJz/x+yQyQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $2, main.main.stkobj(SB)
        0x0012 00018 (./main.go:10)     MOVUPS  X15, main..autotmp_0+40(SP)
        0x0018 00024 (./main.go:10)     LEAQ    main..autotmp_0+40(SP), CX
        0x001d 00029 (./main.go:10)     MOVQ    CX, main..autotmp_2+32(SP)

ldflags

Thông qua ldflags có thể truyền một số tham số cho linker để kiểm soát các hành vi cụ thể, thông qua lệnh sau để xem tất cả các giá trị khả dụng của ldflags, gần hai ba mươi cái.

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
  -H type
        set header type
  -I linker
        use linker as ELF dynamic linker
  -L directory
        add specified directory to library path
  -R quantum
        set address rounding quantum (default -1)
  -T int
        set the start address of text symbols (default -1)
  -V    print version and exit
  -X definition
        add string value definition of the form importpath.name=value
  -a    no-op (deprecated)
  .....

Tham số -X của ldflags là một chức năng rất thiết thực, nó có thể định nghĩa giá trị của biến chuỗi của gói được chỉ định tại thời điểm link. Thông qua chức năng này, chúng ta có thể dễ dàng inject một số thông tin meta tại thời điểm biên dịch. Và nó chỉ là một biến, nên cũng thuận tiện cho việc获取 khi chạy, dưới đây là một ví dụ đơn giản.

go
package main

import "fmt"

var (
  Version string
)

func main() {
  fmt.Println(Version)
}

Thực thi lệnh

sh
go build -ldflags "-X main.Version=$(git describe --always)" main.go

Sau khi chạy sẽ xuất checksum sha1 của commit git.

5e3fd7a

Ngoài ra một số tham số thiết thực khác có

  • -w: Không tạo DWARF, đây là thông tin thuận tiện cho việc gỡ lỗi mã nguồn.
  • -s: Tắt bảng ký hiệu

Hai cái này thường dùng cùng nhau, có thể giảm đáng kể kích thước của tệp nhị phân sau khi biên dịch, khoảng 40%-50%左右, nhược điểm cũng rất rõ ràng, không thể tiến hành gỡ lỗi, dưới đây là một ví dụ.

sh
$ go build -ldflags="-w -s" main.go

Biên dịch chéo

Biên dịch go luôn có hai đặc điểm lớn, một là nhanh, đặc điểm lớn còn lại là biên dịch chéo, biên dịch chéo đề cập đến việc có thể biên dịch thành mã mục tiêu của các hệ thống khác trên máy local, ví dụ trên windows biên dịch thành tệp nhị phân trên linux hoặc darwin, ngược lại cũng vậy. Biên dịch chéo hỗ trợ rất nhiều ngôn ngữ, đây không phải là điều hiếm gặp, nhưng biên dịch chéo của go rất đơn giản, chỉ cần hai bước sau

  1. Thiết lập biến môi trường GOOS, chọn hệ điều hành mục tiêu của bạn
  2. Thiết lập biến môi trường GOARCH, chọn kiến trúc CPU mục tiêu của bạn
  3. Sử dụng go build để biên dịch như bình thường

Toàn bộ quá trình rất ngắn, không cần sử dụng công cụ hoặc cấu hình bổ sung, và tốc độ cũng nhanh như bình thường. Như dưới đây

makefile
build_linux:
  SET CGO_ENABLED=0
  SET GOOS="linux"
  SET GOARCH="amd64"
  go build -o golearn  main.go

build_mac:
  SET CGO_ENABLED=0
  SET GOOS="darwin"
  SET GOARCH="amd64"
  go build -o golearn main.go

build_win:
  SET CGO_ENABLED=0
  SET GOOS="win"
  SET GOARCH="amd64"
  go build -o golearn.exe main.go

.PHONY: build_linux \
    build_mac \
    build_win

Bước đầu tiên SET CGO_ENABLED=0 tắt cgo, một khi mã của bạn sử dụng cgo, thì không thể sử dụng biên dịch chéo bình thường. Bước thứ hai SET GOOS thiết lập hệ thống mục tiêu, các tùy chọn có linux, darwin, windwos, netbsd. Bước thứ ba thiết lập kiến trúc CPU, SET GOARCH, các tùy chọn có amd64, 386, arm, ppc64. Bước cuối cùng là biên dịch như mọi khi.

Kiểm soát biên dịch

Lệnh build có thể đạt được hiệu quả kiểm soát biên dịch thông qua tags, nó tồn tại trong mã nguồn dưới dạng chỉ thị, xem một ví dụ, tệp product.go

go
// +build product

package main

import "fmt"

func main() {
  fmt.Println("product")
}

Tệp debug.go

go
// +build debug

package main

import "fmt"

func main() {
  fmt.Println("debug")
}

Chúng đều có một chỉ thị // +build, biểu thị chúng sẽ được biên dịch trong trường hợp nào. Định dạng cơ bản là

go
// +build tag1 tag2

package pkg_name

Có một số quy tắc bắt buộc phải tuân thủ

  1. //+build phải cách nhau một khoảng trắng
  2. Nó phải nằm trên khai báo gói
  3. Phải cách một dòng trống với khai báo gói

Ngoài ra, nó còn có thể đạt được mục đích kiểm soát logic thông qua khoảng cách đơn giản, khoảng trắng biểu thị OR, dấu phẩy biểu thị AND, ! biểu thị NOT. Ví dụ như ví dụ dưới đây

go
// +build windows linux

package pkg_name

Biểu thị rằng tệp hiện tại sẽ được biên dịch trên nền tảng windows hoặc linux.

go
// +build windows,amd64,!cgo linux,i386,cgo

package pkg_name

Ví dụ này biểu thị rằng chỉ biên dịch khi là nền tảng windows kiến trúc amd64 và chưa bật cgo hoặc là nền tảng linux kiến trúc i386 và đã bật cgo. Nếu bạn chỉ đơn giản là không muốn một tệp nào đó tham gia biên dịch, có thể sử dụng ignore.

go
// +build ignore

package pkg_name

Cũng có thể tồn tại nhiều dòng chỉ thị

go
// +build windows
// +build amd64

package pkg_name

Nhiều dòng chỉ thị được xử lý theo cách AND. Đối với các tag như nền tảng và kiến trúc, go sẽ tự động truyền vào khi biên dịch, chúng ta cũng có thể truyền vào các tag tùy chỉnh, lấy hai tệp ban đầu làm ví dụ

sh
$ go build -tags="debug" . && ./golearn.exe
debug

$ go build -tags="product" . && ./golearn.exe
product

Có thể thấy khi truyền vào các tag khác nhau thì kết quả xuất khác nhau, mục đích kiểm soát biên dịch cũng đạt được.

run

Lệnh runbuild đều sẽ biên dịch mã nguồn, khác biệt là lệnh run sẽ chạy trực tiếp sau khi biên dịch xong. Lệnh run để tăng tốc độ biên dịch, trong quá trình biên dịch sẽ không tạo thông tin gỡ lỗi, nên cũng không hỗ trợ gỡ lỗi, và chỉ tạo một tệp nhị phân tạm thời, thường được lưu trữ trong thư mục GOTMEPDIR, ví dụ /temp/go-build2822241271/b001/exe/main.exe.

sh
$ go run -h
usage: go run [build flags] [-exec xprog] package [arguments...]
Run 'go help run' for details.

Nó cũng hỗ trợ các cờ build của lệnh build, còn cung cấp một tham số -exec để chỉ định chương trình nào chạy tệp nhị phân, [arguments...] đề cập đến các tham số chạy của chương trình. Dưới đây là một ví dụ

go
package main

import (
  "fmt"
  "os"
)

var (
  Version string
)

func main() {
  fmt.Println(Version)
  fmt.Println(os.Args[1:])
}

Sử dụng go run để chạy

sh
$ go run -ldflags="-X main.Version=$(git describe --always)" main.go hello
5e3fd7a
[hello]

Nhìn chung sử dụng không khác biệt quá lớn so với go build, nên sẽ không trình bày quá nhiều.

tool

Bản thân lệnh tool không có chức năng nào, tác dụng của nó là gọi trực tiếp các công cụ trong thư mục cmd/, ví dụ cmd/compile là trình biên dịch đi kèm. Thông qua go tool có thể gọi trực tiếp các công cụ này, không cần phải thực thi các tệp nhị phân của những công cụ này thủ công.

sh
$ go tool -h
usage: go tool [-n] command [args...]

Sử dụng tham số -n để in ra tất cả các tham số lệnh được hỗ trợ

sh
$ go tool -n
addr2line
asm
buildid
cgo
compile
covdata
cover
doc
fix
link
nm
objdump
pack
pprof
test2json
trace
vet

Những công cụ này được lưu trữ trong thư mục GOROOT/pkg/tool, và được phân nhóm theo hệ điều hành và kiến trúc CPU, như dưới đây.

sh
$ ls $GOROOT/pkg/tool/windows_amd64/ -1
addr2line.exe*
asm.exe*
buildid.exe*
cgo.exe*
compile.exe*
covdata.exe*
cover.exe*
doc.exe*
fix.exe*
link.exe*
nm.exe*
objdump.exe*
pack.exe*
pprof.exe*
test2json.exe*
trace.exe*
vet.exe*

Sử dụng định dạng go doc cmd/command để xem cách sử dụng của mỗi lệnh, ví dụ

sh
$ go doc cmd/compile
Usage:

    go tool compile [flags] file...

The specified files must be Go source files and all part of the same package.
The same compiler is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.

Flags:

    -D path
        Set relative path for local imports.
    -I dir1 -I dir2
        Search for imported packages in dir1, dir2, etc,
        after consulting $GOROOT/pkg/$GOOS_$GOARCH.
    -L
        Show complete file path in error messages.
...

Các cờ tham số được hỗ trợ bởi cmd/compile, cũng chính là các tham số được hỗ trợ bởi gcflags đã đề cập trước đó. Sự khác biệt giữa go tool compilego build là, cái trước chỉ phụ trách biên dịch, và chỉ có thể dùng tệp làm tham số, cái sau có thể dùng thư mục, gói, tệp làm tham số, và không chỉ làm mỗi việc biên dịch mã nguồn, còn phụ trách link các tệp, xóa các tệp vô dụng, v.v., cái trước là một phần của cái sau. Chúng ta có thể in ra các lệnh được thực thi trong quá trình build

sh
$ go build -n main.go

#
# internal/goarch
#

mkdir -p $WORK\b004\
cat >$WORK\b004\importcfg << 'EOF' # internal
# import config
EOF
"/golang/pkg/tool/windows_amd64/compile.exe" -o "$WORK/b004/_pkg_.a" -trimpath "$WORK/b004=>" -p internal/goarch -std -+ -complete -buildid 3gunEkUExGdhOPa2rFsh/3gunEkUExGdhOPa2rFsh -goversion go1.21.0 -c=4 -nolocalimports -importcfg "$WORK/b004/importcfg" -pack "/golang/src/internal/goarch/goarch.go" "/golang/src/internal/goarch/goarch_amd64.go" "/golang/src/internal/goarch/zgoarch_amd64.go"
"/golang/pkg/tool/windows_amd64/buildid.exe" -w "$WORK/b004/_pkg_.a" # internal
...

Trong quá trình có thể thấy có một đoạn /golang/pkg/tool/windows_amd64/compile.exe, chính là đã gọi trình biên dịch. Ngoài compile ra, còn rất nhiều công cụ có thể gọi, nhiều lệnh go thực chất là bí danh của chúng.

clean

Lệnh clean dùng để xóa các tệp đối tượng được tạo ra trong quá trình biên dịch

sh
$ go clean -h
usage: go clean [clean flags] [build flags] [packages]
Run 'go help clean' for details.

Nó hỗ trợ các cờ sau

  • -i: Xóa các tệp lưu trữ hoặc tệp nhị phân tương ứng
  • -n: In ra các lệnh sẽ được thực thi trong quá trình xóa nhưng thực tế không thực thi
  • -x: In ra các lệnh sẽ được thực thi trong quá trình xóa và thực thi chúng
  • -r: Xóa đệ quy thông qua import path
  • -cache, xóa tất cả cache do go build tạo ra
  • -testcache: Xóa tất cả cache kiểm thử được tạo ra
  • -modcache: Xóa tất cả cache mô-đun đã tải xuống
  • -fuzzcache: Xóa cache do fuzz test tạo ra.

Khi sử dụng go tool compile, là gọi trực tiếp lệnh trình biên dịch, không giống như go build sẽ làm nhiều xử lý hậu kỳ, sẽ tạo ra các tệp đối tượng. Ví dụ thực thi lệnh sau

sh
go tool compile -N -S -l main.go

sẽ tạo ra một tệp tên là main.o, sử dụng lệnh go clean để xóa là được. Hoặc sử dụng tham số -n để in ra các lệnh sẽ được thực thi.

sh
$ go clean -n
rm -f golearn golearn.exe golearn golearn.exe golearn.test golearn.test.exe golearn.test golearn.test.exe api api.exe main main.exe

Xóa cache biên dịch, nó sẽ xóa các cache biên dịch được tạo ra trong thư mục GOCACHE

sh
$ go clean -cache -n
rm -r /cache/00 /cache/01 /cache/02

Xóa cache do fuzz test tạo ra, những cache này mặc định được lưu trữ trong thư mục GOCACHE/fuzz/

sh
$ go clean -fuzzcache -n
rm -rf /cache/fuzz

fix

Tính đến thời điểm viết bài này, go đã được mười năm, trong quá trình ngôn ngữ không ngừng cập nhật và sửa đổi, khó tránh khỏi xuất hiện một số vấn đề không tương thích do thay đổi API, lệnh fix được sinh ra vì mục đích này, nó sẽ phát hiện những API đã lỗi thời trong các tệp nguồn và thay thế chúng bằng các API mới.

sh
$ go fix -h
usage: go fix [-fix list] [packages]
Run 'go help fix' for details.

Nó hỗ trợ thư mục, tên tệp, đường dẫn làm tham số, nhận cờ -fix để truyền tham số nhằm biểu thị thực hiện sửa đổi nào, có thể xem các giá trị khả dụng thông qua lệnh got tool fix -help

sh
$ go tool fix -help
usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]
  -diff
        display diffs instead of rewriting files
  -force string
        force these fixes to run even if the code looks updated
  -go string
        go language version for files
  -r string
        restrict the rewrites to this comma-separated list

Available rewrites are:

buildtag
        Remove +build comments from modules using Go 1.18 or later

cftype
        Fixes initializers and casts of C.*Ref and JNI types

context
        Change imports of golang.org/x/net/context to context

egl
        Fixes initializers of EGLDisplay

eglconf
        Fixes initializers of EGLConfig

gotypes
        Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types}

jni
        Fixes initializers of JNI's jobject and subtypes

netipv6zone
        Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.

        https://codereview.appspot.com/6849045/

printerconfig
        Add element keys to Config composite literals.

Dưới đây là một ví dụ, trong mã nguồn sử dụng gói golang.org/x/net/context

sh
package main

import (
  "fmt"
  "golang.org/x/net/context"
)

func main() {
  background := context.Background()
  fmt.Println(background.Err())
}

Sử dụng go fix để sửa, thay thế nó bằng gói context trong thư viện chuẩn, chúng ta có thể sử dụng lệnh sau để thay thế

sh
$ go fix -fix context main.go

Cũng có thể không thay thế, xem sự thay đổi của tệp trước và sau.

sh
$ go tool fix -r context -diff  main.go
main.go: fixed context
diff main.go fixed/main.go
--- main.go
+++ fixed/main.go
@@ -1,8 +1,8 @@
 package main

 import (
+       "context"
        "fmt"
-       "golang.org/x/net/context"
 )

 func main() {

Ngôn ngữ go đã ra đời hơn mười năm chỉ có chín tham số thay thế khả dụng, có thể thấy khả năng tương thích được giữ khá tốt.

fmt

Lệnh fmt là công cụ định dạng đi kèm của go, dùng để định dạng các tệp mã nguồn go.

sh
$ go fmt -h
usage: go fmt [-n] [-x] [packages]
Run 'go help fmt' for details.

Thông qua lệnh go doc gofmt để xem tài liệu chi tiết của nó

sh
$ 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.

Usage:

    gofmt [flags] [path ...]

The flags are:

    -d
        Do not print reformatted sources to standard output.
        If a file's formatting is different than gofmt's, print diffs
        to standard output.
    -e
        Print all (including spurious) errors.
    -l
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, print its name
        to standard output.
    -r rule
        Apply the rewrite rule to the source before reformatting.
    -s
        Try to simplify code (after applying the rewrite rule, if any).
    -w
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, overwrite it
        with gofmt's version. If an error occurred during overwriting,
        the original file is restored from an automatic backup.

gofmt sử dụng tab để thụt lề, khoảng trắng để căn chỉnh, trong trường hợp mặc định mã sau khi định dạng sẽ được xuất ra đầu ra chuẩn, không phải ghi đè lên tệp gốc. Lệnh go fmt thực tế sử dụng là lệnh gofmt, nó là một tệp nhị phân độc lập, nằm trong thư mục GOROOT/bin.

sh
$ ls $GOROOT/bin -1
go.exe*
gofmt.exe*

Thêm cờ -n cho lệnh go fmt là có thể biết các lệnh mà nó sẽ thực thi.

sh
$ go fmt main.go
/golang/bin/gofmt.exe -l -w main.go

Có thể thấy go fmt thực chất là bí danh của gofmt -l -w, lệnh gofmt có các tham số sau

  • -d: Xuất sự khác biệt của tệp trước và sau khi định dạng
  • -e: Xuất tất cả lỗi
  • -l: Xuất tên của các tệp có thay đổi
  • -r: Áp dụng quy tắc định dạng
  • -s: Thử đơn giản hóa mã
  • -w: Ghi đè lên tệp nguồn, nếu xảy ra lỗi thì khôi phục bản sao lưu

Giả sử hiện có một tệp nguồn như sau

go
$ cat main.go
package main

import "fmt"

func main() {
fmt.Println("hello world!")}

Thông qua tham số -d có thể xem trước sự thay đổi của nó

sh
$ ƒgofmt -d main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-fmt.Println("hello world!")}
-
+       fmt.Println("hello world!")
+}

Tham số -l sẽ xuất tên của các tệp sẽ được sửa đổi

$ gofmt -l .
main.go

Nếu có lỗi cú pháp, tham số -e có thể xuất chi tiết hơn

sh
$ gofmt -d -e main.go
main.go:6:27: missing ',' in argument list
main.go:6:28: expected operand, found newline
main.go:7:2: expected ')', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected '}', found 'EOF'
main.go:7:2: missing ',' in argument list

-w sẽ áp dụng các sửa đổi vào tệp nguồn

sh
$ gofmt -l -w .
main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world!")
}

Bạn có thể phát hiện ra rằng với tư cách là một công cụ định dạng, gofmt hoàn toàn không cung cấp bất kỳ cấu hình tùy chỉnh nào, trong khi trình định dạng prettify chuyên làm đẹp mã js lại cung cấp khá nhiều cấu hình để định dạng mã, ở đây có thể thấy thái độ của官方 go, đừng nghĩ đến việc tùy chỉnh, tốt nhất là phong cách mã của tất cả mọi người nên nhất quán, ít nhất có một lợi ích là khi đọc mã không cần phải thích nghi với thói quen của người khác. Nhưng thực ra nó vẫn giữ lại một mục tùy chỉnh, đó là quy tắc thay thế mã định dạng, quy tắc có thể tùy chỉnh, định dạng như sau

pattern -> replacement

Ví dụ xóa các dấu ngoặc thừa

(a) -> a

Xem sự thay đổi của tệp

sh
$ gofmt -r "(a) -> a" -d -l .
main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-       fmt.Println(("hello world!"))
+       fmt.Println("hello world!")
 }

Có thể thấy gofmt sẽ xóa các dấu ngoặc thừa.

get

Lệnh get chắc chắn là lệnh được sử dụng nhiều nhất trong quá trình phát triển go, tác dụng của nó là tải xuống mã nguồn của gói có địa chỉ được chỉ định vào thư mục tương ứng của GOMODCACHE.

sh
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.
  • -u: Thử cập nhật các phiên bản minor và patch của gói, nếu liên quan đến thay đổi phiên bản chính, ví dụ v1->v2, sẽ không cập nhật.
  • -t: Cập nhật các phiên bản phụ thuộc trong kiểm thử
  • -v: Xuất các gói được biên dịch, thực tế thuộc về một trong các tham số của build flags

Trong thời kỳ cổ đại, tác dụng của go get tương tự như go install, nó sẽ tải xuống và biên dịch các gói này, tuy nhiên随着 sự ra đời và hoàn thiện của các mô-đun go, phần tác dụng này dần bị loại bỏ, lệnh get hiện được sử dụng phổ biến nhất là tải xuống và phân tích cú pháp các phụ thuộc cho các mô-đun go, nên bạn có thể thấy lệnh go get còn hỗ trợ các cờ build, và nếu bạn thử sử dụng go get bên ngoài một mô-đun giống như sử dụng go install, nó sẽ提示 bạn cách sử dụng này đã bị loại bỏ.

sh
$ go get github.com/wire/wire
go: go.mod file not found in current directory or any parent directory.
        'go get' is no longer supported outside a module.
        To build and install a command, use 'go install' with a version,
        like 'go install example.com/cmd@latest'
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.

至于 tại sao trong mô tả tài liệu vẫn giữ lại những điều này thì cũng không biết, lật xem mã nguồn của lệnh get, bạn còn sẽ phát hiện nó vẫn giữ các flag trước đó.

go
var (
  getD        = CmdGet.Flag.Bool("d", true, "")
  getF        = CmdGet.Flag.Bool("f", false, "")
  getFix      = CmdGet.Flag.Bool("fix", false, "")
  getM        = CmdGet.Flag.Bool("m", false, "")
  getT        = CmdGet.Flag.Bool("t", false, "")
  getU        upgradeFlag
  getInsecure = CmdGet.Flag.Bool("insecure", false, "")
  // -v is cfg.BuildV
)

Trở lại vấn đề chính, lệnh get sẽ tải xuống mã nguồn của gói được chỉ định vào thư mục phụ thuộc toàn cục local, tức là thư mục tương ứng của GOCACHE, sau đó ghi thông tin vào các tệp go.modgo.sum, cái trước phụ trách ghi lại phiên bản, cái sau phụ trách ghi lại checksum sha1 để đảm bảo an toàn. Lệnh get thực tế dựa trên VCS, tức là hệ thống kiểm soát phiên bản local, tổng cộng hỗ trợ các loại sau

  • git
  • hg (Mercurial)
  • bzr (Bazaar)
  • svn
  • fossil

Trong đó, mặc định chỉ hỗ trợ git và hg, có thể cấu hình trong GOVCS, định dạng như sau

GOVCS=github.com:git,example.com:hg,*:git|hg,*:all

GOVCS chỉ hỗ trợ git và hg làm VCS, ba cái còn lại cần cấu hình trong GOPRIVATE.

Lệnh go get tổng cộng có các cách sử dụng sau, có thể trực tiếp dùng địa chỉ phụ thuộc làm tham số

sh
$ go get golang.org/x/net

Cũng có thể chỉ định phiên bản

sh
$ go get golang.org/x/net@0.17.0

Chỉ định phiên bản mới nhất

sh
$ go get golang.org/x/net@latest

Thử cập nhật phiên bản

sh
$ go get -u golang.org/x/net

Xóa một phụ thuộc nào đó

sh
$ go get golang.org/x/net@none

Những điều trên là dùng để quản lý các phụ thuộc thông thường, nó còn có thể dùng để quản lý các phụ thuộc không mấy thông thường, ví dụ cập nhật phiên bản của go

sh
$ go get go@latest
go: updating go.mod requires go >= 1.21.3; switching to go1.21.3
go: downloading go1.21.3 (windows/amd64)
go: upgraded go 1.21.0 => 1.21.3

Thậm chí còn có thể dùng để cập nhật phiên bản của công cụ go

sh
$ go get toolchain@latest

Khi bạn sử dụng go get để cập nhật phiên bản go và công cụ, chúng sẽ cài đặt phiên bản go mới trong thư mục GOMODCACHE/golang.org/

sh
$ ls $(go env GOMODCACHE)/golang.org -1
'toolchain@v0.0.1-go1.21.3.windows-amd64'/
x/

Lúc này chỉ cần sửa đổi thủ công GOROOT là có thể chuyển sang phiên bản được chỉ định.

install

Lệnh install tương tự như lệnh get, chúng đều dùng để tải xuống các phụ thuộc bên thứ ba, nhưng khác biệt là lệnh get tải xuống mã nguồn, còn lệnh install sẽ biên dịch mã nguồn thành các tệp nhị phân có thể thực thi được trên máy này, đường dẫn lưu trữ của các tệp nhị phân trước hết là trong thư mục GOBIN, sau đó là GOPATH/bin. Chức năng chính của lệnh này là dùng để tải xuống một số công cụ dòng lệnh công khai của bên thứ ba, nhờ vào tốc độ biên dịch và khả năng di động của go, không cần tải xuống các tệp nhị phân, mà là tải xuống mã nguồn rồi biên dịch trên local.

sh
$ go install -h
usage: go install [build flags] [packages]
Run 'go help install' for details.

Lệnh install nhận các cờ build và tên gói làm tham số, trong trường hợp bật gomod, tên gói phải mang theo số phiên bản. Ví dụ tải xuống trình gỡ lỗi delve

sh
$ go install -x github.com/go-delve/delve/cmd/dlv@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/@v/list
# get https://goproxy.cn/github.com/go-delve/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.014s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/go-delve/@v/list: 404 Not Found (0.027s)
WORK=/home/user/tmp/go-build2033992495
mkdir -p $WORK/b001/
cat >/home/user/tmp/go-build2033992495/b001/importcfg.link << 'EOF' # internal
packagefile github.com/go-delve/delve/cmd/dlv=/home/user/.cache/go-build/f1/f11d552287458c0fce625abe50bf928c487064c36bbb1251ad8b1968772c3e4b-d
......
......
mkdir -p /home/wyh/gomod/bin/
mv $WORK/b001/exe/a.out /home/wyh/gomod/bin/dlv
rm -r $WORK/b001/

Nó trước hết sẽ tải xuống mã nguồn vào đường dẫn được lưu trữ của GOMODCACHE, điểm này giống với lệnh get, sau đó chuyển sang thư mục làm việc tạm thời, biên dịch nó, sau khi biên dịch xong sẽ di chuyển tệp nhị phân vào thư mục GOPATH/bin, cuối cùng xóa thư mục tạm thời. Lệnh install còn có một hạn chế là gói được tải xuống phải là gói entry của dự án đó, tức là phải chứa tệp entry main.go, nếu không sẽ提示 bạn không thể cài đặt. Ví dụ, sử dụng go install để tải xuống gin

sh
$ go install -x github.com/gin-gonic/gin@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list
# get https://goproxy.cn/github.com/gin-gonic/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.022s)
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/gin-gonic/@v/list: 404 Not Found (0.028s)
package github.com/gin-gonic/gin is not a main package

gin là một thư viện phụ thuộc của web framework, không phải là một công cụ dòng lệnh, đương nhiên cũng không có tệp entry, nên cũng sẽ cài đặt thất bại.

list

Lệnh list sẽ liệt kê các gói ở vị trí được chỉ định, mỗi dòng một, và hỗ trợ xuất định dạng tùy chỉnh, hỗ trợ rất nhiều tham số, điều kiện tiên quyết để sử dụng nó là phải trong một dự án hỗ trợ gomod.

sh
$ go list -h
usage: go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
Run 'go help list' for details.

Các tham số nó hỗ trợ như sau

  • -f: Tham số định dạng
  • -json: Xuất định dạng json
  • -compiled: Hiển thị tất cả các gói sẽ được trình biên dịch biên dịch
  • -deps: Hiển thị tên của mỗi gói và từng phụ thuộc của nó
  • -test: Hiển thị gói kiểm thử của mỗi gói
  • -e: Xuất bình thường khi gặp các gói có lỗi
  • -find: Không phân tích cú pháp các quan hệ phụ thuộc của các gói này
  • -export: Khi sử dụng tham số này, thiết lập giá trị của trường Package.Export của cấu trúc là tệp chứa thông tin export mới nhất của gói được chỉ định, và thiết lập giá trị của trường Package.BuildIDBuildID của gói, chủ yếu dùng cho xuất định dạng.

Tham số thông tin mô-đun,

  • -m: Xuất mô-đun thay vì xuất gói

  • -versions: Hiển thị tất cả thông tin khả dụng của một mô-đun

  • -retracted: Hiển thị các phiên bản bị rút lại của một mô-đun

Tham số [packages] có thể là một tên gói được chỉ định, hoặc một thư mục, cũng có thể là all, biểu thị bất kỳ đâu, khi sử dụng tham số -m, all biểu thị tất cả các phụ thuộc được引用 bởi mô-đun hiện tại.

Ví dụ, tệp hiện tại chỉ có một tệp main.go, và chỉ có một dòng mã xuất "hello world", sau khi thực thi go list -deps ., nó xuất tất cả các gói được引用 từ dự án hiện tại đến fmt và các phụ thuộc của nó.

bash
$ ls
go.mod  go.sum  main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world")
}

$ go list -deps .
internal/goarch
unsafe
internal/abi
internal/unsafeheader
internal/cpu
internal/bytealg
internal/coverage/rtcov
internal/godebugs
internal/goexperiment
internal/goos
runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime
......
......
path
io/fs
os
fmt
golearn

Hoặc xuất tất cả các phụ thuộc mô-đun trong dự án hiện tại

bash
$ go list -m all
golearn
cloud.google.com/go v0.26.0
github.com/246859/containers v0.0.1
github.com/246859/river v0.1.0 => D:\WorkSpace\Code\riverdb
github.com/BurntSushi/toml v0.3.1
github.com/Jleagle/steam-go v0.0.0-20230725082712-1053b441b1f2
github.com/Jleagle/unmarshal-go v0.0.0-20210227002040-694f544f9265
github.com/KyleBanks/depth v1.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/PuerkitoBio/purell v1.1.1
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
github.com/andeya/ameda v1.5.3
github.com/andeya/goutil v1.0.1
...

format

Đầu ra của lệnh list là theo dòng, mỗi dòng xuất ra đều là một gói.官方 cung cấp tham số -f để chúng ta tùy chỉnh định dạng xuất dòng, giá trị mà nó nhận cũng chính là cú pháp mẫu được định nghĩa bởi gói công cụ mẫu template/text, ví dụ như ví dụ dưới đây

sh
-f "package {{ .Dir }} {{ .Name }}"

Mỗi gói được lặp sẽ được truyền vào dưới dạng cấu trúc sau, tất cả các trường trong cấu trúc này đều có thể được sử dụng làm tham số mẫu.

go
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)
    BuildID        string   // build ID of the compiled package (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
    DefaultGODEBUG string  // default GODEBUG setting, for main packages

    // 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
    IgnoredOtherFiles []string // non-.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

    // Embedded files
    EmbedPatterns      []string // //go:embed patterns
    EmbedFiles         []string // files matched by EmbedPatterns
    TestEmbedPatterns  []string // //go:embed patterns in TestGoFiles
    TestEmbedFiles     []string // files matched by TestEmbedPatterns
    XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
    XTestEmbedFiles    []string // files matched by XTestEmbedPatterns

    // 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
}

Nếu lặp là mô-đun, thì sẽ được truyền vào dưới dạng cấu trúc sau, tất cả các trường của nó cũng có thể được sử dụng làm tham số mẫu.

go
type Module struct {
    Path       string        // module path
    Query      string        // version query corresponding to this version
    Version    string        // module version
    Versions   []string      // available module versions
    Replace    *Module       // replaced by this module
    Time       *time.Time    // time version was created
    Update     *Module       // available update (with -u)
    Main       bool          // is this the main module?
    Indirect   bool          // module is only indirectly needed by main module
    Dir        string        // directory holding local copy of files, if any
    GoMod      string        // path to go.mod file describing module, if any
    GoVersion  string        // go version used in module
    Retracted  []string      // retraction information, if any (with -retracted or -u)
    Deprecated string        // deprecation message, if any (with -u)
    Error      *ModuleError  // error loading module
    Origin     any           // provenance of module
    Reuse      bool          // reuse of old module info is safe
}

type ModuleError struct {
    Err string // the error itself
}

Xem tất cả các gói

bash
$ go list -f "package {{.Dir}} {{.Name}}" ./...
package /golearn main
package /golearn/app cmd
package /golearn/cmd cmd
package /golearn/docs docs
package /golearn/tool tool
package /golearn/tool_test tool

Xem mô-đun

bash
$ go list -m -f "mod {{.Path}} {{.Version}} {{.GoVersion}} {{.GoMod}}"
mod golearn  1.21.3 /golearn/go.mod

mod

go mod là lệnh chuyên dùng để quản lý các mô-đun go.

sh
$ go mod help
Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

        go mod <command> [arguments]

The commands are:

        download    download modules to local cache
        edit        edit go.mod from tools or scripts
        graph       print module requirement graph
        init        initialize new module in current directory
        tidy        add missing and remove unused modules
        vendor      make vendored copy of dependencies
        verify      verify dependencies have expected content
        why         explain why packages or modules are needed

Use "go help mod <command>" for more information about a command.

Nó có các lệnh con sau

  • download: Tải xuống tất cả các phụ thuộc được chỉ định trong tệp go.mod vào cache local
  • edit: Chỉnh sửa tệp go.mod, giao diện dòng lệnh mà nó cung cấp chủ yếu dùng để các công cụ hoặc script khác gọi.
  • init: Khởi tạo một dự án gomod trong thư mục hiện tại
  • tidy: Tải xuống các phụ thuộc bị thiếu, xóa các phụ thuộc không dùng
  • graph: Xuất biểu đồ phụ thuộc
  • verify: Xác minh các phụ thuộc local
  • why: Giải thích tại sao lại phụ thuộc vào các mô-đun này
  • vendor: Export các phụ thuộc của dự án vào thư mục vendor

init

sh
$ go help mod init
usage: go mod init [module-path]

Lệnh init dùng để khởi tạo một dự án gomod, tham số duy nhất của nó là đường dẫn mô-đun, sau này nếu người khác muốn tải xuống phụ thuộc của bạn sẽ cần dựa vào đường dẫn mô-đun này làm cơ sở. Quy tắc đặt tên của nó thường là

domain_name/user_name/repo_name

Ví dụ thường thì mọi người đều đặt dự án trên github, nên có thể là

github.com/jack/gotour

Không khuyến nghị sử dụng một số ký tự đặc biệt làm đường dẫn mô-đun. Dưới đây xem một trường hợp sử dụng

sh
$ mkdir gotour

$ cd gotour

$ go mod init "github.com/jack/gotour"
go: creating new go.mod: module github.com/jack/gotour

tidy

$ go help mod tidy
usage: go mod tidy [-e] [-v] [-x] [-go=version] [-compat=version]

Lệnh tidy sẽ xóa các mục phụ thuộc không dùng trong go.mod, tức là các mục phụ thuộc không được引用, và sẽ tải xuống những mục phụ thuộc được引用 nhưng không tồn tại. Nó hỗ trợ các tham số sau

  • -v, xuất những phụ thuộc mô-đun bị xóa
  • -e, nếu trong quá trình xảy ra lỗi thì bỏ qua nó và tiếp tục thực thi
  • -x, xuất quá trình thực thi
  • -go=version, cập nhật phiên bản go trong tệp go.mod
  • -compact=version, giữ lại bất kỳ checksum bổ sung nào cần thiết từ các phiên bản Go chính được chỉ định để tải thành công biểu đồ mô-đun, và nếu lệnh go của phiên bản đó tải bất kỳ gói được import nào từ các phiên bản mô-đun khác nhau, sẽ khiến tidy báo lỗi. Tham số này thường hiếm khi được sử dụng, nói chung chỉ xảy ra lỗi khi thay đổi phiên bản, có thể đến stackoverflow xem câu trả lời này go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow

Xem một ví dụ sử dụng

sh
$ go mod tidy -v
unused github.com/246859/containers
unused github.com/246859/river
unused github.com/Jleagle/steam-go
unused github.com/Jleagle/unmarshal-go
unused github.com/KyleBanks/depth
unused github.com/Microsoft/go-winio
unused github.com/PuerkitoBio/purell
unused github.com/PuerkitoBio/urlesc
unused github.com/andeya/ameda
unused github.com/andeya/goutil
unused github.com/asaskevich/govalidator
unused github.com/buger/jsonparser
unused github.com/bwmarrin/snowflake
unused github.com/bytedance/go-tagexpr/v2
unused github.com/bytedance/sonic
unused github.com/cespare/xxhash/v2
unused github.com/chenzhuoyu/base64x
......

download

sh
$ go help mod download
usage: go mod download [-x] [-json] [-reuse=old.json] [modules]

Mặc dù tên của lệnh download dịch ra là tải xuống, nhưng nó chỉ tải các phụ thuộc vào cache phụ thuộc local, sẽ không sửa đổi tệp go.mod, tác dụng của nó là tải trước các phụ thuộc vào cache tệp local, nếu bạn muốn tải xuống một phụ thuộc nào đó, khuyến nghị sử dụng go get hoặc go mod tidy.

Dưới đây là một vài ví dụ sử dụng

sh
$ go mod download -x gorm.io/gorm
# get https://goproxy.cn/gorm.io/gorm/@v/list
# get https://goproxy.cn/gorm.io/gorm/@v/list: 200 OK (0.084s)

Nếu không có tham số nào, nó sẽ tải xuống tất cả các phụ thuộc tồn tại trong tệp go.mod nhưng không tồn tại trong cache phụ thuộc local, nếu không có gì cần tải xuống nó sẽ xuất

go: no module dependencies to download

edit

sh
$ go help mod edit
usage: go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

edit là một giao diện dòng lệnh, dùng để sửa đổi tệp go.mod, thường là để các chương trình khác sử dụng, một số IDE của trình soạn thảo để cung cấp hỗ trợ gomod sẽ sử dụng các lệnh này. Nó hỗ trợ các tham số sau

  • -module, sửa đổi đường dẫn mô-đun
  • -go=version, sửa đổi phiên bản go mong đợi
  • -require=path@version, thêm một mục phụ thuộc
  • -droprequire=path@version, xóa một mục phụ thuộc
  • -exclude=path@version, thêm một mục loại trừ phụ thuộc
  • -dropexclude=path@version, xóa một mục loại trừ phụ thuộc
  • -replace=old@version=new@version, thêm một mục thay thế phụ thuộc
  • -dropreplace=old@version, xóa một mục thay thế phụ thuộc
  • -retract=version, thêm một mục lùi phiên bản
  • -dropretract=version, xóa một mục lùi phiên bản

Còn có một số tham số khác dùng để hiển thị

  • -print, xuất nội dung tệp
  • -json, xuất dưới dạng json

Ví dụ như ví dụ dưới đây

$ go mod edit -print
module golearn

go 1.21.3

require (
        github.com/dstgo/task v1.2.0
        github.com/spf13/cast v1.5.1
        github.com/swaggo/swag v1.16.2
        golang.org/x/net v0.19.0
        gorm.io/gorm v1.25.5
)

graph

sh
$ go help mod graph
usage: go mod graph [-go=version] [-x]

Lệnh graph sẽ xuất biểu đồ phụ thuộc trong dự án hiện tại, khả năng đọc của nó rất kém, và hầu hết thời gian không phải để con người đọc, kết quả của nó thường được xử lý rồi hiển thị dưới dạng trực quan. Mỗi dòng là một phụ thuộc, định dạng như sau

引用方 被引用方

Ví dụ

golearn go@1.21.3

Nó còn hỗ trợ hai tham số

  • -go=version, sử dụng phiên bản go đã cho để tải biểu đồ phụ thuộc, giá trị của nó không thể nhỏ hơn phiên bản trong tệp go.mod.
  • -x, hiển thị các lệnh được thực thi trong quá trình.

Xem một ví dụ sử dụng đơn giản

sh
$ go mod graph
golearn github.com/246859/containers@v0.0.1
golearn github.com/246859/river@v0.1.0
golearn github.com/Jleagle/steam-go@v0.0.0-20230725082712-1053b441b1f2
golearn github.com/Jleagle/unmarshal-go@v0.0.0-20210227002040-694f544f9265
golearn github.com/KyleBanks/depth@v1.2.1
golearn github.com/Microsoft/go-winio@v0.6.1
golearn github.com/PuerkitoBio/purell@v1.1.1
golearn github.com/PuerkitoBio/urlesc@v0.0.0-20170810143723-de5bf2ad4578
golearn github.com/andeya/ameda@v1.5.3
golearn github.com/andeya/goutil@v1.0.1
golearn github.com/asaskevich/govalidator@v0.0.0-20230301143203-a9d515a09cc2
golearn github.com/buger/jsonparser@v1.1.1
golearn github.com/bwmarrin/snowflake@v0.3.0
golearn github.com/bytedance/go-tagexpr/v2@v2.9.11
......

vendor

sh
$ go help mod vendor
usage: go mod vendor [-e] [-v] [-o outdir]

vendor là một giải pháp thay thế cho gopath trước khi gomod chưa được giới thiệu, mỗi dự án go đều có một thư mục vendor, lưu trữ từng phụ thuộc của mỗi dự án riêng biệt theo định dạng domain/user/project, giống như node_module cồng kềnh của nodeJs bên cạnh, từng phụ thuộc của mỗi dự án được đặt riêng, cách quản lý phụ thuộc này hiện nay trông thực sự rất ngốc, nhưng vào thời điểm đó thực sự không có giải pháp nào tốt hơn, lý do giữ lại vendor là vì go tuân thủ cam kết tương thích ngược, một số dự án cũ bao gồm cả mã nguồn go có thể vẫn đang sử dụng vendor.

Trở lại vấn đề chính, vendor là một lệnh con của go mod, nó có thể export các phụ thuộc toàn cục được引用 bởi mô-đun hiện tại vào thư mục vendor.

sh
$ go mod vendor -h
usage: go mod vendor [-e] [-v] [-o outdir]
Run 'go help mod vendor' for details.

Nó có các tham số sau

  • -o: Chỉ định đường dẫn thư mục xuất
  • -v: Xuất từng phụ thuộc
  • -e: Không thoát khi xảy ra lỗi vẫn tiếp tục

Dưới đây xem một ví dụ, trước hết dùng go list -m all để xem các phụ thuộc được引用 bởi dự án hiện tại

sh
$ go list -m all
github.com/dstgo/task
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.5.0
github.com/stretchr/testify v1.8.4
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/yaml.v3 v3.0.1

Export vào thư mục vendor hiện tại

sh
$ go mod vendor -v -e -o vendor
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
# github.com/pmezard/go-difflib v1.0.0
## explicit
github.com/pmezard/go-difflib/difflib
# github.com/stretchr/testify v1.8.4
## explicit; go 1.20
github.com/stretchr/testify/assert
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3

Cấu trúc thư mục sau khi export như sau

└─vendor
    ├─github.com
    │  ├─davecgh
    │  │  └─go-spew
    │  │      └─spew
    │  ├─pkg
    │  │  └─errors
    │  ├─pmezard
    │  │  └─go-difflib
    │  │      └─difflib
    │  └─stretchr
    │      └─testify
    │          └─assert
    └─gopkg.in
    |    └─yaml.v3
    |
    |--modules.txt

Trong đó modules.txt là tệp mô tả tất cả các mục phụ thuộc, cũng giống như go.mod hiện nay.

verify

sh
$ go help mod verify
usage: go mod verify

Lệnh này sẽ kiểm tra xem các phụ thuộc của dự án có bị sửa đổi sau khi tải xuống local hay không. Ví dụ, nếu không có vấn đề gì sẽ xuất all modules verified

sh
$ go mod verify
all modules verified

Nếu không nó sẽ báo cáo nơi đã xảy ra thay đổi, và kết thúc lệnh với trạng thái không bình thường. Ví dụ

sh
$ go mod verify
gorm.io/gorm v1.25.5: dir has been modified (/go/mod/libs/gorm.io/gorm@v1.25.5)

why

$ go help mod why
usage: go mod why [-m] [-vendor] packages...

Giải thích tại sao gói này lại được phụ thuộc, thực tế là xuất biểu đồ phụ thuộc liên quan đến nó. Ví dụ

sh
$ go mod why gorm.io/gorm
# gorm.io/gorm
golearn
gorm.io/gorm

Mặc định chỉ phân tích cú pháp các import từ main, thêm tham số -m có thể phân tích tình hình import của từng gói.

work

Lệnh work là một công cụ phát triển local dùng để quản lý nhiều mô-đun go

bash
$ go work help
Work provides access to operations on workspaces.

Note that support for workspaces is built into many other commands, not
just 'go work'.

The commands are:

        edit        edit go.work from tools or scripts
        init        initialize workspace file
        sync        sync workspace build list to modules
        use         add modules to workspace file
        vendor      make vendored copy of dependencies

Use "go help work <command>" for more information about a command.

init

Lệnh con init dùng để khởi tạo một workspace, lệnh này sẽ tạo một tệp tên là go.work

bash
$ go work init -h
usage: go work init [moddirs]
Run 'go help work init' for details.

Nhận tham số [moddirs] để chỉ định đưa những mô-đun nào vào quản lý, ví dụ

bash
$ go work init ./service ./api

use

Lệnh con use dùng để thêm các thư mục mô-đun được quản lý vào go.work

bash
$ go help work use
usage: go work use [-r] [moddirs]

Use provides a command-line interface for adding
directories, optionally recursively, to a go.work file.

Nhận [moddirs] làm tham số, còn có một -r biểu thị tìm kiếm đệ quy các mô-đun trong đường dẫn [moddirs], ví dụ

bash
$ go work use -r ./oss-api ./multi_modules

edit

Tác dụng của lệnh con edit giống với go mod edit, đều là để lại giao diện dòng lệnh cho các công cụ và script khác thao tác.

$ go help work edit
usage: go work edit [editing flags] [go.work]

Edit provides a command-line interface for editing go.work,
for use primarily by tools or scripts. It only reads go.work;
it does not look up information about the modules involved.
If no file is specified, Edit looks for a go.work file in the current
directory and its parent directories

Các tham số như sau

  • -fmt, định dạng tệp go.work

  • -use, -dropuse, thêm và xóa đường dẫn mô-đun

  • -replace=old[@v]=new[@v], -dropreplace=old[@v]=new[@v], dùng để thêm và xóa các mô-đun cần thay thế

  • -go, -toolchain=name, chỉ định phiên bản go, và chỉ định công cụ cần sử dụng

  • -print, in ra các sửa đổi cuối cùng, không ghi lại vào tệp

  • -json, xuất dưới dạng json, không thể tồn tại cùng với -print, tương ứng với cấu trúc loại như sau

    go
    type GoWork struct {
            Go        string
            Toolchain string
            Use       []Use
            Replace   []Replace
    }
    
    type Use struct {
            DiskPath   string
            ModulePath string
    }
    
    type Replace struct {
            Old Module
            New Module
    }
    
    type Module struct {
            Path    string
            Version string
    }

Một số ví dụ sử dụng như sau, xuất định dạng

bash
$ go work edit -fmt -print
go 1.22.0

use (
        ./ab/cd
        ./auth
        ./user
)

Xuất json

bash
$ go work edit -fmt -json
{
        "Go": "1.22.0",
        "Use": [
                {
                        "DiskPath": "./ab/cd"
                },
                {
                        "DiskPath": "./auth"
                },
                {
                        "DiskPath": "./user"
                }
        ],
        "Replace": null
}

sync

Lệnh con sync dùng để đồng bộ danh sách các mô-đun trong go.work về các mô-đun trong workspace.

bash
$ go help work sync
usage: go work sync

Sync syncs the workspace's build list back to the
workspace's modules

Quá trình này chủ yếu xảy ra sau khi phát triển local hoàn thành, các mô-đun đã hoàn thành công việc phát hành, lúc này sử dụng sync, nó sẽ cập nhật các phụ thuộc trong go.mod của tất cả các mô-đun trong worksapce dựa trên các quan hệ phụ thuộc của từng mô-đun, từ đó không cần chúng ta cập nhật thủ công.

vendor

Lệnh vendor sẽ sao chép tất cả các thư viện phụ thuộc của tất cả các mô-đun trong workspace vào thư mục vendor.

bash
$ go work help vendor
usage: go work vendor [-e] [-v] [-o outdir]

Chức năng giống với go mod vendor, sẽ không trình bày quá nhiều.

vet

Lệnh vet là một công cụ kiểm tra lỗi tĩnh cho mã nguồn go, giống như các công cụ lint của các ngôn ngữ khác, ví dụ Eslint.

sh
$ go vet -h
usage: go vet [build flags] [-vettool prog] [vet flags] [packages]
Run 'go help vet' for details.
Run 'go tool vet help' for a full list of flags and analyzers.
Run 'go tool vet -help' for an overview.

Trước hết xem một ví dụ đơn giản, hiện có mã nguồn như sau

sh
$ cat main.go
package main

import "fmt"

func main(){
        fmt.Println("hello world!"
}

Thực thi go vet không có tham số nào trong cùng cấp thư mục

sh
$ go vet
vet: ./main.go:6:28: missing ',' before newline in argument list (and 1 more errors)

vet sẽ báo cáo tệp nào dòng nào xảy ra vấn đề gì. Nó hỗ trợ các cờ build làm tham số, ví dụ -n-x, hỗ trợ gói, thư mục, tên tệp làm tham số.

sh
$ go vet .
$ go vet main.go
$ go vet ./cmd
$ go vet runtime

Thông qua lệnh sau để xem các tham số và giải thích chi tiết hơn của nó.

sh
$ go tool vet help
vet is a tool for static analysis of Go programs.

vet examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.

Registered analyzers:

    asmdecl      report mismatches between assembly files and Go declarations
    assign       check for useless assignments
    atomic       check for common mistakes using the sync/atomic package
    bools        check for common mistakes involving boolean operators
    buildtag     check //go:build and // +build directives
    ......

Lệnh go tool vet không thể trực tiếp dùng để kiểm tra mã, nên sử dụng go vet. Tham số [vet flag] của go vet hỗ trợ thiết lập các bộ phân tích mã, các giá trị khả dụng như sau

asmdecl      Kiểm tra xem các tệp hợp ngữ có khớp với các khai báo go không
assign       Kiểm tra xem có các biến không dùng không
atomic       Kiểm tra xem có phá vỡ tính nguyên tử khi sử dụng sync/atomic không
bools        Kiểm tra xem có sử dụng sai các toán tử logic không
buildtag     Kiểm tra các tag build
cgocall      Kiểm tra các hành vi vi phạm quy tắc truyền con trỏ cgao
composites   Kiểm tra các cấu trúc composite chưa khởi tạo, ví dụ map, chan
copylocks    Kiểm tra xem có xảy ra sao chép giá trị của khóa không
directive    Kiểm tra các lệnh của công cụ go
errorsas     Kiểm tra xem có truyền các kiểu không phải con trỏ hoặc không phải kiểu error cho errors.As không
framepointer Kiểm tra xem mã hợp ngữ sau khi tối ưu hóa biên dịch có xóa frame pointer trước khi lưu nó không
httpresponse Kiểm tra xem có sử dụng sai httpresponse không
ifaceassert  Kiểm tra các khẳng định kiểu từ interface sang interface
loopclosure  Vấn đề tham chiếu của các biến vòng lặp
lostcancel   context.WithCancel không sử dụng hàm cancel
nilfunc      Kiểm tra xem có so sánh vô ích giữa các hàm và nil không
printf       Kiểm tra xem các tham số định dạng của printf có đúng không
shift        Kiểm tra xem có các phép dịch chuyển bằng hoặc vượt quá độ rộng số nguyên không
sigchanyzer  Kiểm tra các chan os.Signal không có bộ đệm
slog         Kiểm tra các cuộc gọi log có cấu trúc không hợp lệ
stdmethods   Kiểm tra xem các chữ ký của các phương thức interface đã biết có đúng không
stringintconv Kiểm tra các chuyển đổi chuỗi-số nguyên
structtag    Kiểm tra xem các tag của cấu trúc có đúng không
testinggoroutine Kiểm tra xem có sử dụng goroutine trong kiểm thử để gọi testing.Fatal không
tests        Kiểm tra các lỗi sử dụng phổ biến trong kiểm thử và ví dụ
timeformat   Kiểm tra xem định dạng thời gian của (time.Time).Format hoặc time.Parse có đúng không
unmarshal    Kiểm tra xem có truyền các kiểu không phải con trỏ hoặc không phải interface cho unmarshal không
unreachable  Kiểm tra các mã không thể đến được
unsafeptr    Kiểm tra các chuyển đổi không đúng từ uintptr sang unsafe.Pointer
unusedresult Kiểm tra các giá trị trả về của hàm không được sử dụng

Chúng đều là các bộ phân tích phân tích cho một điểm cụ thể nào đó, ví dụ bộ phân tích timeformat kiểm tra xem cuộc gọi của time.Format có tuân thủ cú pháp đúng không. Trong trường hợp mặc định tất cả các bộ phân tích trên đều được bật, nếu muốn bật riêng có thể sử dụng định dạng sau

sh
$ go vet -timeformat main.go

Tắt riêng

sh
$ go vet -timeformat=false main.go

Mã nguồn của các bộ phân tích này nằm trong cmd/vendor/golang.org/x/tools/go/analysis/passes, mỗi bộ phân tích đều là một lỗi dễ mắc phải trong go, nên rất khuyến nghị sử dụng lệnh vet để kiểm tra mã của bạn. Ngoài những điều này ra, nó còn hỗ trợ một số cờ tham số khác

  • -V, chỉ in phiên bản rồi thoát
  • -json, xuất dưới dạng json
  • -c=n, hiển thị số dòng xung đột được chỉ định trong ngữ cảnh (dường như không có tác dụng gì)

Còn có một số bộ phân tích bên ngoài, ví dụ shadows, nó phụ trách phát hiện các vấn đề ẩn của các biến được đặt tên ngắn, vì là bên ngoài nên cần dùng go install để tải xuống

sh
$ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest

Định dạng sử dụng như sau

sh
$ go vet -vettool=$(which shadow)

test

sh
$ go test -h
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
Run 'go help test' and 'go help testflag' for details.

Lệnh test là lệnh cung cấp chức năng kiểm thử trong bộ công cụ go, chức năng này khá quan trọng, đối với một phần mềm而言, kiểm thử hoàn thiện là điều không thể thiếu. Ở đây chỉ đơn giản giới thiệu cách sử dụng lệnh test, nếu muốn tìm hiểu thêm về kiểm thử, truy cập: Kiểm thử

Nó ngoài hỗ trợ các tham số biên dịch của lệnh build ra, test còn hỗ trợ các tham số sau

  • -args, các tham số entry của chương trình
  • -c, biên dịch các tệp kiểm thử nhị phân của gói hiện tại vào thư mục hiện tại nhưng không thực thi, đặt tên theo cách pkg.test
  • -exec, thực thi một số lệnh khác trước khi bắt đầu kiểm thử
  • -json, phong cách xuất của kiểm thử变为 json
  • -o, chỉ định đường dẫn của tệp nhị phân kiểm thử

Nó còn hỗ trợ nhiều testflag, sử dụng lệnh help để xem tất cả testflag

sh
$ go help testflag
`go test` 命令既接受作用于 `go test` 本身的标志,
也接受作用于生成的测试二进制文件的标志。

`go test` 命令识别以下标志,并用于控制任何测试的执行:
        -bench regexp
        -benchtime t
        -count n
    ......

Giới thiệu một số常用

  • -v, xuất kết quả kiểm thử của từng trường hợp sử dụng.
  • -timeout duration, thời gian超时 thực thi kiểm thử
  • -skip regexp, bỏ qua các trường hợp kiểm thử được chỉ định
  • -short, rút ngắn thời gian chạy của những trường hợp kiểm thử chạy rất lâu
  • -shuffle, xáo trộn thứ tự thực thi của tất cả các trường hợp kiểm thử
  • -run regexp, chạy các trường hợp kiểm thử được chỉ định
  • -list regexp, liệt kê từng trường hợp kiểm thử
  • -cpu 1,2,4, chỉ định số lượng cpu
  • -count n, chỉ định mỗi trường hợp kiểm thử thực thi bao nhiêu lần

Cách sử dụng đơn giản nhất là, không có tham số nào, nó sẽ thực thi tất cả các trường hợp kiểm thử trong gói hiện tại, và xuất kết quả.

sh
$ ls *_test.go
hello_test.go

$ go test
PASS
ok      golearn 0.522s

Chỉ định một tệp kiểm thử nào đó

sh
$ go test hello_test.go
ok      command-line-arguments  0.041s

Thêm tham số -v có thể xem xuất chi tiết hơn, nó khá常用.

sh
$ go test -v hello_test.go
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      command-line-arguments  0.041s

Chỉ định một trường hợp kiểm thử nào đó

sh
$ go test -v -run TestHello
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      golearn 0.028s

Khi kiểm thử, lệnh test có hai chế độ, trước hết nói về chế độ thư mục đầu tiên, khi thực thi lệnh test không có tham số package, nó sẽ thực thi kiểm thử theo chế độ thư mục, ví dụ như các lệnh dưới đây

sh
$ go test
$ go test -v

Trong chế độ này, tắt cache kiểm thử. Chế độ còn lại là chế độ danh sách, khi tham số package không rỗng, sẽ thực thi kiểm thử theo chế độ danh sách, khác biệt của nó với cái trước là có bật cache kiểm thử hay không. Ví dụ như các lệnh dưới đây

sh
$ go test -v .
$ go test -v ./...
$ go test .
$ go test -v net/http

Trong chế độ danh sách, go sẽ biên dịch các tệp kiểm thử của từng gói trong gói được chỉ định thành các tệp nhị phân và thực thi, để tránh chạy lặp lại kiểm thử, go mặc định sẽ cache kết quả, khi chạy lần thứ hai sẽ không biên dịch lại. Khi sử dụng các tham số sau sẽ mặc định bật cache

  • -benchtime
  • -cpu
  • -list
  • -parallel
  • -run
  • short
  • -timeout
  • -failfast
  • -v

Sử dụng các tham số khác ngoài những tham số này có thể tắt cache, cách làm được官方 khuyến khích là sử dụng -count=1 để tắt cache. Ví dụ

sh
$ go test -v -count=1 ./...

Chỉ thị

Khác với lệnh, các chỉ thị của go tồn tại dưới dạng hardcode trong các tệp nguồn, chúng có một tên thuật ngữ khác: chỉ thị biên dịch (progma directives).

Trình biên dịch và linker sẽ thay đổi hành vi của chính chúng do chúng để đạt được hiệu quả kiểm soát biên dịch, giống như macro trong ngôn ngữ c, đương nhiên không phải tất cả các chỉ thị đều dùng để ảnh hưởng đến biên dịch, một phần dùng cho các hành vi chức năng khác, ví dụ chỉ thị generate thường dùng cho chức năng tạo mã. Những chỉ thị này thường tồn tại dưới dạng chú thích, và có tiền tố là //go:, ở giữa không thể chứa bất kỳ khoảng trắng nào, ví dụ chỉ thị //go:generate. Tất cả các loại chỉ thị tổng cộng chia làm hai loại

  • Chỉ thị chức năng, đây là các chỉ thị chức năng mà go cung cấp có thể sử dụng tùy ý, ví dụ generate, embed, build.
  • Chỉ thị trình biên dịch, loại chỉ thị này cần thận trọng khi sử dụng, sử dụng bừa bãi có thể dẫn đến các kết quả không thể dự đoán được.

Ngoài các chỉ thị chức năng ra, hầu hết các chỉ thị chỉ có thể tác dụng lên các chữ ký hàm. Đối với các chỉ thị trình biên dịch có thể thực thi lệnh go doc compile để xem các chỉ thị của nó. Đối với tất cả các chỉ thị, có thể tìm thấy thông tin liên quan đến chúng trong cmd/compile/internal/ir/node.go: 440.

generate

sh
$ go help generate
usage: go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

Chỉ thị generate như tên gọi là liên quan đến tạo, thường tác dụng của nó là dùng để thực thi các lệnh sẽ tạo mã và cập nhật mã nguồn, nhưng thực tế nó có thể thực thi bất kỳ lệnh nào. Và, chỉ thị generate khác với các chỉ thị khác, nó có một lệnh chuyên dụng có thể dùng để thực thi tất cả các chỉ thị generate nằm trong các tệp nguồn. Nó có thể dùng tên tệp hoặc tên gói làm tham số đầu vào để biểu thị thực thi các chỉ thị generate của những tệp nào, dưới đây là các tham số khác của nó.

  • -run=regex, chạy các chỉ thị generate được chỉ định
  • -skip=regex, bỏ qua các chỉ thị generate được chỉ định
  • -n, in ra các lệnh sẽ được thực thi
  • -x, in ra các lệnh được thực thi trong quá trình
  • -v, xuất các tệp được xử lý

Ngoài ra, các lệnh được thực thi trong chỉ thị generate còn hỗ trợ các tham số built-in sau

  • $GOARCH, kiến trúc cpu
  • $GOOS, hệ điều hành
  • $GOFILE, tên tệp
  • $GOLINE, số dòng
  • $GOPACKAGE, tên gói
  • $GOROOT, go root
  • $DOLLAR, ký hiệu đô la
  • $PATH, biến môi trường path

Xem một ví dụ, không có mã gì chỉ có một dòng chú thích

go
package main

//go:generate echo "hello world!"

Thực thi lệnh

$ go generate .
hello world!

Ví dụ này là thực thi lệnh go

go
package main

//go:generate go version

Thực thi lệnh

sh
$ go generate .
go version go1.21.3 windows/amd64

Chỉ thị generate có thể dùng để thực thi bất kỳ lệnh nào, ví dụ swagger tạo tài liệu API, hoặc Wire tạo mã IOC. Nhưng chỉ thị này không thích hợp để thực thi các lệnh quá phức tạp, nó thích hợp để thực thi các lệnh ngắn, nếu có yêu cầu phức tạp có thể sử dụng script hoặc makefile để thay thế.

embed

Chỉ thị embed là mới được thêm vào trong 1.16, tác dụng của nó là có thể đóng gói các tệp tĩnh cùng vào trong tệp nhị phân, ví dụ như các mẫu HTML. Định dạng của nó như sau

go
//go:embed pattern

pattern có thể là biểu thức glob, cũng có thể là một thư mục hoặc một tệp cụ thể nào đó. Xem một ví dụ

go
package main

import "embed"

//go:embed *
var static embed.FS

Chỉ thị embed yêu cầu phải nằm trên một biến toàn cục có kiểu embed.Fs, lưu ý phải là biến toàn cục, và sử dụng nó phải import gói embed, trong ví dụ này, * đại diện cho sẽ đóng gói tất cả các tệp trong thư mục hiện tại vào trong tệp nhị phân, nhưng nó sẽ không cho phép tồn tại các thư mục bắt đầu bằng ..

Ví dụ dưới đây hiển thị đọc nội dung từ các tệp được nhúng

go
package main

import (
  "embed"
  "fmt"
)

//go:embed *.txt
var static embed.FS

func main() {
  bytes, err := static.ReadFile("hello.txt")
  if err != nil {
    panic(err)
  }
  fmt.Println(string(bytes))
}

Nó chỉ có ba phương thức, sử dụng không khác biệt gì so với hệ thống tệp thông thường, và vì nó triển khai interface io/Fs, nên cũng có thể được truyền như một đối tượng Fs.

go
func (f FS) Open(name string) (fs.File, error)
func (f FS) ReadFile(name string) ([]byte, error)
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)

Ví dụ dưới đây hiển thị nhúng các tệp html thông qua chỉ thị embed, và truy cập thông qua dịch vụ http.

go
package main

import (
  "embed"
  "net/http"
)

//go:embed index.html
var htmlFs embed.FS

func main() {
  http.Handle("/", http.FileServer(http.FS(htmlFs)))
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

Kết quả truy cập như sau

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

Chỉ thị embed còn hỗ trợ kiểu của biến toàn cục có thể là []byte, ví dụ như ví dụ dưới đây

go
package main

import (
  _ "embed"
  "net/http"
)

//go:embed index.html
var rawdata []byte

func main() {
  http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    writer.Write(rawdata)
  })
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

Hiệu quả mà nó triển khai cũng tương tự như ví dụ trước.

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

build

Trong phần build-Kiểm soát biên dịch, đã nói đến việc sử dụng chỉ thị // +build để kiểm soát hành vi biên dịch. Còn chỉ thị //go:build là mới ra trong 1.17, với ý định thay thế chỉ thị trước, nhưng hiện tại đã là 1.21 mà vẫn chưa thay thế,估计 sau này sẽ tồn tại dưới dạng cùng tồn tại, về chỉ thị mới này, tài liệu官方 cũng có giới thiệu: build constraints. Chức năng của nó không khác biệt gì so với cái trước, nhưng cú pháp nghiêm ngặt hơn, hỗ trợ biểu thức boolean, xem một ví dụ

go
//go:build (linux && 386) || (darwin && !cgo)

package pkg_name

Cách này có khả năng đọc cao hơn nhiều so với cách trước.

line

Chỉ thị line sẽ ảnh hưởng đến số dòng, số cột, và tên tệp của dòng tiếp theo của nó, tác dụng của nó chỉ có vậy, hầu hết thời gian có thể dùng để gỡ lỗi v.v. Ví dụ khi xảy ra lỗi, sẽ thay đổi thông tin mà trình biên dịch xuất.

go
package main

var a undefinedType

func main() {

}

Trong trường hợp bình thường, trình biên dịch sẽ xuất

.\main.go:3:7: undefined: undefinedType

Nhưng nếu sử dụng chỉ thị line, thì sẽ khác

go
package main

//line abc.go:10:100
var a undefinedType

func main() {

}

Thì kết quả xuất của nó sẽ là

abc.go:10:106: undefined: undefinedType

Và vì lý do lịch sử, chỉ thị line cũng là chỉ thị duy nhất có cách sử dụng khác với các chỉ thị khác. Định dạng của nó là

go
//line filename:line:column

Có thể thấy nó không cần go: làm tiền tố.

linkname

Hoạt động của chỉ thị này có thể link các hàm hoặc biến toàn cục của các gói khác, ngay cả khi nó là kiểu riêng tư, thao tác này thường xuất hiện trong thư viện chuẩn đặc biệt là runtime, có một số hàm không có thân hàm được triển khai thông qua cách này, một phần khác các hàm có thân hàm rỗng thì được triển khai bằng hợp ngữ. Xem cách sử dụng của nó, định dạng sử dụng như sau

go
//go:linkname 链接类型名称 被链接的类型

Và trước khi sử dụng, ví dụ import gói unsafe. Xem một ví dụ đơn giản link các kiểu riêng tư trong thư viện chuẩn

go
import (
  "fmt"
  "unsafe"
)

//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

func MemHash(data []byte) uint64 {
  ptr := unsafe.Pointer(unsafe.SliceData(data))
  return uint64(memhash(ptr, 0, uintptr(len(data))))
}

func main() {
  fmt.Println(MemHash([]byte("hello")))
}

Kết quả xuất

15395306441938000233

Nó đã link hàm riêng tư runtime.memhash với hàm mà chúng ta tự khai báo, hàm này không có thân hàm chỉ có một chữ ký, chỉ đóng vai trò là một vật mang. Tác dụng của memhash là给定 một con trỏ, hạt giống hash, và độ lệch bộ nhớ, tính toán giá trị hash dựa trên bộ nhớ. Quá trình link này được hoàn thành trong thời gian biên dịch,

Nếu không phải là thư viện chuẩn, thì tình hình sẽ có chút khác biệt, ví dụ trong gói example có một hàm test, trước khi link trước hết phải import ẩn danh gói này.

go
package example

// 一个私有类型,外界无法访问。
func test() string {
  return "a"
}
go
package main

import (
  "fmt"
  _ "golearn/example"
  _ "unsafe"
)

//go:linkname test golearn/example.test
func test() string

func main() {
  fmt.Println(test())
}

Kết quả xuất

a

Có thể thấy đã link thành công, phương pháp này có thể vượt qua hệ thống mô-đun của go để làm bất cứ điều gì, nhưng không khuyến nghị sử dụng quy mô lớn, trừ khi bạn biết mình đang làm gì.

noinline

Chỉ thị noinline biểu thị một hàm bị cấm tối ưu hóa inline, ngay cả khi nó rất đơn giản. Xem một ví dụ đơn giản

go
package main

func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

val là một hàm rất đơn giản, tác dụng của nó là trả về một ký tự chuỗi, vì nó quá đơn giản và kết quả luôn có thể dự đoán được, thì trong thời gian biên dịch nó sẽ được trình biên dịch tối ưu hóa thành dạng sau

go
package main

func main() {
  var c = "val"
  _ = c
}

Xem dạng hợp ngữ của nó, có thể thấy không phát hiện cuộc gọi của hàm val.

TEXT    main.val(SB), NOSPLIT|NOFRAME|ABIInternal, $0-0
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
LEAQ    go:string."val"(SB), AX
MOVL    $3, BX

FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
RET

Tiếp theo thêm chỉ thị noinline

go
package main

//go:noinline
func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

Xem lại dạng hợp ngữ của nó

CMPQ    SP, 16(R14)
PCDATA  $0, $-2
JLS     17
PCDATA  $0, $-1
PUSHQ   BP
MOVQ    SP, BP
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
PCDATA  $1, $0
CALL    main.val(SB)
POPQ    BP
RET

Lần này có thể thấy rất rõ cuộc gọi main.val này, và đây cũng chính là chức năng mà chỉ thị noinline phát huy, ngăn chặn inline hàm khi trình biên dịch tối ưu hóa.

nospilit

Tác dụng của chỉ thị nospilit là bỏ qua phát hiện tràn stack. Vì mô hình lập lịch đồng thời của go là lập lịch抢占式, giả sử một hàm sẽ chạy mã rất底层, các goroutine khác khi gọi hàm này không thích hợp bị抢占, có thể sử dụng chỉ thị này để biểu thị.

go
//go:nosplit
func nospilitFn()

Sau khi sử dụng chỉ thị này, cũng sẽ không còn tăng trưởng stack.

noescape

noescape, thông qua tên của nó có thể dễ dàng đoán được là liên quan đến escape, tác dụng của nó là biểu thị hàm hiện tại sẽ không xảy ra hành vi escape bộ nhớ, sau khi thực thi xong tất cả các tài nguyên đều được thu hồi, và hàm này phải chỉ có chữ ký không có thân hàm, trong trường hợp này nói chung việc triển khai hàm được thực hiện bằng hợp ngữ.

Ví dụ memhash đã sử dụng trước đó sẽ sử dụng chỉ thị này

go
//go:noescape
//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

Như vậy, trình biên dịch sẽ không thực hiện phân tích escape cho nó, điều kiện tiên quyết là bạn phải đảm bảo nó sẽ không xảy ra escape, nếu xảy ra, thì không biết sẽ có hậu quả gì.

uintptrescapes

Chỉ thị uintptrescapes biểu thị các tham số kiểu uinptr trong hàm này được chuyển thành giá trị con trỏ và escape vào heap, và phải giữ chúng sống. Chỉ thị này nói chung dùng cho một số cuộc gọi hệ thống底层, trong hầu hết các trường hợp không cần phải tìm hiểu nó.

go
//go:uintptrescapes
func nospilit(ptr uintptr) uintptr

Trước đây应该 còn có một chỉ thị notinheaps dùng để biểu thị một kiểu không cho phép phân bổ bộ nhớ trên heap, không biết đã bị xóa trong phiên bản nào.

norace

Chỉ thị norace biểu thị việc truy cập bộ nhớ của một hàm không cần phân tích race condition, thường là khi chạy mã底层 không thích hợp để thực hiện phân tích race condition.

go
//go:norace
func low_level_code(ptr uintptr) uintptr

TIP

Còn có một số chỉ thị bị hạn chế chỉ có thể được sử dụng bởi gói runtime, bên ngoài không thể sử dụng, chúng sẽ liên quan đến những thứ sâu hơn, muốn tìm hiểu có thể xem giới thiệu liên quan đến chúng trong Runtime-only compiler directives.

Golang by www.golangdev.cn edit