[ Webhosting profitux.cz ]
v6ak [ programování, bezpečnost, web, php, java, ... ] (Vít Šesták)
Buzz - v6ak Twitter - v6ak

štítky

Proč negenerovat zdrojový kód?

Psát zdrojový kód byla svého času výsada programátorů. Dnes existuje mnoho programů, které zdrojový kód generují, ať už jde o Google Protocol Buffers compiler, generování nudných metod jako třeba equals(Object) nebo generování Javascriptu Google Web Toolkitem. Může jít jak o generování kousku kódu, tak o vytvoření nového vlastního jazyka. Vede tudy skutečně cesta? Skutečně tu není jakékoli lepší řešení?

Jak generovat kód?

V každém případě je důležité mít určité návyky na vhodné generování kódu. Nejlepší je si generovaný kód oddělit od ručně psaného. Proč? Stačí si představit, že to tak neuděláme a generovaný kód budeme ručně upravovat. Potom budeme potřebovat kód vygenerovat (kvůli nějaké změně) znovu. Tím ale přijdete o všechny ruční zásahy, které máte v lepším případě někde poznamenané. I v takovém případě ale ztrácí generování kódu část svého významu a objevuje se nám tady nějaká nepříjemná automatická ruční práce.

Anebo budeme mít v souborech nějaké oddělovače generovaného a ručně psaného kódu a před kompilací (případně před spuštěním) vždy spustíme nějaký nástroj (o to se může starat IDE), který to přegeneruje. Brr! Proč mám o generovaný kód „zakopávat“ v kódu ručně psaném? A co když nějak omylem upravím hranici generovaného a ručně psaného kódu a kus ručně psaného kódu bude generátorem smazán? Nebo co když bude v generátoru nějaká chyba? Ne, tudy cesta nevede. Teda vede, ale může být pořádně trnitá.

Automatické zásahy do generovaného kódu

Pokud chcete mírně vygenerovaný kód upravit (například změnit viditelnost vygenerované třídy nebo přidat nějaké @SuppressWarnings), pak se hodí do činnosti zapojit automatiku. Pro Linux i Windows existuje nástroj sed (na Windows jako součást Cygwinu). Ten umožní udělat jednoduché úpravy. Příklad použití:

sed -i 's/public final class/@SuppressWarnings({"all"}) final class/' soubor.java

V tomto případě potlačíme varování na vygenerované třídě, protože kód generovaný Google Protocol Buffers compilerem (protoc) po naostření varování v mém IDE neprošel některými kontrolami a zanášel mi kartu Problems větším množstvím varování. Bohužel, některých jsem se nemohl zbavit selektivnějším @SuppressWarnings, takže jsem použil all. Věřím ale, že Google má protoc dobře otestovaný a že jakékoli varování mě zde nemusí zajímat. Dále v kódu ze třídy odstraňuji modifikátor public, protože generovaná třída je pouze implementačním detailem daného jmenného prostoru (package).

Samozřejmě, toto probíhá ihned po každém vygenerování kódu. A nedělám to ručně, hodí se tu použít přinejmenším nějaký shellový skript nebo baťák.

Kde jsou čísla řádků?

Pokud dojde k chybě za běhu, máme dnes zpravidla účinné nástroje na nalezení místa problému v podobě určení řádku problému. (Místo skutečné chyby může být samozřejmě někde jinde, ale aspoň máme výchozí bod a celkem dobré podmínky pro hledání.)

Pokud ale do svého kódu přidám automaticky něco vygenerovaného (to můžu dělat i automaticky, třeba nástrojem delombok) nebo je kód kompletně generovaný (například Nette LatteFilter pro šablony), pak s tímto nástrojem máme problém. Čísla řádku totiž pravděpodobně nebudou soulasit se zdrojovým kódem a následuje někdy ne úplně příjemné hledání. A abstrakce nám vesele prosakuje. Někdy to nemusí být zásadní problém (typicky u „průhlednějších“ vrstev), někdy by k tomu dojít dokonce nemělo (například u Google Protocol Buffers), jindy to ale příjemné být nemusí.

Jak řešit čísla řádků

Generování bytecode

Pokud to situace dovoluje, považuji za vhodné řešení generovat bytecode. To se týká hlavně Javy a asi i .NETu.

Bytecode totiž může (minimálně v Javě) obsahovat tabulku čísel řádků.

Na práci s bytecode je tu mnoho knihoven, například BCEL (mám zkušenosti a nedoporučuji, doufám, že existuje něco lepšího), ASM, a mnoho dalších.

Metoda je nevhodná pro generování části kódu. Představte si, že budete chtít vygenerovat getter metodu. Tu musíte vygenerovat ještě před kompilací, pokud ji chcete uvnitř kódu volat.

Manipulace s AST

Manipulace a vytváření syntaktického stromu může být příjemnější než práce s bytecode. Některé syntaktické stromy umožňují i určování čísel řádků.

K výhodám patří i to, že je tato metoda vhodná i pro úpravy kódu. Nutná je ale v takovém případě podpora kompilátoru. To může být problém v situaci, kdy různé kompilátory mají různá API pro takovéto handlery.

Tuto možnost využívá i Lombok. Pokud to správně chápu, tento projekt se snaží ulehčit psaní vlastního handleru funkčního napříč kompilátory. S tím souvisí projekt lombok.ast. Lombok je však z tohoto pohledu trošku hybridní (nabízí i jinou možnost), neboť pro případ potřeby nabízí nástroj delombok, který generuje zdrojový kód. Je tu především pro případ, že by to nešlo jinak (JavaDoc, GWT, nekompatibilní změna API kompilátoru...).

