PDFium komponenta nalazi svoju nativnu biblioteku kroz fiksni, uređen lanac pretrage umesto da to prepusti učitaču operativnog sistema, jer je stablo raspoređivanja koje je eksplicitno stablo koje možete otklanjati. Na Windows-u taj lanac traži Win32 ili Win64 poddirektorijum koji instalater već isporučuje. Na drugim ciljevima gradi ime poddirektorijuma iz makroa Free Pascal cilja, kao <cpu>-<os>, pa se stablo raspoređivanja čita tačno kao stablo kompajliranih jedinica. Ta poslednja odluka unela je grešku vrednu celog članka, jer je uzrok bilo veliko slovo, a simptom tišina
Lanac, po redu
Četiri lokacije, pokušavane u nizu, pa učitač platforme kao poslednje utočište. Prvo željeni raspored, DLLs direktorijum pored izvršne datoteke koji sadrži po jedan poddirektorijum po cilju. Drugo alternativni raspored sa ciljnim poddirektorijumom pravo pored izvršne datoteke. Treće ravni nasleđeni raspored, biblioteka sedi pored izvršne datoteke bez ikakvog poddirektorijuma. Četvrto, samo na Windows-u, sistemski direktorijum, koji traži pažnju jer 32-bitni proces mora gledati u SysWOW64 a 64-bitni proces u System32, i na 32-bitnom Windows-u prvi ne postoji pa traženje mora pasti nazad. Tek posle svega toga učitač se traži da traži sam
Nema namerno koraka sistemskog direktorijuma van Windows-a. Sopstvena putanja pretrage učitača platforme, vođena konfiguracijom runtime linkera i okruženjem putanje biblioteka, već pokriva to tlo, i dupliranje u Pascal-u značilo bi ponovno implementiranje pravila koja variraju po distribuciji. Dijagnostikovanje otkazivanja u Windows lancu pokriveno je odvojeno u raspoređivanju PDFium DLL-a i dijagnostikovanju otkazivanja učitavanja
Odakle ime poddirektorijuma dolazi
Na Windows-u to je Win32 ili Win64, odlučeno bitnošću procesa u izvršavanju umesto operativnog sistema, jer to određuje koja binarna datoteka može biti učitana. Svuda drugde ime se gradi iz makroa cilja kompajlera tako da mašina koja gradi za dve arhitekture proizvodi dva jasno odvojena stabla, i tako da fascikla koja drži nativnu biblioteku sedi pored fascikle koja drži kompajlirane jedinice sa istim imenom
function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
if IsWin64 then
Result := 'Win64'
else
Result := 'Win32';
{$ELSE}
// Makroi kompajlera pišu OS velikim početnim slovom ("Linux",
// "Darwin") dok izlazni direktorijum jedinica paketa ne, pa se dva
// slažu tek posle savijanja. Na sistemu datoteka osetljivom na
// velika i mala slova ta razlika je celo traženje
Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;
Zašto je jedno veliko slovo slomilo ceo lanac
Makro kompajlera piše ciljni operativni sistem sa početnim velikim slovom: Win64, Linux, Darwin. Lazarus paket piše svoj izlaz jedinica u direktorijum imenovan iz sopstvene ciljne promenljive, koja je mala slova: win64, linux, darwin. Dva pravopisa iste stvari, i nema načina primetiti na Windows-u, gde sistem datoteka ne razlikuje
Na Linux-u oni su dva različita direktorijuma. Raspoređivanje koje stavi deljeni objekat u DLLs/x86_64-linux nevidljivo je učitaču koji traži DLLs/x86_64-Linux, pa sva četiri eksplicitna koraka lanca promaše i kod pada dalje na pustiti učitača platforme da traži. Ponekad to radi, ako se biblioteka slučajno instalirana sistemski, i ponekad ne, i svakako pažljivo uređeno stablo raspoređivanja ne doprinosi ništa. Otkazivanje nema poruku greške jer ništa nije otkazalo: svaki korak ispravno je izveštavao da datoteka nije tamo gde je gledao
Probni program, kompajliran i pokrenut
Ova klasa grešaka ne može se naći čitanjem, i ne može se naći ni kompajliranjem. Uobičajena tehnika za verifikaciju grane platforme koja se nikad ne kompajlira na razvojnoj mašini jeste kopirati jedinicu u privremeni direktorijum, preimenovati je, zameniti platformski uslovni simbolom koji nikad nije definisan, i kompajlirati kopiju; ako se kompajlira, uses klauza i potpisi poziva na toj putanji barem su samo-konzistentni. To dobro radi za samodovoljnu jedinicu
Ovde ne radi. Glavna jedinica vezivanja vrlo je velika i vuče LCL, pa se ne može jednostavno kopirati i kompajlirati sa Windows simbolom isključenim. Umesto toga, pregršt funkcija koje je izmena dirala prepisano je doslovno u mali samodovoljan program, i taj program je pokrenut. Ispisao je x86_64-Win64, i neslaganje bilo je vidljivo u jednoj liniji izlaza. Kompajliranje istog programa reklo bi vam ništa, jer je niz znakova savršeno valjan; samo je njegova vrednost pogrešna
program ProbeSubDir;
{$MODE DELPHI}
uses
SysUtils;
begin
// Ispišite, ne tvrdite. Poenta je pogledati vrednost koju makro
// zapravo širi na ovom toolchain-u
Writeln('raw: ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
{$I %FPCTARGETOS%}));
end.
Opšta lekcija: kada se međuplatformska izmena tiče vrednosti nečega umesto njegovog tipa, verifikacija samo-kompajliranjem nije verifikacija. Ispišite je. Širi skup razlika među-kompajlera između Delphi-ja i Free Pascal-a sabran je u članku o zamkama Delphi i FPC među-kompajlera
Pustite platformu da objasni sopstvene neuspehe učitavanja
Windows grana učitača ručno nabraja razloge zbog kojih učitavanje može otkazati, jer se korisne razlike tamo, neslaganje arhitekture, nedostajuća tranzitivna zavisnost, putanja koja se ne razrešuje, mapiraju u kodove grešaka vredne imenovanja pojedinačno. Van Windows-a prenosiva jedinica učitača već vraća opisni niz znakova koji pokriva isto tlo, pa non-Windows grana koristi nju direktno umesto ponovnog izvođenja kategorija iz broja greške koji znači različite stvari na različitim sistemima
Otpor nagonu da se te dve normalizuju u jednu poruku namerni je. Neuspeh učitavanja problem je raspoređivanja, i osoba koja čita poruku traži sopstveni rečnik platforme da ga pronađe
Sudar imena koji se rekurzira
Još jedna zamka, mala i oštra. Prenosiva jedinica učitača izvozi proceduru zvanu UnloadLibrary, i jedinica vezivanja ima proceduru istog imena koja radi svoje knjigovodstvo pre oslobađanja handle-a. Unutar te procedure, nekvalifikovan poziv UnloadLibrary razrešava se u onu u tekućoj jedinici, koja zove samu sebe. Popravka je kvalifikovati poziv imenom jedinice
Ovo je isti oblik kao problemi zasenjivanja identifikatora koji dominiraju Free Pascal portovima opšte: Windows jedinica izvozi minimum i maksimum funkcije celobrojnog tipa koje zasenjuju verzije sa pokretnim zarezom, i tip sinhronizacije koji zasenjuje klasu istog imena, i u svakom slučaju razrešenje zavisi od redosleda uses klauze. Kvalifikovanje mesta poziva je popravka koja ne zavisi od nekoga ko čuva taj redosled kasnije
Popis provere raspoređivanja
Tri stvari objašnjavaju većinu neuspeha učitavanja jednom kada je aritmetika putanje ispravna. Arhitektura mora odgovarati procesu, ne mašini, pa 32-bitna aplikacija na 64-bitnom Windows-u traži 32-bitnu binarnu datoteku. Izgradnja sa omogućenim V8 ima drugo ime datoteke, pa raspoređivanje koje ih meša izgledaće ispravno i neće ništa učitati. I samo jedna varijanta može živeti u sistemskom direktorijumu istovremeno, što je dobar razlog da se preferira eksplicitni raspored poddirektorijuma nad instaliranjem bilo čega sistemski
Za Lazarus konkretno, stavite nativnu biblioteku pod DLLs/<cpu>-<os> malim slovima, pored izvršne datoteke, i naći će je prvi korak lanca na svakom cilju. Primer prikazivača koji ovo vežba na Lazarus-u opisan je u članku o Lazarus i FPC prikazivaču, a trenutna podrška platformi navedena je na stranici proizvoda PDFium Delphi component