Komponent PDFium nachádza svoju natívnu knižnicu cez pevný, usporiadaný reťaz hľadania namiesto toho, aby to nechal na loaderi operačného systému, pretože nasadzovací strom, ktorý je explicitný, je nasadzovací strom, ktorý môžete ladiť. Na Windows ten reťaz hľadá podadresár Win32 alebo Win64, ktorý už dodáva inštalátor. Na iných cieľoch stavia meno podadresára z makier cieľov Free Pascalu, ako <cpu>-<os>, takže nasadzovací strom sa číta presne ako strom skompilovaných jednotiek. Toto posledné rozhodnutie zaviedlo chybu, ktorá stojí za celý článok, pretože príčinou bolo veľké písmeno a príznakom bolo ticho
Reťaz, v poradí
Štyri miesta, skúšané postupne, potom platformový loader ako posledná možnosť. Najprv preferované rozloženie, adresár DLLs vedľa spustiteľného súboru obsahujúci jeden podadresár na cieľ. Po druhé alternatívne rozloženie s cieľovým podadresárom priamo vedľa spustiteľného súboru. Po tretie ploché legacy rozloženie, knižnica sediaca vedľa spustiteľného súboru bez akéhokoľvek podadresára. Po štvrté, len na Windows, systémový adresár, ktorý si vyžaduje starostlivosť, pretože 32-bitový proces musí hľadať v SysWOW64 a 64-bitový v System32 a na 32-bitovom Windows ten prvý neexistuje, takže dohľad sa musí vrátiť späť. Až po všetkom tom sa loadera pýta, aby hľadal sám
Mimo Windows zámerne nie je krok so systémovým adresárom. Vlastná cesta hľadania platformového loadera, riadená konfiguráciou runtime linkera a prostredím cesty knižníc, pokrýva tú zem už sama a jej duplikovanie v Pascale by znamenalo reimplementovať pravidlá, ktoré sa líšia podľa distribúcie. Diagnostikovanie zlyhaní v reťazi Windows je pokryté osobitne v nasadzovaní DLL PDFium a diagnostike zlyhaní načítania
Odkiaľ pochádza meno podadresára
Na Windows je to Win32 alebo Win64, rozhodnuté bitovosťou bežiaceho procesu, nie operačného systému, pretože to je to, čo určuje, ktorý binárny súbor sa dá načítať. Všade inde sa meno stavia z makier cieľov kompilátora tak, že stroj zostavujúci pre dve architektúry produkuje dva zreteľne oddelené stromy a tak, že priečinok nesúci natívnu knižnicu sedí vedľa priečinka nesúceho skompilované jednotky pod tým istým menom
function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
if IsWin64 then
Result := 'Win64'
else
Result := 'Win32';
{$ELSE}
// Kompilátorové makrá píšu OS s počiatočným veľkým („Linux“, „Darwin“),
// zatiaľ čo výstupný adresár jednotiek balíka nie, takže sa zhodujú až
// po zložení. Na systéme súborov rozlišujúcom veľkosť je tento rozdiel
// celý dohľad
Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;
Prečo jedno veľké písmeno rozbilo celý reťaz
Makro kompilátora hláskuje cieľový operačný systém s počiatočným veľkým písmenom: Win64, Linux, Darwin. Balík Lazarus zapisuje výstup jednotiek do adresára pomenovaného z vlastnej cieľovej premennej, ktorá je malými písmenami: win64, linux, darwin. Dva pravopisy tej istej veci a žiadny spôsob, ako si to všimnúť na Windows, kde systém súborov ich nerozlišuje
Na Linuxe sú to dva rozdielne adresáre. Nasadenie, ktoré dáva shared object do DLLs/x86_64-linux, je neviditeľné pre loader hľadajúci DLLs/x86_64-Linux, takže všetky štyri explicitné kroky reťaze minú a kód prepadne k tomu, že hľadať nechá platformový loader. Niekedy to funguje, ak sa knižnica náhodou inštalovala systémovo, a niekedy nie a buď tak či onak starostlivo usporiadaný nasadzovací strom neprispieva ničím. Zlyhanie nemá chybové hlásenie, pretože nič nezlyhalo: každý krok správne hlásil, že súbor nie je tam, kde hľadal
Probe program, skompilovaný a spustený
Túto triedu chýb nemožno nájsť čítaním a nemožno ju nájsť ani kompilovaním. Obvyklá technika na overenie platformovej vetvy, ktorá sa na vývojovom stroji nikdy nekompiluje, je skopírovať jednotku do dočasného adresára, premenovať ju, nahradiť platformovú podmienku symbolom, ktorý nie je nikdy definovaný, a skompilovať kópiu; ak sa skompiluje, uses klauzula a signatúry volaní na tej ceste sú aspoň vnútorne konzistentné. To funguje dobre pre sebestačnú jednotku
Tu to nefunguje. Hlavná viažuca jednotka je veľmi veľká a priťahuje LCL, takže ju nemožno jednoducho skopírovať a skompilovať so symbolom Windows vypnutým. Namiesto toho bola hrsť funkcií, ktorých sa zmena dotkla, prepísaná doslova do malého sebestačného programu a ten program bol spustený. Vypísal x86_64-Win64 a nesúlad bol viditeľný v jednom riadku výstupu. Skompilovanie toho istého programu by vám nepovedalo nič, pretože reťazec je úplne platný; zlá je len jeho hodnota
program ProbeSubDir;
{$MODE DELPHI}
uses
SysUtils;
begin
// Vypísať, netvrdiť. Zmysel je pozrieť sa na hodnotu, na ktorú sa makro
// na tomto toolchaine naozaj rozvinie
Writeln('raw: ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
{$I %FPCTARGETOS%}));
end.
Všeobecná lekcia: keď sa zmena naprieč platformami týka hodnoty niečoho, nie jej typu, overenie len kompiláciou nie je overenie. Vypíšte to. Širšia sada rozdielov naprieč kompilátormi medzi Delphi a Free Pascalom je zozbieraná v článku o pasciach kompilátorov Delphi a FPC
Nechajte platformu vysvetliť vlastné zlyhania načítania
Vetva Windows loadera vyratúva rukou dôvody, pre ktoré môže načítanie zlyhať, pretože užitočné rozlíšenia tam — nesúlad architektúr, chýbajúca tranzitívna závislosť, cesta, ktorá sa nerozrieši — sa mapujú na chybové kódy stojace za individuálne pomenovanie. Mimo Windows vracia prenosná loaderová jednotka už popisný reťazec pokrývajúci tú istú zem, takže vetva non-Windows ho používa priamo namiesto znovu odvodzovania kategórií z chybového čísla, ktoré znamená odlišné veci na odlišných systémoch
Vzdorovanie pokušeniu normalizovať tie dva do jednej správy je zámerné. Zlyhanie načítania je problém nasadenia a človek čítajúci správu potrebuje vlastnú slovnú zásobu platformy, aby si ju vyhľadal
Kolízia mien, ktorá rekurzuje
Ešte jedna pasca, malá a ostrá. Prenosná loaderová jednotka exportuje procedúru volanú UnloadLibrary a viažuca jednotka má procedúru toho istého mena, ktorá si pred uvoľnením handlu vedie vlastnú evidenciu. Vnútri tej procedúry sa nekvalifikované volanie UnloadLibrary rozrieši na to v aktuálnej jednotke, ktorá volá samu seba. Opravou je kvalifikovať volanie menom jednotky
Toto je ten istý tvar ako problémy tienenia identifikátorov, ktoré všeobecne dominujú portom do Free Pascalu: jednotka Windows exportuje celočíselné funkcie minima a maxima, ktoré tienenia tie s pohyblivou rádovou čiarkou, a synchronizačný typ, ktorý tienenia triedu toho istého mena, a v každom prípade závisí rozriešenie od poradia uses klauzuly. Kvalifikovanie miesta volania je oprava, ktorá nezávisí od toho, že niekto toto poradie neskôr zachová
Kontrolný zoznam nasadenia
Tri veci vysvetľujú väčšinu zlyhaní načítania, keď je aritmetika ciest správna. Architektúra sa musí zhodovať s procesom, nie so strojom, takže 32-bitová aplikácia na 64-bitovom Windows potrebuje 32-bitový binárny súbor. Build so zapnutým V8 má iné meno súboru, takže nasadenie, ktoré ich zmieša, bude vyzerať správne a nenačíta nič. A naraz môže bývať v systémovom adresári len jedna varianta, čo je dobrý dôvod preferovať explicitné rozloženie s podadresárom pred inštalovaním čohokoľvek systémovo
Špecificky pre Lazarus dajte natívnu knižnicu pod DLLs/<cpu>-<os> malými písmenami, vedľa spustiteľného súboru, a bude nájdená prvým krokom reťaze na každom cieli. Vzorka prehliadača, ktorá to na Lazaruse precvičuje, je popísaná v článku o prehliadači Lazarus a FPC a aktuálna podpora platforiem je uvedená na produktovej stránke PDFium Delphi component