Moduli
Ogni linguaggio moderno ha un proprio strumento di gestione delle dipendenze maturo, come Gradle per Java, Pip per Python, Npm per NodeJs, ecc. Un buon strumento di gestione delle dipendenze può risparmiare molto tempo agli sviluppatori e migliorare l'efficienza dello sviluppo. Tuttavia, Go non aveva una soluzione matura per la gestione delle dipendenze nelle prime fasi. Tutto il codice era memorizzato nella directory GOPATH, il che era molto sfavorevole per i progetti ingegneristici, con versioni caotiche e dipendenze difficili da gestire. Per risolvere questo problema, gli sviluppatori delle varie comunità hanno gareggiato, creando confusione per un periodo. Durante questo periodo sono emersi alcuni eccellenti strumenti come Vendor, fino a quando Go 1.11 ha finalmente lanciato Go Mod, lo strumento ufficiale di gestione delle dipendenze, ponendo fine alla situazione caotica precedente e migliorando continuamente negli aggiornamenti successivi. Oggi, mentre scrivo questo articolo, la versione di Go è arrivata alla 1.20. Quasi tutti i progetti Go utilizzano Go Mod, quindi in questo articolo verrà introdotto solo Go Mod. Il team ufficiale ha anche scritto una documentazione molto dettagliata sui moduli Go: Go Modules Reference.
Scrittura di un Modulo
Go Module è essenzialmente basato su VCS (Version Control System). Quando scarichi una dipendenza, stai effettivamente eseguendo un comando VCS, come git. Quindi, se vuoi condividere la libreria che hai scritto, devi solo fare quanto segue:
- Il repository del codice sorgente deve essere pubblicamente accessibile e VCS deve essere uno dei seguenti:
- git
- hg (Mercurial)
- bzr (Bazaar)
- svn
- fossil
- Deve essere un progetto go mod conforme alle specifiche
- Deve essere conforme alle specifiche di versionamento semantico
Quindi devi solo sviluppare normalmente utilizzando VCS, creare tag conformi agli standard per le tue versioni specifiche, e gli altri potranno scaricare la libreria che hai scritto tramite il nome del modulo. Di seguito verrà dimostrato attraverso un esempio i passaggi per lo sviluppo di un modulo.
Repository di esempio: 246859/hello: say hello (github.com)
Preparazione
Prima di iniziare, assicurati che la tua versione sia sufficientemente completa per supportare go mod (go >= 1.17) e che Go Module sia abilitato. Verifica se è abilitato con il seguente comando:
$ go env GO111MODULESe non è abilitato, abilitalo con il seguente comando:
$ go env -w GO111MODULE=onCreazione
Per prima cosa, hai bisogno di un repository di codice sorgente accessibile pubblicamente. Ci sono molte scelte, ma consiglio Github. Crea un nuovo progetto lì, chiamalo hello. Anche se non ci sono restrizioni speciali sul nome del repository, si consiglia di non utilizzare caratteri speciali, poiché ciò influenzerà il nome del modulo.

Dopo la creazione, puoi vedere che l'URL del repository è https://github.com/246859/hello, e il nome del modulo Go corrispondente è github.com/246859/hello.

