Skip to content

Línea de Comandos

Los comandos de Go incluyen un conjunto completo de toolchain que cubren documentación, formateo, análisis de código, compilación, pruebas, gestión de dependencias y otros aspectos del desarrollo en Go.

text
bug         reportar errores
build       compilar paquetes y dependencias
clean       eliminar archivos objeto
doc         mostrar documentación del código fuente
env         ver información de variables de entorno de Go
fix         reparar problemas de compatibilidad de API causados por cambios de versión
fmt         formatear código fuente
generate    generación de código
get         agregar dependencias
install     instalar y compilar paquetes
list        comando de lista de paquetes/módulos
mod         comando de mantenimiento de módulos
work        comando de mantenimiento de workspace
run         compilar y ejecutar
test        probar
tool        ejecutar herramienta go especificada
version     mostrar información de versión de Go
vet         escanear y mostrar posibles problemas en el código fuente

Este artículo solo describe brevemente su uso, todo el contenido está referenciado de la documentación oficial, para más detalles puedes visitar cmd/go.

help

El primero que debes conocer es el comando help, a través de él puedes leer el uso de los comandos. Hay dos formas de usarlo, si quieres obtener información breve de uso, puedes agregar el flag -h después del comando especificado, por ejemplo

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

Go mostrará de forma concisa el uso de ese comando, también te indicará que para obtener información más detallada necesitas usar el comando 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'.

Aprende a usar bien el comando help, a través de él puedes obtener mucha información sobre los comandos.

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

El comando doc mostrará los comentarios de documentación del paquete, constante, función, tipo, variable, método e incluso campo de estructura especificado. Sin ningún parámetro, mostrará los comentarios del paquete actual

sh
$ go doc

También puedes especificar ver un paquete determinado, por ejemplo ver los comentarios de documentación del paquete 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.
......

O un tipo determinado

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

O una función determinada

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.

Tiene los siguientes flags comúnmente usados

  • -u:ver tipos privados
  • -all:ver toda la documentación del paquete especificado
  • -short:solo una línea de descripción breve
  • -src:mostrar el código fuente
  • -cmd:para algunos paquetes que pertenecen a comandos go, también mostrar la documentación de código dentro del paquete.

Por ejemplo, ver la variable runtime.inf, esta es una variable que no se expone externamente

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

var inf = float64frombits(0x7FF0000000000000)

Aprovechar bien el comando doc puede ayudarte a leer la documentación más fácilmente.

Otra forma de leer la documentación de comandos es leer el código fuente, porque la documentación de algunos comandos no está tan detallada, pero en el código fuente hay explicaciones más detalladas. Como todos estos comandos están escritos en go, leerlos también es bastante conveniente. Estos comandos están ubicados en el paquete src/cmd, cada subpaquete es un comando separado, la entrada está en el archivo 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,
    }
}

Aquí encontrarás todos los subcomandos de go y su información de documentación de ayuda.

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.

Este comando no tiene ningún parámetro ni flag, abrirá tu navegador predeterminado para visitar la interfaz de issues del repositorio github.com/golang/go, facilitándote reportar bugs, aparte de eso no tiene ninguna otra función.

version

A través del comando version puedes ver la información de versión actual de go.

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

Sin ningún parámetro, mostrará la versión actual del lenguaje go

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

También acepta rutas de archivo como parámetro, mostrará la versión de go usada al compilar todos los archivos binarios reconocibles en esa ruta.

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

El parámetro -v indica al comando version que intente mostrar la versión de go de archivos que no se pueden reconocer, el parámetro -m muestra información de módulos del archivo binario y algunos parámetros de compilación, aquí hay un ejemplo simple.

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 mismo es un archivo binario, de hecho sin ningún parámetro, go version muestra la versión del lenguaje go de su propio archivo binario, porque todo el toolchain de cmd/go está implementado en el lenguaje go mismo.

env

A través del comando env puedes ver la información de todas las variables de entorno de go, modificar estas variables de entorno afectará el comportamiento del toolchain de go.

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

Sin ningún parámetro, mostrará los valores de todas las variables de entorno de go

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

Pasando el nombre de una variable de entorno como parámetro puedes mostrar solo el valor de esa variable

sh
$ go env GO111MODULE
on

Agregando -json puedes mostrar su formato json

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

A través del flag -w, y usando la forma var=value como parámetro, modificarás permanentemente el valor de una variable

sh
$ go env -w GO111MODULE=on

Usando el flag -u, puedes restaurar una variable a su valor predeterminado

sh
$ go env -u GO111MODULE

Ejecutando go help environment puedes ver la introducción de cada variable de entorno

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

A continuación se presentan algunas variables de entorno comúnmente usadas

GOVERSION

El valor de esta variable de entorno depende de la versión del lenguaje go, y el número de versión proviene del archivo $GOROOT/VERSION, que registra el número de versión actual de go y el tiempo de compilación.

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

El valor de la variable runtime.Version es el mismo que el valor de GOVERSION, y esta variable de entorno no puede ser modificada.

GOENV

En el directorio $GOROOT habrá un archivo de configuración predeterminado llamado 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

Su formato es simplemente la forma key=value, los valores de variables de entorno modificados a través del comando go env -w key=value se escribirán en el archivo de configuración. Sin embargo, también puedes no usar el archivo de configuración predeterminado, la variable de entorno GOENV puede especificar manualmente la dirección del archivo de configuración env, y el valor de la variable de entorno GOENV solo puede ser sobrescrito por variables de entorno del sistema operativo, no puede ser modificado por el comando go env -w.

GOHOSTARCH

Representa la arquitectura de CPU de la máquina local, solo se usa para mostrar, el valor de esta variable de entorno no se lee del archivo de configuración, ni puede ser modificado.

GOHOSTOS

Representa el sistema operativo de la máquina local, solo se usa para mostrar, el valor de esta variable de entorno no se lee del archivo de configuración, ni puede ser modificado.

GOOS

Al compilar, el valor de GOOS determinará en qué sistema operativo se compilará el código fuente a archivo binario, el valor predeterminado es GOHOSTOS, es decir, el sistema operativo de la máquina local, tiene las siguientes opciones

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

Los sistemas operativos realmente soportados no son solo estos, usa el comando go tool dist list para ver todos los valores soportados

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

Al compilar, el valor de GOARCH determinará qué instrucciones de arquitectura de CPU se usarán al compilar, el valor predeterminado es GOHOSTARCH, es decir, la arquitectura de CPU de la máquina local, tiene las siguientes opciones

  • amd64
  • 386
  • arm
  • ppc64

Las arquitecturas realmente soportadas no son solo estas, usa el comando go tool dist list para ver todos los valores soportados

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

Ten en cuenta que GOOS y GOARCH no se pueden combinar arbitrariamente, algunos sistemas operativos solo soportan arquitecturas de CPU específicas.

GOROOT

GOROOT representa el directorio raíz de la instalación del lenguaje go, el valor de GOROOT no se puede modificar directamente, y solo puede ser sobrescrito por variables de entorno del sistema operativo.

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

En el directorio raíz hay las siguientes carpetas o archivos más importantes

  • lib, almacena algunas dependencias, actualmente solo hay una biblioteca que contiene información de zonas horarias de países de todo el mundo, ubicada en $GOROOT/lib/time, los archivos binarios compilados no incluirán esta información de zonas horarias.

  • pkg, almacena algunas bibliotecas de herramientas y archivos de cabecera, por ejemplo el comando go tool buscará los archivos binarios del toolchain de go en el directorio $GOROOT/pkg/tool

  • bin, almacena archivos binarios, por defecto solo hay dos archivos ejecutables go y gofmt, $GOROOT/bin debe ser agregado a las variables de sistema, de lo contrario no se podrán usar los comandos de go.

  • src, almacena el código fuente de go

  • VERSION, este archivo almacena la información de versión del lenguaje go

  • go.env, este archivo es el archivo de configuración env predeterminado

