Tehnički članak

Učitavanje izvorne PDFium knjižnice na bilo kojem cilju

PDFium komponenta pronalazi svoju izvornu knjižnicu kroz fiksni, uređeni lanac pretraživanja umjesto da to prepusti učitavaču operativnog sustava, jer je stablo raspoređivanja koje je izričito stablo raspoređivanja koje možete otklanjati. Na Windowsu taj lanac traži poddirektorij Win32 ili Win64 koji instalacijski program već isporučuje. Na drugim ciljevima gradi ime poddirektorija iz makronaredbi Free Pascal cilja, kao <cpu>-<os>, pa stablo raspoređivanja čita točno kao stablo kompajliranih jedinica. Ta posljednja odluka uvela je grešku vrijednu cijelog članka, jer je uzrok bilo veliko slovo, a simptom tišina

Lanac, po redu

Četiri lokacije, isprobane u nizu, zatim platformski učitavač kao posljednje utočište. Prvo preferirani raspored, direktorij DLLs uz izvršnu datoteku koji sadrži jedan poddirektorij po cilju. Drugo zamjenski raspored s poddirektorijem cilja izravno uz izvršnu datoteku. Treće ravni naslijeđeni raspored, knjižnica uz izvršnu datoteku bez ikakvog poddirektorija. Četvrto, samo na Windowsu, sistemski direktorij, koji treba pažnje jer 32-bitni proces mora gledati u SysWOW64, a 64-bitni u System32, i na 32-bitnom Windowsu prvi ne postoji pa se traženje mora vratiti. Tek nakon svega toga učitavaču se daje da traži sam

Dijagram lanca pretraživanja izvorne PDFium knjižnice za Delphi, od poddirektorija cilja DLLs kroz zamjenski, ravni i sistemski Windows raspored do platformskog učitavača
Četiri izričite lokacije ispituju se redom prije nego se OS učitavač zamoli da traži sam

Izvan Windowsa namjerno ne postoji korak sistemskog direktorija. Vlastita putanja pretraživanja platformskog učitavača, vođena konfiguracijom povezivača vremena izvođenja i okruženjem knjižničnih putanja, već pokriva to područje, i dupliciranje u Pascalu značilo bi ponovno implementiranje pravila koja se razlikuju po distribuciji. Dijagnosticiranje neuspjeha u Windows lancu pokriveno je odvojeno u raspoređivanju PDFium DLL-a i dijagnosticiranju neuspjeha učitavanja

Otkud ime poddirektorija

Na Windowsu to je Win32 ili Win64, odlučeno bitnošću procesa koji radi, a ne operativnog sustava, jer to odlučuje koja se binarna datoteka može učitati. Svugdje drugo ime se gradi iz makronaredbi cilja kompajlera tako da stroj koji gradi za dvije arhitekture proizvodi dva jasno odvojena stabla, i tako da mapa koja drži izvornu knjižnicu sjedi uz mapu koja drži kompajlirane jedinice s istim imenom

function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
  if IsWin64 then
    Result := 'Win64'
  else
    Result := 'Win32';
{$ELSE}
  // Kompajlerske makronaredbe pišu OS s velikim početnim slovom ("Linux",
  // "Darwin"), a izlazni direktorij jedinice paketa ne, pa se njih dvoje
  // slažu tek nakon savijanja malih slova. Na sustavu datoteka koji
  // razlikuje velika i mala slova ta je razlika cijelo traženje
  Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;

Zašto je veliko slovo slomilo cijeli lanac

Kompajlerska makronaredba piše ciljni operativni sustav s početnim velikim slovom: Win64, Linux, Darwin. Lazarusov paket piše svoj izlaz jedinica u direktorij imenovan iz vlastite ciljne varijable, koja je malim slovima: win64, linux, darwin. Dva pravopisa iste stvari, i bez načina da se primijeti na Windowsu, gdje sustav datoteka ne razlikuje ih

Na Linuxu oni su dva različita direktorija. Raspoređivanje koje stavi dijeljeni objekt u DLLs/x86_64-linux nevidljivo je učitavaču koji traži DLLs/x86_64-Linux, pa sva četiri izričita koraka lanca promaše i kôd padne na to da platformski učitavač traži sam. Ponekad to radi, ako se knjižnica slučajno nalazi instalirana cijelim sustavom, i ponekad ne, i u oba slučaja pažljivo uređeno stablo raspoređivanja ne pridonosi ničemu. Neuspjeh nema poruku pogreške jer ništa nije palo: svaki je korak ispravno javio da datoteka nije bila gdje je gledao

Kako jedno veliko slovo u FPC ciljnoj makronaredbi lomi traženje PDFium DLL-a na Linuxu: učitavač traži DLLs/x86_64-Linux dok je raspoređena mapa DLLs/x86_64-linux, što se poklapa samo na Windowsu koji ne razlikuje velika i mala slova
Isti cilj napisan na dva načina poklapa se na Windowsu i tiho promašuje na sustavu datoteka koji razlikuje velika i mala slova

Probni program, kompajliran i pokrenut

