Skip to content

คำสั่งบรรทัดคำสั่ง

คำสั่งใน Go ประกอบด้วยชุดเครื่องมือทั้งหมด ซึ่งครอบคลุมการจัดทำเอกสาร การจัดรูปแบบ การตรวจสอบโค้ด การคอมไพล์ การทดสอบ การจัดการ dependencies และอื่น ๆ อีกมากมาย

text
bug         รายงานช่องโหว่
build       คอมไพล์แพ็กเกจและ dependencies
clean       ลบไฟล์ object
doc         แสดงเอกสารจากซอร์สโค้ด
env         ดูข้อมูลตัวแปรสภาพแวดล้อมของ Go
fix         แก้ไขปัญหาความเข้ากันได้ของ API เนื่องจากเวอร์ชัน go เปลี่ยนแปลง
fmt         จัดรูปแบบซอร์สโค้ด
generate    สร้างโค้ด
get         เพิ่ม dependencies
install     ติดตั้งและคอมไพล์แพ็กเกจ
list        คำสั่งแสดงรายการแพ็กเกจ/โมดูล
mod         คำสั่งจัดการโมดูล
work        คำสั่งจัดการ workspace
run         คอมไพล์และรัน
test        ทดสอบ
tool        รันเครื่องมือ go ที่ระบุ
version     แสดงข้อมูลเวอร์ชันของ go
vet         สแกนและรายงานปัญหาที่อาจมีในซอร์สโค้ด

บทความนี้อธิบายการใช้งานอย่างง่ายเท่านั้น เนื้อหาทั้งหมดอ้างอิงจากเอกสารทางการ หากต้องการเรียนรู้รายละเอียดเพิ่มเติมสามารถไปที่ cmd/go

help

คำสั่งแรกที่ต้องรู้จักคือคำสั่ง help ซึ่งสามารถอ่านวิธีการใช้งานของคำสั่งได้ มีสองวิธีใช้งาน หากต้องการรับข้อมูลการใช้งานแบบย่อ สามารถเพิ่มแฟลก -h หลังคำสั่งที่ต้องการ เช่น

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

go จะแสดงวิธีการใช้งานของคำสั่งนั้นอย่างกระชับ และยังแจ้งว่าหากต้องการข้อมูลรายละเอียดเพิ่มเติมต้องใช้คำสั่ง 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'.

การใช้คำสั่ง help อย่างเชี่ยวชาญจะช่วยให้คุณได้รับข้อมูลเกี่ยวกับคำสั่งมากมาย

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

คำสั่ง doc จะแสดงเอกสารประกอบสำหรับแพ็กเกจ ค่าคงที่ ฟังก์ชัน ประเภท ตัวแปร เมธอด หรือแม้แต่ฟิลด์ของโครงสร้างที่ระบุ เมื่อไม่ระบุพารามิเตอร์ใด ๆ จะแสดงความคิดเห็นของแพ็กเกจปัจจุบัน

sh
$ go doc

สามารถระบุเพื่อดูแพ็กเกจใดแพ็กเกจหนึ่งได้ เช่น ดูเอกสารของแพ็กเกจ 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.
......

หรือดูประเภทใดประเภทหนึ่ง

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

หรือดูฟังก์ชันใดฟังก์ชันหนึ่ง

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.

มีแฟลกที่ใช้บ่อยดังนี้

  • -u ดูประเภทส่วนตัว
  • -all ดูเอกสารทั้งหมดของแพ็กเกจที่ระบุ
  • -short แสดงคำอธิบายแบบย่อหนึ่งบรรทัด
  • -src แสดงซอร์สโค้ด
  • -cmd แสดงเอกสารโค้ดภายในแพ็กเกจที่เป็นคำสั่ง go

เช่น ดูตัวแปร runtime.inf ซึ่งเป็นตัวแปรที่ไม่เปิดเผย

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

var inf = float64frombits(0x7FF0000000000000)

การใช้คำสั่ง doc อย่างเหมาะสมจะช่วยให้คุณอ่านเอกสารได้สะดวกขึ้น

อีกวิธีหนึ่งในการอ่านเอกสารคำสั่งคือการอ่านซอร์สโค้ด เนื่องจากเอกสารของคำสั่งบางคำสั่งไม่ได้เขียนอย่างละเอียด แต่ในซอร์สโค้ดจะมีคำอธิบายที่ค่อนข้างละเอียด เนื่องจากคำสั่งเหล่านี้เขียนด้วย go ทั้งหมด การอ่านจึงสะดวกมาก คำสั่งเหล่านี้อยู่ในแพ็กเกจ src/cmd แต่ละซับแพ็กเกจคือคำสั่งแยกต่างหาก จุดเริ่มต้นอยู่ที่ไฟล์ 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,
    }
}

ที่นี่คุณจะพบคำสั่งย่อยทั้งหมดของ go และข้อมูลเอกสารช่วยเหลือ

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.

คำสั่งนี้ไม่มีพารามิเตอร์หรือแฟลกใด ๆ จะใช้เบราว์เซอร์เริ่มต้นของคุณเพื่อเข้าถึงหน้า issue ของรีพอสิทอรี github.com/golang/go เพื่อสะดวกในการรายงานข้อบกพร่อง นอกเหนือจากนี้ไม่มีฟังก์ชันอื่นใด

version

สามารถดูข้อมูลเวอร์ชันของ go ปัจจุบันผ่านคำสั่ง version

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

เมื่อรันโดยไม่มีพารามิเตอร์ใด ๆ จะแสดงเวอร์ชันของภาษา go ปัจจุบัน

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

ยังรับพาธไฟล์เป็นพารามิเตอร์ จะแสดงเวอร์ชัน go ที่ใช้เมื่อคอมไพล์ไฟล์ไบนารีทั้งหมดที่สามารถระบุได้ที่พาธนั้น

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

พารามิเตอร์ -v ระบุให้คำสั่ง version พยายามแสดงเวอร์ชัน go ของไฟล์ที่ไม่สามารถระบุได้ พารามิเตอร์ -m แสดงข้อมูลโมดูลของไฟล์ไบนารี และพารามิเตอร์การคอมไพล์บางตัว ด้านล่างเป็นตัวอย่างง่าย ๆ

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

go เองก็เป็นไฟล์ไบนารี ในความเป็นจริงเมื่อไม่มีพารามิเตอร์ใด ๆ go version จะแสดงเวอร์ชันภาษา go ของไฟล์ไบนารีของตัวเอง เนื่องจากเครื่องมือทั้งหมดของ cmd/go ถูกใช้งานโดยภาษา go เอง

env

สามารถดูค่าของตัวแปรสภาพแวดล้อม go ทั้งหมดผ่านคำสั่ง env การแก้ไขตัวแปรสภาพแวดล้อมเหล่านี้จะส่งผลต่อพฤติกรรมของเครื่องมือ go

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

รันคำสั่งนี้โดยไม่มีพารามิเตอร์ใด ๆ จะแสดงค่าของตัวแปรสภาพแวดล้อม go ทั้งหมด

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

ระบุชื่อตัวแปรสภาพแวดล้อมใดตัวแปรหนึ่งเป็นพารามิเตอร์เพื่อแสดงค่าของตัวแปรนั้นเพียงตัวเดียว

sh
$ go env GO111MODULE
on

เพิ่ม -json เพื่อแสดงในรูปแบบ json

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

ผ่านแฟลก -w และใช้รูปแบบ var=value เป็นพารามิเตอร์ จะแก้ไขค่าของตัวแปรใดตัวแปรหนึ่งอย่างถาวร

sh
$ go env -w GO111MODULE=on

ใช้แฟลก -u เพื่อรีเซ็ตตัวแปรใดตัวแปรหนึ่งกลับเป็นค่าเริ่มต้น

sh
$ go env -u GO111MODULE

รัน go help environment เพื่อดูคำอธิบายของตัวแปรสภาพแวดล้อมแต่ละตัว

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

ต่อไปนี้จะแนะนำตัวแปรสภาพแวดล้อมที่ใช้บ่อยบางตัว

GOVERSION

ค่าของตัวแปรสภาพแวดล้อมนี้ขึ้นอยู่กับเวอร์ชันของภาษา go และหมายเลขเวอร์ชันมาจากไฟล์ $GOROOT/VERSION ซึ่งบันทึกเวอร์ชันปัจจุบันและเวลาสร้างของ go

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

ค่าของตัวแปร runtime.Version เหมือนกับค่าของ GOVERSION และตัวแปรสภาพแวดล้อมนี้ไม่สามารถแก้ไขได้

GOENV

ในไดเรกทอรี $GOROOT จะมีไฟล์การตั้งค่าเริ่มต้นชื่อ 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

รูปแบบของมันคือ key=value อย่างง่าย ค่าของตัวแปรสภาพแวดล้อมที่แก้ไขผ่านคำสั่ง go env -w key=value จะถูกเขียนลงในไฟล์การตั้งค่า อย่างไรก็ตาม可以不ใช้ไฟล์การตั้งค่าเริ่มต้นก็ได้ ตัวแปรสภาพแวดล้อม GOENV สามารถระบุที่อยู่ของไฟล์การตั้งค่า env ด้วยตนเอง และค่าของตัวแปรสภาพแวดล้อม GOENV สามารถถูกแทนที่ด้วยตัวแปรสภาพแวดล้อมของระบบปฏิบัติการเท่านั้น ไม่สามารถแก้ไขด้วยคำสั่ง go env -w

GOHOSTARCH

แสดงถึงสถาปัตยกรรม CPU ของเครื่องนี้ ใช้สำหรับแสดงเท่านั้น ค่าของตัวแปรสภาพแวดล้อมนี้ไม่ได้อ่านจากไฟล์การตั้งค่า และไม่สามารถแก้ไขได้

GOHOSTOS

แสดงถึงระบบปฏิบัติการของเครื่องนี้ ใช้สำหรับแสดงเท่านั้น ค่าของตัวแปรสภาพแวดล้อมนี้ไม่ได้อ่านจากไฟล์การตั้งค่า และไม่สามารถแก้ไขได้

GOOS

เมื่อคอมไพล์ ค่าของ GOOS จะกำหนดว่าจะคอมไพล์ซอร์สโค้ดเป็นไฟล์ไบนารีของระบบเป้าหมายใด ค่าเริ่มต้นคือ GOHOSTOS ซึ่งก็คือระบบปฏิบัติการของเครื่องนี้ มีตัวเลือกดังนี้

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

ระบบปฏิบัติการที่รองรับจริง ๆ ไม่ได้มีเพียงเท่านี้ ใช้คำสั่ง go tool dist list เพื่อดูค่าที่รองรับทั้งหมด

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

เมื่อคอมไพล์ ค่าของ GOARCH จะกำหนดว่าจะใช้สถาปัตยกรรม CPU ใดในการคอมไพล์ ค่าเริ่มต้นคือ GOHOSTARCH ซึ่งก็คือสถาปัตยกรรม CPU ของเครื่องนี้ มีตัวเลือกดังนี้

  • amd64
  • 386
  • arm
  • ppc64

สถาปัตยกรรมที่รองรับจริง ๆ ไม่ได้มีเพียงเท่านี้ ใช้คำสั่ง go tool dist list เพื่อดูค่าที่รองรับทั้งหมด

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

ควรทราบว่า GOOS และ GOARCH ไม่สามารถรวมกันได้อย่างอิสระ ระบบปฏิบัติการบางระบบรองรับสถาปัตยกรรม CPU เฉพาะบางตัวเท่านั้น

GOROOT

GOROOT แสดงถึงไดเรกทอรีรากของตำแหน่งติดตั้งภาษา go ค่าของ GOROOT ไม่สามารถแก้ไขโดยตรง และสามารถถูกแทนที่ด้วยตัวแปรสภาพแวดล้อมของระบบปฏิบัติการเท่านั้น

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

