Hlavní navigace

Systém modulů v programovacím jazyce Go

Pavel Tišnovský

Jedním z problematických rysů jazyka Go byl neexistující systém pro práci s balíčky. Postupem času vzniklo několik nástrojů, které správu balíčků realizují. Z dlouhodobého hlediska je lepší používat moduly zavedené ve verzi 1.11.

Doba čtení: 39 minut

Sdílet

11. Verzování balíčků

12. Problematika potencionálního rozbití API

13. Změna minoritní verze modulu

14. Změna majoritní verze modulu

15. Vytvoření externí knihovny s několika majoritními verzemi a jednou verzí minoritní

16. Použití externí knihovny s více majoritními verzemi v demonstračním projektu

17. Sémantické importy

18. Změna demonstračního příkladu – využití sémantického importu

19. Repositáře s demonstračními příklady

20. Odkazy na Internetu

1. Systém modulů v programovacím jazyce Go

V dnešní části seriálu o programovacím jazyce Go se seznámíme se základními vlastnostmi oficiálního systému modulů, na který mnozí vývojáři poměrně dlouho čekali. V původních verzích jazyka Go se totiž nepočítalo s tím, že by vyvíjené aplikace mohly vyžadovat konkrétní verze balíčků (třetích stran) a už vůbec ne s tím, že se při vývoji jednotlivých balíčků (typicky knihoven) může měnit jejich API. Oficiální, ovšem v reálném světě těžko zajistitelnou doktrínou bylo, že balíček (package) určitého jména nikdy nezmění své API a pokud by ke změně mělo dojít, měl by se balíček přejmenovat. Jednalo se o dosti problematický rys celého ekosystému jazyka Go, který byl jen do určité míry řešen externími nástroji, jichž za relativně krátkou dobu existence Go vzniklo prakticky nepřeberné množství. Tento stav však začal být neúnosný a možná podstatným způsobem zbrzdil rozšíření programovacího jazyka Go (už jen z toho důvodu, že mnohé ostatní ekosystémy do značné míry tento problém mají vyřešený: Maven, npm, pip, Cargo atd.):

dep was the „official experiment.“ The Go toolchain, as of 1.11, has adopted an approach that sharply diverges from dep. As a result, we are continuing development of dep, but gearing work primarily towards the development of an alternative prototype for versioning behavior in the toolchain.

Jak je z předchozí citace zřejmé, objevil se v toolchainu programovacího jazyka Go verze 1.11 (i když jen v „preliminary“ variantě) oficiální systém pro práci s takzvanými moduly. Tento systém je postupně stabilizován, takže si v dnešním článku ukážeme jeho použití s prozatím nejnovějším oficiálním vydáním programovacího jazyka Go – Go verze 1.13.

Poznámka: právě z toho důvodu, že dnes již existuje de facto oficiální správce modulů v toolchainu Go, jsme se v tomto seriálu nezabývali dalšími projekty, které řeší správu balíčků a ani se těmito projekty pravděpodobně zabývat nebudeme. Některé z těchto projektů sice jsou (alespoň podle mého skromného názoru) technologicky propracovanější, než oficiální systém modulů, ovšem v tomto případě hraje největší roli standardizace (a právě jazyk Go je jazykem, který se snaží standardy dodržovat).

2. Původní koncept jazyka Go

Nejdříve si alespoň ve stručnosti připomeňme, jak vlastně vypadal původní koncept programovacího jazyka Go při práci s balíčky (package), a to jak s balíčky lokálními, tak i s externími knihovnami. Již v úvodním článku tohoto seriálu jsme si řekli, že při instalaci programovacího jazyka Go je vhodné nastavit proměnnou GOPATH, například na adresář ~/home/go. Ve skutečnosti je problematika proměnné prostředí GOPATH a jejího významu poněkud složitější, protože tato proměnná by měla obsahovat absolutní (!) cestu k adresáři, který má či bude mít následující strukturu:

.
├── bin
│   ├──
│   └──
├── pkg
│   ├──
│   └──
└── src
    ├──
    ├──
    └──

Celý adresář se v dokumentaci nazývá pracovní plocha (workspace) a obsahuje tři podadresáře pojmenované src, pkg a bin. Z pohledu vývojáře je nejdůležitější podadresář src, v němž jsou typicky uloženy repositáře (repository) a v každém repositáři je umístěn libovolný počet balíčků (package). V balíčcích už nalezneme jednotlivé zdrojové soubory .go, pomocné skripty, datové soubory, dokumentaci, testy atd. Workspace představovaný adresářem nazvaným go, v němž jsou umístěny tři repositáře, může vypadat například následovně:

.
└── go
    ├── bin
    ├── pkg
    └── src
        ├── repository1
        │   ├── hello1
        │   │   └── hello.go
        │   └── hello2
        │       └── hello.go
        ├── repository2
        └── repository3

Vidíme, že v repositáři pojmenovaném repository1 jsou umístěny dva balíčky, které mají názvy hello1 a hello2, přičemž v každém balíčku je jediný zdrojový soubor, který je (čistě náhodou) shodně pojmenován hello.go (což v tomto případě nevadí, protože nedojde ke kolizi).

Soubor hello.go může vypadat následovně:

package main
 
func main() {
        println("repository1: Hello world!")
}
Poznámka: aby byl výsledný soubor, který vznikne překladem, spustitelný, musí obsahovat funkci main a samotné označení balíčku se taktéž musí jmenovat main.

Instalace externích knihoven, na nichž mohla nějaká aplikace záviset, se prováděla (a stále může provádět) příkazem go get.