GOPATH

El valor predeterminado de GOPATH es $HOME/go, el valor de esta variable de entorno especifica dónde buscar los archivos importados al resolver declaraciones import. En los primeros tiempos sin gomod, GOPATH se usaba específicamente para almacenar varias bibliotecas de terceros, su estructura es la siguiente

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)

Después del nacimiento de gomod, GOPATH es solo un lugar para almacenar las dependencias descargadas por go get, y para almacenar los archivos binarios descargados y compilados por go install. Ten en cuenta que la ubicación de GOPATH no puede ser la misma que GOROOT, de lo contrario no tendrá ningún efecto.

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

Hasta el momento en que escribo este artículo, la versión del lenguaje go ha llegado a go1.21.3, excepto por proyectos muy antiguos, básicamente nadie usa gopath para gestionar dependencias.

GOBIN

GOBIN se usa para almacenar los archivos binarios ejecutables de terceros descargados y compilados por go install, su valor predeterminado es $GOPATH/bin. Al igual que $GOROOT/bin, este directorio debe ser agregado a las variables de entorno del sistema operativo, de lo contrario tampoco se podrán usar los archivos binarios en el directorio GOBIN.

GOMODCACHE

GOMODCACHE indica la ubicación donde se almacenan las dependencias descargadas por go get, el valor predeterminado es $GOPATH/pkg/mod. Su formato de almacenamiento es el siguiente

$GOMODCACHE/domain/username/project@verion

En el mismo directorio de nivel también habrá una carpeta llamada sumdb, usada para almacenar información relacionada con la base de datos de suma de verificación de dependencias.

GOCACHE

Almacena información de caché para compilación, su valor predeterminado es $HOME/.cache/go-build, en este directorio se generará un archivo 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.

Cada build producirá muchos archivos, go almacenará en caché estos archivos para reutilizarlos en la siguiente compilación.

GOTEMPDIR

Usado para archivos temporales generados durante la compilación, por ejemplo el archivo binario temporal que go run va a ejecutar. Su valor predeterminado es el directorio temporal especificado por el sistema operativo, en mac o linux es /tmp, en windows es %TEMP%, también se puede modificar a una ubicación especificada por el usuario.

GO111MODULE

Esta variable de entorno indica qué método usar para gestionar las dependencias del proyecto go, tiene los siguientes tres valores disponibles

  • off, desactivar gomod, usar gopath, e ignorar todos los archivos go.mod
  • on, usar gomod, no usar gopath (predeterminado).
  • auto, detección automática, si el archivo del proyecto contiene go.mod se usará gomod para gestionarlo

TIP

¿Por qué se llama GO111MODULE y no GOMODULE directamente? Porque gomod se introdujo por primera vez en la versión go1.11.

GOPROXY

Proxy de módulos go, el valor predeterminado es https://proxy.golang.org,direct, las url se separan por comas, direct significa usar directamente VCS saltando el proxy de módulos, solo se ejecutará el segundo cuando el primero no pueda acceder, otra opción disponible es off, que significa prohibir la descarga de cualquier módulo. Además, GOPROXY también puede ser una dirección de archivo, por ejemplo

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

A través de go get -x puedes ver los comandos ejecutados durante el proceso de descarga de dependencias, para saber si se usó el proxy.

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/0/x068/334
# 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: 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

Usar un proxy de módulos puede mejorar efectivamente la velocidad de descarga de módulos, para usuarios en China continental básicamente no se puede acceder al proxy oficial predeterminado sin usar un proxy, actualmente los proxies de módulos de terceros públicos y confiables son los siguientes

  • https://proxy.golang.com.cn, código abierto y también proporciona servicios empresariales
  • https://goproxy.cn, proporcionado y con código abierto por Qiniu Cloud

Por supuesto, también hay soluciones de proxy de módulos auto-construidas de código abierto: goproxy

GOSUMDB

GOSUMDB se usa para configurar la dirección de la base de datos de verificación de suma de bibliotecas de dependencias, el valor predeterminado es sum.golang.org, cuando configuras un proxy, go accederá a la base de datos de verificación a través del proxy.

GOPRIVATE

La variable de entorno GOPRIVATE se usa para configurar bibliotecas privadas, las bibliotecas que coincidan no serán verificadas a través de sumdb, ni pasarán por el proxy, descargarán dependencias directamente a través de VCS. Soporta configuración con comodines, separados por comas, como se muestra a continuación, todas las dependencias con sufijo corp.example.com y llamadas github.com/gohper/myproject no pasarán por proxy ni sumdb.

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

También se puede configurar directamente para un usuario u organización

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

GONOPROXY

Indica qué dependencias no necesitan pasar por el proxy, las reglas son las mismas que GOPRIVATE, y sobrescribirá GOPRIVATE.

GONOSUMDB

Indica qué dependencias no necesitan pasar por la base de datos de verificación, las reglas son las mismas que GOPRIVATE, y sobrescribirá GOPRIVATE.

GOINSECURE

Indica qué dependencias se descargan directamente usando VCS, las reglas son las mismas que GOPRIVATE, y será sobrescrito por GONOPROXY y GONOSUMDB.

GOVCS

Configura el sistema de control de versiones para la gestión de módulos, por defecto public:git|hg,private:all. También se puede limitar el VCS para dominios específicos, por ejemplo

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

En la limitación anterior, github solo puede usar git, evil.com no permite usar ninguno, se usa | para indicar múltiples VCS. Si no quieres hacer ninguna limitación, puedes configurar

GOVCS=*:all

Si no quieres permitir el uso de ningún VCS, puedes configurar

GOVCS=*:off

GOWORK

Configura si se habilita el workspace, por defecto está vacío es decir habilitado, si se configura como off, no se habilitará, ignorará todos los archivos go.work.

GOTOOLDIR

Configura la ubicación del toolchain de go a usar, por defecto es $GOROOT/pkg/tool, el toolchain predeterminado también se almacena en esa ubicación.

GODEBUG

Configura opciones de depuración, en forma de pares clave-valor para controlar parte del comportamiento de ejecución de los programas go, por ejemplo

GODEBUG=http2client=0,http2server=0

Estas configuraciones son para facilitar el retorno al comportamiento antiguo cuando aparecen cambios incompatibles durante el proceso de actualización de versión, por ejemplo en 1.21 ya no se permite la situación panic(nil), para esto, el oficial de go ha registrado específicamente GODEBUG History, visita GODEBUG para más detalles.

CGO_ENABLED

Indica si se habilita cgo, el valor predeterminado es 1, es decir habilitado, configurar como 0 lo deshabilita.

Las variables de entorno anteriores son todas comúnmente usadas, para algunas no tan comúnmente usadas no se hará demasiada introducción, como CGO, WASM, etc., si te interesa puedes investigar por tu cuenta.

build

Go soporta dos tipos de compiladores, gccgo y gc. gcc es un compilador de c/c++ con historia, soporta múltiples lenguajes incluyendo go, este último gc no se refiere a garbage collection, se refiere a go compiler, el lenguaje go completó el bootstrap en go1.5, gc es un compilador completamente escrito en el lenguaje go, su código fuente está ubicado en el paquete cmd/compile, como está completamente implementado en go, es muy conveniente para entender y aprender sus mecanismos internos. Por defecto, el compilador usa gc para compilar. Por cierto, el depurador del lenguaje go también tiene dos tipos, gdb y dlv, el primero es el depurador de c/c++ con historia, soporta múltiples lenguajes incluyendo go, el segundo es un depurador escrito en go, tiene mejor soporte para el lenguaje go, también es de código abierto, se recomienda usar el segundo.