ในไดเรกทอรีรากมีโฟลเดอร์หรือไฟล์ที่สำคัญบางตัวดังนี้

  • lib เก็บ dependencies ปัจจุบันมีเพียงไลบรารีที่มีข้อมูลเขตเวลาของประเทศต่าง ๆ ทั่วโลก อยู่ที่ $GOROOT/lib/time ไฟล์ไบนารีที่คอมไพล์แล้วจะไม่มีข้อมูลเขตเวลาเหล่านี้

  • pkg เก็บไลบรารีเครื่องมือและไฟล์เฮดเดอร์ เช่น คำสั่ง go tool จะค้นหาไฟล์ไบนารีของเครื่องมือ go ภายใต้ไดเรกทอรี $GOROOT/pkg/tool

  • bin เก็บไฟล์ไบนารี โดยค่าเริ่มต้นมีเพียงไฟล์ที่รันได้ go และ gofmt เท่านั้น ควรเพิ่ม $GOROOT/bin ลงในตัวแปรระบบ มิฉะนั้นจะไม่สามารถใช้คำสั่ง go ได้

  • src เก็บซอร์สโค้ดของ go

  • VERSION ไฟล์นี้เก็บข้อมูลเวอร์ชันของภาษา go

  • go.env ไฟล์นี้เป็นไฟล์การตั้งค่า env เริ่มต้น

GOPATH

ค่าเริ่มต้นของ GOPATH คือ $HOME/go ค่าของตัวแปรสภาพแวดล้อมนี้ระบุว่าจะไปหาไฟล์ที่นำเข้าที่ไหนเมื่อแยกวิเคราะห์คำสั่ง import ในยุคแรก ๆ ที่ยังไม่มี gomod GOPATH ใช้สำหรับเก็บไลบรารีบุคคลที่สามต่าง ๆ โดยเฉพาะ มีโครงสร้างดังนี้

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)

หลังจาก gomod เกิดขึ้น GOPATH ก็เป็นเพียงที่ใช้เก็บ dependencies ที่ดาวน์โหลดด้วย go get และใช้เก็บไฟล์ไบนารีที่ดาวน์โหลดและคอมไพล์ด้วย go install ควรทราบว่าตำแหน่งของ GOPATH ต้องไม่เหมือนกับ GOROOT มิฉะนั้นจะไม่มีผลใด ๆ

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

จนถึงขณะที่ผู้เขียนเขียนบทความนี้ เวอร์ชันของภาษา go ได้มาถึง go1.21.3 แล้ว ยกเว้นโปรเจกต์ที่เก่าแก่มาก ๆ โดยพื้นฐานแล้วไม่มีใครใช้ gopath เพื่อจัดการ dependencies อีกต่อไป

GOBIN

GOBIN ใช้สำหรับเก็บไฟล์ไบนารีที่รันได้ของบุคคลที่สามที่ดาวน์โหลดและคอมไพล์ด้วย go install ค่าเริ่มต้นคือ $GOPATH/bin เช่นเดียวกับ $GOROOT/bin ไดเรกทอรีนี้ควรเพิ่มลงในตัวแปรสภาพแวดล้อมของระบบปฏิบัติการ มิฉะนั้นจะไม่สามารถใช้ไฟล์ไบนารีภายใต้ไดเรกทอรี GOBIN ได้

GOMODCACHE

GOMODCACHE แสดงถึงตำแหน่งที่เก็บ dependencies ที่ดาวน์โหลดด้วย go get ค่าเริ่มต้นคือ $GOPATH/pkg/mod รูปแบบการเก็บมีดังนี้

$GOMODCACHE/domain/username/project@verion

ในไดเรกทอรีระดับเดียวกันจะมีโฟลเดอร์ชื่อ sumdb ใช้สำหรับเก็บข้อมูลที่เกี่ยวข้องกับฐานข้อมูล checksum ของ dependencies

GOCACHE

เก็บข้อมูลแคชสำหรับการคอมไพล์ ค่าเริ่มต้นคือ $HOME/.cache/go-build ภายใต้ไดเรกทอรีนี้จะสร้างไฟล์ 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.

ทุกครั้งที่ build จะสร้างไฟล์จำนวนมาก go จะแคชไฟล์เหล่านี้เพื่อใช้ซ้ำในการคอมไพล์ครั้งต่อไป

GOTEMPDIR

ใช้สำหรับไฟล์ชั่วคราวที่เกิดขึ้นระหว่างการคอมไพล์ เช่น ไฟล์ไบนารีชั่วคราวที่จะรันด้วย go run ค่าเริ่มต้นคือไดเรกทอรีชั่วคราวที่ระบบปฏิบัติการระบุ บน mac หรือ linux คือ /tmp บน windows คือ %TEMP% สามารถแก้ไขเป็นตำแหน่งที่ผู้ใช้ระบุได้

GO111MODULE

ตัวแปรสภาพแวดล้อมนี้แสดงว่าจะใช้วิธีใดในการจัดการ dependencies ของโปรเจกต์ go มีค่าที่ใช้ได้สามค่า

  • off ปิด gomod ใช้ gopath และจะละเว้นไฟล์ go.mod ทั้งหมด
  • on ใช้ gomod ไม่ใช้ gopath (ค่าเริ่มต้น)
  • auto ตรวจจับอัตโนมัติ หากไฟล์โปรเจกต์มี go.mod จะใช้ gomod ในการจัดการ

TIP

ทำไมถึงเรียกว่า GO111MODULE ไม่เรียก GOMODULE โดยตรง เพราะ gomod เปิดตัวครั้งแรกในเวอร์ชัน go1.11

GOPROXY

ตัวแทนโมดูล go ค่าเริ่มต้นคือ https://proxy.golang.org,direct url คั่นด้วยเครื่องหมายจุลภาค direct หมายถึงใช้ VCS โดยตรงเพื่อข้ามตัวแทนโมดูล จะดำเนินการอันหลังก็ต่อเมื่ออันแรกไม่สามารถเข้าถึงได้还有一个ตัวเลือกที่ใช้ได้คือ off หมายถึงห้ามดาวน์โหลดโมดูลใด ๆ นอกจากนั้น GOPROXY ยังสามารถเป็นที่อยู่ของไฟล์ เช่น

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

ผ่าน go get -x สามารถดูคำสั่งที่ดำเนินการในกระบวนการดาวน์โหลด dependencies เพื่อทราบว่ามีการใช้พร็อกซีหรือไม่

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

การใช้ตัวแทนโมดูลสามารถเพิ่มความเร็วในการดาวน์โหลดโมดูลได้อย่างมีประสิทธิภาพ สำหรับผู้ใช้ในจีนโดยพื้นฐานแล้วหากไม่ใช้พร็อกซีจะไม่สามารถเข้าถึงตัวแทนทางการเริ่มต้นได้ ปัจจุบันตัวแทนโมดูลบุคคลที่สามที่เปิดเผยและเชื่อถือได้มีดังนี้

  • https://proxy.golang.com.cn เปิดแหล่งที่มาและให้บริการเวอร์ชันองค์กร
  • https://goproxy.cn ให้บริการและเปิดแหล่งที่มาโดย Qiniu Cloud

แน่นอนว่ามีแผนการสร้างตัวแทนโมดูลด้วยตนเองแบบเปิดแหล่งที่มา goproxy

GOSUMDB

GOSUMDB ใช้สำหรับตั้งค่าที่อยู่ฐานข้อมูลตรวจสอบ checksum ของไลบรารี dependencies ค่าเริ่มต้นคือ sum.golang.org เมื่อคุณตั้งค่าพร็อกซีแล้ว go จะเข้าถึงฐานข้อมูลตรวจสอบผ่านพร็อกซี

GOPRIVATE

ตัวแปรสภาพแวดล้อม GOPRIVATE ใช้สำหรับตั้งค่าไลบรารีส่วนตัว ไลบรารีที่ตรงกันจะไม่ตรวจสอบผ่าน sumdb และจะไม่ผ่านพร็อกซี จะดาวน์โหลด dependencies ผ่าน VCS โดยตรง รองรับการใช้ wildcard ตั้งค่า คั่นด้วยเครื่องหมายจุลภาค ดังต่อไปนี้ dependencies ทั้งหมดที่ลงท้ายด้วย corp.example.com และชื่อ github.com/gohper/myproject จะไม่ผ่านพร็อกซีและ sumdb

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

สามารถตั้งค่าผู้ใช้หรือองค์กรใดองค์กรหนึ่งโดยตรง

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

GONOPROXY

ระบุว่า dependencies ใดไม่จำเป็นต้องผ่านพร็อกซี กฎเหมือนกับ GOPRIVATE และจะแทนที่ GOPRIVATE

GONOSUMDB

ระบุว่า dependencies ใดไม่จำเป็นต้องผ่านฐานข้อมูลตรวจสอบ กฎเหมือนกับ GOPRIVATE และจะแทนที่ GOPRIVATE

GOINSECURE

ระบุว่า dependencies ใดใช้ VCS ในการดาวน์โหลดโดยตรง กฎเหมือนกับ GOPRIVATE และจะถูกแทนที่ด้วย GONOPROXY และ GONOSUMDB

GOVCS

ตั้งค่าระบบควบคุมเวอร์ชันของการจัดการโมดูล ค่าเริ่มต้นคือ public:git|hg,private:all สามารถจำกัด VCS ของโดเมนที่ระบุได้ เช่น

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

ในการจำกัดข้างต้น github ใช้ได้เฉพาะ git, evil.com ไม่อนุญาตให้ใช้ ใช้ | เพื่อแสดง VCS หลายตัว หากไม่มีการจำกัดใด ๆ สามารถตั้งค่าดังนี้

GOVCS=*:all

หากไม่อนุญาตให้ใช้ VCS ใด ๆ สามารถตั้งค่าดังนี้

GOVCS=*:off

GOWORK

ตั้งค่าว่า workspace เปิดใช้งานหรือไม่ ค่าเริ่มต้นคือว่างหมายถึงเปิดใช้งาน หากตั้งค่าเป็น off จะไม่เปิดใช้งาน และจะละเว้นไฟล์ go.work ทั้งหมด

GOTOOLDIR

ตั้งค่าตำแหน่งที่จะใช้เครื่องมือ go ค่าเริ่มต้นคือ $GOROOT/pkg/tool เครื่องมือเริ่มต้นก็เก็บอยู่ที่ตำแหน่งนี้

GODEBUG

ตั้งค่าตัวเลือกการดีบัก ควบคุมพฤติกรรมการรันบางส่วนของการโปรแกรม go ในรูปแบบคู่คีย์-ค่า เช่น

GODEBUG=http2client=0,http2server=0

การตั้งค่าเหล่านี้มีไว้เพื่อความสะดวกในการย้อนกลับของ go เมื่อมีความไม่เข้ากันเกิดขึ้นในระหว่างกระบวนการอัปเดตเวอร์ชัน เช่น ใน 1.21 ไม่อนุญาตให้เกิดสถานการณ์ panic(nil) อีกต่อไป เพื่อจุดประสงค์นี้ go ทางการได้บันทึก GODEBUG History ไว้โดยเฉพาะ ไปที่ GODEBUG เพื่อดูรายละเอียดเพิ่มเติม

CGO_ENABLED

ระบุว่าเปิดใช้งาน cgo หรือไม่ ค่าเริ่มต้นคือ 1 หมายถึงเปิดใช้งาน ตั้งค่าเป็น 0 เพื่อปิด

ตัวแปรสภาพแวดล้อมข้างต้นเป็นตัวแปรที่ใช้บ่อย สำหรับตัวแปรที่ไม่ค่อยได้ใช้จะไม่แนะนำมากเกินไป เช่น CGO, WASM เป็นต้น หากสนใจสามารถศึกษาด้วยตนเอง

build

go รองรับคอมไพเลอร์สองประเภท คือ gccgo และ gc gcc เป็นคอมไพเลอร์ c/c++ แบบเก่า รองรับหลายภาษา รวมถึง go ส่วนหลัง gc ไม่ได้หมายถึง garbage collection แต่หมายถึง go compiler ภาษา go ได้ทำการ bootstrap เสร็จใน go1.5 gc เป็นคอมไพเลอร์ที่เขียนด้วยภาษา go ทั้งหมด ซอร์สโค้ดอยู่ที่แพ็กเกจ cmd/compile เนื่องจากเขียนด้วยการใช้งานภาษา go ทั้งหมด จึงสะดวกมากสำหรับการเรียนรู้และเข้าใจกลไกภายใน โดยค่าเริ่มต้น คอมไพเลอร์ใช้ gc ในการคอมไพล์ ขอแถมเล็กน้อย ตัวดีบักเกอร์ของ go ก็แบ่งเป็นสองประเภทเช่นกัน คือ gdb และ dlv ตัวแรกเป็นดีบักเกอร์ c/c++ แบบเก่า รองรับหลายภาษา รวมถึง go ส่วนหลังเป็นดีบักเกอร์ที่เขียนด้วยภาษา go รองรับภาษา go ได้ดีกว่า และยังเป็นโอเพนซอร์สอีกด้วย แนะนำให้ใช้ตัวหลัง คำสั่ง build ใช้สำหรับคอมไพล์ไฟล์ซอร์ส go เป็นไฟล์ไบนารีที่รันได้ คุณจะสัมผัสประสบการณ์การคอมไพล์ที่รวดเร็วมาก ซึ่งเป็นหนึ่งในลักษณะของภาษา go

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

