Techninis straipsnis

PDFium savosios bibliotekos įkėlimas bet kuriai paskirčiai

PDFium komponentė randa savąją biblioteką per fiksuotą, surūšiuotą paieškos grandinę, o ne palieka tai operacinės sistemos įkėlikliui, nes aiškus diegimo medis yra diegimo medis, kurį galite derinti. Windows ji ieško Win32 arba Win64 pakatalogio, kurį jau tiekia įdiegimo programa. Kitose paskirtyse ji stato pakatalogio vardą iš Free Pascal paskirties makroesų, kaip <cpu>-<os>, todėl diegimo medis skaitosi lygiai kaip sukompiliuotų modulių medis. Tas paskutinis sprendimas įvedė klaidą, vertą viso straipsnio, nes priežastis buvo didžioji raidė, o simptomas — tyla

Grandinė, tvarka

Keturių vietų, bandomų seka, tada platformos įkėliklis kaip paskutinė priemonė. Pirma pageidaujama išdėstytis — DLLs katalogas greta vykdomojo failo su vienu pakatalogiu paskirčiai. Antra alternatyvi išdėstytis su paskirties pakatalogiu tiesiai greta vykdomojo failo. Trečia plokščioji senoji išdėstytis — biblioteka greta vykdomojo failo be jokio pakatalogio. Ketvirta, tik Windows, sistemos katalogas, reikalaujantis atsargos, nes 32 bitų procesas privalo žiūrėti SysWOW64, o 64 bitų procesas — System32, o 32 bitų Windows pastarasis neegzistuoja, todėl paieška turi nukristi atgal. Tik po viso to įkėliklis prašomas ieškoti pats

PDFium savosios bibliotekos paieškos grandinės Delphi diagrama — nuo DLLs paskirties pakatalogio per alternatyvią, plokščią ir Windows sistemos katalogo išdėstytis iki platformos įkėliklio
Keturios aiškios vietos zondavamos tvarka, prieš prašant OS įkėliklio ieškoti pačiam

Už Windows ribų sąmoningai nėra sistemos katalogo žingsnio. Pačios platformos įkėliklio paieškos kelias, valdomas vykdymo laiko linkerio konfigūracijos ir bibliotekų kelių aplinkos, jau dengia tą plotą, ir jo dubliavimas Pascal reikštų taisyklių, kintančių pagal distribuciją, įgyvendinimą iš naujo. Nesėkmių Windows grandinėje diagnostika atskirai dengiama PDFium DLL diegimo ir įkėlimo nesėkmių diagnostikos straipsnyje

Iš kur atkeliauja pakatalogio vardas

Windows tai Win32 arba Win64, sprendžiama pagal besileidžiančio proceso bitumą, o ne pagal operacinės sistemos, nes būtent tai nulemia, kurį dvejetainį failą galima įkelti. Visur kitur vardas statomas iš kompiliatoriaus paskirties makroesų, kad mašina, statanti dviem architektūroms, duotų dvi aiškiai atskirtas medžio struktūras, ir kad aplankas, laikantis savosios bibliotekos, sėdėtų greta aplanko, laikančio sukompiliuotus modulius, tokiu pačiu vardu

function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
  if IsWin64 then
    Result := 'Win64'
  else
    Result := 'Win32';
{$ELSE}
  // Kompiliatoriaus makroesės rašo OS didžiąja raide („Linux“, „Darwin“),
  // o paketo modulių išvesties katalogas — ne, todėl abu sutampa tik po
  // suvienodinimo. Raidžių dydį skiriančioje failų sistemoje tas skirtumas
  // yra visa paieška
  Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;

Kodėl viena didžioji raidė sugriovė visą grandinę

Kompiliatoriaus makroesė rašo paskirties operacinę sistemą su pradine didžiąja raide: Win64, Linux, Darwin. Lazarus paketas rašo savo modulių išvestį į katalogą, pavadintą pagal savo paskirties kintamąjį, kuris mažosiomis: win64, linux, darwin. Dvi to paties dalyko rašybos, ir nė kaip pastebėti Windows, kur failų sistema jų neskiria

Linux tai du skirtingi katalogai. Diegimas, padedantis bendro naudojimo objektą į DLLs/x86_64-linux, yra nematomas įkėlikliui, ieškančiam DLLs/x86_64-Linux, todėl visi keturi aiškūs grandinės žingsniai praleidžia, ir kodas nukrenta iki platformos įkėliklio paieškos leidimo. Kartais tai veikia, jeigu biblioteka atsitiktinai įdiegta sistemos lygiu, o kartais ne, ir bet kuriuo atveju kruopščiai sudėliotas diegimo medis neduoda nieko. Nesėkmė neturi klaidos pranešimo, nes nesugedo niekas: kiekvienas žingsnis teisingai pranešė, kad failo ten, kur jis žiūrėjo, nėra

Kaip viena didžioji raidė FPC paskirties makroesėje sugriauna PDFium DLL paiešką Linux: įkėliklis ieško DLLs/x86_64-Linux, o įdiegtas aplankas yra DLLs/x86_64-linux, kas sutampa tik neatskiriančioje raidžių dydžio Windows
Ta pati paskirtis, parašyta dviem rašybomis, sutampa Windows ir tyloje praleidžia raidžių dydį skiriančioje failų sistemoje

Zondo programa, sukompiliuota ir paleista