El comando build compila archivos fuente de go en archivos binarios ejecutables, experimentarás una compilación bastante rápida, esta es también una de las características del lenguaje go.

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

Acepta tres parámetros, uno es la ruta de salida de archivo indicada por el flag -o, otro son los flags de compilación build flags usados para definir el comportamiento de compilación, y el último es el paquete a compilar, este parámetro debe ir al final. Aquí hay un ejemplo simple, sin usar flags de compilación.

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

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

./bin/golearn.exe indica la ruta de salida, golearn indica el módulo a compilar, también puede ser un archivo de entrada, o una carpeta. Por ejemplo, el siguiente ejemplo simple usa el archivo de entrada main.go como objetivo de compilación.

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

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

Al compilar ignorará todos los archivos que terminen en _test.go, porque estos archivos por convención son todos archivos de prueba.

Además, el comando build también soporta bastantes flags de compilación para controlar algunos comportamientos durante la compilación.

  • -x:muestra las instrucciones detalladas durante el proceso de compilación
  • -n:similar a -x, pero la diferencia es que solo muestra estas instrucciones, pero en realidad no las ejecuta.
  • -v:muestra los paquetes compilados
  • -p:número de concurrentes durante el proceso de compilación
  • -a:fuerza la recompilación, incluso si ya está actualizado.
  • -compiler:especifica qué compilador usar, gccgo o gc, este último es un compilador escrito en go.
  • -race:habilita la detección de race condition
  • -msan:habilita el análisis de memoria
  • -asan:habilita el análisis de direcciones
  • -cover:habilita la detección de cobertura de código
  • -buildmode:especifica el modo de compilación, tiene las opciones archive, c-archive, c-shared, default, shared, exe, pie, plugin.
  • -pgo, especifica el archivo pgo
  • -trimpath:elimina el prefijo de ruta de archivos fuente, por ejemplo la ruta relativa /var/lib/go/src/main.go, después de eliminar, el nombre de archivo obtenido a través de runtime en tiempo de ejecución solo tendrá la ruta relativa relativa a la ruta del módulo /main.go, después de habilitar esto, el tiempo de compilación aumentará significativamente, aproximadamente un 20-40%, dependiendo del número de archivos.
  • -toolexec, algunos comandos go ejecutados antes de compilar, el formato es -toolexec 'cmd args'.
  • -gcflags:especifica algunos tag del compilador gc
  • -gccgoflags:especifica algunos tag del compilador gccgo
  • -ldflags:especifica algunos tag de la herramienta link

Para algunos parámetros pasados como ldflags, puedes pasar "-help" para obtener sus posibles valores, por ejemplo

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

Los anteriores son comúnmente usados, para otros no tan comúnmente usados puedes investigar por tu cuenta.

gcflags

A través de gcflags puedes pasar algunos parámetros al compilador gc para controlar comportamientos específicos, su formato de uso es -gcflags="pattern=args list", ages list es la lista de parámetros, pattern es el alcance, tiene los siguientes valores disponibles

  • main, la ruta del paquete de nivel superior donde se encuentra el archivo de entrada
  • all, el módulo actual y todas las dependencias del modo actual
  • std, biblioteca estándar
  • cmd, afecta todos los archivos fuente bajo el paquete cmd
  • Comodines, como ., ./..., cmd/....

Esta regla de pattern aplica a todos los flags que soportan este formato, por ejemplo ldflags. A través del siguiente comando puedes ver los valores disponibles de sus parámetros

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

A continuación se presentan algunos parámetros comúnmente usados

  • -S:muestra el código en forma de ensamblador
  • -N:desactiva las optimizaciones del compilador
  • -m:muestra las decisiones de optimización
  • -l:desactiva el inline de funciones
  • -c:número de concurrentes en la compilación
  • -dwarf:genera banderas DWARF

Por ejemplo, si quieres ver la forma de ensamblador del código, puedes usar el parámetro -S, y también debes desactivar las optimizaciones y el inline, para poder restaurar su forma original, de la siguiente manera

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

A través de ldflags puedes pasar algunos parámetros al linker para controlar comportamientos específicos, a través del siguiente comando puedes ver todos los valores disponibles de ldflags, cerca de veinte o treinta.

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

El parámetro -X de ldflags es una función muy práctica, puede definir el valor de una variable de cadena de un paquete especificado en tiempo de enlace. A través de esta función, podemos inyectar información meta muy convenientemente en tiempo de compilación. Y como es solo una variable, también es conveniente obtenerla en tiempo de ejecución, aquí hay un ejemplo simple.

go
package main

import "fmt"

var (
  Version string
)

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

Ejecuta el comando

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

Después de ejecutar, mostrará el checksum sha1 del commit de git.

5e3fd7a

Otros parámetros prácticos incluyen

  • -w:no generar DWARF, esto es información para facilitar la depuración del código fuente.
  • -s:desactivar la tabla de símbolos

Estos dos generalmente se usan juntos, pueden reducir significativamente el tamaño del archivo binario compilado, aproximadamente un 40%-50%, la desventaja también es obvia, no se puede depurar, aquí hay un ejemplo.

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

Compilación Cruzada

La compilación del lenguaje go tiene dos características principales, la primera es rápida, la otra característica principal es la compilación cruzada, la compilación cruzada se refiere a poder compilar localmente en código objeto de otros sistemas, por ejemplo compilar en windows archivos binarios para linux o darwin, y viceversa. La compilación cruzada soporta muchos lenguajes, esto no es nada raro, pero la compilación cruzada del lenguaje go es muy simple, solo requiere los siguientes dos pasos

  1. Configurar la variable de entorno GOOS, elegir tu sistema operativo objetivo
  2. Configurar la variable de entorno GOARCH, elegir la arquitectura de CPU objetivo
  3. Usar go build para compilar como de costumbre

Todo el proceso es muy corto, no requiere usar herramientas adicionales o configuración, y la velocidad es tan rápida como de costumbre. Como se muestra a continuación

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

El primer paso SET CGO_ENABLED=0 desactiva cgo, una vez que tu código usa cgo, no podrás usar la compilación cruzada normalmente. El segundo paso SET GOOS configura el sistema objetivo, las opciones disponibles son linux, darwin, windwos, netbsd. El tercer paso configura la arquitectura de CPU, SET GOARCH, las opciones disponibles son amd64, 386, arm, ppc64. El último paso es compilar como de costumbre.

Control de Compilación

El comando build puede lograr el efecto de controlar la compilación a través de tags, existe en el código fuente en forma de directivas, veamos un ejemplo, archivo product.go

go
// +build product

package main

import "fmt"

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

Archivo debug.go

go
// +build debug

package main

import "fmt"

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

Ambos tienen una directiva // +build, que indica en qué condiciones serán compilados. Su formato básico es

go
// +build tag1 tag2

package pkg_name

Hay algunas reglas que se deben cumplir

  1. Debe haber un espacio entre // y +build
  2. Debe estar ubicado encima de la declaración del paquete
  3. Debe haber una línea vacía entre la declaración del paquete

Además, también puede lograr control lógico a través de separación simple, el espacio indica OR, la coma indica AND, ! indica NOT. Por ejemplo, el siguiente ejemplo

go
// +build windows linux

package pkg_name