รับพารามิเตอร์สามตัว หนึ่งคือพาธเอาต์พุตของไฟล์ที่ระบุด้วยแฟลก -o หนึ่งคือแฟลกการคอมไพล์ build flags สำหรับกำหนดพฤติกรรมการคอมไพล์ และสุดท้ายคือแพ็กเกจที่จะคอมไพล์ พารามิเตอร์นี้ต้องอยู่สุดท้าย ด้านล่างเป็นตัวอย่างง่าย ๆ ไม่ได้ใช้แฟลกการคอมไพล์

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

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

./bin/golearn.exe แสดงถึงพาธเอาต์พุต golearn แสดงถึงโมดูลที่จะคอมไพล์ หรืออาจเป็น ไฟล์ entry หรือ โฟลเดอร์ ก็ได้ เช่น ตัวอย่างง่าย ๆ ด้านล่างนี้ใช้ไฟล์ entry main.go เป็นเป้าหมายการคอมไพล์

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

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

ในระหว่างการคอมไพล์ จะละเว้นไฟล์ที่ลงท้ายด้วย _test.go ทั้งหมด เนื่องจากตามข้อตกลงแล้วไฟล์เหล่านี้เป็นไฟล์ทดสอบ

นอกจากนี้ คำสั่ง build ยังรองรับแฟลกการคอมไพล์จำนวนมากสำหรับควบคุมพฤติกรรมบางอย่างในระหว่างการคอมไพล์

  • -x แสดงคำสั่งรายละเอียดในกระบวนการคอมไพล์
  • -n คล้ายกับ -x แต่แตกต่างตรงที่แสดงคำสั่งเหล่านี้เท่านั้น แต่ไม่ดำเนินการจริง
  • -v แสดงแพ็กเกจที่คอมไพล์
  • -p จำนวนการทำงานพร้อมกันในกระบวนการคอมไพล์
  • -a บังคับสร้างใหม่ แม้ว่าจะเป็นล่าสุดแล้ว
  • -compiler ระบุว่าใช้คอมไพเลอร์ใด gccgo หรือ gc หลังเป็นคอมไพเลอร์ที่เขียนด้วย go
  • -race เปิดใช้งานการตรวจสอบ race condition
  • -msan เปิดใช้งานการวิเคราะห์หน่วยความจำ
  • -asan เปิดใช้งานการวิเคราะห์ที่อยู่
  • -cover เปิดใช้งานการตรวจสอบความครอบคลุมของโค้ด
  • -buildmode ระบุโหมดการคอมไพล์ มีตัวเลือก archive, c-archive, c-shared, default, shared, exe, pie, plugin
  • -pgo ระบุไฟล์ pgo
  • -trimpath ลบคำนำหน้าพาธของไฟล์ซอร์ส เช่น พาธสัมพัทธ์ /var/lib/go/src/main.go หลังลบแล้วชื่อไฟล์ที่ได้รับผ่าน runtime ในขณะรันจะเป็นพาธสัมพัทธ์เทียบกับพาธโมดูล /main.go เท่านั้น หลังเปิดใช้งาน此项 เวลาในการคอมไพล์จะเพิ่มขึ้นอย่างเห็นได้ชัด ประมาณ 20-40% ขึ้นอยู่กับจำนวนไฟล์
  • -toolexec คำสั่ง go บางคำสั่งที่ดำเนินการก่อนการคอมไพล์ รูปแบบคือ -toolexec 'cmd args'
  • -gcflags ระบุ tag บางตัวของคอมไพเลอร์ gc
  • -gccgoflags ระบุ tag บางตัวของคอมไพเลอร์ gccgo
  • -ldflags ระบุ tag บางตัวของเครื่องมือ link

สำหรับพารามิเตอร์ที่ส่งผ่านเช่น ldflags สามารถส่งพารามิเตอร์เช่น "-help" เพื่อรับค่าที่เป็นไปได้ เช่น

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

ข้างต้นเป็นตัวแปรที่ใช้บ่อย สำหรับตัวแปรอื่น ๆ ที่ไม่ค่อยได้ใช้สามารถศึกษาด้วยตนเอง

gcflags

ผ่าน gcflags สามารถส่งพารามิเตอร์บางตัวไปยังคอมไพเลอร์ gc เพื่อควบคุมพฤติกรรมเฉพาะ รูปแบบการใช้งานคือ -gcflags="pattern=args list" args list คือรายการพารามิเตอร์ pattern คือขอบเขตการใช้งาน มีค่าที่ใช้ได้ดังนี้

  • main พาธแพ็กเกจระดับบนสุดที่ไฟล์ entry อยู่
  • all โมดูลปัจจุบันและ dependencies ทั้งหมดในโหมดปัจจุบัน
  • std ไลบรารีมาตรฐาน
  • cmd ใช้กับไฟล์ซอร์สทั้งหมดภายใต้แพ็กเกจ cmd
  • wildcard เช่น ., ./..., cmd/...

กฎ pattern นี้ใช้กับแฟลกทั้งหมดที่รองรับรูปแบบนี้ เช่น ldflags ดูค่าที่ใช้ได้ของพารามิเตอร์ผ่านคำสั่งต่อไปนี้

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

ต่อไปนี้จะแนะนำพารามิเตอร์ที่ใช้บ่อยบางตัว

  • -S แสดงรูปแบบ assembly ของโค้ด
  • -N ปิดการคอมไพล์แบบเพิ่มประสิทธิภาพ
  • -m แสดงการตัดสินใจเพิ่มประสิทธิภาพ
  • -l ปิดการ inline ฟังก์ชัน
  • -c จำนวนการทำงานพร้อมกันในการคอมไพล์
  • -dwarf สร้างเครื่องหมาย DWARF

เช่น หากต้องการดูรูปแบบ assembly ของโค้ด สามารถใช้พารามิเตอร์ -S และต้องปิดการเพิ่มประสิทธิภาพและการ inline เพื่อคืนรูปแบบเดิมของมัน ดังนี้

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

ผ่าน ldflags สามารถส่งพารามิเตอร์บางตัวไปยัง linker เพื่อควบคุมพฤติกรรมเฉพาะ ดูค่าที่ใช้ได้ทั้งหมดของ ldflags ผ่านคำสั่งต่อไปนี้ มีเกือบยี่สิบหรือสามสิบค่า

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)
  .....

พารามิเตอร์ -X ของ ldflags เป็นฟังก์ชันที่มีประโยชน์มาก สามารถกำหนดค่าของตัวแปรสตริงของแพ็กเกจที่ระบุในขณะลิงก์ ผ่านฟังก์ชันนี้ เราสามารถแทรกข้อมูลเมตาบางตัวในขณะคอมไพล์ได้อย่างสะดวก และมันเป็นเพียงตัวแปร ดังนั้นจึงสะดวกในการรับในขณะรัน ด้านล่างเป็นตัวอย่างง่าย ๆ

go
package main

import "fmt"

var (
  Version string
)

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

ดำเนินการคำสั่ง

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

หลังรันจะแสดง checksum sha1 ของการ commit git

5e3fd7a

พารามิเตอร์ที่มีประโยชน์อื่น ๆ มี

  • -w ไม่สร้าง DWARF ซึ่งเป็นข้อมูลที่สะดวกสำหรับการดีบักซอร์สโค้ด
  • -s ปิดใช้งานตารางสัญลักษณ์

สองตัวนี้มักใช้ร่วมกัน สามารถลดขนาดของไฟล์ไบนารีหลังคอมไพล์ได้อย่างมีนัยสำคัญ ประมาณ 40%-50% ข้อเสียก็ชัดเจนเช่นกัน ไม่สามารถดีบักได้ ด้านล่างเป็นตัวอย่าง

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

การคอมไพล์ข้าม

การคอมไพล์ภาษา go มีสองลักษณะใหญ่ ๆ หนึ่งคือเร็ว อีกหนึ่งคือการคอมไพล์ข้าม การคอมไพล์ข้ามหมายถึงสามารถคอมไพล์เป็นโค้ดเป้าหมายของระบบอื่นในท้องถิ่นได้ เช่น บน windows คอมไพล์เป็นไฟล์ไบนารีบน linux หรือ darwin ในทางกลับกันก็เช่นกัน ภาษาที่รองรับการคอมไพล์ข้ามมีมากมาย นี่ไม่ใช่เรื่องแปลก แต่การคอมไพล์ข้ามของภาษา go ง่ายมาก เพียงสองขั้นตอนต่อไปนี้

  1. ตั้งค่าตัวแปรสภาพแวดล้อม GOOS เลือกระบบปฏิบัติการเป้าหมายของคุณ
  2. ตั้งค่าตัวแปรสภาพแวดล้อม GOARCH เลือกสถาปัตยกรรม CPU เป้าหมายของคุณ
  3. ใช้ go build ในการคอมไพล์ตามปกติ

กระบวนการทั้งหมดสั้นมาก ไม่ต้องใช้เครื่องมือหรือการตั้งค่าเพิ่มเติม และความเร็วก็เร็วเหมือนปกติ ดังนี้

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

ขั้นตอนแรก SET CGO_ENABLED=0 ปิดใช้งาน cgo เมื่อโค้ดของคุณใช้ cgo จะไม่สามารถใช้การคอมไพล์ข้ามได้ตามปกติ ขั้นตอนที่สอง SET GOOS ตั้งค่าระบบเป้าหมาย ตัวเลือกที่มีคือ linux, darwin, windwos, netbsd ขั้นตอนที่สามตั้งค่าสถาปัตยกรรม CPU SET GOARCH ตัวเลือกที่มีคือ amd64, 386, arm, ppc64 ขั้นตอนสุดท้ายคือการคอมไพล์ตามปกติ

การควบคุมการคอมไพล์

คำสั่ง build สามารถบรรลุผลการควบคุมการคอมไพล์ผ่าน tags ซึ่งมีอยู่ในซอร์สโค้ดในรูปแบบคำสั่ง ดูตัวอย่าง ไฟล์ product.go

go
// +build product

package main

import "fmt"

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

ไฟล์ debug.go

go
// +build debug

package main

import "fmt"

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

ทั้งคู่มีคำสั่ง // +build แสดงว่าจะถูกคอมไพล์ในสถานการณ์ใด รูปแบบพื้นฐานคือ

go
// +build tag1 tag2

package pkg_name

มีกฎที่ต้องปฏิบัติตามหลายข้อ

  1. // กับ +build ต้องเว้นวรรคหนึ่งช่อง
  2. ต้องอยู่เหนือการประกาศแพ็กเกจ
  3. ต้องเว้นบรรทัดว่างหนึ่งบรรทัดกับการประกาศแพ็กเกจ

นอกจากนี้ ยังสามารถบรรลุการควบคุมเชิงตรรกะผ่านการเว้นระยะง่าย ๆ ช่องว่างแสดงว่า OR เครื่องหมายจุลภาคแสดงว่า AND ! แสดงว่า NOT เช่น ตัวอย่างด้านล่างนี้

go
// +build windows linux

package pkg_name

แสดงว่าภายใต้แพลตฟอร์ม windows หรือ linux จะคอมไพล์ไฟล์ปัจจุบันเข้าไป

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

package pkg_name

ตัวอย่างนี้แสดงว่าจะคอมไพล์ก็ต่อเมื่อแพลตฟอร์ม windows สถาปัตยกรรม amd64 และไม่ได้เปิดใช้งาน cgo หรือแพลตฟอร์ม linux สถาปัตยกรรม i386 และเปิดใช้งาน cgo เท่านั้น หากคุณไม่ต้องการให้ไฟล์ใดไฟล์หนึ่งเข้าร่วมการคอมไพล์อย่างง่าย ๆ สามารถใช้ ignore