Pokud například budeme potřebovat vypočítat Levenštejnovu vzdálenost dvou řetězců (což se provádí poměrně často například při implementaci uživatelsky přívětivé funkce Search), můžeme pro tento účel použít knihovnu/balíček s názvem levenshtein a s cestou github.com/agext/levenshtein, která je dostupná na GitHubu, konkrétně na adrese https://github.com/agext/levenshtein (součástí plné cesty balíčku je skutečně i „github.com“).

Mimochodem – odkazy na tuto a další knihovny naleznete na stránce https://github.com/avelino/awesome-go, která samozřejmě neobsahuje všechny balíčky, které pro Go vznikly, ale pouze okomentované a odzkoušené knihovny (neměl by se zde tedy vyskytnout žádný „odpad“ ani potenciálně škodlivé knihovny).

Balíček agext/levenshtein se instaluje velmi snadno, a to již výše zmíněným příkazem go get, kterému předáme jméno repositáře s balíčkem (ovšem vynechá se protokol!):

$ go get github.com/agext/levenshtein

Pokud chcete vidět, jaké operace se provádí, přidejte přepínač -v:

$ go get -v github.com/agext/levenshtein
 
github.com/agext/levenshtein (download)

Nyní by měla adresářová struktura ~/go (přesněji řečeno adresář, na který ukazuje proměnná prostředí $GOPATH) vypadat zhruba následovně:

.
.
└── go
    ├── bin
    ├── pkg
    │   └── linux_amd64
    │       └── github.com
    │           └── agext
    │               └── levenshtein.a
    └── src
        ├── github.com
        ├── agext
        │   └── levenshtein
        │       ├── DCO
        │       ├── levenshtein.go
        │       ├── levenshtein_test.go
        │       ├── LICENSE
        │       ├── MAINTAINERS
        │       ├── NOTICE
        │       ├── params.go
        │       ├── params_test.go
        │       └── README.md
        ├── repository1
        │   ├── hello1
        │   │   └── hello.go
        │   └── hello2
        │       └── hello.go
        ├── repository2
        └── repository3
 
15 directories, 16 files

Povšimněte si, že se balíček nainstaloval jak do podadresáře src (vlastní zdrojové kódy, testy, licence, další dokumentace), tak i do podadresáře pkg (binární knihovna určená pro slinkování s kódem výsledných aplikací). Po instalaci je součástí cesty k balíčku skutečně i prefix github.com, protože zdrojové kódy balíčku leží v podadresáři src/github.com/agext/levenshtein.

Příkaz go list by nyní měl ukázat informace i o nově nainstalované knihovně agext/levenshtein:

$ go list ...
 
...
...
...
github.com/agext/levenshtein
...
...
...

Podobně uvidíme základní informace o balíčku i na dynamicky generovaných stránkách s dokumentací.

3. Největší nevýhody původního konceptu

V předchozí kapitole jsme si ukázali, že i v původním Go, resp. přesněji řečeno při použití původního konceptu práce s balíčky, bylo možné alespoň nějakým způsobem standardně pracovat s externími balíčky a knihovnami, na nichž závisela vyvíjená aplikace. Ovšem současně měl tento systém hned několik špatných vlastností, mezi které patří zejména:

  1. Všechny projekty, nezávisle na tom, jakého mají zákazníka a popř. pro koho jsou vytvářeny, musí být umístěny ve stejném adresáři, na nějž ukazuje proměnná prostředí $GOPATH. Teoreticky je sice možné $GOPATH přepínat, ale jedná se o dosti křehké řešení.
  2. Prakticky žádným rozumným způsobem není řešeno verzování balíčků. To znamená, že není možné specifikovat verzi balíčku, s nímž budeme potřebovat pracovat a tím pádem ani není možné použít dvě verze stejného balíčku. Tuto vlastnost ještě více umocňuje bod číslo 1, tedy umístění všech zdrojových kódů v jediné adresářové struktuře.
  3. Všechny externí balíčky je nutné nějakým způsobem udržovat, zjišťovat případná CVE atd. Ovšem jak již plyne z bodů #1 a #2, je možné, že updatem jediného balíčku se může rozbít prakticky libovolná vyvíjená aplikace – typický „dependency hell“.
  4. Pokud externí balíček závisí na dalších balíčcích, musíme řešit i takzvanétranzitivní závislosti, s jejichž správou jednoduchý systém založený na go get příliš nepočítá.

Dále představený standardní systém balíčků se výše uvedené problémy snaží do velké míry vyřešit.

4. Instalace Go verze 1.13

Doslova před několika dny bylo vydáno Go verze 1.13, viz též https://golang.org/doc/de­vel/release.html. Všechny dnes popisované ukázky a demonstrační příklady již budou tuto verzi používat (už jen z toho důvodu, že oficiálně byly moduly v Go 1.11 a Go 1.12 jen ve stavu „preliminary“), takže si v této kapitole ve stručnosti ukážeme, jakým způsobem je možné Go 1.13 nainstalovat, a to i v případě, kdy tato verze ještě není dostupná v balíčcích vaší Linuxové distribuce. Předpokladem je, že předchozí verze Go byla nainstalována do adresáře /opt/go, ovšem pochopitelně změna cíle instalace je triviální.

Nejprve se zbavíme předchozí verze:

$ rm -rf /opt/go
nebo
$ sudo rm -rf /opt/go

Dále stáhneme novou verzi Go 1.13, zde konkrétně pro 64bitovou platformu x86–64:

$ wget https://dl.google.com/go/go1.13.linux-amd64.tar.gz
 