Indica que en la plataforma windows o linux se compilará el archivo actual.

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

package pkg_name

Este ejemplo indica que en la plataforma windows con arquitectura amd64 y cgo no habilitado, o en la plataforma linux con arquitectura i386 y cgo habilitado se compilará. Si solo no quieres que un archivo participe en la compilación, puedes usar ignore.

go
// +build ignore

package pkg_name

También puede haber múltiples líneas de directivas

go
// +build windows
// +build amd64

package pkg_name

Las directivas de múltiples líneas se procesan en modo AND. Para los tags de plataforma y arquitectura, go los pasará automáticamente al compilar, también podemos pasar tags personalizados, tomando los dos archivos del ejemplo anterior

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

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

Se puede ver que al pasar diferentes tags la salida es diferente, el propósito del control de compilación se ha logrado.

run

El comando run al igual que build compilará el código fuente, la diferencia es que el comando run lo ejecutará directamente después de completar la compilación. El comando run para acelerar la compilación, no genera información de depuración durante el proceso de compilación, por lo que no soporta depuración, y solo genera un archivo binario temporal, generalmente almacenado en el directorio GOTMEPDIR, por ejemplo /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.

También soporta los flags de compilación del comando build, y también proporciona un parámetro -exec para indicar qué programa ejecutará el archivo binario, [arguments...] se refiere a los parámetros de ejecución del programa. Aquí hay un ejemplo

go
package main

import (
  "fmt"
  "os"
)

var (
  Version string
)

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

Usa go run para ejecutar

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

En general, el uso no es muy diferente de go build, por lo que no se hará más explicación.

tool

El comando tool en sí mismo no tiene ninguna función, su función es llamar directamente a las herramientas en el directorio cmd/, por ejemplo cmd/compile es el compilador incluido. A través de go tool puedes llamar directamente a estas herramientas, sin tener que ejecutar manualmente los archivos binarios de estas herramientas.

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

Usa el parámetro -n para imprimir todos los parámetros de comando que soporta

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

Estas herramientas se almacenan en el directorio GOROOT/pkg/tool, y se agrupan según el sistema operativo y la arquitectura de CPU, de la siguiente manera.

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*

Usa el formato go doc cmd/command para ver el uso de cada comando, por ejemplo

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

Los flags soportados por cmd/compile son los mismos que los soportados por gcflags mencionados anteriormente. La diferencia entre go tool compile y go build es que el primero solo es responsable de compilar, y solo puede tomar archivos como parámetros, el segundo puede tomar carpetas, paquetes, archivos como parámetros, y no solo compila el código fuente, también es responsable de enlazar archivos, limpiar archivos inútiles, etc., el primero es parte del segundo. Podemos imprimir los comandos ejecutados durante el proceso de 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
...

Durante el proceso puedes ver este segmento /golang/pkg/tool/windows_amd64/compile.exe, que está llamando al compilador. Además de compile, hay muchas herramientas que se pueden llamar, muchos comandos go en realidad son alias de ellas.

clean

El comando clean se usa para eliminar los archivos objeto generados durante el proceso de compilación

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

Soporta los siguientes flags

  • -i:elimina los archivos de almacenamiento o archivos binarios correspondientes
  • -n:imprime los comandos que se ejecutarán durante el proceso de limpieza pero en realidad no los ejecuta
  • -x:imprime los comandos que se ejecutarán durante el proceso de limpieza y los ejecuta
  • -r:elimina recursivamente a través de import path
  • -cache, elimina toda la caché generada por go build
  • -testcache:elimina toda la caché de pruebas generada
  • -modcache:elimina toda la caché de módulos descargados
  • -fuzzcache:elimina la caché generada por fuzz test.

Cuando usas go tool compile, estás llamando directamente al comando del compilador, no como go build que hace mucho procesamiento posterior, se generarán archivos objeto. Por ejemplo, ejecuta el siguiente comando

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

Se generará un archivo llamado main.o, usa el comando go clean para eliminarlo. O usa el parámetro -n para imprimir los comandos que se ejecutará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

Limpia la caché de compilación, eliminará la caché de compilación generada en el directorio GOCACHE

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

Limpia la caché generada por fuzz test, esta caché por defecto se almacena en el directorio GOCACHE/fuzz/

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

fix

El lenguaje go hasta el momento de escribir este artículo ya tiene diez años, en el proceso de actualización y modificación continua del lenguaje, inevitablemente aparecerán algunos problemas de incompatibilidad debido a cambios en la API, el comando fix nació para esto, detectará esas API obsoletas en los archivos fuente y las reemplazará con nuevas API.

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

Soporta carpetas, nombres de archivo, directorios como parámetros, acepta el flag -fix para pasar parámetros que indican qué modificaciones hacer, puedes ver los valores disponibles a través del comando 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.

Veamos un ejemplo, el código fuente usa el paquete golang.org/x/net/context

sh
package main

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

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

Usa go fix para corregirlo, reemplazándolo con el paquete context de la biblioteca estándar, podemos usar el siguiente comando para hacer el reemplazo

sh
$ go fix -fix context main.go

También puedes ver los cambios antes y después sin reemplazar.

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

El lenguaje go ha existido por más de diez años y solo tiene nueve parámetros de reemplazo disponibles, se puede ver que la compatibilidad se ha mantenido bastante bien.

fmt

El comando fmt es una herramienta de formateo incluida en el lenguaje go, usada para formatear archivos de código fuente go.

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

A través del comando go doc gofmt puedes ver su documentación detallada

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 usa tab para la indentación, espacios para la alineación, por defecto el código formateado se mostrará en la salida estándar, no sobrescribe el archivo original. El comando go fmt en realidad usa el comando gofmt, es un archivo binario independiente, ubicado en el directorio GOROOT/bin.

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

Agregando el flag -n al comando go fmt puedes saber qué instrucciones se ejecutarán.

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

Se puede ver que go fmt es en realidad un alias de gofmt -l -w, el comando gofmt tiene los siguientes parámetros

  • -d:muestra la diferencia del archivo antes y después del formateo
  • -e:muestra todos los errores
  • -l:muestra los nombres de archivos que han cambiado
  • -r:aplica reglas de formateo
  • -s:intenta simplificar el código
  • -w:sobrescribe el archivo fuente, si ocurre un error restaura la copia de seguridad

Supongamos que ahora tenemos el siguiente archivo fuente

go
$ cat main.go
package main

import "fmt"

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

A través del parámetro -d puedes previsualizar sus cambios

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

El parámetro -l mostrará los nombres de archivos que se modificarán

$ gofmt -l .
main.go

Si hay errores de sintaxis, el parámetro -e puede mostrar más detalles

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 aplicará los cambios al archivo fuente

sh
$ gofmt -l -w .
main.go

$ cat main.go
package main

import "fmt"

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

Puedes notar que como herramienta de formateo, gofmt no proporciona ninguna configuración personalizada, mientras que el formateador prettify diseñado para embellecer código js proporciona bastantes configuraciones para formatear código, aquí se puede reflejar una actitud del oficial de go, no pienses en hacer personalización, es mejor que el estilo de código de todos sea consistente, al menos hay un beneficio, al leer código no tienes que adaptarte a los hábitos de otros. Sin embargo, en realidad todavía conserva una opción de personalización, que es la regla de reemplazo del código formateado, la regla se puede personalizar, el formato es el siguiente

pattern -> replacement

Por ejemplo, eliminar paréntesis redundantes

(a) -> a

Ver los cambios del archivo

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

Se puede ver que gofmt eliminará los paréntesis redundantes.

get