Poi clonalo localmente e inizializza il modulo tramite il comando go mod init:
$ git clone git@github.com:246859/hello.git
Cloning into 'hello'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (5/5), done.
$ cd hello && go mod init github.com/246859/hello
go: creating new go.mod: module github.com/246859/helloScrittura
Poi puoi iniziare lo sviluppo. La sua funzionalità è molto semplice, ha solo una funzione:
// hello.go
package hello
import "fmt"
// Hello restituisce un messaggio di saluto
func Hello(name string) string {
if name == "" {
name = "world"
}
return fmt.Sprintf("hello %s!", name)
}Scrivi anche un file di test per il test unitario:
// hello_test.go
package hello_test
import (
"testing"
"fmt"
"github.com/246859/hello"
)
func TestHello(t *testing.T) {
data := "jack"
expected := fmt.Sprintf("hello %s!", data)
result := hello.Hello(data)
if result != expected {
t.Fatalf("expected result %s, but got %s", expected, result)
}
}Successivamente, scrivi un programma a riga di comando per outputtare hello. Anche la sua funzionalità è molto semplice. Per i programmi a riga di comando, secondo le specifiche, dovrebbero essere creati in cmd/app_name/ del progetto, quindi il file del programma hello si trova nella directory cmd/hello/, poi scrivi il codice correlato:
// cmd/hello/main.go
package main
import (
"flag"
"github.com/246859/hello"
"os"
)
var name string
func init() {
flag.StringVar(&name, "name", "world", "nome per dire hello")
}
func main() {
flag.Parse()
msg := hello.Hello(name)
_, err := os.Stdout.WriteString(msg)
if err != nil {
os.Stderr.WriteString(err.Error())
}
}Test
Dopo aver scritto, formatta il codice sorgente e testa:
$ go fmt && go vet ./...
$ go test -v .
=== RUN TestHello
--- PASS: TestHello (0.00s)
PASS
ok github.com/246859/hello 0.023sEsegui il programma a riga di comando:
$ go run ./cmd/hello -name jack
hello jack!Documentazione
Infine, devi scrivere un README chiaro e conciso per questa libreria, in modo che gli altri sviluppatori sappiano come usarla a prima vista:
# hello
just say hello
## Install
import code
```bash
go get github.com/246859/hello@latest
```
install cmd
```bash
go install github.com/246859/hello/cmd/hello@latest
```
## Example
Here's a simple example as follows:
```go
package main
import (
"fmt"
"github.com/246859/hello"
)
func main() {
result := hello.Hello("jack")
fmt.Println(result)
}
```Questo è un documento README molto semplice. Puoi anche arricchirlo autonomamente.
Upload
Quando tutto il codice è stato scritto e testato, puoi inviare le modifiche e spingerle al repository remoto:
$ git add go.mod hello.go hello_test.go cmd/ example/ README.md
$ git commit -m "chore(mod): mod init" go.mod
[main 5087fa2] chore(mod): mod init
1 file changed, 3 insertions(+)
create mode 100644 go.mod
$ git commit -m "feat(hello): complete Hello func" hello.go
[main 099a8bf] feat(hello): complete Hello func
1 file changed, 11 insertions(+)
create mode 100644 hello.go
$ git commit -m "test(hello): complete hello testcase" hello_test.go
[main 76e8c1e] test(hello): complete hello testcase
1 file changed, 17 insertions(+)
create mode 100644 hello_test.go
$ git commit -m "feat(hello): complete hello cmd" cmd/hello/
[main a62a605] feat(hello): complete hello cmd
1 file changed, 22 insertions(+)
create mode 100644 cmd/hello/main.go
$ git commit -m "docs(example): add hello example" example/
[main 5c51ce4] docs(example): add hello example
1 file changed, 11 insertions(+)
create mode 100644 example/main.go
$ git commit -m "docs(README): update README" README.md
[main e6fbc62] docs(README): update README
1 file changed, 27 insertions(+), 1 deletion(-)Ci sono solo sei commit in totale, non molti. Dopo il commit, crea un tag per l'ultimo commit:
$ git tag v1.0.0
$ git tag -l
v1.0.0
$ git log --oneline
e6fbc62 (HEAD -> main, tag: v1.0.0, origin/main, origin/HEAD) docs(README): update README
5c51ce4 docs(example): add hello example
a62a605 feat(hello): complete hello cmd
76e8c1e test(hello): complete hello testcase
099a8bf feat(hello): complete Hello func
5087fa2 chore(mod): mod init
1f422d1 Initial commitInfine, spingi al repository remoto:
$ git push --tags
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 16 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (21/21), 2.43 KiB | 1.22 MiB/s, done.
Total 21 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), done.
To github.com:246859/hello.git
1f422d1..e6fbc62 main -> main
* [new tag] v1.0.0 -> v1.0.0Dopo aver spinto, crea anche una release per esso (un tag è sufficiente, la release è solo per conformarsi alle specifiche di github):

In questo modo, la scrittura del modulo è completata. Quanto sopra è un processo di base per lo sviluppo di un modulo. Gli altri sviluppatori possono ora introdurre il codice o installare strumenti a riga di comando tramite il nome del modulo.
Utilizzo
Utilizza go get per importare la libreria:
$ go get github.com/246859/hello@latest
go: downloading github.com/246859/hello v1.0.0
go: added github.com/246859/hello v1.0.0Installa il programma a riga di comando tramite go install:
$ go install github.com/246859/hello/cmd/hello@latest && hello -name jack
hello jack!Oppure usa go run per eseguirlo direttamente:
$ go run -mod=mod github.com/246859/hello/cmd/hello -name jack
hello jack!Dopo che una libreria è stata importata, Go Package creerà automaticamente una pagina per essa. Questo processo è automatico e non richiede alcun lavoro da parte dello sviluppatore. Ad esempio, la libreria hello ha una pagina di documentazione dedicata, come mostrato nella figura seguente:

Per maggiori dettagli sul caricamento dei moduli, consulta Add a package.
Per informazioni su come eliminare le informazioni del modulo, consulta Removing a package.
Impostazione del Proxy
Go non ha un repository centrale come Maven Repo, PyPi o NPM, ma ha un repository proxy ufficiale: Go modules services (golang.org). Cache i moduli scaricati dagli sviluppatori in base alla versione e al nome del modulo. Tuttavia, poiché i suoi server sono distribuiti all'estero, la velocità di accesso non è amichevole per gli utenti nazionali. Quindi dobbiamo modificare l'indirizzo proxy del modulo predefinito. Attualmente in Cina ci sono i seguenti provider migliori:

Qui scegliamo il proxy di Qiniu Cloud. Esegui il seguente comando per modificare il proxy Go, dove direct indica di bypassare la cache del proxy e accedere direttamente al repository del codice sorgente se il download del proxy fallisce:
$ go env -w GOPROXY=https://goproxy.cn,directDopo aver modificato con successo il proxy, scaricare le dipendenze in futuro sarà molto veloce.
Download delle Dipendenze
Dopo aver modificato il proxy, proviamo a installare una dipendenza di terze parti. Go ha un sito web dedicato per la查询 delle dipendenze: Go Packages.
Utilizzo del Codice
Cerca il famoso framework Web Gin al suo interno:

Appariranno molti risultati di ricerca. Quando si utilizzano dipendenze di terze parti, è necessario decidere se adottare la dipendenza in base al numero di citazioni e al tempo di aggiornamento. Qui scegliamo direttamente il primo:

Dopo essere entrati nella pagina corrispondente, si può vedere che questa è una pagina di documentazione della dipendenza, con molte informazioni dettagliate su di essa. Puoi anche venire qui per consultare la documentazione in seguito.