go
// +build ignore

package pkg_name

สามารถมีคำสั่งหลายบรรทัด

go
// +build windows
// +build amd64

package pkg_name

คำสั่งหลายบรรทัดจะประมวลผลในรูปแบบ AND สำหรับแท็กเช่นแพลตฟอร์มและสถาปัตยกรรมเหล่านี้ ในระหว่างการคอมไพล์ go จะส่งผ่านโดยอัตโนมัติ เรายังสามารถส่งแท็กที่กำหนดเองได้ เช่น ใช้สองไฟล์แรกเป็นตัวอย่าง

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

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

จะเห็นว่าเมื่อส่งแท็กต่างกัน ผลลัพธ์จะต่างกัน จุดประสงค์ของการควบคุมการคอมไพล์ก็บรรลุแล้ว

run

คำสั่ง run กับ build จะคอมไพล์ซอร์สโค้ดเหมือนกัน แต่ต่างกันที่คำสั่ง run จะรันโดยตรงหลังคอมไพล์เสร็จ เพื่อเพิ่มความเร็วในการคอมไพล์ คำสั่ง run จะไม่สร้างข้อมูลดีบักในกระบวนการคอมไพล์ ดังนั้นจึงไม่รองรับการดีบัก และสร้างไฟล์ไบนารีชั่วคราวเท่านั้น โดยปกติเก็บอยู่ในไดเรกทอรี GOTMEPDIR เช่น /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.

ยังรองรับแฟลกการคอมไพล์ของคำสั่ง build และให้พารามิเตอร์ -exec เพื่อระบุว่าโปรแกรมใดใช้รันไฟล์ไบนารี [arguments...] หมายถึงพารามิเตอร์การรันของโปรแกรม ด้านล่างเป็นตัวอย่าง

go
package main

import (
  "fmt"
  "os"
)

var (
  Version string
)

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

ใช้ go run รัน

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

โดยทั่วไปแล้วการใช้งานไม่แตกต่างจาก go build มากนัก จึงไม่ขออธิบายเพิ่มเติม

tool

คำสั่ง tool เองไม่มีฟังก์ชันใด ๆ ฟังก์ชันของมันคือเรียกเครื่องมือภายใต้ไดเรกทอรี cmd/ โดยตรง เช่น cmd/compile คือคอมไพเลอร์ที่มาพร้อม ผ่าน go tool สามารถเรียกเครื่องมือเหล่านี้ได้ ไม่ต้องไปดำเนินการไฟล์ไบนารีของเครื่องมือเหล่านี้ด้วยตนเอง

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

ใช้พารามิเตอร์ -n เพื่อพิมพ์พารามิเตอร์คำสั่งทั้งหมดที่รองรับ

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

เครื่องมือเหล่านี้เก็บอยู่ในไดเรกทอรี GOROOT/pkg/tool และจัดกลุ่มเครื่องมือตามระบบปฏิบัติการและสถาปัตยกรรม CPU ดังนี้

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*

ใช้รูปแบบ go doc cmd/command เพื่อดูวิธีการใช้ของแต่ละคำสั่ง เช่น

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

พารามิเตอร์ที่รองรับโดย cmd/compile ก็คือพารามิเตอร์ที่รองรับโดย gcflags ที่กล่าวถึงก่อนหน้านี้ ความแตกต่างระหว่าง go tool compile กับ go build คือ ตัวแรกรับผิดชอบการคอมไพล์เท่านั้น และรับไฟล์เป็นพารามิเตอร์เท่านั้น ตัวหลังสามารถรับโฟลเดอร์ แพ็กเกจ ไฟล์ เป็นพารามิเตอร์ได้ และไม่เพียงแต่ทำการคอมไพล์ซอร์สโค้ดเท่านั้น แต่ยังรับผิดชอบในการลิงก์ไฟล์ ลบไฟล์ที่ไม่จำเป็น ฯลฯ ตัวแรกเป็นส่วนหนึ่งของตัวหลัง เราสามารถพิมพ์คำสั่งที่ดำเนินการในกระบวนการ 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
...

ในกระบวนการจะเห็นว่ามีส่วน /golang/pkg/tool/windows_amd64/compile.exe นี้คือการเรียกคอมไพเลอร์ นอกจาก compile แล้ว ยังมีเครื่องมืออีกมากมายที่สามารถเรียกได้ คำสั่ง go หลายคำสั่งจริง ๆ แล้วเป็นชื่อเล่นของเครื่องมือเหล่านี้

clean

คำสั่ง clean ใช้สำหรับลบไฟล์ object ที่สร้างขึ้นในระหว่างการคอมไพล์

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

รองรับแฟลกต่อไปนี้

  • -i ลบไฟล์ archive หรือไฟล์ไบนารีที่สอดคล้องกัน
  • -n พิมพ์คำสั่งที่จะดำเนินการในกระบวนการลบ แต่ไม่ดำเนินการจริง
  • -x พิมพ์คำสั่งที่จะดำเนินการในกระบวนการลบและดำเนินการ
  • -r ลบแบบเรียกซ้ำผ่าน import path
  • -cache ลบแคชทั้งหมดที่เกิดจาก go build
  • -testcache ลบแคชการทดสอบทั้งหมดที่เกิดขึ้น
  • -modcache ลบแคชโมดูลที่ดาวน์โหลดทั้งหมด
  • -fuzzcache ลบแคชที่เกิดจาก fuzz test

เมื่อใช้ go tool compile จะเป็นการเรียกคำสั่งคอมไพเลอร์โดยตรง ไม่เหมือน go build ที่จะทำการจัดการหลังบ้านมากมาย จะสร้างไฟล์ object เช่น ดำเนินคำสั่งต่อไปนี้

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

จะสร้างไฟล์ชื่อ main.o ใช้คำสั่ง go clean ลบได้ หรือใช้พารามิเตอร์ -n เพื่อพิมพ์คำสั่งที่จะดำเนินการ

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

ลบแคชการคอมไพล์ จะลบแคชการคอมไพล์ที่เกิดขึ้นภายใต้ไดเรกทอรี GOCACHE

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

ลบแคชที่เกิดจาก fuzz test แคชเหล่านี้เก็บอยู่ในไดเรกทอรี GOCACHE/fuzz/ โดยค่าเริ่มต้น

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

fix

ภาษา go จนถึงขณะที่เขียนบทความนี้มีอายุสิบปีแล้ว ในกระบวนการอัปเดตและแก้ไขภาษาอย่างต่อเนื่อง ย่อมมีบางปัญหาความไม่เข้ากันเนื่องจาก API เปลี่ยนแปลง คำสั่ง fix เกิดมาเพื่อจุดประสงค์นี้ จะตรวจสอบไฟล์ซอร์สเพื่อหา API ที่ล้าสมัยและแทนที่ด้วย API ใหม่

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

รองรับโฟลเดอร์ ชื่อไฟล์ ไดเรกทอรี เป็นพารามิเตอร์ รับแฟลก -fix เพื่อส่งพารามิเตอร์เพื่อระบุว่าจะทำการแก้ไขแบบใด สามารถดูค่าที่ใช้ได้ผ่านคำสั่ง 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.

ต่อไปนี้เป็นตัวอย่าง ซอร์สโค้ดใช้แพ็กเกจ golang.org/x/net/context

sh
package main

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

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

แก้ไขด้วย go fix แทนที่ด้วยแพ็กเกจ context ในไลบรารีมาตรฐาน เราสามารถใช้คำสั่งต่อไปนี้เพื่อแทนที่

sh
$ go fix -fix context main.go

หรือ可以不แทนที่ ดูการเปลี่ยนแปลงของไฟล์ก่อนและหลัง

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() {

ภาษา go เกิดมาสิบกว่าปีมีพารามิเตอร์แทนที่ได้เพียงเก้าตัว เห็นได้ว่าการรักษาความเข้ากันได้นั้นค่อนข้างดี

fmt

คำสั่ง fmt เป็นเครื่องมือจัดรูปแบบที่มาพร้อมกับภาษา go ใช้สำหรับจัดรูปแบบไฟล์ซอร์สโค้ด go

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

ดูเอกสารรายละเอียดผ่านคำสั่ง go doc gofmt

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 ใช้ tab สำหรับการเยื้อง และใช้ช่องว่างสำหรับการจัดตำแหน่ง โดยค่าเริ่มต้นโค้ดหลังจัดรูปแบบจะแสดงออกทางเอาต์พุตมาตรฐาน ไม่ได้ทับไฟล์ต้นฉบับ คำสั่ง go fmt จริง ๆ แล้วใช้คำสั่ง gofmt เป็นไฟล์ไบนารีแยกต่างหาก อยู่ที่ไดเรกทอรี GOROOT/bin

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

เพิ่มแฟลก -n ให้กับคำสั่ง go fmt จะทราบว่าคำสั่งที่จะดำเนินการ

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

จะเห็นว่า go fmt จริง ๆ แล้วเป็นชื่อเล่นของ gofmt -l -w คำสั่ง gofmt มีพารามิเตอร์ดังนี้

  • -d แสดงความแตกต่างของไฟล์ก่อนและหลังจัดรูปแบบ
  • -e แสดงข้อผิดพลาดทั้งหมด
  • -l แสดงชื่อไฟล์ที่มีการเปลี่ยนแปลง
  • -r ใช้กฎการจัดรูปแบบ
  • -s พยายามทำให้โค้ดง่ายขึ้น
  • -w ทับไฟล์ต้นฉบับ หากเกิดข้อผิดพลาดจะคืนค่าสำรอง

สมมติว่ามีไฟล์ต้นฉบับดังนี้

go
$ cat main.go
package main

import "fmt"

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

ดูการเปลี่ยนแปลงผ่านพารามิเตอร์ -d

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!")
+}

พารามิเตอร์ -l จะแสดงชื่อไฟล์ที่จะแก้ไข

$ gofmt -l .
main.go

หากมีข้อผิดพลาดทางไวยากรณ์ พารามิเตอร์ -e สามารถแสดงรายละเอียดมากขึ้น

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 จะใช้การแก้ไขกับไฟล์ต้นฉบับ

sh
$ gofmt -l -w .
main.go

$ cat main.go
package main

import "fmt"

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

คุณจะพบว่าในฐานะเครื่องมือจัดรูปแบบ gofmt ไม่ได้ให้การกำหนดค่าที่กำหนดเองใด ๆ เลย ในขณะที่ตัวจัดรูปแบบ prettify ที่专为美化โค้ด js มันให้การกำหนดค่าจำนวนมากสำหรับจัดรูปแบบโค้ด ที่นี่สามารถสะท้อนทัศนคติของทางการ go อย่าคิดที่จะทำอะไรที่เป็นส่วนตัว ทุกคนควรมีสไตล์โค้ดเหมือนกันอย่างน้อยก็มีข้อดีอย่างหนึ่งคือไม่ต้องปรับตัวกับนิสัยของผู้อื่นเมื่ออ่านโค้ด แต่จริง ๆ แล้วมันยังคงรักษาตัวเลือกที่กำหนดเองไว้หนึ่งตัว คือกฎการแทนที่โค้ดที่จัดรูปแบบแล้ว กฎสามารถกำหนดเองได้ รูปแบบดังนี้

pattern -> replacement

เช่น ลบวงเล็บที่ซ้ำซ้อน

(a) -> a

ดูการเปลี่ยนแปลงของไฟล์

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!")
 }

จะเห็นว่า gofmt จะลบวงเล็บที่ซ้ำซ้อนออก

get

คำสั่ง get แน่นอนว่าเป็นคำสั่งที่ใช้บ่อยที่สุดในระหว่างการพัฒนา go ฟังก์ชันของมันคือดาวน์โหลดซอร์สโค้ดของแพ็กเกจที่อยู่ที่ระบุไปยังไดเรกทอรีที่สอดคล้องกับ GOMODCACHE

sh
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.
  • -u พยายามอัปเดตเวอร์ชันรองและเวอร์ชันแพตช์ของแพ็กเกจ หากเกี่ยวข้องกับการเปลี่ยนแปลงเวอร์ชันหลัก เช่น v1->v2 จะไม่อัปเดต
  • -t อัปเดตเวอร์ชัน dependencies ในการทดสอบ
  • -v แสดงแพ็กเกจที่คอมไพล์ จริง ๆ แล้วเป็นพารามิเตอร์หนึ่งของ build flags