Překlad čísel řádků

Je možné udělat tabulku, podle které si programátor z čísla řádku v generovaném kódu zjistí číslo řádku ve zdrojovém kódu. Tento překlad jsem navrhl pro Nette, zatím se ale můj návrh nedočkal jakékoli reakce. Překlad samozřejmě nemusí být vždy úplně pohodlný, protože programátor může být nucen se dívat do externí tabulky. To je ale spíše otázka vhodných nástrojů a integrace.

Pozor na odkazy v IDE

Používáte (např. voláte) nějakou metodu, třídu, funkci (...) a chcete v IDE přejít na její definici. Například Eclipse na to nabízí klávesu F3. To je výborné, jenže IDE vás může zavést stejně tak dobře na generovaný kód. Vzhledem k tomu, že se můžete dostat třeba doprostřed souboru, varování o genrovaném kódu nemusí být účinné. Pokud je mezi generovaným a původním kódem menší rozdíl, nemusíte si toho všimnout. (Jasně, v případě Google Protocol Buffers by to asi vyžadovalo pořádnou dávku nepozornosti nebo neseznámenosti se situací.) Asi jsem v Nette dělal šablony trošku špatně, ale při jejich tvorbě jsem se již takto spálil. Samozřejmě, pokud se kód přegeneruje a vy jej nemáte kdekoli otevřený, pak je dost možná ztracen, zvlášť je-li adresář temp v něčem jako .gitignore. A překvapení nad náhle zmizelými změnami při další celkem nesouvisející úpravě může být také dosti nepříjemné.

Tady je jedna jednoduchá rada (možná poněkud těžší na provedení): omezte svému IDE přístup k tomuto kódu. Problém je v situaci, kdy to IDE musí i kompilovat a tím pádem musí kód mít přístupný. IDE může obsahovat nějakou volbu zákazu editace, ale ještě jsem se s tím nesetkal.

A co částečné třídy?

V C# jsem se setkal s částečnými třídami (partial classes). Narazil jsem na ně nedávno náhodou. V C# jsem zatím neprogramoval, ale myslím, že mohou být zajímavým řešením. Ale pořád se mi na nich něco nelíbí a nedovedu to úplně přesně popsat. Bez nich mám (aspoň v Javě) při kompilaci třídy téměř jistotu: buď má při kompilaci vše potřebné a je to OK, nebo tomu něco chybí a kompilátor mi to dá hlasitě najevo. Ano, můžete namítnout, že to zjistíme nejpozději při kompilaci jiné třídy a někdy ještě dříve. Ale i tak se mi to nelíbí. Potřeboval bych někde seznam souborů nebo něco podobného, abych proti tomu neměl tyto výhrady.

Vygenerovaný kód: „Kam s ním?“

U vygenerovaného kódu si můžeme položit přímo Nerudovskou otázku „Kam s ním?“. Asi tušíte, že dávat jej společně s ručně psaným kódem nebude ideální.

Pokud máte tu možnost, pak je vhodné vytvořit zvláštní adresář pro generovaný kód, který bude umístěn mimo kód ručně psaný. Musíte tak nastavit kompilátor a případně i IDE. V případě IDE může být nejlepší mu poskytnout generované soubory pouze ve zkompilované podobě kvůli zmíněnému problému s odkazy. Pokud problém s odkazy řešit nechcete (považujete jej za nepravděpodobný - např. u Google Protocol Buffers), může být nejjednodušším řešením prostě vytvořit další adresář se zdrojovými kódy (screenshot z Eclipse):
Přidání adresáře se zdrojovými kódy v Eclipse

Nemáte-li jinou možnost, pak aspoň soubory s generovaným kódem vhodně na začátku označte komentářem, že jde o generovaný soubor. Není to všelék, vizte odkazy v IDE, ale aspoň trošku pořádku to zajistí.

Jak kompilovat?

Můžete použít nějaký buildovací nástroj jako je třeba Ant nebo Maven. Někdy se může ale jevit jako příliš těžkopádný a dáte přednost řešení v IDE. Například Eclipse nabízí systém builderů, kde si můžete přidat svůj vlastní:
Buildery v Eclipse

Je také vhodné si dobře nastavit kdy má být daný builder spouštěn, aby změna zdrojového souboru způsobila přegenerování.

Závěr

Generování zdrojového kódu je sice cesta, ale může být více nebo méně trnitá. Abychom se nespálili (teda vlastně nepopíchali :-) ), měli bychom si být vědomi jejich úskalí. Tuto cestu v zásadě nezavrhuji, koneckonců sám používám Lombok a někdy i Google Protocol Buffers a Nette šablony.

Diskuzi ke článku naleznete zde.

Linkování

Líbí se Vám tato stránka? Zalinkujte ji!

Chcete sledovat novinky? Pokud si právě prohlížíte článek a hledáte RSS pro celý web, pak jste trošku jinde. Možná hledáte poslední změny.

Validní HTML 4.01 StrictValidní CSS 2.0Validní hlavní RSS kanálPHP 5Apache
referer: UA:CCBot/2.0 (http://commoncrawl.org/faq/) time:0.07863300 1495508631
web
mail
comment