--2019-09-04 15:50:48--  https://dl.google.com/go/go1.13.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 2a00:1450:4014:801::200e, 216.58.201.110
Connecting to dl.google.com (dl.google.com)|2a00:1450:4014:801::200e|:443... failed: No route to host.
Connecting to dl.google.com (dl.google.com)|216.58.201.110|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 120050424 (114M) [application/octet-stream]
Saving to: ‘go1.13.linux-amd64.tar.gz’
 
go1.13.linux-amd64.tar.gz               100%[==============================================================================>] 114.49M  68.6MB/s    in 1.7s
 
2019-09-04 15:50:50 (68.6 MB/s) - ‘go1.13.linux-amd64.tar.gz’ saved [120050424/120050424]

Stažený tarball rozbalíme, a to se specifikací cíle instalace, jímž je adresář /opt/:

$ tar xvfz go1.13.linux-amd64.tar.gz -C /opt/

V tomto adresáři by se měla vytvořit struktura s celým toolingem programovacího jazyka Go 1.13:

$ tree -d /opt -L 2
 
/opt
└── go
    ├── api
    ├── bin
    ├── doc
    ├── lib
    ├── misc
    ├── pkg
    ├── src
    └── test
 
9 directories

Přesvědčíme se, že adresář /opt/go/bin je na PATH:

$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/tester/bin:/opt/go/bin:/home/tester/.local/bin:/home/tester/bin

Dále se přesvědčíme, zda je nastavena proměnná GOPATH, a to z důvodu zpětné kompatibility se starším systémem správy balíčků, který jsme si popsali ve druhé kapitole:

$ echo $GOPATH
/home/tester/go

V případě, že je PATH nastavená korektně, měl by následující příkaz vypsat verzi právě nově nainstalovaného jazyka Go:

$ go version
go version go1.13 linux/amd64

5. Balíčky, moduly a repositáře

Při práci s moduly v programovacím jazyku Go se rozlišuje mezi třemi termíny – balíčkem, modulem a repositářem:

  • Balíček je představován adresářem obsahujícím zdrojové kódy, většinou i včetně testů. Zdrojové kódy v jednom adresáři většinou začínají stejnou deklarací package.
  • Modul se obecně může skládat z většího množství balíčků. Všechny tyto balíčky jsou poskytovány a vydávány společně. V nejjednodušším případě modul obsahuje jediný balíček (což bude i náš případ).
  • Repositář obecně obsahuje více modulů (většinou jediný modul). V našich příkladech budeme počítat s tím, že v jednom repositáři je jediný modul popř. více verzí jednoho modulu.

6. Vytvoření prvního jednoduchého modulu

Nyní by již mělo být vše připravené pro otestování standardního systému pro práci s moduly. Všechny další příklady budou vytvářeny v adresáři ~/src, což konkrétně znamená, že se vyhneme použití adresáře, na který ukazuje proměnná prostředí GOPATH.

Nejprve provedeme inicializaci modulu, což zajistí příkaz go mod init, kterému předáme jméno repositáře, v němž může být modul umístěn. Ovšem platí, že pokud tento modul nebude použitý jiným modulem, nemusí být jméno repositáře shodné se skutečným repositářem (ovšem později si tím zbytečně zkomplikujeme další práci, proto je lepší rozlišovat pouze sémantické verze).

Náš modul se bude jmenovat github.com/tisnik/my-first-module, takže se jeho inicializace provede následovně:

$ go mod init github.com/tisnik/my-first-module
 
go: creating new go.mod: module github.com/tisnik/my-first-module

Tento příkaz provede jen jedinou operaci – do aktuálního adresáře přidá soubor pojmenovaný go.mod, v němž budou (prozatím) pouze dva řádky – vlastní jméno modulu a taktéž verze programovacího jazyka Go, resp. přesněji řečeno celého toolchainu tohoto jazyka:

$ cat go.mod
 
module github.com/tisnik/my-first-module
 
go 1.13

7. Zdrojový kód modulu, překlad a spuštění modulu

Nyní vytvoříme zdrojový kód modulu. Bude se jednat o jednoduchou aplikaci, v níž je deklarována funkce Add, kterou si odzkoušíme na čtyřech skupinách celých čísel. Vzhledem k tomu, že se skutečně má jednat o spustitelnou aplikaci, použijeme, jak je zvykem, balíček main a deklarovat budeme muset i funkci main:

package main
 
import "fmt"
 
func Add(x int, y int) int {
        return x + y
}
 
func main() {
        fmt.Println(Add(0, 0))
        fmt.Println(Add(1, 0))
        fmt.Println(Add(1, 2))
        fmt.Println(Add(-1, 2))
}
Poznámka: úplný zdrojový kód tohoto příkladu (správně naformátovaný s využitím tabulátorů, což redakční systém Rootu neumožňuje) naleznete na adrese https://github.com/tisnik/go-1st-module/blob/master/adder.go (povšimněte si, že název repositáře je v tomto případě skutečně odlišný od jména modulu, což však nevadí).

Modul přeložíme běžným způsobem, tj. příkazem go build:

$ go build -o adder

A pro jistotu otestujeme, že výsledná aplikace (spustitelný binární soubor) je skutečně plně funkční:

$ ./adder
0
1
3
1
Poznámka: co se vlastně změnilo oproti stavu, kdy jazyk Go moduly nepodporoval? V našem případě se prozatím jedná jen o maličkost – zdrojové kódy aplikace můžeme mít uloženy kdekoli a nemusíme se ohlížet na adresářovou strukturu $GOPATH. Další výhody si pochopitelně ukážeme v navazujících kapitolách.