ในยุคแรก ๆ ฟังก์ชันของ go get คล้ายกับ go install จะดาวน์โหลดและคอมไพล์แพ็กเกจเหล่านี้ อย่างไรก็ตาม ด้วยการเกิดขึ้นและความสมบูรณ์ของโมดูล go ฟังก์ชันส่วนนี้ก็ค่อย ๆ ถูกยกเลิกไป คำสั่ง get ในปัจจุบันใช้บ่อยที่สุดสำหรับการดาวน์โหลดและแยกวิเคราะห์ dependencies ของโมดูล go ดังนั้นคุณจะเห็นว่าคำสั่ง go get ยังรองรับแฟลกการสร้างประเภท build flags และหากคุณพยายามใช้ go get นอกโมดูลเหมือนใช้ go install มันจะแจ้งว่าวิธีการใช้นี้ถูกยกเลิกแล้ว

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'.

ส่วนเหตุผลที่ยังคงรักษาไว้ในคำอธิบายเอกสารก็ไม่ทราบเช่นกัน เมื่อพลิกดูซอร์สโค้ดของคำสั่ง get คุณจะพบว่ามันยังคงรักษา flag เดิมไว้

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
)

กลับเข้าเรื่อง คำสั่ง get จะดาวน์โหลดซอร์สโค้ดของแพ็กเกจที่ระบุไปยังไดเรกทอรี dependencies ส่วนกลางของท้องถิ่น ซึ่งก็คือไดเรกทอรีที่สอดคล้องกับ GOCACHE จากนั้นบันทึกข้อมูลลงในไฟล์ go.mod และ go.sum ไฟล์แรกรับผิดชอบการบันทึกเวอร์ชัน ไฟล์หลังรับผิดชอบการบันทึก checksum sha1 เพื่อความปลอดภัย คำสั่ง get จริง ๆ แล้วอยู่บนพื้นฐานของ VCS ซึ่งก็คือระบบควบคุมเวอร์ชันของท้องถิ่น รองรับต่อไปนี้ทั้งหมด

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

ในนั้น รองรับ git และ hg โดยค่าเริ่มต้น สามารถกำหนดค่าใน GOVCS รูปแบบดังนี้

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

GOVCS รองรับ git และ hg เป็น VCS เท่านั้น อีกสามตัวต้องกำหนดค่าใน GOPRIVATE

คำสั่ง go get มีวิธีการใช้ดังนี้ สามารถใช้ที่อยู่ dependencies เป็นพารามิเตอร์โดยตรง

sh
$ go get golang.org/x/net

หรือระบุเวอร์ชัน

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

ระบุเวอร์ชันล่าสุด

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

พยายามอัปเดตเวอร์ชัน

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

ลบ dependencies ใด dependencies หนึ่ง

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

ข้างต้นใช้สำหรับจัดการ dependencies ทั่วไป ยังสามารถใช้จัดการ dependencies ที่ไม่ธรรมดาได้ เช่น อัปเดตเวอร์ชันของภาษา 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

หรือแม้แต่ใช้สำหรับอัปเดตเวอร์ชันของเครื่องมือ go

sh
$ go get toolchain@latest

เมื่อคุณใช้ go get อัปเดตเวอร์ชันของ go และเครื่องมือ go มันจะติดตั้งเวอร์ชันใหม่ของ go ภายใต้ไดเรกทอรี GOMODCACHE/golang.org/

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

ตอนนี้แก้ไข GOROOT ด้วยตนเองก็สามารถสลับไปยังเวอร์ชันที่ระบุได้

install

คำสั่ง install คล้ายกับคำสั่ง get ทั้งคู่ใช้สำหรับดาวน์โหลด dependencies ของบุคคลที่สาม แต่ต่างกันที่คำสั่ง get ดาวน์โหลดซอร์สโค้ด ในขณะที่คำสั่ง install จะคอมไพล์ซอร์สโค้ดเป็นไฟล์ไบนารีที่รันได้ของเครื่องนี้ พาธเก็บไฟล์ไบนารีอยู่ในไดเรกทอรี GOBIN ก่อน其次是 GOPATH/bin ฟังก์ชันหลักของคำสั่งนี้ใช้สำหรับดาวน์โหลดเครื่องมือบรรทัดคำสั่งสาธารณะบางตัวของบุคคลที่สาม เนื่องจากความเร็วในการคอมไพล์และความสามารถในการพกพาของภาษา go ไม่จำเป็นต้องดาวน์โหลดไฟล์ไบนารี แต่ดาวน์โหลดซอร์สโค้ดแล้วคอมไพล์ในท้องถิ่น

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

คำสั่ง install รับแฟลกการสร้างและชื่อแพ็กเกจเป็นพารามิเตอร์ ในกรณีที่เปิดใช้งาน gomod ชื่อแพ็กเกจต้องพกหมายเลขเวอร์ชัน เช่น ดาวน์โหลดดีบักเกอร์ 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/

ก่อนอื่นจะดาวน์โหลดซอร์สโค้ดไปยังพาธที่เก็บใน GOMODCACHE จุดนี้เหมือนกับคำสั่ง get จากนั้นสลับไปยังไดเรกทอรีทำงานชั่วคราว คอมไพล์ หลังจากคอมไพล์เสร็จจะย้ายไฟล์ไบนารีไปยังไดเรกทอรี GOPATH/bin สุดท้ายลบโฟลเดอร์ชั่วคราว คำสั่ง install ยังมีข้อจำกัดหนึ่งคือแพ็กเกจที่ดาวน์โหลดต้องเป็นแพ็กเกจ entry ของโปรเจกต์นั้น หมายความว่าต้องมีไฟล์ entry main.go มิฉะนั้นจะแจ้งว่าไม่สามารถติดตั้งได้ เช่น ใช้ go install ดาวน์โหลด 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 เป็นไลบรารี dependencies ของเฟรมเวิร์กเว็บ ไม่ใช่เครื่องมือบรรทัดคำสั่ง แน่นอนว่าไม่มีไฟล์ entry ดังนั้นการติดตั้งจะล้มเหลว

list

คำสั่ง list จะแสดงแพ็กเกจที่ตำแหน่งที่ระบุ ทีละบรรทัด และรองรับการเอาต์พุตที่จัดรูปแบบเอง รองรับพารามิเตอร์จำนวนมาก การใช้มันต้องอยู่ในโปรเจกต์ที่รองรับ gomod

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

พารามิเตอร์ที่รองรับมีดังนี้

  • -f พารามิเตอร์จัดรูปแบบ
  • -json เอาต์พุตในรูปแบบ json
  • -compiled แสดงแพ็กเกจทั้งหมดที่จะถูกคอมไพล์โดยคอมไพเลอร์
  • -deps แสดงชื่อของแพ็กเกจแต่ละแพ็กเกจและ dependencies แต่ละตัวที่พึ่งพา
  • -test แสดงแพ็กเกจทดสอบของแต่ละแพ็กเกจ
  • -e เอาต์พุตปกติเมื่อพบแพ็กเกจที่มีข้อผิดพลาด
  • -find ไม่แยกวิเคราะห์ความสัมพันธ์ dependencies ของแพ็กเกจเหล่านี้
  • -export เมื่อใช้พารามิเตอร์นี้ ตั้งค่าค่าฟิลด์ Package.Export ของโครงสร้างเป็นไฟล์ที่มีข้อมูลการส่งออกที่ล่าสุดของแพ็กเกจที่ระบุ และตั้งค่าค่าฟิลด์ Package.BuildID เป็น BuildID ของแพ็กเกจ ส่วนใหญ่ใช้สำหรับการเอาต์พุตที่จัดรูปแบบ

พารามิเตอร์ข้อมูลโมดูล

  • -m แสดงโมดูลแทนการแสดงแพ็กเกจ

  • -versions แสดงข้อมูลที่ใช้ได้ทั้งหมดของโมดูล

  • -retracted แสดงเวอร์ชันที่ถูกเพิกถอนของโมดูล

พารามิเตอร์ [packages] สามารถเป็นชื่อแพ็กเกจที่ระบุ หรือโฟลเดอร์ หรือ all แสดงว่าทุกที่ เมื่อใช้พารามิเตอร์ -m all หมายถึง dependencies ทั้งหมดที่โมดูลปัจจุบันอ้างอิง

เช่น ไฟล์ปัจจุบันมีไฟล์ main.go เพียงไฟล์เดียว และมีโค้ดเอาต์พุต "hello world" เพียงบรรทัดเดียว หลังดำเนินการ go list -deps . จะแสดงจากโปรเจกต์ปัจจุบันไปยัง fmt และแพ็กเกจ dependencies ทั้งหมดที่อ้างอิง

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

หรือแสดง dependencies โมดูลทั้งหมดภายใต้โปรเจกต์ปัจจุบัน

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

เอาต์พุตของคำสั่ง list เป็นหน่วยบรรทัด แต่ละบรรทัดที่เอาต์พุตคือแพ็กเกจ ทางการให้พารามิเตอร์ -f ที่เราสามารถกำหนดรูปแบบการเอาต์พุตบรรทัดได้ ค่าที่รับคือไวยากรณ์เทมเพลตที่กำหนดโดยแพ็กเกจเทมเพลตเอ็นจิน template/text เช่น ตัวอย่างด้านล่าง

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

แพ็กเกจที่วนซ้ำแต่ละแพ็กเกจจะถูกส่งในรูปแบบโครงสร้างต่อไปนี้ ฟิลด์ทั้งหมดในโครงสร้างนี้สามารถใช้เป็นพารามิเตอร์เทมเพลตได้

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
}

หากวนซ้ำโมดูล จะส่งในรูปแบบโครงสร้างต่อไปนี้ ฟิลด์ทั้งหมดของมันสามารถใช้เป็นพารามิเตอร์เทมเพลตได้เช่นกัน

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
}

ดูแพ็กเกจทั้งหมด

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

ดูโมดูล

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

mod

go mod เป็นคำสั่งสำหรับจัดการโมดูล 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.

มีคำสั่งย่อยดังนี้

  • download ดาวน์โหลด dependencies ทั้งหมดที่ระบุในไฟล์ go.mod ไปยังแคชท้องถิ่น
  • edit แก้ไขไฟล์ go.mod อินเทอร์เฟซบรรทัดคำสั่งที่ให้ไว้ส่วนใหญ่สำหรับเครื่องมือหรือสคริปต์อื่นเรียกใช้
  • init เริ่มต้นโปรเจกต์ gomod ในไดเรกทอรีปัจจุบัน
  • tidy ดาวน์โหลด dependencies ที่ขาดหายไป ลบ dependencies ที่ไม่ใช้
  • graph แสดงกราฟ dependencies
  • verify ตรวจสอบ dependencies ท้องถิ่น
  • why อธิบายว่าทำไมต้องพึ่งพาโมดูลเหล่านี้
  • vendor ส่งออก dependencies ของโปรเจกต์ไปยังไดเรกทอรี vendor

init

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

คำสั่ง init ใช้สำหรับเริ่มต้นโปรเจกต์ gomod พารามิเตอร์เดียวคือพาธโมดูล ในอนาคตหากผู้อื่นต้องการดาวน์โหลด dependencies ของคุณจะต้องใช้พาธโมดูลนี้เป็นพื้นฐาน กฎการตั้งชื่อโดยทั่วไปคือ

domain_name/user_name/repo_name

เช่น โดยทั่วไปแล้วทุกคนมักจะวางโปรเจกต์บน github ดังนั้นอาจเป็น

github.com/jack/gotour

ไม่แนะนำให้ใช้สัญลักษณ์พิเศษเป็นพาธโมดูล ดูตัวอย่างการใช้งาน

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]

