HotXLS se gradi pod Free Pascalom i Lazarusom na Windowsima, i port se svodio na četiri odluke koje nemaju ništa s Object Pascal sintaksom: držati core u DELPHIUNICODE modu, deklarirati OLE structured-storage interface kao CORBA interface s ručno vođenim reference countingom, zamijeniti Win32 AES objektne datoteke Pascal implementacijom, i popraviti inflate petlju koja je mogla prihvatiti skraćeni ZIP kao kompletan
Tko je ikad portirao zrelu Delphi biblioteku zna oblik ovog posla. Kompajler prihvati skoro sve u prvom prolazu. Slijedi dug rep behavioralnih razlika koje se čisto kompajliraju a proizvode krive rezultate, i spreadsheet engine je njima neobično izložen jer dodiruje text encoding, COM structured storage, kompresiju i kriptografiju u jednom code putu
Zašto core inzistira na DELPHIUNICODE a ne na običnom DELPHI?
Jer formula engine ovisi o tome da String i Char nose UTF-16 semantiku, a ANSI alternativa gubi znakove prije nego išta stigne do datoteke. Primamljivo je graditi core u FPC DELPHI modu, jer je to kompatibilnost switch za koji se većina portova poseže, i kod se kompajlira. Onda radna knjiga s kineskim imenima listova ili ćiriličnim labelama prođe kroz calculation put natrag i znakovi su nestali do trenutka kad ih writer vidi, bez ijedne greške bilo gdje
Mod nije uniforman kroz cijelu biblioteku, i to je namjerno a ne neuredno. PNG byte dekoder i LCL overridei stvarno trebaju ANSI signature, jer rade s bajtovima i s onim što im widgetset pruža. Ti uniti uključuju poseban LX_FPC_ANSI switch. Dva moda u jednoj biblioteci zvuči kao smell dok ne primijetite da je alternativa byte dekoder koji svoj ulaz tretira kao tekst
Tu je prateći detalj koji kasnije ulovi ljude. DELPHIUNICODE ne čini TFormatSettings.DecimalSeparator WideCharom u FPC runtimeu. Ulaz koji nosi Unicode decimalni separator mora se najprije normalizirati na ASCII separator unutar Unicode stringa, i svaki ulaz čiji se separator ne poklapa s očekivanim mora se odbiti umjesto tiho skratiti na znaku koji parser nije prepoznao
program ExportReport;
{$MODE DELPHI}
uses
Interfaces, // mora doći prvi: inicijalizira LCL widgetset
SysUtils, lxHandle; // i UTF-8 konverzijski sloj
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 opcionalan i mora doći prvi. On je ono što inicijalizira LCL widgetset i UTF-8 konverzijski sloj, i HotXLS se oslanja na oba čim fontovi, putanje datoteka ili tekst prijeđu RTL i LCL granicu. Console program koji ga preskoči kompajlirat će se i pogrešat će na svakoj ne-ASCII putanji. To je ujedno i razlog zašto uspješna kompilacija ovdje dokazuje tako malo: port je dokazano radio tek kad su pravi dokumenti s pravim imenima fontova i pravim putanjama napravili potpuni round trip
Class VMT nije COM vtable
Free Pascal vam neće dopustiti da class VMT predate Windowsima kao COM interface vtable, čak i kad deklaracija izgleda identično onoj koju Delphi prihvaća. Rasporedi se razlikuju na načine koji proizvode poziv u krivi slot, što se manifestira kao crash negdje nepovezan s call siteom. Structured storage je tu bitan jer je klasični binary format radne knjige OLE compound datoteka, i čitati je ili pisati znači implementirati ILockBytes u koje će Windows storage API pozivati natrag
Radni aranžman je CORBA interface s eksplicitno deklariranim COM slotovima i AddRef i Release vođenim rukom. To znači odustati od automatskog reference countinga za ove tipove i preuzeti odgovornost za životni vijek, što je pošten trade za šaku interfacea koji žive unutar jednog unita. Specifična zamka u tom poslu je QueryInterface: mora vratiti interface pokazivač, a ne pokazivač objekta. Oboje se kompajlira. Jedno od toga predaje Windowsima adresu čija prva mašinska riječ nije vtable
FPC-specifične deklaracije žive u lxOleInterfaces.inc, pokraj lxAESBackend.inc i lxZlibBackend.inc u FPC source direktoriju, pa kompajlerski specifični izbori sjede na jednom mjestu umjesto da su rasuti kroz engine. Sam format i kako ga biblioteka prelazi opisani su u čitanju OLE2 compound datoteka u Pascalu
Još jedan tip detalj pripada istoj familiji. LargeInt se mora razriješiti u Int64 u FPC grani, i kompajlerska klasifikacija Compa se dovoljno razlikuje između dva toolchaina da overload resolution može odabrati drugog kandidata. Testirajte large-offset ponašanje file streamom umjesto HGLOBAL streamom: Windows global-memory stream sam se omota oko seekova preko 4 GiB, pa prolazni test tu ne dokazuje ništa o vlastitoj aritmetici
Što samokonzistentna AES implementacija krije
Win32 AES objektne datoteke koje Delphi build linka su OMF, i Free Pascal linker ih ne može konzumirati, pa FPC grana umjesto toga koristi Pascal AES implementaciju. Delphi i dalje linka objektne datoteke koje je oduvijek linkao, što izdani binary drži nepromijenjenim za postojeće kupce
Zahtjev verifikacije je dio koji vrijedi ponijeti u svaki projekt. Šifrirati podatke i dešifrirati ih opet istom implementacijom ne dokazuje apsolutno ništa: simetrični algoritam s krivim key scheduleom, krivim redoslijedom blokova ili krivim chainingom je savršeno samokonzistentan i svaki će put round-tripati svoj vlastiti izlaz. Samo known-answer vektori ga ulove, provjeravajući key expansion, redoslijed blokova i CBC chaining protiv objavljenih vrijednosti. Isporučite samokonzistentnu krivu implementaciju i simptom se pojavi prvi put kad kupac otvori datoteku u Excelu
Kompresija je imala defekt drugačijeg karaktera. Pascal inflate backend može još imati izlaz na čekanju nakon što je potrošio sav kompresirani ulaz, pa pozivatelj mora zvati dok stream ne javi svoj kraj. Tretirati iscrpljeni ulaz kao kraj streama skraćuje zadnji blok. Gore, pretvara oštećenu arhivu u tiho prihvaćenu, a to je točno failure mod koji hardening u validaciji ZIP end-of-central-directory recorda postoji da spriječi. Pravilo je da nema napretka plus nije gotovo je truncation greška, nikad EOF
Dvije build-sustav zamke koje koštaju stvarnih sati
LCL search putanje moraju prethoditi FPC package wildcard putanjama, ili Free Vision Menus unit zasjeni LCL unit istog imena i dobijete PPU checksum mismatch koji ne govori ništa o nijednome. Lazarus instalacija premještena nakon instalacije može također ostaviti zastarjele putanje u fpc.cfg, pa build entry pointi specificiraju unit i binary putanje eksplicitno umjesto da nasljeđuju što god okolina ponudi
Druga zamka nema nikakve veze s Pascalom. .cmd batch datoteka pisana s LF završetcima linija radi dok datoteka ne naraste preko veličine interpreterovog read buffera, u kojem trenutku call :label puca s tvrdnjom da batch labela ne postoji, i neuspjeh se pojavi na bilo kojem programu koji slučajno sjedi iza granice. Svaki alat koji prepisuje batch skriptu mora napisati CRLF natrag. I lazbuild --build-all očisti package unit output direktorij prije kompilacije, pa options datoteka parkirana u tom direktoriju bude izbrisana prije nego što se može pročitati: držite je izvan, i zapamtite da se @ putanja razrješuje relativno prema package direktoriju jer lazbuild poziva kompajler odatle
// Lazarus grid export: TGridToXLS dolazi 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;
Vrijednost kompajlerskih upozorenja
Free Pascal javlja neinicijalizirane lokalne varijable koje Delphi ne javlja, i pokretanje FPC builda pretvorilo je tu razliku u dva stvarna defekta u calculation unitu. Jedna function čitala je count varijablu koja nikad nije bila dodijeljena prije upotrebe, a druga je koristila dvije koordinate u jednoj grani prije nego što je kod koji ih je računao trčao u drugoj grani. Pod Delphijem su se obje ponašale prema tome što je stog slučajno držao, a to je definicija buga koji se reproducira na jednoj mašini a ne na drugoj
Praktični zaključak je da vrijedi drugi kompajler držati u petlji čak i za proizvod koji se prvenstveno isporučuje na prvome. Pregledavati FPC warning klase periodično je jeftin static analysis prolaz preko Delphi codebasea, i nalazi kategoriju defekta do koje nijedan test suite pouzdano ne dopire. Šira disciplina verzija-matrice u koju se ovo uklapa opisana je u cross-compiler build matrici
Free Pascal i Lazarus podrška za Windows isporučuje se uz HotXLS Delphi spreadsheet komponentu kao Lazarus paket uz Delphi i C++Builder pakete, građena iz istog stabla izvora umjesto forka. U tome je poanta vježbe: jedan engine, četiri toolchaina, i kompajlerski specifične odluke izolirane u include datotekama gdje ih se može pročitati u jednom sjedenju