8. Přidání balíčku, na němž modul závisí

Nyní si ukažme, jakým způsobem se situace změní ve chvíli, kdy do našeho projektu přidáme balíček, na němž projekt závisí. Samotný balíček bude velmi jednoduchý (až triviální) a nalezneme ho na adrese https://github.com/tisnik/intvalues. V tomto balíčku je umístěn jediný zdrojový soubor, který pro jednoduchost obsahuje několik důležitých celočíselných konstant, které později v našem projektu použijeme, abychom nemuseli používat „magické konstanty“:

package intvalues
 
const Zero = 0
const One = 0
 
const MaxInt = int(^uint(0) >> 1)
const MinInt = -MaxInt - 1
Poznámka: důležité je, že jména konstant mají na začátku velké písmeno. Takové konstanty je možné použít i v dalších balíčcích – jsou externě viditelné. Podobně bychom mohli do projektu přidat i funkce, globální proměnné a metody, ovšem nyní pro jednoduchost použijeme pouze konstanty.

Přidání externího balíčku, na němž náš projekt závisí, je při použití modulů většinou zcela triviální, protože do zdrojového kódu pouze postačuje přidat příslušný import se jménem balíčku včetně cesty na GitHubu či na jiném podporovaném externím repositáři (z tohoto pohledu je GitHub skutečně chápán jako repositář s verzovanými balíčky):

import "github.com/tisnik/intvalues"

V našem konkrétním příkladu bude zdrojový kód vypadat následovně (nový import je zvýrazněn):

package main
 
import (
        "fmt"
        "github.com/tisnik/intvalues"
)
 
func Add(x int, y int) int {
        return x + y
}
 
func main() {
        fmt.Println(Add(intvalues.Zero, intvalues.Zero))
        fmt.Println(Add(intvalues.One, 0))
        fmt.Println(Add(1, 2))
        fmt.Println(Add(-1, 2))
}

Před spuštěním popř. před překladem takto upraveného příkladu se můžeme podívat na strukturu adresáře ~/go, popř. toho adresáře, na který ukazuje proměnná prostředí se jménem GOPATH. Tento adresář může být prázdný, popř. může obsahovat některé starší projekty (ty nás ovšem nemusí příliš zajímat):

.
├── bin
├── pkg
└── src

Můžeme se pokusit upravený příklad (s externí závislostí) spustit, a to klasicky příkazem go run::

$ go run adder.go 
 
go: finding github.com/tisnik/intvalues latest
go: downloading github.com/tisnik/intvalues v0.0.0-20190905132008-dad69ec19589
go: extracting github.com/tisnik/intvalues v0.0.0-20190905132008-dad69ec19589
0
0
3
1

Z předchozího výpisu je patrné, že ještě před samotným spuštěním přeloženého projektu nástroje jazyka Go stáhly balíček, na němž náš projekt závisí. To znamená, že už není zapotřebí používat příkaz go get, který má problematické chování! Dále si povšimněte, jak nástroje Go zajistily verzi externího balíčku. Tuto verzi jsme nikde nespecifikovali (tedy ani v projektu ani v závislém balíčku), proto bylo číslo verze uměle vytvořeno ze dvou dostupných údajů:

  1. Časového razítka posledního commitu (2019–09–05 přibližně v 13:20)
  2. Hashe posledního commitu (plný hash je commit dad69ec1958979dd2b25b68e8­62550c1a29f17da)

Spuštěním či překladem projektu navíc došlo i ke změně obsahu souboru go.mod, v němž se kromě předchozích dvou řádků popsaných výše objevil nový řádek require obsahující nám již známé údaje o přidané závislosti i její přesné verzi (zde automaticky vygenerované):

module github.com/tisnik/my-first-module
 
go 1.13
 
require github.com/tisnik/intvalues v0.0.0-20190905132008-dad69ec19589 // indirect
Rekapitulace: pokud používáme systém modulů a potřebujeme do projektu přidat nějakou externí knihovnu (balíček) ve stabilní verzi, postačuje do zdrojového kódu přidat příslušný import a aplikaci běžným způsobem přeložit.

9. Soubor go.sum

Současně při překladu aplikace v adresáři s projektem automaticky vznikl i nový soubor se jménem go.sum. Obsah tohoto souboru by v našem triviálním demonstračním příkladu měl vypadat následovně:

github.com/tisnik/intvalues v0.0.0-20190905132008-dad69ec19589 h1:AAvNlAqsqYEVQUG4hHFNJOvvKVRh8DC+OI0wuEn0v7Y=
github.com/tisnik/intvalues v0.0.0-20190905132008-dad69ec19589/go.mod h1:Pc260EFOSnRzU0A/CN2vfBETjFJ4B+LijhuPYjfUWrA=

Obecná struktura souboru go.sum dodržuje formát:

<modul> <verze>[/go.mod] <hash>

Každý modul (zde externí balíček), na němž projekt závisí, je zapsán na dvou po sobě jdoucích řádcích. Na prvním řádku je na konci uveden otisk (hash) stromu se soubory modulu, na druhém řádku pak hash souboru go.mod obsaženého přímo v modulu či automaticky vygenerovaného nástroji jazyka Go (pokud tento soubor v repositáři s modulem není uložen).

Poznámka: pokud vás zajímá, jakým algoritmem se vlastně otisky počítají, podívejte se na prefix otisku. Ten by měl v současné verzi začínat řetězcem „h1:“, který označuje algoritmus SHA-256. Ostatně v současné verzi Go je podporován pouze tento algoritmus, ovšem v budoucnosti se mohou objevit algoritmy další. Podrobnější informace naleznete na stránce https://tip.golang.org/cmd/go/#hdr-Module_authentication_using_go_sum.