คำสั่ง tidy จะล้างรายการ dependencies ที่ไม่ใช้ใน go.mod ซึ่งก็คือรายการ dependencies ที่ไม่ถูกอ้างอิง และจะดาวน์โหลด dependencies ที่ถูกอ้างอิงแต่ไม่มีอยู่ รองรับพารามิเตอร์ต่อไปนี้

  • -v แสดง dependencies โมดูลที่ถูกลบ
  • -e ละเว้นข้อผิดพลาดหากเกิดขึ้นในระหว่างกระบวนการและดำเนินการต่อ
  • -x แสดงกระบวนการดำเนินการ
  • -go=version อัปเดตเวอร์ชัน go ในไฟล์ go.mod
  • -compact=version รักษา checksum เพิ่มเติมที่จำเป็นจากเวอร์ชัน Go หลักที่ระบุเพื่อโหลดกราฟโมดูลให้สำเร็จ และหากคำสั่ง go ของเวอร์ชันนี้โหลดแพ็กเกจที่นำเข้าใด ๆ จากเวอร์ชันโมดูลที่ต่างกัน จะทำให้ tidy เกิดข้อผิดพลาด โดยทั่วไปแล้วพารามิเตอร์นี้ไม่ค่อยได้ใช้ มักจะเกิดข้อผิดพลาดเมื่อมีการเปลี่ยนแปลงเวอร์ชัน สามารถดูคำตอบนี้ใน stackoverflow go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow

ดูตัวอย่างการใช้งาน

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]

แม้ว่าชื่อของคำสั่ง download จะแปลว่าการดาวน์โหลด แต่เพียงดาวน์โหลด dependencies ไปยังแคช dependencies ท้องถิ่นเท่านั้น ไม่แก้ไขไฟล์ go.mod ฟังก์ชันของมันคือพรีดาวน์โหลด dependencies ไปยังแคชไฟล์ท้องถิ่น หากคุณต้องการดาวน์โหลด dependencies ใด dependencies หนึ่ง แนะนำให้ใช้ go get หรือ go mod tidy

ต่อไปนี้เป็นตัวอย่างการใช้งานบางตัว

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)

หากไม่มีพารามิเตอร์ใด ๆ จะดาวน์โหลด dependencies ทั้งหมดที่มีอยู่ในไฟล์ go.mod แต่ไม่มีอยู่ในแคช dependencies ท้องถิ่น หากไม่มีอะไรให้ดาวน์โหลดจะแสดง

go: no module dependencies to download

edit

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

edit เป็นอินเทอร์เฟซบรรทัดคำสั่งสำหรับแก้ไขไฟล์ go.mod มัก提供给โปรแกรมอื่นใช้ IDE ของบรรณาธิการบางตัวเพื่อรองรับ gomod จะใช้คำสั่งเหล่านี้ รองรับพารามิเตอร์ต่อไปนี้

  • -module แก้ไขพาธโมดูล
  • -go=version แก้ไขเวอร์ชัน go ที่คาดหวัง
  • -require=path@version เพิ่มรายการ dependencies
  • -droprequire=path@version ลบรายการ dependencies
  • -exclude=path@version เพิ่มรายการยกเว้น dependencies
  • -dropexclude=path@version ลบรายการยกเว้น dependencies
  • -replace=old@version=new@version เพิ่มรายการแทนที่ dependencies
  • -dropreplace=old@version ลบรายการแทนที่ dependencies
  • -retract=version เพิ่มรายการเพิกถอนเวอร์ชัน
  • -dropretract=version ลบรายการเพิกถอนเวอร์ชัน

มีพารามิเตอร์อื่น ๆ สำหรับแสดง

  • -print แสดงเนื้อหาไฟล์
  • -json แสดงในรูปแบบ json

เช่น ตัวอย่างด้านล่างนี้

$ 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]

คำสั่ง graph จะแสดงกราฟ dependencies ภายใต้โปรเจกต์ปัจจุบัน การอ่านได้แย่มาก และส่วนใหญ่แล้วไม่ใช่สำหรับมนุษย์อ่าน ผลลัพธ์มักจะถูกประมวลผลแล้วแสดงในรูปแบบภาพ แต่ละบรรทัดคือ dependencies หนึ่งรายการ รูปแบบดังนี้

引用方 被引用方

เช่น

golearn go@1.21.3

ยังรองรับพารามิเตอร์สองตัว

  • -go=version ใช้เวอร์ชัน go ที่กำหนดโหลดกราฟ dependencies ค่าของมันต้องไม่น้อยกว่าเวอร์ชันในไฟล์ go.mod
  • -x แสดงคำสั่งที่ดำเนินการในระหว่างกระบวนการ

ดูตัวอย่างการใช้งานง่าย ๆ

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 เป็นแผนการแทนที่ gopath ก่อนที่ gomod จะถูกเปิดตัว ภายใต้แต่ละโปรเจกต์ go จะมีไดเรกทอรี vendor หนึ่งไดเรกทอรี เก็บ dependencies ของแต่ละโปรเจกต์แยกกันในรูปแบบ domain/user/project เช่นเดียวกับ node_module ที่อ้วนของ nodeJs ข้าง ๆ dependencies ของแต่ละโปรเจกต์แยกกัน วิธีจัดการ dependencies แบบนี้现在看来确实很โง่ แต่ในเวลานั้น确实ไม่มีแผนที่ดีกว่า เหตุผลที่ยังคงรักษา vendor ไว้เพราะ go ยึดมั่นในสัญญาความเข้ากันได้แบบลงล่าง โปรเจกต์เก่าบางโปรเจกต์รวมถึงซอร์สโค้ดของ go อาจยังใช้ vendor

กลับเข้าเรื่อง vendor เป็นคำสั่งย่อยของ go mod สามารถส่งออก dependencies ส่วนกลางที่โมดูลปัจจุบันอ้างอิงไปยังไดเรกทอรี vendor

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

มีพารามิเตอร์ดังนี้

  • -o ระบุพาธโฟลเดอร์เอาต์พุต
  • -v แสดง dependencies แต่ละตัว
  • -e ไม่ออกเมื่อเกิดข้อผิดพลาดยังคงดำเนินการต่อ

ดูตัวอย่าง ใช้ go list -m all ดู dependencies ที่โปรเจกต์ปัจจุบันอ้างอิงก่อน

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

ส่งออกไปยังไดเรกทอรี vendor ปัจจุบัน

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

โครงสร้างไดเรกทอรีหลังส่งออกมีดังนี้

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

ในนั้น modules.txt เป็นไฟล์ที่อธิบายรายการ dependencies ทั้งหมด เหมือนกับ go.mod ในปัจจุบัน

verify

sh
$ go help mod verify
usage: go mod verify

คำสั่งนี้จะตรวจสอบว่า dependencies ของโปรเจกต์ถูกแก้ไขหรือไม่หลังจากดาวน์โหลดไปยังท้องถิ่น เช่น หากไม่มีปัญหาจะแสดง all modules verified

sh
$ go mod verify
all modules verified

มิฉะนั้นจะรายงานว่ามีการเปลี่ยนแปลงที่ไหน และสิ้นสุดคำสั่งด้วยสถานะผิดปกติ เช่น

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

อธิบายว่าทำไมแพ็กเกจนี้ถึงถูกพึ่งพา จริง ๆ แล้วคือการแสดงกราฟ dependencies เกี่ยวกับมัน เช่น

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

โดยค่าเริ่มต้นจะแยกวิเคราะห์การนำเข้าจาก main เท่านั้น เพิ่มพารามิเตอร์ -m สามารถวิเคราะห์สถานการณ์การนำเข้าของแต่ละแพ็กเกจ

work

คำสั่ง work เป็นเครื่องมือพัฒนาท้องถิ่นสำหรับจัดการหลายโมดูลของ 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

คำสั่งย่อย init ใช้สำหรับเริ่มต้น workspace หนึ่งคำสั่งนี้จะสร้างไฟล์ชื่อ go.work

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

รับพารามิเตอร์ [moddirs] ระบุว่าโมดูลใดบ้างที่จะรวมในการจัดการ เช่น

bash
$ go work init ./service ./api

use

คำสั่งย่อย use ใช้สำหรับเพิ่มไดเรกทอรีโมดูลที่รวมในการจัดการลงใน 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.

รับ [moddirs] เป็นพารามิเตอร์ และมี -r แสดงว่าค้นหาโมดูลแบบเรียกซ้ำภายใต้พาธ [moddirs] เช่น

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

edit

ฟังก์ชันของคำสั่งย่อย edit เหมือนกับ go mod edit ทั้งคู่留给อินเทอร์เฟซบรรทัดคำสั่งสำหรับเครื่องมือและสคริปต์อื่นดำเนินการ

$ 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

พารามิเตอร์มีดังนี้

  • -fmt จัดรูปแบบไฟล์ go.work

  • -use, -dropuse เพิ่มและลบพาธโมดูล

  • -replace=old[@v]=new[@v], -dropreplace=old[@v]=new[@v] ใช้สำหรับเพิ่มและลบโมดูลที่จะแทนที่

  • -go, -toolchain=name ระบุเวอร์ชัน go และระบุเครื่องมือที่จะใช้

  • -print พิมพ์การแก้ไขสุดท้าย ไม่เขียนกลับไฟล์

  • -json แสดงในรูปแบบ json ไม่สามารถใช้กับ -print พร้อมกันได้ โครงสร้างประเภทมีดังนี้

    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
    }

ตัวอย่างการใช้งานบางตัว แสดงผลลัพธ์ที่จัดรูปแบบ

bash
$ go work edit -fmt -print
go 1.22.0

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

แสดงผลลัพธ์ json

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

sync

คำสั่งย่อย sync ใช้สำหรับซิงค์รายการโมดูลใน go.work กลับไปยังแต่ละโมดูลใน workspace

bash
$ go help work sync
usage: go work sync

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

กระบวนการนี้เกิดขึ้นหลักหลังจากการพัฒนาท้องถิ่นเสร็จสิ้น โมดูลแต่ละโมดูลเสร็จสิ้นการเผยแพร่แล้ว ใช้ sync ในเวลานี้ จะอัปเดต dependencies ใน go.mod ของโมดูลทั้งหมดใน worksapce ตามความสัมพันธ์ dependencies ของโมดูลแต่ละโมดูล จึงไม่จำเป็นต้องอัปเดตด้วยตนเอง

vendor

คำสั่ง vendor จะทำสำเนาไลบรารี dependencies ทั้งหมดของโมดูลใน workspace ไปยังไดเรกทอรี vendor

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

ฟังก์ชันเหมือนกับ go mod vendor ไม่ขออธิบายเพิ่มเติม

vet

คำสั่ง vet เป็นเครื่องมือตรวจสอบข้อผิดพลาดแบบคงที่สำหรับซอร์สโค้ดภาษา go เหมือนกับเครื่องมือ lint ของภาษาอื่น เช่น 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.

ดูตัวอย่างง่าย ๆ ก่อน มีซอร์สโค้ดดังนี้

sh
$ cat main.go
package main

import "fmt"

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

ดำเนินการ go vet ในไดเรกทอรีเดียวกันโดยไม่มีพารามิเตอร์ใด ๆ

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

vet จะรายงานว่าไฟล์ใด บรรทัดใด เกิดปัญหาอะไร รองรับแฟลกการสร้างเป็นพารามิเตอร์ เช่น -n และ -x รองรับแพ็กเกจ โฟลเดอร์ ชื่อไฟล์ เป็นพารามิเตอร์

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

ดูพารามิเตอร์และคำอธิบายที่ละเอียดมากขึ้นผ่านคำสั่งต่อไปนี้

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

คำสั่ง go tool vet ไม่สามารถใช้ตรวจสอบโค้ดได้โดยตรง ควรใช้ go vet พารามิเตอร์ [vet flag] ในพารามิเตอร์ของ go vet รองรับตั้งค่าตัววิเคราะห์โค้ด ค่าที่ใช้ได้มีดังนี้