El comando get es definitivamente uno de los más usados en el desarrollo de go, su función es descargar el código fuente de la dirección especificada al directorio correspondiente a GOMODCACHE.

sh
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.
  • -u:intenta actualizar la versión menor y la versión de parche del paquete, si hay un cambio en la versión principal, como v1->v2, no se actualizará.
  • -t:actualiza la versión de dependencias en las pruebas
  • -v:muestra los paquetes compilados, en realidad es uno de los parámetros de build flags

En tiempos antiguos, go get funcionaba de manera similar a go install, descargaba y compilaba estos paquetes, sin embargo con el nacimiento y perfeccionamiento de los módulos de go, esta parte de la función gradualmente quedó obsoleta, el comando get ahora se usa más comúnmente para descargar y resolver dependencias de módulos go, por lo que puedes ver que el comando go get también soporta flags de compilación como build flags, y si intentas usar go get fuera de un módulo como usarías go install, te indicará que este uso ha sido obsoleto.

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

En cuanto a por qué en la descripción de la documentación todavía se conservan estos, es desconocido, revisando el código fuente del comando get, también descubrirás que conserva esos flags antiguos.

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
)

Volviendo al tema, el comando get descargará el código fuente del paquete especificado al directorio de dependencias global local, es decir, el directorio correspondiente a GOCACHE, y luego registrará la información en los archivos go.mod y go.sum, el primero es responsable de registrar versiones, el segundo es responsable de registrar checksums sha1 para garantizar la seguridad. El comando get en realidad se basa en VCS, es decir, el sistema de control de versiones local, soporta los siguientes

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

Entre ellos, por defecto solo soporta git y hg, se puede configurar en GOVCS, el formato es el siguiente

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

GOVCS solo soporta git y hg como VCS, los otros tres necesitan configurarse en GOPRIVATE.

El comando go get tiene los siguientes usos, puedes pasar la dirección de dependencia directamente como parámetro

sh
$ go get golang.org/x/net

También puedes especificar la versión

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

Especificar la última versión

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

Intentar actualizar la versión

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

Eliminar una dependencia

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

Los usos anteriores son para gestionar dependencias normales, también se puede usar para gestionar dependencias menos normales, por ejemplo actualizar la versión del lenguaje 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

Incluso se puede usar para actualizar la versión del toolchain de go

sh
$ go get toolchain@latest

Cuando usas go get para actualizar las versiones de go y toolchain, se instalará la nueva versión de go en el directorio GOMODCACHE/golang.org/

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

Entonces puedes modificar manualmente GOROOT para cambiar a la versión especificada.

install

El comando install es similar al comando get, ambos se usan para descargar dependencias de terceros, pero la diferencia es que el comando get descarga el código fuente, mientras que el comando install compila el código fuente en archivos binarios ejecutables nativos, la ruta de almacenamiento de los archivos binarios es primero en el directorio GOBIN, y luego en GOPATH/bin. La función principal de este comando es descargar algunas herramientas de línea de comandos de terceros públicas, gracias a la velocidad de compilación y portabilidad del lenguaje go, no es necesario descargar archivos binarios, sino descargar directamente el código fuente y luego compilarlo localmente.

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

El comando install acepta flags de compilación y nombres de paquetes como parámetros, con gomod habilitado, el nombre del paquete debe llevar el número de versión. Por ejemplo, descargar el depurador 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/

Primero descarga el código fuente a la ruta almacenada en GOMODCACHE, esto es consistente con el comando get, luego cambia al directorio de trabajo temporal, lo compila, después de completar la compilación mueve el archivo binario al directorio GOPATH/bin, y finalmente elimina la carpeta temporal. El comando install también tiene una limitación, el paquete descargado debe ser el paquete de entrada del proyecto, es decir, debe contener el archivo de entrada main.go, de lo contrario te indicará que no se puede instalar. Por ejemplo, usar go install para descargar 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 es una biblioteca de dependencias de framework web, no una herramienta de línea de comandos, naturalmente no tiene archivo de entrada, por lo que la instalación fallará.

list

El comando list listará los paquetes en la ubicación especificada, uno por línea, y soporta formato de salida personalizado, soporta muchos parámetros, el requisito previo para usarlo es estar en un proyecto que soporte gomod.

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

Soporta los siguientes parámetros

  • -f:parámetro de formato
  • -json:salida en formato json
  • -compiled:muestra todos los paquetes que serán compilados por el compilador
  • -deps:muestra cada paquete y el nombre de cada paquete del que depende
  • -test:muestra el paquete de pruebas de cada paquete
  • -e:salida normal cuando se encuentran paquetes con errores
  • -find:no resuelve las dependencias de estos paquetes
  • -export:cuando se usa este parámetro, se establece el campo Package.Export de la estructura para que contenga el archivo con la información de exportación más reciente del paquete especificado, y se establece el campo Package.BuildID con el BuildID del paquete, principalmente para salida formateada.

Parámetros de información de módulos,

  • -m:muestra módulos en lugar de paquetes

  • -versions:muestra toda la información disponible de un módulo

  • -retracted:muestra las versiones retiradas de un módulo

El parámetro [packages] puede ser un nombre de paquete especificado, o una carpeta, o all, que significa cualquier lugar, cuando se usa el parámetro -m, all significa todas las dependencias referenciadas por el módulo actual.

Por ejemplo, el archivo actual solo tiene un archivo main.go, y solo hay una línea de código que muestra "hello world", después de ejecutar go list -deps ., mostrará todos los paquetes desde el proyecto actual hasta fmt y todas sus dependencias referenciadas.

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

O mostrar todas las dependencias de módulos en el proyecto actual

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

La salida del comando list es por línea, cada línea de salida es un paquete. El oficial proporciona un parámetro -f que nos permite personalizar el formato de salida de línea, el valor que acepta es la sintaxis de plantilla definida por el paquete de motor de plantillas template/text, por ejemplo el siguiente ejemplo

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

Cada paquete iterado se pasará en forma de la siguiente estructura, todos los campos de esta estructura se pueden usar como parámetros de plantilla.

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
}

Si se itera un módulo, se pasa en forma de la siguiente estructura, todos sus campos también se pueden usar como parámetros de plantilla.

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
}

Ver todos los paquetes

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

Ver módulos

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

mod

go mod es un comando dedicado a gestionar módulos 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.

Tiene los siguientes subcomandos

  • download:descarga todas las dependencias indicadas en el archivo go.mod a la caché local
  • edit:edita el archivo go.mod, la interfaz de línea de comandos que proporciona es principalmente para que otras herramientas o scripts la llamen.
  • init:inicializa un proyecto gomod en el directorio actual
  • tidy:descarga dependencias faltantes, elimina dependencias no usadas
  • graph:muestra el grafo de dependencias
  • verify:verifica las dependencias locales
  • why:explica por qué se dependen de estos módulos
  • vendor:exporta las dependencias del proyecto al directorio vendor

init

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

El comando init se usa para inicializar un proyecto gomod, su único parámetro es la ruta del módulo, si otros quieren descargar tu dependencia en el futuro, necesitarán usar esta ruta del módulo como referencia. Su regla de nomenclatura generalmente es

domain_name/user_name/repo_name

Por ejemplo, generalmente todos ponen sus proyectos en github, así que puede ser

github.com/jack/gotour

No se recomienda usar símbolos especiales como ruta del módulo. Veamos un ejemplo de uso

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]

