Files sorgente multipli
Il package main non deve essere necessariamente in un unico file
L’unico requisito è che la func main()
sia nel package main
Tutti i file che dichiarano package main devono essere compilati e costruiti in un unico comando go
Proviamo un esempio in una directory dedicata:
mkdir 72multiple
cd 72multiple
In questa directory creiamo due file
main.go
– con la funzione mainutils.go
– con funzioni di utilità
Entrambi i file appartengono al package main
(main.go):
package main
func main() {
// Se usata spesso p è meglio di fmt.Println
p("Version: ", version)
}
(utils.go):
package main
import "fmt"
// Definizione di p
func p(a ...interface{}) {
fmt.Println(a)
}
var version = "unknown"
Per il run:
go run main.go util.go
Per il build:
go build main.go util.go
L’eseguibile è main, come il file che ha func main()
Se I file che dichiarano package main sono molti si può usare un po’ di magia shell:
go build `grep -H "package main" *| cut -d: -f1`
(attenzione agli apici rovesciati)
Per più flessibilità si può scrivere un Makefile
.
(Makefile):
# Basta cambiare le tre linee seguenti
PROGS = main.go utils.go
MAIN = main
EXEC = printver
# ----- in qualsiasi progetto
help:
@echo "usage: make target"
@echo "\trun - run immediately"
@echo "\tbuild - compile and link the executable"
@echo "\tclobber - clean up the project"
run:
go run $(PROGS)
build:
go build $(PROGS)
mv $(MAIN) $(EXEC)
clobber:
rm -f $(EXEC)
Non è veramente parte della filosofia Go scrivere un Makefile. Può tornare però utile quando si mescola la tecnologia Go con altre e diverse utilities di Linux.
Attenzione
Col ‘copia-e-incolla’ i TAB del Makefile diventano spazi.
Il Makefile richiede che le linee siano indentate da un TAB
Usare il comando vi di sostituzione globale
:g/^ */s//^I/
Qui ^ indica l’inizio riga, poi vi sono due spazi e un *
Almeno uno spazio seguito da un numero qualsiasi di spazi (anche zero)
Il ^I
rappresenta il TAB e si ottiene battendo Ctrl-V
seguito dal TAB
.
Ctrl-V toglie il significato al carattere che segue e lo rappresenta letteralmente
Interfaccia con git
Il nostro programma sopra stampa
[Version: unknown]
che è proprio quello che dice utils.go
.
Di solito però i nostri progetti, che si evolvono nel tempo, sono sottoposti al controllo versione con l'utility git
.
Facciamolo:
git init
git add .
git commit -m "Prima versione"
Ora aggiungiamo un tag di versione:
git tag -a 0.1 -m "Versione 0.1"
La compilazione del programma avviene ora con un flag passato al linker:
go build -i -v -ldflags="-X main.version=\
$(git describe --always --long --dirty)" \
main.go utils.go
Il \
rappresenta che in realtà il comando sopra deve occupare un'unica linea logica ma qui è spezzato su più linee fisiche per
convenienza visiva. (Si può compiere il copia e incolla anche con i \
e la shell riconoscerà lo spezzamento fisico).
Se ora lanciamo il programma verrà qualcosa tipo:
[Version: 0.1-0-g1cf7ed9]
Nel comando sopra:
-ldflags
passa i flag al linker-X
dice di compiere una sostituzionemain.version
è il simbolo version del package main$( ... )
è un costrutto shell che esegue il comando racchiuso tra tonde e interpola localmente l'output del comandogit describe --always --long --dirty
è il comando git per recuperare l'ultimo tag--always
anche se il tag è light e non annotato--long
formato lungo--dirty
anche se non vi è stato ancora un commit dopo l'attribuzione del tag
Si può modificare il Makefile per inglobare questo comando:
(Makefile):
# Basta cambiare le tre linee seguenti
PROGS = main.go utils.go
MAIN = main
EXEC = printver
# ----- in qualsiasi progetto
# $$ diventa $ quando a sua volta interpolato
LDFLAGS = "-X main.version=$$(git describe \
--always --long --dirty)"
help:
@echo "usage: make target"
@echo "\trun - run immediately"
@echo "\tbuild - compile and link the executable"
@echo "\tclobber - clean up the project"
run:
go run $(PROGS)
build:
go build -i -v -ldflags=$(LDFLAGS) $(PROGS)
mv $(MAIN) $(EXEC)
clobber:
rm -f $(EXEC)