asmdecl      ตรวจสอบว่าไฟล์ assembly ไม่ตรงกับการประกาศ go
assign       ตรวจสอบว่ามีตัวแปรที่ไม่ได้ใช้
atomic       ตรวจสอบว่าใช้ sync/atomic ทำลายความเป็นอะตอมหรือไม่
bools        ตรวจสอบว่าใช้ตัวดำเนินการตรรกะผิดพลาดหรือไม่
buildtag     ตรวจสอบ build tag
cgocall      ตรวจสอบพฤติกรรมที่ละเมิดกฎการส่งพอยน์เตอร์ของ cgao
composites   ตรวจสอบโครงสร้างคอมโพสิตที่ไม่ได้เริ่มต้น เช่น map, chan
copylocks    ตรวจสอบว่าเกิดการคัดลอกค่าของล็อกหรือไม่
directive    ตรวจสอบคำสั่งเครื่องมือ go
errorsas     ตรวจสอบว่าส่งประเภทที่ไม่ใช่พอยน์เตอร์หรือประเภทที่ไม่ใช่ error ไปยัง errors.As หรือไม่
framepointer ตรวจสอบว่าโค้ด assembly ที่เพิ่มประสิทธิภาพการคอมไพล์แล้วล้างเฟรมพอยน์เตอร์ก่อนบันทึกหรือไม่
httpresponse ตรวจสอบว่าใช้ httpresponse ผิดพลาดหรือไม่
ifaceassert  ตรวจสอบการยืนยันประเภทจากอินเทอร์เฟซไปยังอินเทอร์เฟซ
loopclosure  ปัญหาการอ้างอิงตัวแปรลูป
lostcancel   context.WithCancel ไม่ได้ใช้ฟังก์ชัน cancel
nilfunc      ตรวจสอบว่าการเปรียบเทียบระหว่างฟังก์ชันและ nil ไม่มีประโยชน์หรือไม่
printf       ตรวจสอบว่าพารามิเตอร์การจัดรูปแบบของ printf ถูกต้องหรือไม่
shift        ตรวจสอบว่ามีการเลื่อนที่เท่ากับหรือเกินความกว้างของจำนวนเต็มหรือไม่
sigchanyzer  ตรวจสอบ chan os.Signal ที่ไม่มีบัฟเฟอร์
slog         ตรวจสอบการเรียกบันทึกที่มีโครงสร้างไม่ถูกต้อง
stdmethods   ตรวจสอบว่าลายเซ็นของเมธอดอินเทอร์เฟซที่รู้จักถูกต้องหรือไม่
stringintconv ตรวจสอบการแปลงจำนวนเต็มสตริง
structtag    ตรวจสอบว่า tag ของโครงสร้างถูกต้องหรือไม่
testinggoroutine ตรวจสอบว่าใช้协程ในการเรียก testing.Fatal ในการทดสอบหรือไม่
tests        ตรวจสอบข้อผิดพลาดทั่วไปในการทดสอบและตัวอย่าง
timeformat   ตรวจสอบว่ารูปแบบเวลาที่เรียกใช้ (time.Time).Format หรือ time.Parse ถูกต้องหรือไม่
unmarshal    ส่งประเภทที่ไม่ใช่พอยน์เตอร์หรืออินเทอร์เฟซไปยัง unmarshal
unreachable  ตรวจสอบโค้ดที่ไปไม่ถึง
unsafeptr    ตรวจสอบการแปลง uintptr เป็น unsafe.Pointer ไม่ถูกต้อง
unusedresult ตรวจสอบค่าส่งกลับของฟังก์ชันที่ไม่ได้ใช้

ล้วนเป็นตัววิเคราะห์ที่วิเคราะห์จุดใดจุดหนึ่ง เช่น ตัววิเคราะห์ timeformat ตรวจสอบว่าการเรียก time.Format ตรงตามไวยากรณ์ที่ถูกต้องหรือไม่ โดยค่าเริ่มต้นตัววิเคราะห์ทั้งหมดข้างต้นจะเปิดใช้งาน เปิดใช้งานแยกต่างหากสามารถใช้รูปแบบดังนี้

sh
$ go vet -timeformat main.go

ปิดใช้งานแยกต่างหาก

sh
$ go vet -timeformat=false main.go

ซอร์สโค้ดของตัววิเคราะห์เหล่านี้อยู่ที่ cmd/vendor/golang.org/x/tools/go/analysis/passes ตัววิเคราะห์แต่ละตัวเป็นหลุมที่ภาษา go มักจะตก ดังนั้นจึงแนะนำให้ใช้คำสั่ง vet ตรวจสอบโค้ดของคุณ นอกจากเหล่านี้แล้ว ยังรองรับแฟลกพารามิเตอร์อื่น ๆ

  • -V พิมพ์เวอร์ชันแล้วออกเท่านั้น
  • -json แสดงในรูปแบบ json
  • -c=n แสดงบรรทัดที่มีความขัดแย้งในบริบทที่ระบุจำนวน (ดูเหมือนจะไม่มีผลใด ๆ)

ยังมีตัววิเคราะห์ภายนอก เช่น shadows รับผิดชอบตรวจสอบปัญหาการซ่อนตัวแปรของการตั้งชื่อตัวแปรแบบสั้น เนื่องจากเป็นภายนอกจึงต้องใช้ go install ในการดาวน์โหลด

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

รูปแบบการใช้งานดังนี้

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.

คำสั่ง test เป็นคำสั่งที่ให้ฟังก์ชันการทดสอบในเครื่องมือ go ฟังก์ชันนี้สำคัญมาก สำหรับซอฟต์แวร์แล้ว การทดสอบที่สมบูรณ์เป็นสิ่งที่ขาดไม่ได้ ที่นี่เพียงแนะนำวิธีใช้คำสั่ง test อย่างง่าย ๆ หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการทดสอบ ไปที่ การทดสอบ

นอกจากพารามิเตอร์การคอมไพล์ของคำสั่ง build แล้ว test ยังรองรับพารามิเตอร์ต่อไปนี้

  • -args พารามิเตอร์ entry ของโปรแกรม
  • -c คอมไพล์ไฟล์ไบนารีทดสอบของแพ็กเกจปัจจุบันไปยังไดเรกทอรีปัจจุบันแต่ไม่ดำเนินการ ตั้งชื่อในรูปแบบ pkg.test
  • -exec ดำเนินการคำสั่งอื่น ๆ ก่อนเริ่มการทดสอบ
  • -json สไตล์การเอาต์พุตของการทดสอบเป็น json
  • -o ระบุพาธชื่อไฟล์ของไฟล์ไบนารีทดสอบ

ยังรองรับ testflag จำนวนมาก ใช้คำสั่ง help ดู testflag ทั้งหมด

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

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

แนะนำตัวที่ใช้บ่อย

  • -v แสดงผลการทดสอบของแต่ละเคส
  • -timeout duration เวลา超时ของการดำเนินการทดสอบ
  • -skip regexp ข้ามเคสการทดสอบที่ระบุ
  • -short ให้เคสการทดสอบที่ใช้เวลานานมากลดเวลาการรัน
  • -shuffle สลับลำดับการดำเนินการของเคสการทดสอบทั้งหมด
  • -run regexp รันเคสการทดสอบที่ระบุ
  • -list regexp แสดงรายการเคสการทดสอบแต่ละเคส
  • -cpu 1,2,4 ระบุจำนวน cpu
  • -count n ระบุให้แต่ละเคสการทดสอบดำเนินการกี่ครั้ง

วิธีการใช้ง่ายที่สุดคือ ไม่มีพารามิเตอร์ใด ๆ จะดำเนินการเคสการทดสอบทั้งหมดภายใต้แพ็กเกจปัจจุบัน และแสดงผลลัพธ์

sh
$ ls *_test.go
hello_test.go

$ go test
PASS
ok      golearn 0.522s

ระบุไฟล์ทดสอบใดไฟล์ทดสอบหนึ่ง

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

เพิ่มพารามิเตอร์ -v สามารถดูเอาต์พุตที่ละเอียดมากขึ้น มันค่อนข้างใช้บ่อย

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

ระบุเคสการทดสอบใดเคสการทดสอบหนึ่ง

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

ในการทดสอบ คำสั่ง test มีสองโหมด ก่อนอื่นมาพูดถึงโหมดโฟลเดอร์แรก เมื่อดำเนินการคำสั่ง test โดยไม่มีพารามิเตอร์ package จะดำเนินการทดสอบในโหมดโฟลเดอร์ เช่น คำสั่งต่อไปนี้

sh
$ go test
$ go test -v

ในโหมดนี้ ปิดใช้งานแคชการทดสอบ อีกโหมดหนึ่งคือโหมดรายการ เมื่อพารามิเตอร์ package ไม่ว่าง จะดำเนินการทดสอบในโหมดรายการ ความแตกต่างระหว่างมันกับโหมดก่อนหน้าคือเปิดใช้งานแคชการทดสอบหรือไม่ เช่น คำสั่งต่อไปนี้

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

ในโหมดรายการ go จะคอมไพล์ไฟล์ทดสอบของแต่ละแพ็กเกจภายใต้แพ็กเกจที่ระบุเป็นไฟล์ไบนารีและดำเนินการ เพื่อหลีกเลี่ยงการรันการทดสอบซ้ำ go จะแคชผลลัพธ์โดยค่าเริ่มต้น การรันครั้งที่สองจะไม่คอมไพล์ใหม่ เมื่อใช้พารามิเตอร์ต่อไปนี้จะเปิดใช้งานแคชโดยค่าเริ่มต้น

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

ใช้พารามิเตอร์อื่นนอกเหนือจากพารามิเตอร์เหล่านี้สามารถปิดแคชได้ วิธีการที่ทางการแนะนำคือใช้ -count=1 เพื่อปิดแคช เช่น

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

คำสั่ง

แตกต่างจากคำสั่ง คำสั่งของ go มีอยู่ในไฟล์ซอร์สในรูปแบบฮาร์ดโค้ด มีชื่ออีกชื่อหนึ่งที่เป็นศัพท์เทคนิคมากขึ้น คือ编译指示 (progma directives)

คอมไพเลอร์และลิงก์เกอร์จะเปลี่ยนพฤติกรรมของตัวเองเนื่องจากพวกมันเพื่อบรรลุผลการควบคุมการคอมไพล์ คล้ายกับมาโครในภาษา c แน่นอนว่าไม่ใช่คำสั่งทั้งหมดที่ใช้สำหรับการ影響การคอมไพล์ บางคำสั่งใช้สำหรับพฤติกรรมฟังก์ชันอื่น ๆ เช่น คำสั่ง generate มักใช้สำหรับฟังก์ชันการสร้างโค้ด คำสั่งเหล่านี้มักมีอยู่ในรูปแบบความคิดเห็น และขึ้นต้นด้วย //go: ไม่มีช่องว่างระหว่างกลาง เช่น คำสั่ง //go:generate ประเภทคำสั่งทั้งหมดแบ่งเป็นสองประเภท

  • คำสั่งฟังก์ชัน นี่คือคำสั่งฟังก์ชันที่ go ให้สามารถใช้ได้อย่างอิสระ เช่น generate, embed, build
  • คำสั่งคอมไพเลอร์ คำสั่งประเภทนี้ต้องใช้ด้วยความระมัดระวัง การใช้โดยไม่ระวังอาจนำไปสู่ผลลัพธ์ที่ไม่สามารถคาดเดาได้

ยกเว้นคำสั่งฟังก์ชัน คำสั่งส่วนใหญ่สามารถ作用于ฟังก์ชัน签名เท่านั้น สำหรับคำสั่งคอมไพเลอร์สามารถดำเนินการคำสั่ง go doc compile เพื่อดูคำสั่ง สำหรับคำสั่งทั้งหมด สามารถค้นหาข้อมูลเกี่ยวกับพวกมันได้ที่ 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]

คำสั่ง generate ตามชื่อหมายถึงเกี่ยวข้องกับการสร้าง โดยปกติฟังก์ชันของมันใช้สำหรับดำเนินการคำสั่งที่จะสร้างโค้ดและอัปเดตซอร์สโค้ด แต่จริง ๆ แล้วสามารถดำเนินการคำสั่งใด ๆ ก็ได้ และคำสั่ง generate แตกต่างจากคำสั่งอื่น มีคำสั่งเฉพาะสำหรับดำเนินการคำสั่ง generate ทั้งหมดที่อยู่ในไฟล์ซอร์ส สามารถใช้ชื่อไฟล์หรือชื่อแพ็กเกจเป็นพารามิเตอร์เข้าเพื่อระบุว่าดำเนินการคำสั่ง generate ของไฟล์ใด ด้านล่างเป็นพารามิเตอร์อื่นของมัน

  • -run=regex ดำเนินการคำสั่ง generate ที่ระบุ
  • -skip=regex ข้ามคำสั่ง generate ที่ระบุ
  • -n พิมพ์คำสั่งที่จะดำเนินการ
  • -x พิมพ์คำสั่งที่ดำเนินการในระหว่างกระบวนการ
  • -v แสดงไฟล์ที่ประมวลผล