El comando tidy limpiará las dependencias no usadas en go.mod, es decir, dependencias que no están referenciadas, y descargará aquellas dependencias que están referenciadas pero no existen. Soporta los siguientes parámetros

  • -v, muestra los módulos de dependencia que se eliminan
  • -e, si ocurre un error durante el proceso, lo ignora y continúa ejecutando
  • -x, muestra el proceso de ejecución
  • -go=version, actualiza la versión de go en el archivo go.mod
  • -compact=version, retiene cualquier checksum adicional necesario para cargar correctamente el grafo de módulos desde la versión principal de Go especificada, y si el comando go de esa versión carga cualquier paquete importado desde diferentes versiones de módulos, causará un error en tidy. Este parámetro raramente se usa, generalmente solo causa errores durante cambios de versión, puedes ver esta respuesta en stackoverflow go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow

Veamos un ejemplo de uso

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]

Aunque el nombre del comando download se traduce como descargar, solo descarga las dependencias a la caché de dependencias local, no modifica el archivo go.mod, su función es pre-descargar dependencias a la caché de archivos local, si quieres descargar una dependencia determinada, se recomienda usar go get o go mod tidy.

Aquí hay algunos ejemplos de uso

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)

Sin ningún parámetro, descargará todas las dependencias que existen en el archivo go.mod pero no existen en la caché de dependencias local, si no hay nada que descargar mostrará

go: no module dependencies to download

edit

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

edit es una interfaz de línea de comandos para modificar el archivo go.mod, generalmente se proporciona para que otros programas la usen, algunos editores IDE proporcionan soporte para gomod usando estos comandos. Soporta los siguientes parámetros

  • -module, modifica la ruta del módulo
  • -go=version, modifica la versión de go esperada
  • -require=path@version, añade una dependencia
  • -droprequire=path@version, elimina una dependencia
  • -exclude=path@version, añade una dependencia de exclusión
  • -dropexclude=path@version, elimina una dependencia de exclusión
  • -replace=old@version=new@version, añade una dependencia de reemplazo
  • -dropreplace=old@version, elimina una dependencia de reemplazo
  • -retract=version, añade un item de reversión de versión
  • -dropretract=version, elimina un item de reversión de versión

También hay otros parámetros para mostrar

  • -print, muestra el contenido del archivo
  • -json, muestra en formato json

Por ejemplo, el siguiente ejemplo

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

El comando graph mostrará el grafo de dependencias del proyecto actual, su legibilidad es muy pobre, y la mayoría de las veces no es para que lo lean los humanos, su resultado generalmente se procesa y luego se muestra de forma visualizada. Cada línea es una dependencia, su formato es el siguiente

referenciador referenciado

Por ejemplo

golearn go@1.21.3

También soporta dos parámetros

  • -go=version, usa la versión de go dada para cargar el grafo de dependencias, su valor no puede ser menor que la versión en el archivo go.mod.
  • -x, muestra los comandos ejecutados durante el proceso.

Veamos un ejemplo simple de uso

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 era una alternativa a gopath antes de que gomod fuera lanzado, cada proyecto go tenía un directorio vendor, que almacenaba cada dependencia del proyecto por separado en el formato domain/user/project, como el hinchado node_module de nodeJs donde cada dependencia del proyecto se almacena por separado, esta forma de gestión de dependencias ahora parece bastante estúpida, pero en ese momento no había una mejor solución, y la razón por la que se retuvo vendor es porque go se adhiere a la promesa de compatibilidad con versiones anteriores, algunos proyectos antiguos incluyendo el código fuente de go todavía pueden estar usando vendor.

Volviendo al tema, vendor es un subcomando de go mod, puede exportar las dependencias globales referenciadas por el módulo actual al directorio vendor.

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

Tiene los siguientes parámetros

  • -o:especifica la ruta de la carpeta de salida
  • -v:muestra cada dependencia
  • -e:no sale cuando ocurre un error, continúa

Veamos un ejemplo, primero usa go list -m all para ver las dependencias referenciadas por el proyecto actual

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

Exportar al directorio vendor actual

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

La estructura del directorio después de exportar es la siguiente

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

El archivo modules.txt es el archivo que describe todas las dependencias, similar al actual go.mod.

verify

sh
$ go help mod verify
usage: go mod verify

Este comando verificará si las dependencias del proyecto han sido modificadas desde que se descargaron localmente. Por ejemplo, si no hay problemas, mostrará all modules verified

sh
$ go mod verify
all modules verified

De lo contrario, reportará dónde ocurrieron los cambios, y terminará el comando con un estado anormal. Por ejemplo

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

Explica por qué este paquete es dependencia, en realidad muestra el grafo de dependencias relacionado con él. Por ejemplo

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

Por defecto solo analiza las importaciones desde main, agregando el parámetro -m puede analizar la situación de importación de cada paquete.

work

El comando work es una herramienta de desarrollo local para la gestión de múltiples módulos 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

El subcomando init se usa para inicializar un workspace, este comando creará un archivo llamado go.work

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

Acepta el parámetro [moddirs] para especificar qué directorios de módulos incluir en la gestión, por ejemplo

bash
$ go work init ./service ./api

use

El subcomando use se usa para agregar directorios de módulos al go.work para su gestión

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.

Acepta [moddirs] como parámetro, y también tiene un -r que indica búsqueda recursiva de módulos en la ruta [moddirs], por ejemplo

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

edit

El subcomando edit tiene la misma función que go mod edit, ambos son interfaces de línea de comandos dejadas para que otras herramientas y scripts operen.

$ 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

Los parámetros son los siguientes

  • -fmt, formatea el archivo go.work

  • -use, -dropuse, agregan y eliminan rutas de módulos

  • -replace=old[@v]=new[@v], -dropreplace=old[@v]=new[@v], usados para agregar y eliminar módulos a reemplazar

  • -go, -toolchain=name, especifican la versión de go, y especifican el toolchain a usar

  • -print, imprime las modificaciones finales, no escribe en el archivo

  • -json, muestra en formato json, no puede existir al mismo tiempo que -print, el tipo de estructura correspondiente es el siguiente

    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
    }

Algunos ejemplos de uso, formatear la salida

bash
$ go work edit -fmt -print
go 1.22.0

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

Salida json

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

sync

El subcomando sync se usa para sincronizar la lista de compilación del workspace de vuelta a los módulos del workspace.

bash
$ go help work sync
usage: go work sync

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

Este proceso ocurre principalmente después de completar el desarrollo local, cuando cada módulo ya ha completado el trabajo de publicación, en este momento al usar sync, actualizará las dependencias en el go.mod de todos los módulos del workspace según las dependencias de cada módulo, para que no tengamos que actualizar manualmente.

vendor

El comando vendor hará una copia de todas las bibliotecas dependientes de los módulos en el workspace al directorio vendor.

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

La función es la misma que go mod vendor, no se hará más explicación.

vet

El comando vet es una herramienta de comprobación de errores estáticos del código fuente del lenguaje go, como las herramientas lint de otros lenguajes, como 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.

Primero veamos un ejemplo simple, tenemos el siguiente código fuente

sh
$ cat main.go
package main

import "fmt"

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

Ejecuta go vet sin ningún parámetro en el mismo directorio

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

vet reportará qué archivo qué línea tiene qué problema. Soporta flags de compilación como parámetros, como -n y -x, soporta paquetes, carpetas, nombres de archivo como parámetros.

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

A través del siguiente comando puedes ver sus parámetros más detallados y explicaciones.

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

El comando go tool vet no se puede usar directamente para comprobar el código, debes usar go vet. El [vet flag] en los parámetros de go vet soporta configurar analizadores de código, los valores disponibles son los siguientes