10. Struktura adresáře $GOPATH po stažení balíčku

Po automatickém stažení balíčku github.com/tisnik/intvalues a spuštění našeho demonstračního projektu se struktura adresáře, na který ukazuje proměnná prostředí $GOPATH dosti rapidním způsobem změní. V případě, že tento adresář byl před překladem projektu prázdný, měl by po jeho překladu vypadat zhruba následovně:

.
├── bin
├── pkg
│   ├── mod
│   │   ├── cache
│   │   │   ├── download
│   │   │   │   ├── github.com
│   │   │   │   │   └── tisnik
│   │   │   │   │       └── intvalues
│   │   │   │   │           └── @v
│   │   │   │   │               ├── list
│   │   │   │   │               ├── list.lock
│   │   │   │   │               ├── v0.0.0-20190905132008-dad69ec19589.info
│   │   │   │   │               ├── v0.0.0-20190905132008-dad69ec19589.lock
│   │   │   │   │               ├── v0.0.0-20190905132008-dad69ec19589.mod
│   │   │   │   │               ├── v0.0.0-20190905132008-dad69ec19589.zip
│   │   │   │   │               └── v0.0.0-20190905132008-dad69ec19589.ziphash
│   │   │   │   └── sumdb
│   │   │   │       └── sum.golang.org
│   │   │   │           ├── lookup
│   │   │   │           │   └── github.com
│   │   │   │           │       └── tisnik
│   │   │   │           │           └── intvalues@v0.0.0-20190905132008-dad69ec19589
│   │   │   │           └── tile
│   │   │   │               └── 8
│   │   │   │                   ├── 0
│   │   │   │                   │   └── 668.p
│   │   │   │                   │       └── 214
│   │   │   │                   ├── 1
│   │   │   │                   │   └── 002.p
│   │   │   │                   │       └── 156
│   │   │   │                   └── 2
│   │   │   │                       └── 000.p
│   │   │   │                           └── 2
│   │   │   └── lock
│   │   └── github.com
│   │       └── tisnik
│   │           └── intvalues@v0.0.0-20190905132008-dad69ec19589
│   │               ├── intvalues.go
│   │               └── README.md
│   └── sumdb
│       └── sum.golang.org
│           └── latest
└── src
 
28 directories, 16 files

Vidíme, že vznikl celý nový podadresář pkg/mod, v němž můžeme nalézt jak vlastní zdrojový kód staženého balíčku, tak i množství souborů s metadaty o verzi balíčku, jeho otisku (přesněji řečeno otiscích) atd. Tento podadresář je kdykoli možné smazat, protože se jeho struktura automaticky obnoví při novém překladu vyvíjených aplikací (ovšem s tím, že se metadata znovu zjistí z externích repositářů).

Poznámka: je zajímavé a pravděpodobně i nezbytné, že adresářová struktura prakticky všech systémů pro správu balíčků (Maven, pip, npm) mnohdy obsahují více metadat, než vlastních zdrojových kódů :-)

11. Verzování balíčků

Vlastnosti systému modulů, které jsme si až doposud popsali, jsou sice určitým pokrokem oproti použití klasického go get, ovšem celou sílu nového konceptu doceníme až tehdy, když začneme pracovat s verzemi balíčků (a modulů). Systém modulů nám totiž umožní použít konkrétní verzi balíčku a do určité míry řeší i tranzitivní závislosti, tj. situaci, kdy knihovna A vyžaduje balíček Z ve verzi 1.0 a knihovna B taktéž balíček Z, ovšem tentokrát ve verzi 2.0. Některé ukázky budou uvedeny v navazujícím textu.

12. Problematika potencionálního rozbití API

Mohlo by se zdát, že na konkrétní verzi modulu/balíčku nemusí záležet, protože nové verze buď „pouze“ opravují chyby verze předchozí, nebo přidávají další funkcionalitu. Na základě tohoto pohledu by tedy bylo jen výhodné slepě updatovat všechny moduly na nejnovější verzi, protože ta by měla být nejstabilnější a měla by obsahovat plnou funkcionalitu verzí předchozích. Ve skutečnosti tomu tak pochopitelně být nemusí, protože mezi jednotlivými verzemi balíčků (představme si zde nějakou knihovnu) může dojít ke změně jejího API, ať již přidáním/ubráním parametrů funkcí a metod, tak i mnohdy změnou celé filozofie ovládání. Tento problém, který ostatně musí řešit všechny balíčkovací systémy pro další programovací jazyky, je v Go řešen s využitím známého sémantického verzování [1], kde je verze balíčku popř. modulu zapsána trojicí celý čísel – majoritní verze.minoritní verze.číslo patche.

Poznámka: ve skutečnosti je (alespoň prozatím) situace jazyka Go poměrně přehledná, protože v naprosté většině případů moduly nevyžadují konkrétní verzi překladače. Nemusíme tedy řešit například problémy typu Python 2/Python 3.

13. Změna minoritní verze modulu

V ekosystému programovacího jazyka Go (ovšem nejenom zde) se předpokládá, že se oficiální aplikační programové rozhraní modulu s minoritní verzí nemění. Může se pochopitelně do určité míry měnit konkrétní chování (ostatně i oprava chyby mění chování), ovšem exportované konstanty, proměnné, datové typy, funkce a metody by alespoň měly mít zachovánu svoji signaturu a sémantiku chování. Otázkou zůstává, zda má být API zcela neměnné, nebo zda je do něj možné přidávat další funkce a metody. Samotné přidání – pokud se ovšem nejedná o rozdělení činnosti původní jediné funkce do funkcí dvou – by v Go nemělo vadit, už jen z toho důvodu, že se zde nepoužívá koncept tříd a OOP postaveného na třídách a explicitně implementovaných rozhraní.