Qui devi solo copiare il suo indirizzo, poi utilizzare il comando go get nel progetto creato in precedenza. Il comando è il seguente:
$ go get github.com/gin-gonic/ginDurante il processo verranno scaricate molte dipendenze. Finché non ci sono errori, il download è riuscito:
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.8.0
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.0
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.11.2
go: added github.com/goccy/go-json v0.10.0
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.0.9
go: added github.com/leodido/go-urn v1.2.1
go: added github.com/mattn/go-isatty v0.0.17
go: added github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.6
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.9
go: added golang.org/x/arch v0.0.0-20210923205945-b76863e36670
go: added golang.org/x/crypto v0.5.0
go: added golang.org/x/net v0.7.0
go: added golang.org/x/sys v0.5.0
go: added golang.org/x/text v0.7.0
go: added google.golang.org/protobuf v1.28.1
go: added gopkg.in/yaml.v3 v3.0.1Dopo il completamento, visualizza il file go.mod:
$ cat go.mod
module golearn
go 1.20
require github.com/gin-gonic/gin v1.9.0
require (
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)Puoi vedere che ci sono molte cose in più rispetto a prima, e scoprirai anche che c'è un nuovo file chiamato go.sum nella directory:
$ ls
go.mod go.sum main.goLasciamo stare per ora. Modifica il file main.go con il seguente codice:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
gin.Default().Run()
}Esegui nuovamente il progetto:
$ go run golearn
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080Quindi, con una sola riga di codice, è stato eseguito un server Web molto semplice. Quando non hai più bisogno di una certa dipendenza, puoi anche usare il comando go get per eliminarla. Qui prendiamo come esempio l'eliminazione di Gin:
$ go get github.com/gin-gonic/gin@none
go: removed github.com/gin-gonic/gin v1.9.0Aggiungendo @none dopo l'indirizzo della dipendenza, puoi eliminare quella dipendenza. Il risultato indica anche che l'eliminazione è riuscita. A questo punto, visualizzando nuovamente il file go.mod, non c'è più la dipendenza Gin:
$ cat go.mod | grep github.com/gin-gonic/ginQuando è necessario aggiornare all'ultima versione, puoi aggiungere il suffisso @latest, oppure puoi cercare autonomamente i numeri di versione Release disponibili:
$ go get -u github.com/gin-gonic/gin@latestInstallazione di Programmi a Riga di Comando
Il comando go install scaricherà la dipendenza di terze parti localmente e la compilerà in un file binario. Grazie alla velocità di compilazione di Go, questo processo di solito non richiede molto tempo. Poi Go lo memorizzerà nella directory $GOPATH/bin o $GOBIN, in modo che il file binario possa essere eseguito globalmente (a前提是 che tu abbia aggiunto questi percorsi alle variabili d'ambiente).
TIP
Quando si utilizza il comando install, è necessario specificare il numero di versione.
Ad esempio, scarica delve, un debugger scritto in Go:
$ go install github.com/go-delve/delve/cmd/dlv@latest
go: downloading github.com/go-delve/delve v1.22.1
go: downloading github.com/cosiner/argv v0.1.0
go: downloading github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d
go: downloading github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62
go: downloading github.com/google/go-dap v0.11.0
go: downloading github.com/hashicorp/golang-lru v1.0.2
go: downloading golang.org/x/arch v0.6.0
go: downloading github.com/cpuguy83/go-md2man/v2 v2.0.2
go: downloading go.starlark.net v0.0.0-20231101134539-556fd59b42f6
go: downloading github.com/cilium/ebpf v0.11.0
go: downloading github.com/mattn/go-runewidth v0.0.13
go: downloading github.com/russross/blackfriday/v2 v2.1.0
go: downloading github.com/rivo/uniseg v0.2.0
go: downloading golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
$ dlv -v
Error: unknown shorthand flag: 'v' in -v
Usage:
dlv [command]
Available Commands:
attach Attach to running process and begin debugging.
completion Generate the autocompletion script for the specified shell
connect Connect to a headless debug server with a terminal client.
core Examine a core dump.
dap Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
debug Compile and begin debugging main package in current directory, or the package specified.
exec Execute a precompiled binary, and begin a debug session.
help Help about any command
test Compile test binary and begin debugging program.
trace Compile and begin tracing program.
version Prints version.
Additional help topics:
dlv backend Help about the --backend flag.
dlv log Help about logging flags.
dlv redirect Help about file redirection.
Use "dlv [command] --help" for more information about a command.Gestione dei Moduli
Tutto il contenuto sopra riguarda solo l'uso di base di Go Mod, ma in realtà imparare Go Mod non è assolutamente sufficiente solo con questo. La definizione ufficiale di modulo è: un insieme di pacchetti contrassegnati da versione. Nella definizione sopra, il pacchetto dovrebbe essere un concetto ormai familiare, mentre la versione deve seguire il numero di versione semantico, definito nel formato v(major).(minor).(patch), ad esempio la versione di Go v1.20.1, la versione principale è 1, la versione minore è 20, la versione patch è 1, insieme formano v1.20.1. Di seguito è riportata una spiegazione più dettagliata:
major: Quando la versione major cambia, significa che il progetto ha subito modifiche incompatibili. È probabile che i progetti della vecchia versione non possano essere eseguiti normalmente dopo l'aggiornamento alla nuova versione.minor: Quando la versioneminorcambia, significa che il progetto ha aggiunto nuove funzionalità, basandosi solo sulle funzionalità della versione precedente.patch: Quando la versionepatchcambia, significa che sono stati risolti solo bug, senza aggiungere nuove funzionalità.
Comandi Comuni
| Comando | Descrizione |
|---|---|
go mod download | Scarica i pacchetti delle dipendenze del progetto corrente |
go mod edit | Modifica il file go.mod |
go mod graph | Output del grafico delle dipendenze del modulo |
go mod init | Inizializza go mod nella directory corrente |
go mod tidy | Pulisce i moduli del progetto |
go mod verify | Verifica la legittimità delle dipendenze del progetto |
go mod why | Spiega dove il progetto utilizza le dipendenze |
go clean -modcache | Utilizzato per eliminare la cache delle dipendenze del progetto |
go list -m | Elenca i moduli |
Vai a go mod cmd per maggiori informazioni sui comandi
Archiviazione dei Moduli
Quando si utilizza Go Mod per la gestione del progetto, la cache dei moduli è memorizzata per impostazione predefinita nella directory $GOPATH/pkg/mod. È anche possibile modificare $GOMODCACHE per specificare un'altra posizione:
$ go env -w GOMODCACHE=il tuo percorso di cache dei moduliTutti i progetti Go Module sulla stessa macchina condividono la cache in questa directory. La cache non ha limiti di dimensione e non viene eliminata automaticamente. I file sorgente delle dipendenze decompressi nella cache sono in sola lettura. Per svuotare la cache, esegui il seguente comando:
$ go clean -modcacheNella directory $GOMODCACHE/cache/download sono memorizzati i file originali delle dipendenze, inclusi file hash, pacchetti compressi originali, ecc. Ad esempio:
$ ls $(go env GOMODCACHE)/cache/download/github.com/246859/hello/@v -1
list
v1.0.0.info
v1.0.0.lock
v1.0.0.mod
v1.0.0.zip
v1.0.0.ziphashLe dipendenze decompresse sono organizzate come mostrato di seguito, ovvero il codice sorgente del modulo specificato:
$ ls $(go env GOMODCACHE)/github.com/246859/hello@v1.0.0 -1
LICENSE
README.md
cmd/
example/
go.mod
hello.go
hello_test.goSelezione della Versione
Go segue il principio di selezione della versione minima quando sceglie le versioni delle dipendenze. Di seguito è riportato un esempio dal sito ufficiale. Il modulo principale fa riferimento alla versione 1.2 del modulo A e alla versione 1.2 del modulo B. Allo stesso tempo, la versione 1.2 del modulo A fa riferimento alla versione 1.3 del modulo C, e la versione 1.2 del modulo B fa riferimento alla versione 1.4 del modulo C. Inoltre, le versioni 1.3 e 1.4 del modulo C fanno entrambe riferimento alla versione 1.2 del modulo D. Secondo il principio della versione minima utilizzabile, Go sceglierà infine le versioni A1.2, B1.2, C1.4 e D1.2. Quelle evidenziate in azzurro chiaro indicano i file go.mod caricati, quelle selezionate con riquadro indicano le versioni finalmente scelte.
Il sito ufficiale fornisce anche altri esempi, che hanno più o meno lo stesso significato.
go.mod
Ogni volta che si crea un progetto Go Mod, viene generato un file go.mod. Pertanto, è molto necessario familiarizzare con il file go.mod, anche se nella maggior parte dei casi non è necessario modificarlo manualmente:
module golearn
go 1.20
require github.com/gin-gonic/gin v1.9.0
require (
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)Nel file si può scoprire che la maggior parte degli indirizzi delle dipendenze contengono parole come github. Questo perché Go non ha un repository di dipendenze pubblico. La maggior parte dei progetti open source sono ospitati su Github. Alcuni sono costruiti autonomamente, come google.golang.org/protobuf, golang.org/x/crypto. Normalmente, questa serie di URL è anche il nome del modulo del progetto Go, il che crea un problema: gli URL non distinguono tra maiuscole e minuscole, ma le cartelle che memorizzano le dipendenze le distinguono. Quindi go get github.com/gin-gonic/gin e go get github.com/gin-gonic/Gin fanno riferimento alla stessa dipendenza, ma il percorso di archiviazione locale è diverso. Quando si verifica questa situazione, Go non tratta direttamente le lettere maiuscole come percorso di archiviazione, ma le converte in !lettereminuscole. Ad esempio, github.com/BurntSushi verrà infine convertito in github.com/!burnt!sushi.
module
La parola chiave module dichiara il nome del modulo del progetto corrente. In un file go.mod può apparire solo una volta la parola chiave module. Nell'esempio:
module golearnRappresenta che il nome del modulo corrente è golearn. Ad esempio, aprendo il file go.mod della dipendenza Gin, puoi scoprire il suo nome module:
module github.com/gin-gonic/ginIl nome del modulo di Gin è l'indirizzo utilizzato quando si scarica la dipendenza. Questo è anche il formato del nome del modulo consigliato normalmente: dominio/utente/nome-repository.
TIP
Un punto da notare è che quando la versione principale è maggiore di 1, il numero della versione principale deve essere riflesso nel nome del modulo. Ad esempio:
github.com/my/exampleSe la versione viene aggiornata a v2.0.0, il nome del modulo deve essere modificato come segue:
github.com/my/example/v2Se il progetto originale fa riferimento alla vecchia versione e la nuova versione non viene differenziata, quando si importano le dipendenze, poiché i percorsi sono coerenti, gli utenti non possono distinguere le modifiche incompatibili causate dai cambiamenti della versione principale, il che potrebbe causare errori nel programma.
Deprecation
Commenta Deprecated all'inizio della riga sopra module per indicare che il modulo è deprecato. Ad esempio:
// Deprecated: use example.com/mod/v2 instead.
module example.com/modgo
La parola chiave go indica la versione di Go utilizzata per scrivere il progetto corrente. Il numero di versione deve seguire le regole di versionamento semantico. A seconda della versione di Go, Go Mod si comporterà in modo diverso. Di seguito è riportato un semplice esempio. Per le versioni di Go disponibili, consulta autonomamente il sito ufficiale:
go 1.20require
La parola chiave require indica che è stata importata una dipendenza esterna. Ad esempio:
require github.com/gin-gonic/gin v1.9.0Il formato è require nome-modulo versione. Quando ci sono più importazioni, possono essere racchiuse tra parentesi:
require (
github.com/bytedance/sonic v1.8.0 // indirect
)Quelle con il commento // indirect indicano che la dipendenza non è stata importata direttamente dal progetto corrente. Potrebbe essere che la dipendenza importata direttamente dal progetto abbia importato questa dipendenza, quindi per il progetto corrente è un'importazione indiretta. Come menzionato in precedenza, quando la versione principale cambia, deve essere riflessa nel nome del modulo. I moduli che non seguono questa regola sono chiamati moduli non conformi. Quando si usa require, verrà aggiunto il commento incompatible:
require example.com/m v4.1.2+incompatibleVersioni Pseudo
Nel file go.mod sopra, si può scoprire che alcune versioni delle dipendenze non sono numeri di versione semantici, ma una stringa incomprensibile. Questo è in realtà il CommitID della versione corrispondente. Le versioni semantiche si riferiscono generalmente a un determinato Release. I numeri di versione pseudo possono specificare un determinato Commit. Il formato normale è vx.y.z-yyyyMMddHHmmss-CommitId. Poiché vx.y.z non esiste necessariamente, viene chiamato versione pseudo. Ad esempio, v0.0.0 nell'esempio seguente non esiste. Quello realmente efficace è il CommitID a 12 cifre successivo:
// CommitID generalmente prende le prime 12 cifre
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectAllo stesso modo, quando si scaricano le dipendenze, si può anche specificare il CommitID per sostituire il numero di versione semantico:
go get github.com/chenzhuoyu/base64x@fe3a3abad311exclude
La parola chiave exclude indica di non caricare la versione specificata della dipendenza. Se require fa riferimento alla stessa versione della dipendenza, verrà anche ignorata. Questa parola chiave ha effetto solo nel modulo principale. Ad esempio:
exclude golang.org/x/net v1.2.3
exclude (
golang.org/x/crypto v1.4.5
golang.org/x/text v1.6.7
)replace
replace sostituirà la versione specificata della dipendenza. Può essere sostituita con il percorso e la versione del modulo, oppure con il percorso del file specificato su un'altra piattaforma. Esempi:
replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
replace (
golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
golang.org/x/net => example.com/fork/net v1.4.5
golang.org/x/net v1.2.3 => ./fork/net
golang.org/x/net => ./fork/net
)Solo la versione a sinistra di => viene sostituita. Altre versioni della stessa dipendenza possono ancora essere accessibili normalmente. Sia che si utilizzi un percorso locale o un percorso di modulo per specificare la sostituzione, se il modulo di sostituzione ha un file go.mod, la sua direttiva module deve corrispondere al percorso del modulo sostituito.
retract
La direttiva retract indica che non si dovrebbe fare affidamento sulla versione o sull'intervallo di versioni della dipendenza specificata da retract. Ad esempio, dopo aver rilasciato una nuova versione, si scopre un problema grave. In questo momento si può utilizzare la direttiva retract:
Ritira alcune versioni:
retract (
v1.0.0 // Published accidentally.
v1.0.1 // Contains retractions only.
)Ritira un intervallo di versioni:
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
v1.0.0
[v1.0.0, v1.9.9]
)go.sum
Il file go.sum non esiste all'inizio della creazione del progetto. Viene generato solo dopo aver importato realmente dipendenze esterne. Il file go.sum non è adatto alla lettura umana e non è consigliabile modificarlo manualmente. Il suo scopo principale è risolvere il problema della compilazione coerente, ovvero garantire che persone diverse in ambienti diversi utilizzino esattamente le stesse dipendenze quando costruiscono lo stesso progetto. Questo non può essere garantito solo da un file go.mod.
Successivamente, vediamo cosa fa Go dall'inizio alla fine quando scarica una dipendenza. Prima scarica una dipendenza utilizzando il seguente comando:
go get github.com/bytedance/sonic v1.8.0Il comando go get scaricherà prima il pacchetto della dipendenza nella directory di cache locale. Generalmente questa directory è $GOMODCACHE/cache/download/. Questa directory è divisa in base al nome di dominio per le dipendenze di diversi siti web, quindi potresti vedere la seguente struttura di directory:
$ ls
cloud.google.com/ go.opencensus.io/ gopkg.in/ nhooyr.io/
dmitri.shuralyov.com/ go.opentelemetry.io/ gorm.io/ rsc.io/
github.com/ go.uber.org/ honnef.co/ sumdb/
go.etcd.io/ golang.org/ lukechampine.com/
go.mongodb.org/ google.golang.org/ modernc.org/Quindi il percorso di archiviazione della dipendenza scaricata nell'esempio sopra si trova in:
$GOMODCACHE/cache/download/github.com/bytedance/sonic/@v/La possibile struttura di directory è la seguente, con diversi file denominati per versione:
$ ls
list v1.8.0.lock v1.8.0.ziphash v1.8.3.mod
v1.5.0.mod v1.8.0.mod v1.8.3.info v1.8.3.zip
v1.8.0.info v1.8.0.zip v1.8.3.lock v1.8.3.ziphashNormalmente, ci deve essere un file list in questa directory, utilizzato per registrare i numeri di versione noti della dipendenza. Per ogni versione, ci saranno i seguenti file:
zip: pacchetto compresso del codice sorgente della dipendenzaziphash: hash calcolato in base al pacchetto compresso della dipendenzainfo: metadati di versione in formato jsonmod: filego.moddi questa versionelock: file temporaneo, l'ufficiale non ha detto a cosa serve
Generalmente, Go calcolerà i valori hash di due file: il pacchetto compresso e il file go.mod. Poi interroga il server specificato da GOSUMDB (predefinito è sum.golang.org) per il valore hash della dipendenza. Se il valore hash calcolato localmente non corrisponde al risultato della query, non verrà eseguito ulteriormente. Se corrisponde, aggiornerà il file go.mod e inserirà due record nel file go.sum, approssimativamente come segue:
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=TIP
Se GOSUMDB è disabilitato, Go scriverà direttamente il valore hash calcolato localmente nel file go.sum. Generalmente non è consigliabile farlo.
Normalmente ogni dipendenza avrà due record. Il primo è l'hash del pacchetto compresso, il secondo è l'hash del file go.mod della dipendenza. Il formato del record è nome-modulo versione nome-algoritmo:valore-hash. Alcune dipendenze più vecchie potrebbero non avere un file go.mod, quindi non ci sarà il secondo record hash. Quando questo progetto viene costruito in un altro ambiente, Go calcolerà il valore hash della dipendenza locale specificata in go.mod, poi lo confronterà con il valore hash registrato in go.sum. Se i valori hash non corrispondono, significa che le versioni delle dipendenze sono diverse e rifiuterà la compilazione. Quando si verifica questa situazione, sia la dipendenza locale che il file go.sum potrebbero essere stati modificati. Ma poiché go.sum è un record interrogato da GOSUMDB, si tenderà a fidarsi di più del file go.sum.
Moduli Privati
La maggior parte degli strumenti Go Mod sono rivolti a progetti open source, ma Go supporta anche i moduli privati. Per i progetti privati, generalmente è necessario configurare le seguenti variabili d'ambiente per gestire la privacy dei moduli:
GOPROXY: insieme di server proxy per le dipendenzeGOPRIVATE: elenco di modelli comuni di prefissi di percorso dei moduli per moduli privati. Se il nome del modulo corrisponde alle regole, indica che il modulo è un modulo privato. Il comportamento specifico è coerente con GONOPROXY e GONOSUMDB.GONOPROXY: elenco di modelli comuni di prefissi di percorso dei moduli che non vengono scaricati dal proxy. Se corrisponde alle regole, non passerà attraverso GOPROXY durante il download del modulo, tentando di scaricare direttamente dal sistema di controllo versione.GONOSUMDB: elenco di modelli comuni di prefissi di percorso dei moduli che non eseguono la verifica pubblica GOSUMDB. Se corrisponde alle regole, non passerà attraverso il database pubblico checksum durante la verifica del download del modulo.GOINSECURE: elenco di modelli comuni di prefissi di percorso dei moduli che possono essere recuperati tramite HTTP e altri protocolli non sicuri.
Workspace
Come menzionato in precedenza, il file go.mod supporta la direttiva replace, che ci consente di utilizzare temporaneamente modifiche locali che non hanno ancora rilasciato una versione. Ad esempio:
replace (
github.com/246859/hello v1.0.1 => ./hello
)Durante la compilazione, go utilizzerà il modulo hello locale. Dopo aver rilasciato una nuova versione in futuro, lo rimuoverai.
Tuttavia, se si utilizza la direttiva replace, il contenuto del file go.mod verrà modificato. Questa modifica potrebbe essere accidentalmente inviata al repository remoto, il che non è ciò che desideriamo. Poiché il target specificato dalla direttiva replace è un percorso di file e non un URL di rete, il percorso che funziona su questa macchina potrebbe non funzionare su un'altra macchina. I percorsi dei file sono anche un grande problema per la cross-piattaforma. Per risolvere questo tipo di problemi, sono nati i workspace.
Il workspace è una nuova soluzione per la gestione di più moduli introdotta da Go nella versione 1.18. Mira a facilitare meglio lo sviluppo locale di più moduli. Di seguito verrà spiegato attraverso un esempio.
Repository di esempio: 246859/work: go work example (github.com)
Esempio
Per prima cosa, il progetto ha due moduli go indipendenti, rispettivamente auth e user:
$ ls -1
LICENSE
README.md
auth
go.work
userIl modulo auth dipende dalla struct User del modulo user. Il contenuto è il seguente:
package auth
import (
"errors"
"github.com/246859/work/user"
)
// Verify user credentials if is ok
func Verify(user user.User) (bool, error) {
password, err := query(user.Name)
if err != nil {
return false, err
}
if password != user.Password {
return false, errors.New("authentication failed")
}
return true, nil
}
func query(username string) (string, error) {
if username == "jack" {
return "jack123456", nil
}
return "", errors.New("user not found")
}Il contenuto del modulo user è il seguente:
package user
type User struct {
Name string
Password string
Age int
}In questo progetto, possiamo scrivere il file go.work come segue:
go 1.22
use (
./auth
./user
)Il suo contenuto è molto facile da capire. Utilizza la direttiva use per specificare quali moduli partecipano alla compilazione. Successivamente, esegui il codice nel modulo auth:
// auth/example/main.go
package main
import (
"fmt"
"github.com/246859/work/auth"
"github.com/246859/work/user"
)
func main() {
ok, err := auth.Verify(user.User{Name: "jack", Password: "jack123456"})
if err != nil {
panic(err)
}
fmt.Printf("%v", ok)
}Esegui il seguente comando. Dal risultato si sa che il modulo è stato importato con successo:
$ go run ./auth/example
trueNelle versioni precedenti, per questi due moduli indipendenti, se il modulo auth voleva utilizzare il codice del modulo user, c'erano solo due metodi:
- Inviare le modifiche del modulo user e spingerle al repository remoto, rilasciare una nuova versione, poi modificare il file
go.modper specificare la versione - Modificare il file
go.modper reindirizzare la dipendenza al file locale
Entrambi i metodi richiedono la modifica del file go.mod. L'esistenza del workspace è proprio per poter importare altri moduli senza modificare il file go.mod. Tuttavia, è necessario capire che il file go.work viene utilizzato solo durante lo sviluppo. La sua esistenza serve solo a rendere più comodo lo sviluppo locale, non per la gestione delle dipendenze. Ti permette solo di saltare temporaneamente il processo di invio e rilascio, consentendoti di utilizzare immediatamente le nuove modifiche del modulo user senza attendere. Quando il modulo user è stato testato, alla fine devi comunque rilasciare una nuova versione, e il modulo auth deve comunque modificare il file go.mod per importare l'ultima versione (questo processo può essere completato con il comando go work sync). Pertanto, nel normale processo di sviluppo go, go.work non dovrebbe essere inviato a VCS (il go.work nel repository di esempio è solo per dimostrazione), poiché il suo contenuto dipende dai file locali e la sua funzionalità è limitata allo sviluppo locale.
Comandi
Di seguito sono riportati alcuni comandi del workspace:
| Comando | Descrizione |
|---|---|
| edit | Modifica go.work |
| init | Inizializza un nuovo workspace |
| sync | Sincronizza le dipendenze dei moduli del workspace |
| use | Aggiunge un nuovo modulo a go.work |
| vendor | Copia le dipendenze nel formato vendor |
Vai a go work cmd per maggiori informazioni sui comandi
Direttive
Il contenuto del file go.work è molto semplice, ha solo tre direttive:
go, specifica la versione di gouse, specifica i moduli utilizzatireplace, specifica i moduli da sostituire
Oltre alla direttiva use, le altre due sono praticamente equivalenti alle direttive in go.mod, solo che la direttiva replace in go.work agirà su tutti i moduli. Un go.work completo è il seguente:
go 1.22
use(
./auth
./user
)
repalce github.com/246859/hello v1.0.0 => /home/jack/code/hello