Mėgstu programuoti Go. Dar mėgstu nedaryti rankomis to, ką turėtų daryti kompiuteris. Todėl šiandien parodysiu kaip neseniai suderinau šitus du gerus dalykus vienoje kombinacijoje, kurios nauda viršija dedamųjų sumą.
Kas per daiktas yra Go jau turbūt nereikia pasakot, visgi jau penki metai praėjo ir pakankamai buzzo nubuzėjo. (O pakol aš šito rašinio draftą voliojau, tai net ir 1.4 versiją išleido :-)). Tik paminėsiu, kad kuriant šitą kalbą, vienas iš tikslų buvo padaryti ją toolable, t.y. tokią, kuriai lengva rašyti įrankius.
Iš to išplaukia visokie malonūs, iš pirmo žvilgsnio nebūtinai akivaizdūs
efektai. Pvz., Go kalboje nesiginčijama apie kodo formatavimą. Nes yra
(standartinis) įrankis go fmt
, kuris suformatuoja kodą košer būdu. Ir bet
kokie ginčai yra beprasmiai ne filosofine, o labai praktine prasme. Panašiai
kaip koks Pythono PEP-8, tik realizuotas kode, su kurio pagalba aš galiu
10MLOC svetimo prastai suformatuoto kodo paversti standartiniu. Smulkmena, bet
maloni :-)
Taigi, mano projekte, kuris yra šio blogo kodas, buvo kažkiek instrukcijų kiek ir ko reikia susiinstaliuoti, kad susikompiliuotų. Maždaug toks sąrašiukas:
$ npm install
$ bower install ribs
$ cd src && go get
$ go get code.google.com/p/go-html-transform/css/selector
Pamaniau, būtų smagu viso to atsikratyti kaip nors gudriai, kad esant reikalui suveiktų automagiškai. Lyg tyčia sutapo keletas aplinkybių, pabaksnojusių, kad tinkamiausia tam vieta yra meikfailas, o ir būdas tą padaryti pasipainiojo laiku ir vietoje.
Kol kas paliksiu npm
ir bower
nuošaly, jie yra paprastesni, tad kai
išsiaiškinsime su go get
, anie du paaiškės savaime, pagal analogiją.
Pirmiausia pasišalino paskutinė eilutė. Ji buvo reikalinga parsiųsti
paketui, kuris naudojamas tiktai testuose, bet ne pagrindiniam kode. Pasirodo,
jos reikėjo tik dėl to, kad aš nepaskaičiau dokumentacijos ir nežinojau, kad
prie go get
pridėjus flagą -t
, parsiunčiami ir testams reikalingi paketai.
Valio.
Galima būtų kviesti šitą komandą kas kartą kompiliuojant. Ne baisiai ilgai
trunka pakartotinis jos paleidimas, kokią sekundę. Bet labai jau ilga ta
sekundė :-). Rimtai, ilga. Ant mano jau beveik trijų metų senumo laptopo,
paredagavus vieną failą, projekto perkompiliavimas ir visi testai užtrunka
3.3sec. make clean && time make
-- apie 5.5sec. Tai plius viena sekundė yra
daug.
Vietoj to galima pasinaudoti make
pagal paskirtį: kiekvieną biblioteką
nurodyti kaip targetą ir jeigu kurios nors trūksta, tik tada leisti go get
.
Kažkaip taip:
DEPENDENCIES = \
path/to/library1 \
path/to/library2 \
path/to/library3
$(DEPENDENCIES):
go get -t
Tik, žinoma, labai nesinori atskirai laikyti šito bibliotekų sąrašo. Visų pirma, jos jau yra suvardintos kažkur programos kode, nesinori rankioti. O kartą surankiojus, sąrašas būtinai ilgainiui išsiskirs su realybe ir pasens.
Štai čia ir pagelbės Go kalbos toolability.
Yra toks kartu su visa Go kalba ateinantis toolsas go
, kurio subkomandos
yra skirtingi įrankiai. Šiai užduočiai bus reikalingas go list
. Pastarasis
moka parodyti visokius dalykus apie kodą. Tame tarpe, lyg tyčia, moka išrašyti
visas importuojamas bibliotekas:
go list -f '{{.Deps}}' ./src
Šita komanda parodo viską, kas yra importuojama, maždaug taip (sąrašas sutrumpintas, palikti tik keli paketai iš beveik šimto):
bufio
compress/gzip
container/list
fmt
github.com/docopt/docopt-go
github.com/russross/blackfriday
golang.org/x/crypto/bcrypt
golang.org/x/crypto/blowfish
golang.org/x/net/html
Kaip matom, parodo visai viską, kas yra importuojama, įskaitant ir standartinės bibliotekos paketus, todėl reikia truputį pafiltruoti. Tikras kodas meikfaile atrodo maždaug taip:
GO_DEPS_CMD=\
go list -f '{{ join .Deps "\n"}}' ./src
GO_UNFILTERED_DEPS=${shell ${GO_DEPS_CMD}}
THIRD_PARTY_PKGS=github% golang.org/x/%
GO_UNIQUE_DEPS=\
$(sort $(filter $(THIRD_PARTY_PKGS),${GO_UNFILTERED_DEPS}))
Note: make’o
sort
ne tik rūšiuoja, bet ir išmeta dublikatus, kaip shellosort | uniq
Kai turim paketų sąrašą, belieka sukonstruoti tikrus kelius failų sistemoje.
Tą padaryti labai lengva, nes paketus Go parsisiunčia ir deda į $GOPATH
nurodytą vietą. Tereikia pridėti prefiksą:
GO_DEPS = $(addprefix $(GOPATH)/src/, $(GO_UNIQUE_DEPS))
Ir pagaliau galima pasimėgauti rezultatais:
$(GO_DEPS):
@echo "Running 'go get', this will take a few minutes..."
@go get -t ./...
Dabar kaskart paleidus make
, patikrinama ar neatsirado naujų importuojamų
paketų ir jeigu atsirado, parsiunčiama. Taip pat, parsisiuntus projektą
naujoje mašinoje (arba VM’e, arba naujam žmogui, arba continuous integration
serveryje), sutrumpėja šamaniškas prerekvizitų susirankiojimo šokis, kurį
reikia sušokti. Parsisiunti kodą ir iškart kvieti make
, tiek žinių.
Skirtumas tik toks, kad pirmą kartą make
ilgiau veiks kol viską parsiųs.
Baigdamas noriu padėkoti Pythono workshopo žmonėms, kurie savo projekto
aplinkos kūrimą užtęsė tiek, kad atkreipiau dėmesį į tai, kad mano
meikfailas yra buvo visai ne meikfailas, o kažkoks skriptas atsiprašant :-)