14. Změna majoritní verze modulu

Zcela jiná situace nastane ve chvíli, kdy se změní majoritní verze modulu. V tomto případě se (obecně) předpokládá, že nová verze může mít API zcela odlišné od API předchozí verze a tudíž není možné bez dalších úprav vyvíjené aplikace na tuto verzi přejít. V programovacím jazyce Go existuje vcelku elegantní řešení tohoto problému, které spočívá v použití takzvaných sémantických importů, s nimiž se seznámíme v sedmnácté kapitole.

Poznámka: v ideálním světě by se „rozbití“ API zjistilo snadno a taktéž včas pomocí jednotkových testů, popř. by alespoň například na nekompatibilní počty a typy parametrů upozornil překladač. Samotnou změnu chování však překladač nedokáže detekovat a pokrytí kódu jednotkovými testy mnohdy taktéž není příliš slavné…

15. Vytvoření externí knihovny s několika majoritními verzemi a jednou verzí minoritní

Pro otestování funkčnosti systému modulů v programovacím jazyku Go byl vytvořen repositář https://github.com/tisnik/integers, v němž je uložen jediný modul s jediným balíčkem (viz též pátou kapitolu, kde jsou vztahy mezi těmito pojmy vysvětleny. V tomto repositáři byly příkazem:

$ git tag -a _verze_ -m "zpráva"

vytvořeny celkem čtyři tagy označující čtyři různé stavy repositáře. Navíc byly ze všech čtyř tagů vytvořena vydání (release), jejichž seznam naleznete na stránce https://github.com/tisnik/in­tegers/releases.

Informace o vydáních našeho testovacího balíčku

Verzi 1.0.0 označenou tagem v1.0.0 naleznete na adrese https://github.com/tisnik/in­tegers/releases/tag/v1.0.0. V této verzi je vydán balíček nazvaný integers s dvojicí celočíselných konstant MaxInt a MinInt:

package integers
 
const MaxInt = int(^uint(0) >> 1)
const MinInt = -MaxInt - 1

Další verze je minoritní, což znamená, že její první číslo je stále 1. Konkrétně se jedná o verzi 1.1.0 s tagem v1.1.0 a najdeme ji na adrese https://github.com/tisnik/in­tegers/releases/tag/v1.1.0. API v tomto případě bylo rozšířeno o další dvě celočíselné konstanty Zero a One:

package integers
 
const MaxInt = int(^uint(0) >> 1)
const MinInt = -MaxInt - 1
 
const Zero = 0
const One = 1

Další verze nese číslo 2.0.0 a došlo zde ke změně API, která může vést k tomu, že bude zapotřebí upravit vyvíjenou aplikaci (konkrétně byly odstraněny konstanty MinInt a MaxInt). Tag této verze je podle očekávání v2.0.0 a další podrobnosti najdete na adrese https://github.com/tisnik/in­tegers/releases/tag/v2.0.0:

package integers
 
const Zero = 0
const One = 1

Navíc byla vydána i verze 4.0, ovšem touto verzí se budeme zabývat až v sedmnácté kapitole.

16. Použití externí knihovny s více majoritními verzemi v demonstračním projektu

Nyní si zkusme vytvořit jednoduchý demonstrační projekt, který bude výše uvedený repositář a v něm uložený balíček používat. V tomto projektu nejdříve jednoduše naimportujeme balíček github.com/tisnik/integers a následně použijeme konstanty v tomto balíčku deklarované:

package main
 
import (
        "fmt"
        "github.com/tisnik/integers"
)
 
func Add(x int, y int) int {
        return x + y
}
 
func main() {
        fmt.Println(Add(integers.Zero, integers.Zero))
        fmt.Println(Add(integers.One, 0))
        fmt.Println(Add(integers.MaxInt, integers.MinInt))
        fmt.Println(Add(1, 2))
        fmt.Println(Add(-1, 2))
}

V případě, že by byla vydána pouze verze 1.0.0 našeho testovacího balíčku, vypadal by pokus o překlad takto:

$ go build
 
go: downloading github.com/tisnik/integers v1.0.0
go: extracting github.com/tisnik/integers v1.0.0
go: finding github.com/tisnik/integers v1.0.0
 
./adder.go:13:18: undefined: integers.Zero
./adder.go:14:18: undefined: integers.One
Poznámka: do tohoto stavu se můžeme dostat i po vydání dalších verzí – pouhou úpravou verze v souboru go.mod. Tím se verze pro danou vyvíjenou aplikace „uzamče“ a systém již nebude hledat poslední vydanou verzi.

Vidíme, že se systém jazyka Go pokusil o stažení poslední stabilní verze (1.0.0), ve které ovšem nejsou specifikovány konstanty Zero a One, takže překlad skončí s chybou.

Ve chvíli, kdy je vydána verze 1.1.0 (a je automaticky označena jako „latest version“), se dočkáme bezchybného překladu, protože všechny čtyři potřebné konstanty existují pouze v této verzi:

$ go build
 
go: downloading github.com/tisnik/integers v1.1.0
go: extracting github.com/tisnik/integers v1.1.0
go: finding github.com/tisnik/integers v1.1.0