นอกจากนี้ ในคำสั่งที่ดำเนินการในคำสั่ง generate ยังรองรับพารามิเตอร์ในตัวต่อไปนี้

  • $GOARCH สถาปัตยกรรม cpu
  • $GOOS ระบบปฏิบัติการ
  • $GOFILE ชื่อไฟล์
  • $GOLINE หมายเลขบรรทัด
  • $GOPACKAGE ชื่อแพ็กเกจ
  • $GOROOT go root
  • $DOLLAR สัญลักษณ์ดอลลาร์
  • $PATH ตัวแปรสภาพแวดล้อม path

ดูตัวอย่าง ไม่มีโค้ดใด ๆ มีเพียงความคิดเห็นหนึ่งบรรทัด

go
package main

//go:generate echo "hello world!"

ดำเนินการคำสั่ง

$ go generate .
hello world!

ตัวอย่างนี้คือดำเนินการคำสั่ง go

go
package main

//go:generate go version

ดำเนินการคำสั่ง

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

คำสั่ง generate สามารถใช้สำหรับดำเนินการคำสั่งใด ๆ เช่น swagger สร้างเอกสาร API หรือ Wire สร้างโค้ด IOC อย่างไรก็ตาม คำสั่งนี้ไม่เหมาะสำหรับดำเนินการคำสั่งที่ซับซ้อนมาก มันเหมาะสำหรับดำเนินการคำสั่งสั้น ๆ หากมีความต้องการที่ซับซ้อนสามารถใช้สคริปต์หรือ makefile แทน

embed

คำสั่ง embed เป็นคำสั่งใหม่ที่เพิ่มใน 1.16 ฟังก์ชันของมันคือสามารถแพ็กไฟล์สถิตร่วมกับไฟล์ไบนารีได้ เช่น เทมเพลต HTML เป็นต้น รูปแบบของมันมีดังนี้

go
//go:embed pattern

pattern สามารถเป็นนิพจน์ glob หรือโฟลเดอร์หรือไฟล์เฉพาะใดไฟล์หนึ่ง ดูตัวอย่าง

go
package main

import "embed"

//go:embed *
var static embed.FS

คำสั่ง embed ต้องอยู่เหนือตัวแปร全局ที่มีประเภท embed.Fs 注意必须是ตัวแปร全局 และต้องใช้ต้องนำเข้าแพ็กเกจ embed ในตัวอย่างนี้ * หมายถึงจะแพ็กไฟล์ทั้งหมดในโฟลเดอร์ปัจจุบันเข้ากับไฟล์ไบนารี แต่จะไม่อนุญาตให้มีโฟลเดอร์ที่ขึ้นต้นด้วย .

ตัวอย่างด้านล่างนี้แสดงการอ่านเนื้อหาจากไฟล์ที่ฝัง

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

มีเพียงสามเมธอด การใช้งานไม่แตกต่างจากระบบไฟล์ปกติ และเนื่องจากมันใช้งานอินเทอร์เฟซ io/Fs จึงสามารถส่งเป็นอ็อบเจกต์ 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)

ตัวอย่างด้านล่างนี้แสดงการฝังไฟล์ html ผ่านคำสั่ง embed และเข้าถึงผ่านบริการ 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)
}

ผลลัพธ์การเข้าถึงมีดังนี้

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>

คำสั่ง embed ยังรองรับประเภทตัวแปร全局เป็น []byte เช่น ตัวอย่างด้านล่างนี้

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

ผลลัพธ์ที่บรรลุไม่แตกต่างจากตัวอย่างก่อนหน้า

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

ในส่วน build-การควบคุมการคอมไพล์ ได้กล่าวถึงการใช้คำสั่ง // +build เพื่อควบคุมพฤติกรรมการคอมไพล์ ส่วนคำสั่ง //go:build เป็นคำสั่งใหม่ที่ออกใน 1.17 ตั้งใจที่จะแทนที่คำสั่งก่อนหน้า แต่ตอนนี้ 1.21 ก็ยังไม่แทนที่ คาดว่าในอนาคตจะอยู่ร่วมกัน สำหรับคำสั่งใหม่นี้ เอกสารทางการก็มี介绍 build constraints ฟังก์ชันของมันไม่แตกต่างจากคำสั่งก่อนหน้า แต่ไวยากรณ์เข้มงวดกว่า รองรับนิพจน์บูลีน ดูตัวอย่าง

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

package pkg_name

วิธีนี้อ่านได้ง่ายกว่าวิธีเดิมมาก

line

คำสั่ง line จะ影響หมายเลขบรรทัด หมายเลขคอลัมน์ และชื่อไฟล์ของบรรทัดถัดไป ฟังก์ชันของมันจำกัดเพียงเท่านี้ ส่วนใหญ่อาจใช้สำหรับดีบักข้อผิดพลาด เป็นต้น เช่น เมื่อเกิดข้อผิดพลาด จะเปลี่ยนข้อมูลที่คอมไพเลอร์เอาต์พุต

go
package main

var a undefinedType

func main() {

}

โดยปกติ คอมไพเลอร์จะเอาต์พุต

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

แต่หากใช้คำสั่ง line จะแตกต่าง

go
package main

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

func main() {

}

ดังนั้นเอาต์พุตของมันคือ

abc.go:10:106: undefined: undefinedType

และเนื่องจากเหตุผลทางประวัติศาสตร์ คำสั่ง line เป็นคำสั่งเดียวที่มีการใช้งานแตกต่างจากคำสั่งอื่น รูปแบบของมันคือ

go
//line filename:line:column

จะเห็นว่าไม่จำเป็นต้องมี go: เป็นคำนำหน้า

linkname

การดำเนินการของคำสั่งนี้สามารถใช้ลิงก์ฟังก์ชันหรือตัวแปร全局ของแพ็กเกจอื่น แม้ว่าจะเป็นประเภทส่วนตัว การดำเนินการนี้มักปรากฏในไลบรารีมาตรฐานโดยเฉพาะ runtime มีฟังก์ชันบางฟังก์ชันที่ไม่มีฟังก์ชันบอดี้อาศัยวิธีนี้ในการ实现 อีกส่วนหนึ่งของฟังก์ชันบอดีว่างเปล่า则由汇编实现 ดูวิธีการใช้ รูปแบบการใช้งานมีดังนี้

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

และก่อนใช้ ต้องนำเข้าแพ็กเกจ unsafe ดูตัวอย่างง่าย ๆ ในการลิงก์ประเภทส่วนตัวในไลบรารีมาตรฐาน

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")))
}

เอาต์พุต

15395306441938000233

มันลิงก์ฟังก์ชันส่วนตัว runtime.memhash เข้ากับฟังก์ชันที่เราประกาศเอง ฟังก์ชันนี้ไม่มีฟังก์ชันบอดีมีเพียง签名เท่านั้น ทำหน้าที่เป็นตัวพาเท่านั้น ฟังก์ชัน memhash คือกำหนดพอยน์เตอร์ เมล็ดแฮช และการเยื้องหน่วยความจำ คำนวณแฮชตามหน่วยความจำ กระบวนการลิงก์นี้เสร็จสิ้นในระหว่างการคอมไพล์

หากไม่ใช่ไลบรารีมาตรฐาน สถานการณ์จะแตกต่างเล็กน้อย เช่น มีฟังก์ชัน test หนึ่งฟังก์ชันภายใต้แพ็กเกจ example ก่อนลิงก์ต้องนำเข้าแพ็กเกจนี้แบบไม่ระบุชื่อก่อน

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())
}

เอาต์พุต

a

จะเห็นว่าลิงก์สำเร็จแล้ว วิธีนี้สามารถเลี่ยงระบบโมดูลของ go ทำอะไรก็ได้ แต่ไม่แนะนำให้ใช้ในวงกว้าง เว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไร

noinline

คำสั่ง noinline แสดงว่าฟังก์ชันห้ามเพิ่มประสิทธิภาพการ inline แม้ว่ามันจะง่ายมาก ดูตัวอย่างง่าย ๆ

go
package main

func val() string {
  return "val"
}

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

val เป็นฟังก์ชันที่ง่ายมาก ฟังก์ชันของมันคือคืนค่าสตริงลิเทอรัล เนื่องจากมันง่ายเกินไปและผลลัพธ์สามารถคาดเดาได้เสมอ ในการคอมไพล์จะถูกคอมไพเลอร์เพิ่มประสิทธิภาพเป็นรูปแบบดังนี้

go
package main

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

ดูรูปแบบ assembly ของมัน จะเห็นว่าไม่พบการเรียกฟังก์ชัน 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

ต่อไปเพิ่มคำสั่ง noinline

go
package main

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

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

ดูรูปแบบ assembly อีกครั้ง

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

ครั้งนี้สามารถเห็นการเรียก main.val ได้อย่างชัดเจน และนี่คือฟังก์ชันที่คำสั่ง noinline ทำหน้าที่ ป้องกันการ inline ฟังก์ชันเมื่อคอมไพเลอร์เพิ่มประสิทธิภาพ

nospilit

คำสั่ง nospilit ทำหน้าที่ข้ามการตรวจสอบสแต็กโอเวอร์โฟลว์ เนื่องจากโมเดลการกำหนดการพร้อมกันของ go เป็นการกำหนดการแบบแย่งชิง สมมติว่าฟังก์ชันหนึ่งจะรันโค้ดระดับต่ำมาก โค้ดอื่นเมื่อเรียกฟังก์ชันนี้ไม่เหมาะที่จะถูกแย่งชิง สามารถใช้คำสั่งนี้เพื่อแสดง

go
//go:nosplit
func nospilitFn()

หลังใช้คำสั่งนี้ จะไม่มีการเติบโตของสแต็กอีกต่อไป

noescape

noescape จากชื่อของมันสามารถเดาได้ง่ายว่าเกี่ยวข้องกับการ逃逸 ฟังก์ชันของมันคือแสดงว่าฟังก์ชันปัจจุบันจะไม่เกิดพฤติกรรมการ逃逸ของหน่วยความจำ หลังดำเนินการทรัพยากรทั้งหมดถูกกู้คืน และฟังก์ชันนี้ต้องมีเพียง签名ไม่มีฟังก์ชันบอดี ในสถานการณ์นี้โดยทั่วไปการ实现ของฟังก์ชัน则由汇编实现

เช่น memhash ที่ใช้ก่อนหน้านี้จะใช้คำสั่งนี้

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

เช่นนี้ คอมไพเลอร์จะไม่ทำการวิเคราะห์การ逃逸สำหรับมัน เงื่อนไขคือคุณต้องรับประกันว่ามันจะไม่เกิดการ逃逸 หากเกิด ก็ไม่รู้ว่า会有什么后果

uintptrescapes

คำสั่ง uintptrescapes แสดงว่าพารามิเตอร์ประเภท uinptr ในฟังก์ชันนี้ถูกแปลงเป็นค่าพอยน์เตอร์และ逃逸ไปยังฮีป และต้องรักษาให้存活 คำสั่งนี้มักใช้สำหรับการเรียกใช้ระบบระดับต่ำ ส่วนใหญ่ไม่จำเป็นต้องเข้าใจ

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

ในอดีตควรมีคำสั่ง notinheaps สำหรับแสดงว่าประเภทไม่อนุญาตให้จัดสรรหน่วยความจำไปยังฮีป ไม่ทราบว่าถูกลบในเวอร์ชันใด

norace

คำสั่ง norace แสดงว่าการเข้าถึงหน่วยความจำของฟังก์ชันไม่จำเป็นต้องวิเคราะห์ race condition มักใช้เมื่อรันโค้ดระดับต่ำไม่เหมาะสำหรับการวิเคราะห์ race condition

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

TIP

ยังมีคำสั่งบางคำสั่งที่จำกัดให้ใช้โดยแพ็กเกจ runtime เท่านั้น ภายนอกไม่สามารถใช้ได้ พวกมันจะเกี่ยวข้องกับสิ่งที่ลึกกว่า หากต้องการเข้าใจสามารถดู介绍เกี่ยวกับพวกมันได้ที่ Runtime-only compiler directives

Golang by www.golangdev.cn edit