asmdecl      comprueba si los archivos ensamblador no coinciden con las declaraciones go
assign       comprueba si hay variables inútiles
atomic       comprueba si se rompe la atomicidad al usar sync/atomic
bools        comprueba si se usan incorrectamente operadores lógicos
buildtag     comprueba build tag
cgocall      comprueba comportamientos que violan las reglas de paso de punteros cgao
composites   comprueba estructuras compuestas no inicializadas, como map, chan
copylocks    comprueba si se ha producido una copia de valor de un bloqueo
directive    comprueba las directivas del toolchain go
errorsas     comprueba si se pasa un tipo no puntero o no error a errors.As
framepointer comprueba si el código ensamblador optimizado borra el frame pointer antes de guardarlo
httpresponse comprueba si se usa incorrectamente httpresponse
ifaceassert  comprueba las aserciones de tipo de interfaz a interfaz
loopclosure  problemas de referencia de variables de bucle
lostcancel   context.WithCancel no usa la función cancel
nilfunc      comprueba si hay comparaciones inútiles entre funciones y nil
printf       comprueba si los parámetros de formato de printf son correctos
shift        comprueba si hay desplazamientos iguales o superiores al ancho de enteros
sigchanyzer  comprueba chan os.Signal sin búfer
slog         comprueba llamadas de registro estructurado no válidas
stdmethods   comprueba si las firmas de métodos de interfaz conocidos son correctas
stringintconv comprueba la conversión de entero a cadena
structtag    comprueba si los tags de estructura son correctos
testinggoroutine comprueba si se usa goroutine para llamar a testing.Fatal en pruebas
tests        comprueba errores comunes de uso en pruebas y ejemplos
timeformat   comprueba si el formato de (time.Time).Format o time.Parse es correcto
unmarshal    pasa tipos no puntero o no interfaz a unmarshal
unreachable  comprueba código inalcanzable
unsafeptr    comprueba conversiones incorrectas de uintptr a unsafe.Pointer
unusedresult comprueba valores de retorno de función no usados

Todos son analizadores que analizan un punto específico, por ejemplo el analizador timeformat comprueba si el formato de llamada de time.Format cumple con la sintaxis correcta. Por defecto, todos los analizadores anteriores están habilitados, para habilitar individualmente puedes usar el siguiente formato

sh
$ go vet -timeformat main.go

Para deshabilitar individualmente

sh
$ go vet -timeformat=false main.go

El código fuente de estos analizadores está ubicado en cmd/vendor/golang.org/x/tools/go/analysis/passes, cada analizador es un problema común en el que el lenguaje go puede caer fácilmente, por lo que se recomienda encarecidamente usar el comando vet para comprobar tu código. Además de estos, también soporta algunos analizadores externos, como shadows, que es responsable de detectar problemas de ocultamiento de variables nombradas con variables cortas, como es externo, necesitas descargarlo con go install

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

El formato de uso es el siguiente

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.

El comando test es el comando que proporciona la funcionalidad de pruebas en el toolchain del lenguaje go, esta funcionalidad es bastante importante, para un software, pruebas completas son indispensables. Aquí solo se presenta brevemente cómo usar el comando test, para aprender más sobre pruebas, visita: 测试

Además de soportar los parámetros de compilación del comando build, test también soporta los siguientes parámetros

  • -args, parámetros de entrada del programa
  • -c, compila el archivo binario de pruebas del paquete actual al directorio actual pero no lo ejecuta, nombrado como pkg.test
  • -exec, ejecuta algunos otros comandos antes de que comiencen las pruebas
  • -json, el estilo de salida de las pruebas se convierte en json
  • -o, especifica el nombre de ruta del archivo binario de pruebas

También soporta muchos testflag, usa el comando help para ver todos los testflag

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

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

Se presentan algunos comúnmente usados

  • -v, muestra el resultado de cada caso de prueba.
  • -timeout duration, tiempo de espera de ejecución de pruebas
  • -skip regexp, salta los casos de prueba especificados
  • -short, hace que los casos de prueba que tardan mucho tiempo en ejecutar acorten su tiempo de ejecución
  • -shuffle, desordena el orden de ejecución de todos los casos de prueba
  • -run regexp, ejecuta los casos de prueba especificados
  • -list regexp, lista cada caso de prueba
  • -cpu 1,2,4, especifica el número de cpu
  • -count n, especifica cuántas veces se ejecuta cada caso de prueba

El uso más simple es, sin ningún parámetro, ejecutará todos los casos de prueba en el paquete actual, y mostrará los resultados.

sh
$ ls *_test.go
hello_test.go

$ go test
PASS
ok      golearn 0.522s

Especificar un archivo de prueba determinado

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

Agregando el parámetro -v puedes ver una salida más detallada, es bastante común.

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

Especificar un caso de prueba determinado

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

Al realizar pruebas, el comando test tiene dos modos, primero hablemos del modo de carpeta, cuando se ejecuta el comando test sin el parámetro package, se ejecutará en modo de carpeta, por ejemplo los siguientes comandos

sh
$ go test
$ go test -v

En este modo, se deshabilita la caché de pruebas. El otro modo es el modo de lista, cuando el parámetro package no está vacío, se realizarán pruebas en modo de lista, la diferencia con el anterior es si se habilita la caché de pruebas. Por ejemplo los siguientes

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

En el modo de lista, go compilará los archivos de prueba de cada paquete bajo el paquete especificado en archivos binarios y los ejecutará, para evitar ejecutar pruebas repetidamente, go por defecto almacenará los resultados en caché, no se recompilará en la segunda ejecución. El uso de los siguientes parámetros habilitará la caché por defecto

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

Usando otros parámetros además de estos se puede deshabilitar la caché, el método recomendado por el oficial es usar -count=1 para deshabilitar la caché. Por ejemplo

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

Directivas

A diferencia de los comandos, las directivas de go existen en forma de código duro en los archivos fuente, tienen otro nombre más técnico: pragma directives.

El compilador y el linker cambiarán su comportamiento debido a ellas, logrando así el efecto de controlar la compilación, es algo similar a las macros en c, por supuesto, no todas las directivas se usan para afectar la compilación, algunas se usan para comportamientos funcionales, por ejemplo la directiva generate generalmente se usa para la funcionalidad de generación de código. Estas directivas generalmente existen en forma de comentarios, y con el prefijo //go:, sin ningún espacio en el medio, por ejemplo la directiva //go:generate. Todos los tipos de directivas se dividen en dos categorías

  • Directivas funcionales, estas son directivas funcionales proporcionadas por go que se pueden usar libremente, como generate, embed, build.
  • Directivas del compilador, estas directivas se deben usar con precaución, su uso indiscriminado puede causar resultados impredecibles.

Excepto las directivas funcionales, la mayoría de las directivas solo pueden actuar sobre firmas de funciones. Para las directivas del compilador puedes ejecutar el comando go doc compile para ver sus directivas. Para todas las directivas, puedes encontrar información sobre ellas en 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]

La directiva generate como su nombre indica tiene que ver con la generación, generalmente su función es ejecutar comandos que generarán código y actualizarán el código fuente, pero en realidad puede ejecutar cualquier comando. Además, la directiva generate es diferente de otras directivas, tiene un comando dedicado que se puede usar para ejecutar todas las directivas generate en los archivos fuente. Puede tomar nombres de archivo o nombres de paquetes como parámetros de entrada para indicar qué archivos ejecutar las directivas generate, estos son sus otros parámetros.

  • -run=regex , ejecuta las directivas generate especificadas
  • -skip=regex, salta las directivas generate especificadas
  • -n, imprime los comandos que se ejecutarán
  • -x, imprime los comandos ejecutados durante el proceso
  • -v, muestra los archivos procesados

