HotXLS se gradi pod Free Pascal-om i Lazarus-om na Windows-ima, i port je počivao na četiri odluke koje nemaju veze sa Object Pascal sintaksom: zadržati jezgro u DELPHIUNICODE režimu, deklarisati OLE structured-storage interfejse kao CORBA interfejse sa ručno vođenim brojanjem referenci, zameniti Win32 AES objektne datoteke Pascal implementacijom, i popraviti inflate petlju koja je mogla odsečeni ZIP da prihvati kao potpun
Svako ko je portovao zrelu Delphi biblioteku zna oblik ovog posla. Kompajler prihvata skoro sve u prvom prolazu. Sledi dug rep bihevioralnih razlika koje se čisto kompajliraju a proizvode pogrešne rezultate, i spreadsheet engine je neobično izložen njima jer dodiruje enkodiranje teksta, COM structured storage, kompresiju i kriptografiju u jednom kodnom putu
Zašto jezgro insistira na DELPHIUNICODE, a ne na čistom DELPHI?
Zato što formula engine zavisi od toga da String i Char nose UTF-16 semantiku, a ANSI alternativa gubi znakove pre nego što išta stigne do datoteke. Mamljivo je graditi jezgro u FPC DELPHI režimu, pošto je to kompatabilni switch kome većina portova poseže, i kod se kompajlira. Onda radna sveska sa kineskim imenima listova ili ćiriličnim etiketama ode kroz putanju računanja i znakovi su nestali do trenutka kad ih pisac vidi, bez ijedne greške bilo gde
Režim nije ujednačen kroz biblioteku, i to je namerno, a ne neuredno. PNG byte dekoder i LCL override-i stvarno trebaju ANSI potpise, jer se bave bajtovima i onim što im widgetset uruči. Ti unit-i uključuju poseban LX_FPC_ANSI switch. Dva režima u jednoj biblioteci zvuči kao smrad u kodu dok ne primetite da je alternativa byte dekoder koji svoj ulaz tretira kao tekst
Postoji i prateći detalj koji ljude uhvati kasnije. DELPHIUNICODE ne čini TFormatSettings.DecimalSeparator WideChar-om u FPC runtime-u. Ulaz koji nosi Unicode decimalni separator mora se prvo normalizovati na ASCII separator unutar Unicode stringa, i svaki ulaz čiji separator ne odgovara očekivanom mora se odbiti, a ne tiho odseći na znaku koji parser nije prepoznao
program ExportReport;
{$MODE DELPHI}
uses
Interfaces, // mora doći prvo: inicijalizuje LCL widgetset
SysUtils, lxHandle; // i sloj UTF-8 konverzije
var
Book: TXLSWorkbook;
begin
Book := TXLSWorkbook.Create(nil);
try
Book.LoadFromFile('input.xls');
Book.Sheets[0].AsString[1, 1] := 'Quarterly summary';
Book.SaveToFile('output.xls');
finally
Book.Free;
end;
end.
Interfaces unit nije opcion i mora doći prvi. On inicijalizuje LCL widgetset i sloj UTF-8 konverzije, i HotXLS se oslanja na oba čim fontovi, putanje datoteka ili tekst pređu RTL i LCL granicu. Konzolni program koji ga preskoči kompajliraće se i ponašaće pogrešno na svakoj ne-ASCII putanji. To je i razlog što uspešno kompajliranje ovde dokazuje tako malo: port je demonstrabilno radio tek kad su stvarni dokumenti sa stvarnim imenima fontova i stvarnim putanjama napravili pun povratni krug
Class VMT nije COM vtable
Free Pascal vam neće dozvoliti da Windows-u uručite class VMT kao COM interface vtable, čak i kad deklaracija izgleda identično onoj koju Delphi prihvata. Rasporedi se razlikuju na načine koji proizvode poziv u pogrešan slot, što se manifestuje kao pad negde nepovezan sa mestom poziva. Structured storage je ovde važan jer je klasični binarni format radne sveske OLE compound datoteka, i njeno čitanje ili pisanje znači implementirati ILockBytes u koje će Windows storage API pozivati nazad
Radni aranžman je CORBA interfejs sa eksplicitno deklarisanim COM slotovima i AddRef i Release vođenim ručno. To znači odreći se automatskog brojanja referenci za ove tipove i preuzeti odgovornost za životni vek, što je fer razmena za šaku interfejsa koji žive unutar jednog unit-a. Konkretna zamka unutar toga posla je QueryInterface: mora da vrati interface pointer, a ne object pointer. Obe varijante se kompajliraju. Jedna od njih uruči Windows-u adresu čija prva mašinska reč nije vtable
FPC-specifične deklaracije žive u lxOleInterfaces.inc, pored lxAESBackend.inc i lxZlibBackend.inc u FPC direktorijumu izvora, pa kompajlerski specifične odluke sede na jednom mestu umesto da su razasute kroz engine. Sam format i kako ga biblioteka obilazi opisani su u članku o čitanju OLE2 compound datoteka u Pascal-u
Još jedan tip detalj pripada istoj familiji. LargeInt mora da se razreši u Int64 u FPC grani, i kompajlerska klasifikacija Comp-a dovoljno se razlikuje između dve alatke da overload rezolucija može izabrati drugog kandidata. Testirajte ponašanje velikih ofseta file stream-om, a ne HGLOBAL stream-om: Windows global-memory stream sam se omotava pri seeks preko 4 GiB, pa položen test tamo ne dokazuje ništa o vašoj aritmetici
Šta samousaglašena AES implementacija krije
Win32 AES objektne datoteke koje Delphi build povezuje su OMF, i Free Pascal linker ne ume da ih koristi, pa FPC grana koristi Pascal AES implementaciju umesto njih. Delphi i dalje povezuje objektne datoteke koje je oduvek, što objavljeni binarni artefakt drži nepromenjenim za postojeće kupce
Zahtev verifikacije je deo vredan nošenja u svaki projekat. Šifrovanje podataka i dešifrovanje istom implementacijom ne dokazuje ništa: simetričan algoritam sa pogrešnim key schedule-om, pogrešnim redosledom blokova ili pogrešnim ulančavanjem savršeno je samousaglašen i svaki put će round-trip-ovati svoj izlaz. Samo known-answer vektori ga hvataju, proverom key ekspanzije, redosleda blokova i CBC ulančavanja naspram objavljenih vrednosti. Isporučite samousaglašenu pogrešnu implementaciju i simptom se pojavi prvi put kad kupac otvori datoteku u Excel-u
Kompresija je imala defekt drugačijeg karaktera. Pascal inflate backend može i dalje imati izlaz na čekanju pošto je potrošio sav komprimovani ulaz, pa pozivalac mora da nastavi da zove dok tok ne prijavi svoj kraj. Tretiranje istrošenog ulaza kao kraja toka odseca poslednji blok. Gore od toga, od oštećene arhive pravi tiho prihvaćenu, a to je baš režim otkaza kome očvršćavanje iz članka o validaciji ZIP end-of-central-directory zapisa postoji da ga spreči. Pravilo je da nema napretka plus nije gotovo jeste greška odsecanja, nikada EOF
Dve zamke build sistema koje koštaju stvarnih sati
LCL search putanje moraju prethoditi FPC wildcard putanjama paketa, ili Free Vision Menus unit zasenjuje LCL unit istog imena i dobijate PPU checksum neslaganje koje ne kaže ništa o nijednom od ta dva. Lazarus instalacija premeštena posle instaliranja može takođe ostaviti zastarele putanje u fpc.cfg, pa build ulazne tačke eksplicitno navode unit i binarne putanje umesto da naslede šta god okruženje ponudi
Druga zamka nema nikakve veze sa Pascal-om. .cmd batch datoteka pisana LF krajevima linija radi dok datoteka ne naraste preko veličine read bafera interpreter-a, kad call :label padne sa tvrdnjom da batch labela ne postoji, i otkaz se pojavi na bilo kom programu koji slučajno sedi preko granice. Svaka alatka koja prepisuje batch skriptu mora da upiše CRLF nazad. I lazbuild --build-all čisti direktorijum izlaza paketnih unit-a pre kompajliranja, pa datoteka sa opcijama parkirana u tom direktorijumu bude obrisana pre nego što se može pročitati: držite je van, i setite se da se @ putanja razrešava relativno na direktorijum paketa jer lazbuild poziva kompajler odatle
// Lazarus grid export: TGridToXLS stiže u Lazarus paketu, pa isti
// DB-grid export kod radi u LCL aplikaciji
var
Exporter: TGridToXLS;
begin
Exporter := TGridToXLS.Create(nil);
try
Exporter.DBGrid := GridOrders;
Exporter.WorksheetName := 'Orders';
Exporter.ExportHeader := True;
Exporter.SetColumnsWidth := True;
Exporter.ExportDBGrid;
Exporter.SaveAs('orders.xls');
finally
Exporter.Free;
end;
end;
Koliko vredi upozorenje kompajlera
Free Pascal prijavljuje neinicijalizovane lokalne promenljive koje Delphi ne, i pokretanje FPC build-a pretvorilo je tu razliku u dva stvarna defekta u unit-u računanja. Jedna funkcija čitala je brojačku promenljivu koja nikada nije dodeljena pre upotrebe, a druga koristila je dve koordinate u jednoj grani pre nego što je kod koji ih je računao radio u drugoj grani. Pod Delphi-jem su se obe ponašale prema tome što je stek slučajno držao, a to je definicija baga koji se reprodukuje na jednoj mašini, a ne na drugoj
Praktičan zaključak je da drugi kompajler vredi držati u krugu čak i za proizvod koji se prvenstveno isporučuje na prvom. Pregledanje FPC klasa upozorenja periodično je jeftin prolaz statičke analize nad Delphi kodnom bazom, i nalazi kategoriju defekata do koje nijedan test suite pouzdano ne dopire. Šira disciplina verzione matrice unutar koje ovo sedi opisana je u članku o cross-kompajlerskoj build matrici
Podrška Free Pascal-a i Lazarus-a za Windows stiže uz HotXLS Delphi spreadsheet komponentu kao Lazarus paket uz Delphi i C++Builder pakete, građen iz istog stabla izvora, a ne iz forka. U tome je poenta vežbe: jedan engine, četiri alatke, i kompajlerski specifične odluke izolovane u include datotekama gde se mogu pročitati za jedno sedenje