Toks klaidos tipas negali būti rastas skaitant, ir negali būti rastas kompiliuojant. Įprasta technika patikrinti platformos šaką, kuri niekada nesukompiliuoja kūrimo mašinoje, yra nukopijuoti modulį į laikinąjį katalogą, pervadinti jį, pakeisti platformos sąlygą niekada neapibrėžtu simboliu ir sukompiliuoti kopiją; jei sukompiliuoja, uses sakinys ir iškvietimų parašai tame kelyje bent savi nuoseklūs. Tai gerai veikia savaip uždarą modulį

Čia tai neveikia. Pagrindinis susiejimo modulis labai didelis ir patraukia LCL, todėl jo negalima paprasčiausiai nukopijuoti ir sukompiliuoti išjungus Windows simbolį. Vietoje to saujelė funkcijų, kurių palietė pakeitimas, buvo perrašyta iki žodžio į mažą savaip uždarą programą, ir ta programa buvo paleista. Ji išspausdino x86_64-Win64, ir nesutapimas buvo matomas vienoje išvesties eilutėje. Tos pačios programos kompiliavimas nebūtų pasakęs nieko, nes eilutė visiškai teisėta; neteisinga tik jos reikšmė

program ProbeSubDir;
{$MODE DELPHI}
uses
  SysUtils;
begin
  // Spausdinkite, neteiginkite. Esmė pažiūrėti, į kokią reikšmę makroesė
  // iš tikrųjų išsiskleidžia šioje įrankių grandinėje
  Writeln('raw:    ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
  Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
    {$I %FPCTARGETOS%}));
end.

Bendra pamoka: kai tarpplatformis pakeitimas liečia kažko reikšmę, o ne jo tipą, tik kompiliavimo patikra nėra patikra. Spausdinkite ją. Platesnis tarpkompiliatorių skirtumų tarp Delphi ir Free Pascal rinkinys surinktas Delphi ir FPC tarpkompiliatorių spąstų straipsnyje

Leiskite platformai paaiškinti savas įkėlimo nesėkmes

Įkėliklio Windows šaka rankomis išvardija priežastis, kuriomis įkėlimas gali nepavykti, nes ten naudingos išskirtys — architektūros nesutapimas, dingusi percheinė priklausomybė, neišsiskleidžiantis kelias — atitinka klaidų kodus, vertus įvardinti atskirai. Už Windows ribų perkeliamasis įkėliklio modulis jau grąžina aprašomąją eilutę, dengiančią tą patį plotą, todėl nebūsianti Windows šaka ją naudoja tiesiogiai, vietoj iš naujo išvedimo kategorijų iš klaidos numerio, reiškiančio skirtingus dalykus skirtingose sistemose

Atsilaikymas prieš norą tuos du suvienodinti į vieną pranešimą yra sąmoningas. Įkėlimo nesėkmė yra diegimo problema, ir žmogui, skaitančiam pranešimą, reikia pačios platformos žodyno, kuriuo ieškoti

Vardų susidūrimas, kuris rekursuoja

Dar vieni spąstai, maži ir aštrūs. Perkeliamasis įkėliklio modulis eksportuoja procedūrą, vadinamą UnloadLibrary, o susiejimo modulis turi to paties vardo procedūrą, prieš atleidžianti rankenėlę atliekančią savą apskaitą. Tos procedūros viduje nekvalifikuotas UnloadLibrary iškvietimas išsiskleidžia į tą, esančią esamame modulyje, kuri kviečia save. Pataisa — kvalifikuoti iškvietimą modulio vardu

Tai tos pačios formos kaip identifikatorių užtemdymo problemos, dominuojančios Free Pascal perkeliamumuose apskritai: Windows modulis eksportuoja sveikojo tipo minimumo ir maksimumo funkcijas, užtemdžiančias slankiojo kablelio, ir sinchronizavimo tipą, užtemdžiantį to paties vardo klasę, ir kiekvienu atveju išsiskleidimas priklauso nuo uses sakinio tvarkos. Iškvietimo vietos kvalifikavimas yra pataisa, nepriklausanti nuo to, kad kas nors vėliau išsaugos tą tvarką

UnloadLibrary vardų susidūrimas PDFium komponentėje: nekvalifikuotas iškvietimas Delphi susiejimo modulyje rekursuoja į save, o modulio vardu kvalifikuotas pasiekia perkeliamąjį įkėliklio modulį ir atleidžia rankenėlę
Iškvietimo vietos kvalifikavimas nukreipia atleidimą per įkėliklio modulį, vietoj rekursijos į susiejimo modulį

Diegimo kontrolinis sąrašas

Trys dalykai sudaro daugumą įkėlimo nesėkmių, kai kelio aritmetika teisinga. Architektūra privalo atitikti procesą, o ne mašiną, todėl 32 bitų programa 64 bitų Windows reikalauja 32 bitų dvejetainio failo. V8 įjungtas statymas turi kitokį failo vardą, todėl diegimas, juos sumaišantis, atrodys teisingai ir nieko neįkels. Ir tik vienas variantas gali gyventi sistemos kataloge vienu metu, kas yra gera priežastis teikti pirmenybę aiškiai pakatalogio išdėstyčiai vietoj ko nors sistemos lygio diegimo

Lazarus atveju konkrečiai padėkite savąją biblioteką po DLLs/<cpu>-<os> mažosiomis raidėmis, greta vykdomojo failo, ir ji bus rasta pirmuoju grandinės žingsniu kiekvienoje paskirtyje. Peržiūros programos pavyzdys, tai pratąsantis Lazarus aplinkoje, aprašytas Lazarus ir FPC peržiūros programos straipsnyje, o esamas platformų palaikymas išvardytas PDFium Delphi component produkto puslapyje