Blogity blog blog


Dependensų bootstrapinimas su Go

2014-12-13, rtfb

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 shello sort | 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 :-)

 
 

Niekas dar nekomentavo

Mano nacis spam-filtras visus laiko botais.
Išspręskite šitą captcha jeigu jūs ne toks:

9 + 2 =