Además, los comandos ejecutados en la directiva generate también soportan los siguientes parámetros integrados

  • $GOARCH, arquitectura de cpu
  • $GOOS, sistema operativo
  • $GOFILE, nombre de archivo
  • $GOLINE, número de línea
  • $GOPACKAGE, nombre del paquete
  • $GOROOT, go root
  • $DOLLAR, símbolo de dólar
  • $PATH, variable de entorno path

Veamos un ejemplo, no hay código, solo un comentario

go
package main

//go:generate echo "hello world!"

Ejecuta el comando

$ go generate .
hello world!

Este ejemplo ejecuta un comando go

go
package main

//go:generate go version

Ejecuta el comando

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

La directiva generate se puede usar para ejecutar cualquier comando, como swagger para generar documentación de API, o Wire para generar código IOC. Sin embargo, esta directiva no es adecuada para ejecutar comandos particularmente complejos, es adecuada para ejecutar comandos cortos, si hay necesidades complejas puedes usar scripts o makefile para reemplazar.

embed

La directiva embed es nueva en 1.16, su función es que puede empaquetar archivos estáticos junto en el archivo binario, como plantillas HTML. Su formato es el siguiente

go
//go:embed pattern

pattern puede ser una expresión glob, o una carpeta o un archivo específico. Veamos un ejemplo

go
package main

import "embed"

//go:embed *
var static embed.FS

La directiva embed requiere que esté encima de una variable global de tipo embed.Fs, ten en cuenta que debe ser una variable global, y para usarla debes importar el paquete embed, en este ejemplo, * representa que todos los archivos en la carpeta actual se empaquetarán en el archivo binario, pero no permitirá que existan carpetas que empiecen por ..

El siguiente ejemplo muestra cómo leer contenido de archivos embebidos

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

Solo tiene tres métodos, usarlo es similar a un sistema de archivos normal, y como implementa la interfaz io/Fs, también se puede pasar como objeto 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)

El siguiente ejemplo muestra cómo embeber archivos html a través de la directiva embed, y acceder a través de un servicio 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)
}

El resultado del acceso es el siguiente

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>

La directiva embed también soporta que el tipo de variable global pueda ser []byte, por ejemplo el siguiente ejemplo

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

El efecto que logra es similar al ejemplo anterior.

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

En la sección build-control de compilación, se explicó cómo usar la directiva // +build para controlar el comportamiento de compilación. La directiva //go:build es nueva en 1.17, con la intención de reemplazar la directiva anterior, pero ahora en 1.21 todavía no ha sido reemplazada, probablemente coexistirán en el futuro, sobre esta nueva directiva, la documentación oficial también tiene una introducción: build constraints. Su función no es diferente de la anterior, pero la sintaxis es más estricta, soporta expresiones booleanas, veamos un ejemplo

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

package pkg_name

Esta forma es mucho más legible que la anterior.

line

La directiva line afectará el número de línea, número de columna, y el nombre de archivo de la siguiente línea, su función se limita a esto, la mayoría de las veces puede ser usada para depurar errores, etc. Por ejemplo, cuando ocurre un error, cambiará la información que muestra el compilador.

go
package main

var a undefinedType

func main() {

}

Normalmente, el compilador mostrará

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

Pero si se usa la directiva line, es diferente

go
package main

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

func main() {

}

Entonces su salida será

abc.go:10:106: undefined: undefinedType

Y por razones históricas, la directiva line es también la única directiva cuyo uso es diferente de otras directivas. Su formato es

go
//line filename:line:column

Se puede ver que no necesita go: como prefijo.

linkname

Esta operación de directiva se puede usar para enlazar funciones o variables globales de otros paquetes, incluso si son tipos privados, esta operación aparece frecuentemente en la biblioteca estándar especialmente en runtime, hay algunas funciones que no tienen cuerpo de función, esto se logra a través de esta forma, otra parte de las funciones con cuerpo vacío se implementan en ensamblador. Veamos su uso, el formato de uso es el siguiente

go
//go:linkname nombre del tipo enlazado tipo a enlazar

Y antes de usar, debes importar el paquete unsafe. Veamos un ejemplo simple de enlace a un tipo privado en la biblioteca estándar

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

Salida

15395306441938000233

Enlaza la función privada runtime.memhash con nuestra propia función declarada, esta función no tiene cuerpo de función, solo una firma, solo sirve como portador. La función de memhash es dado un puntero, semilla de hash, y offset de memoria, calcular el valor hash basándose en la memoria. Este proceso de enlace se completa en tiempo de compilación.

Si no es de la biblioteca estándar, la situación es algo diferente, por ejemplo hay una función test en el paquete example, antes de enlazar primero debes importar anónimamente este paquete.

go
package example

// Un tipo privado, no accesible desde fuera.
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())
}

Salida

a

Se puede ver que el enlace ha sido exitoso, este método puede evitar el sistema de módulos de go y hacer lo que quiera, pero no se recomienda su uso a gran escala, a menos que sepas lo que estás haciendo.

noinline

La directiva noinline indica que una función prohíbe la optimización de inline, incluso si es muy simple. Veamos un ejemplo simple

go
package main

func val() string {
  return "val"
}

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

val es una función muy simple, su función es devolver un literal de cadena, como es demasiado simple y su resultado siempre es predecible, al compilar será optimizada por el compilador a la siguiente forma

go
package main

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

Veamos su forma en ensamblador, se puede ver que no se encontró la llamada a la función 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

Ahora agreguemos la directiva noinline

go
package main

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

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

Veamos su forma en ensamblador

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

Esta vez puedes ver muy claramente la llamada main.val, y esta es exactamente la función que noinline desempeña, evitar el inline de funciones durante la optimización del compilador.

nospilit

La directiva nospilit sirve para saltar la detección de desbordamiento de pila. Como el modelo de programación concurrente de go es de programación preventiva, supongamos que una función ejecutará código muy de bajo nivel, no es apropiado que otras corrutinas sean preventadas al llamar a esta función, se puede usar esta directiva para indicarlo.

go
//go:nosplit
func nospilitFn()

Después de usar esta directiva, tampoco se realizará el crecimiento de pila.

noescape

noescape, por su nombre puedes adivinar fácilmente que tiene que ver con el escape, su función es indicar que la función actual no tendrá comportamiento de escape de memoria, después de ejecutarse todos los recursos son recuperados, y esta función debe tener solo una firma sin cuerpo de función, en este caso generalmente la implementación de la función es en ensamblador.

Por ejemplo, memhash usada anteriormente usará esta directiva

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

De esta manera, el compilador no realizará análisis de escape en ella, la premisa es que debes garantizar que no ocurrirá escape, si ocurre, no se sabe qué consecuencias habrá.

uintptrescapes

La directiva uintptrescapes indica que el parámetro de tipo uinptr en esta función ha sido convertido a un valor de puntero y ha escapado al heap, y debe mantenerse vivo. Esta directiva generalmente se usa para algunas llamadas de sistema de bajo nivel, la mayoría de las veces no necesitas conocerla.

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

Anteriormente debería haber una directiva notinheaps usada para indicar que un tipo no permite asignar memoria en el heap, no se sabe en qué versión fue eliminada.

norace

La directiva norace indica que el acceso a memoria de una función ya no necesita análisis de race condition, generalmente se usa cuando se ejecuta código de bajo nivel que no es adecuado para análisis de race condition.

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

TIP

También hay algunas directivas que están restringidas para ser usadas solo por el paquete runtime, externamente no se pueden usar, involucran cosas más profundas, para conocerlas puedes ver Runtime-only compiler directives.

Golang editado por www.golangdev.cn