Ovšem může taktéž nastat situace, kdy již byla vydána verze 2.0.0, která původní API rozbije. Potom se opět překlad nepovede, tentokrát ovšem kvůli tomu, že chybí konstanty MinInt a MaxInt:

$ go build
 
go: finding github.com/tisnik/integers v2.0.0
./adder.go:15:18: undefined: integers.MaxInt
./adder.go:15:35: undefined: integers.MinInt

Zajímavé bude se v tomto okamžiku podívat na obsah souboru go.sum. Ten by měl obsahovat následující záznamy (tři dvojice verzí):

$ cat go.sum 
 
github.com/tisnik/integers v1.0.0 h1:hTvDeOyjmLt1BJqeQmSnJNOlSst9LthLfQnU53h0Ik0=
github.com/tisnik/integers v1.0.0/go.mod h1:qfkSORAgW6E8jwfni4JdVE4AKzFvQs7239NSw/cf+fU=
github.com/tisnik/integers v1.1.0 h1:xygWKRg5EGDS/PZyR7SuhYp3PXQTkRdY6TcPsOd4iio=
github.com/tisnik/integers v1.1.0/go.mod h1:qfkSORAgW6E8jwfni4JdVE4AKzFvQs7239NSw/cf+fU=
github.com/tisnik/integers v2.0.0+incompatible h1:wJnU/uiLwDL1CWiDyhb+lhS/ByAkfWlAsRlh54wL5mc=
github.com/tisnik/integers v2.0.0+incompatible/go.mod h1:qfkSORAgW6E8jwfni4JdVE4AKzFvQs7239NSw/cf+fU=

Všechny prozatím stažené a dostupné verze vypíše příkaz:

$ go list -m -versions github.com/tisnik/integers
 
github.com/tisnik/integers v1.0.0 v1.1.0 v2.0.0+incompatible

17. Sémantické importy

Pro řešení kolizí mezi více majoritními verzemi balíčku (či modulu) s rozdílným API podporuje programovací jazyk Go i koncept takzvaných sémantických importů. Jedná se o téma na samostatný článek, ovšem již dnes si můžeme ukázat, jak snadno je možné upravit náš balíček tak, aby se mohl sémantický import provést. Do repositáře s balíčkem vložíme soubor go.mod vytvořený příkazem go mod init (ten jsme až doposud nepotřebovali, protože náš balíček na žádném dalším balíčku nezávisel). Důležité je, že nově vytvořený soubor go.mod vypadá takto:

module github.com/tisnik/integers/v4
 
go 1.13

Povšimněte si, že jsme jméno modulu rozšířili o suffix /v4. Po přidání tohoto souboru do repositáře byl vytvořen nový tag:

$ git tag -a v4.0.0 -m "Version 4.0.0 as module"

A z něj byla vydána nová verze balíčku.

18. Změna demonstračního příkladu – využití sémantického importu

Nyní musíme upravit i náš demonstrační příklad, a to tak, že změníme jméno importovaného modulu přidáním suffixu /v4:

package main
 
import (
        "fmt"
        "github.com/tisnik/integers/v4"
)
 
func Add(x int, y int) int {
        return x + y
}
 
func main() {
        fmt.Println(Add(integers.Zero, integers.Zero))
        fmt.Println(Add(integers.One, 0))
        //fmt.Println(Add(integers.MaxInt, integers.MinInt))
        fmt.Println(Add(1, 2))
        fmt.Println(Add(-1, 2))
}

V této chvíli je možné používat API verze 4.0.0, které obecně nemusí být kompatibilní se starším API. Povšimněte si, že další úpravy zdrojového kódu příkladu nebyly nutné, tj. stále používáme konstanty integers.Zero atd. a nikoli (například) v4.Zero.

Na závěr se podívejme, jak se změnil obsah souboru go.sum po vydání čtvrté verze s podporou sémantického importu:

$ cat go.sum 
 
github.com/tisnik/integers v1.0.0 h1:hTvDeOyjmLt1BJqeQmSnJNOlSst9LthLfQnU53h0Ik0=
github.com/tisnik/integers v1.0.0/go.mod h1:qfkSORAgW6E8jwfni4JdVE4AKzFvQs7239NSw/cf+fU=
github.com/tisnik/integers v1.1.0 h1:xygWKRg5EGDS/PZyR7SuhYp3PXQTkRdY6TcPsOd4iio=
github.com/tisnik/integers v1.1.0/go.mod h1:qfkSORAgW6E8jwfni4JdVE4AKzFvQs7239NSw/cf+fU=
github.com/tisnik/integers v2.0.0+incompatible h1:wJnU/uiLwDL1CWiDyhb+lhS/ByAkfWlAsRlh54wL5mc=
github.com/tisnik/integers v2.0.0+incompatible/go.mod h1:qfkSORAgW6E8jwfni4JdVE4AKzFvQs7239NSw/cf+fU=
github.com/tisnik/integers/v4 v4.0.0 h1:ynI0tihEdy9TGA5hbQFNcJHekTEBE5sgDJhfyIifGdA=
github.com/tisnik/integers/v4 v4.0.0/go.mod h1:O74h6zHFVUNb1K13mibPK1GGgRgJqKdEpp5Z6vay2Pc=

Vidíme, že na posledních dvou řádcích se objevila informace o nové verzi, včetně suffixu /v4.

19. Repositáře s demonstračními příklady

V dnešním článku jsme použili hned několik repositářů obsahujících jak demonstrační příklady, tak i repositáře s balíčky (jak verzovanými, tak i neverzovanými). Odkazy na jednotlivé repositáře jsou uvedeny v následující tabulce:

# Repositář Stručný popis Adresa na GitHubu
1 go-1st-module první testovaný modul https://github.com/tisnik/go-1st-module
2 go-2nd-module druhý testovaný modul https://github.com/tisnik/go-2nd-module
3 intvalues repositář s jedinou verzí balíčku intvalues https://github.com/tisnik/intvalues
4 integers repositář se čtyřmi vydáními balíčku integers https://github.com/tisnik/integers

20. Odkazy na Internetu

  1. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  2. Sémantické verzování
    https://semver.org/
  3. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  4. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  5. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  6. Modules
    https://github.com/golang/go/wi­ki/Modules
  7. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  8. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  9. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  10. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  11. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  12. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  13. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  14. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  15. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  16. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  17. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  18. gopls
    https://github.com/golang/go/wi­ki/gopls
  19. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  20. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  21. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  22. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  23. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  24. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  25. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  26. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  27. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  28. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  29. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  30. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  31. Package trace
    https://golang.org/pkg/runtime/trace/
  32. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  33. Command trace
    https://golang.org/cmd/trace/
  34. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  35. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  36. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  37. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  38. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  39. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  40. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  41. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  42. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  43. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  44. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  45. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  46. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  47. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  48. The LLDB Debugger
    http://lldb.llvm.org/
  49. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  50. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  51. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  52. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  53. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  54. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  55. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  56. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  57. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  58. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  59. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  60. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  61. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  62. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  63. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  64. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  65. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  66. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  67. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  68. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  69. go-cron
    https://github.com/rk/go-cron
  70. gocron
    https://github.com/jasonlvhit/gocron
  71. clockwork
    https://github.com/whiteShtef/cloc­kwork
  72. clockwerk
    https://github.com/onatm/clockwerk
  73. JobRunner
    https://github.com/bamzi/jobrunner
  74. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  75. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  76. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  77. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  78. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  79. go-prompt
    https://github.com/c-bata/go-prompt
  80. readline
    https://github.com/chzyer/readline
  81. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  82. go-readline
    https://github.com/fiorix/go-readline
  83. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  84. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  85. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  86. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  87. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  88. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  89. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  90. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  91. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  92. Editline Library (libedit)
    http://thrysoee.dk/editline/
  93. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  94. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  95. WinEditLine
    http://mingweditline.sourceforge.net/
  96. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  97. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  98. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  99. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  100. history(3) – Linux man page
    https://linux.die.net/man/3/history
  101. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  102. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  103. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  104. Balíček ogletest
    https://github.com/jacobsa/ogletest
  105. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  106. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  107. package testing
    https://golang.org/pkg/testing/
  108. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  109. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  110. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  111. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  112. GoConvey
    http://goconvey.co/
  113. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  114. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  115. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  116. package gg
    https://godoc.org/github.com/fo­gleman/gg
  117. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  118. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  119. The Go image package
    https://blog.golang.org/go-image-package
  120. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  121. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  122. YAML
    https://yaml.org/
  123. edn
    https://github.com/edn-format/edn
  124. Smile
    https://github.com/FasterXML/smile-format-specification
  125. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  126. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  127. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  128. Introducing JSON
    http://json.org/
  129. Package json
    https://golang.org/pkg/encoding/json/
  130. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  131. Go by Example: JSON
    https://gobyexample.com/json
  132. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  133. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  134. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  135. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  136. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  137. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  138. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  139. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  140. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  141. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  142. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  143. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  144. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  145. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  146. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  147. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  148. Algorithms to Go
    https://yourbasic.org/
  149. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  150. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  151. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  152. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  153. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  154. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  155. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  156. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  157. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  158. The Go Programming Language (home page)
    https://golang.org/
  159. GoDoc
    https://godoc.org/
  160. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  161. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  162. The Go Programming Language Specification
    https://golang.org/ref/spec
  163. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  164. Package builtin
    https://golang.org/pkg/builtin/
  165. Package fmt
    https://golang.org/pkg/fmt/
  166. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  167. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  168. Learning Go
    https://www.miek.nl/go/
  169. Go Bootcamp
    http://www.golangbootcamp.com/
  170. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  171. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  172. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  173. The Go Blog
    https://blog.golang.org/
  174. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  175. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  176. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  177. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  178. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  179. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  180. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  181. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  182. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  183. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  184. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  185. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  186. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  187. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  188. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  189. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  190. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  191. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  192. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  193. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  194. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  195. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  196. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  197. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  198. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  199. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  200. Go vs. Python
    https://www.peterbe.com/plog/govspy
  201. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  202. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  203. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  204. Go by Example: Slices
    https://gobyexample.com/slices
  205. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  206. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  207. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  208. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  209. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  210. nils In Go
    https://go101.org/article/nil.html
  211. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  212. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  213. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  214. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  215. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  216. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  217. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  218. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  219. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  220. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  221. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  222. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  223. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  224. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  225. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  226. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  227. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  228. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  229. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  230. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  231. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  232. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  233. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  234. Selectors
    https://golang.org/ref/spec#Selectors
  235. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  236. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  237. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  238. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  239. Part 21: Goroutines
    https://golangbot.com/goroutines/
  240. Part 22: Channels
    https://golangbot.com/channels/
  241. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  242. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  243. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  244. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  245. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  246. Control Structures
    https://www.golang-book.com/books/intro/5
  247. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  248. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  249. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  250. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  251. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  252. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  253. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  254. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  255. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  256. Effective Go
    https://golang.org/doc/ef­fective_go.html
  257. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  258. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  259. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  260. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  261. Nils in Go
    https://www.doxsey.net/blog/nils-in-go