Ovu klasu grešaka ne može se naći čitanjem, i ne može se naći ni kompajliranjem. Uobičajena tehnika za provjeru platformske grane koja se na razvojnom stroju nikad ne kompajlira jest kopirati jedinicu u privremeni direktorij, preimenovati je, zamijeniti platformski uvjetni izraz simbolom koji nikad nije definiran i kompajlirati kopiju; ako se kompajlira, red uses i potpisi poziva na tom putu barem su samousklađeni. To dobro radi za samodostatnu jedinicu

Ovdje ne radi. Glavna vezivna jedinica vrlo je velika i povlači LCL, pa se ne može jednostavno kopirati i kompajlirati s isključenim Windows simbolom. Umjesto toga šaka funkcija koje je promjena dirala prepisana je doslovno u mali samodostatni program, i taj program je pokrenut. Ispisao je x86_64-Win64, i nepodudarnost bila je vidljiva u jednom retku izlaza. Kompajliranje istog programa ne bi vam reklo ništa, jer je niz savršeno valjan; samo je njegova vrijednost pogrešna

program ProbeSubDir;
{$MODE DELPHI}
uses
  SysUtils;
begin
  // Ispisujte, ne tvrdite. Poanta je pogledati u što se makronaredba
  // stvarno širi na ovom alatnom lancu
  Writeln('raw:    ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
  Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
    {$I %FPCTARGETOS%}));
end.

Opća lekcija: kada se višeplatformska promjena tiče vrijednosti nečega umjesto njegova tipa, provjera samo kompajliranjem nije provjera. Ispišite je. Širi skup razlika između kompajlera Delphi i Free Pascal sabran je u članku o zamkama unakrsnog kompajliranja Delphi i FPC

Pustite platformu da objasni vlastite neuspjehe učitavanja

Windows grana učitavača ručno nabraja razloge zbog kojih učitavanje može pasti, jer se korisne razlike tamo, nepodudarnost arhitekture, nedostajuća tranzitivna ovisnost, putanja koja se ne razriješi, preslikavaju u kodove pogrešaka vrijedne pojedinačnog imenovanja. Izvan Windowsa prijenosna jedinica učitavača već vraća opisni niz koji pokriva isto područje, pa ne-Windows grana koristi nju izravno umjesto ponovnog izvođenja kategorija iz broja pogreške koji znači različite stvari na različitim sustavima

Otpor nagonu da se ta dva normaliziraju u jednu poruku namjerni je. Neuspjeh učitavanja problem je raspoređivanja, i osoba koja čita poruku treba vlastiti rječnik platforme da ga potraži

Sudar imena koji se rekurzira

Još jedna zamka, mala i oštra. Prijenosna jedinica učitavača izvozi proceduru nazvanu UnloadLibrary, a vezivna jedinica ima proceduru istog imena koja obavlja vlastito knjigovodstvo prije puštanja handlea. Unutar te procedure nekvalificirani poziv UnloadLibrary razrješuje se na onu u trenutnoj jedinici, koja poziva samu sebe. Ispravak je kvalificirati poziv imenom jedinice

To je isti oblik kao problemi zasjenjivanja identifikatora koji dominiraju prijenosima na Free Pascal općenito: jedinica Windows izvozi cjelobrojno tipizirane funkcije najmanjeg i najvećeg koje zasjenjuju one s pomičnim zarezom, i tip sinkronizacije koji zasjenjuje klasu istog imena, i u svakom slučaju razrješenje ovisi o redoslijedu reda uses. Kvalificiranje mjesta poziva ispravak je koji ne ovisi o tome hoće li netko kasnije očuvati taj redoslijed

Sudar imena UnloadLibrary u PDFium komponenti: nekvalificirani poziv u Delphi vezivnoj jedinici rekurzira u samu sebe, dok jedinicom kvalificiran poziv dospijeva do prijenosne jedinice učitavača i pušta handle
Kvalificiranje mjesta poziva šalje puštanje kroz jedinicu učitavača umjesto rekurzije u vezivnu jedinicu

Popis provjera raspoređivanja

Tri stvari objašnjavaju većinu neuspjeha učitavanja jednom kad je aritmetika putanja ispravna. Arhitektura mora odgovarati procesu, ne stroju, pa 32-bitna aplikacija na 64-bitnom Windowsu treba 32-bitnu binarnu datoteku. Izgradnja s omogućenim V8-om ima drugo ime datoteke, pa raspoređivanje koje ih pomiješa izgledat će ispravno i neće ništa učitati. I samo jedna varijanta može u isto vrijeme živjeti u sistemskom direktoriju, što je dobar razlog preferirati izričiti raspored poddirektorija nad instaliranjem bilo čega cijelim sustavom

Za Lazarus posebno, smjestite izvornu knjižnicu pod DLLs/<cpu>-<os> malim slovima, uz izvršnu datoteku, i naći će je prvi korak lanca na svakom cilju. Uzorak preglednika koji ovo vježba na Lazarusu opisan je u članku o pregledniku Lazarus i FPC, a trenutna podrška platformi navedena je na stranici proizvoda PDFium Delphi component