Komponen PDFium menemukan pustaka nativenya melalui rantai pencarian tetap yang terurut alih-alih menyerahkannya ke loader sistem operasi, karena pohon deployment yang eksplisit adalah pohon deployment yang bisa Anda debug. Di Windows rantai itu mencari sub-direktori Win32 atau Win64 yang sudah dikirim installer. Pada target lain ia membangun nama sub-direktori dari makro target Free Pascal, sebagai <cpu>-<os>, sehingga pohon deployment terbaca persis seperti pohon unit terkompilasi. Keputusan terakhir itu memunculkan bug yang layak satu artikel penuh, karena penyebabnya satu huruf kapital dan gejalanya keheningan
Rantainya, berurutan
Empat lokasi, dicoba berurutan, lalu loader platform sebagai upaya terakhir. Pertama layout yang diutamakan, direktori DLLs di samping executable yang memuat satu sub-direktori per target. Kedua layout alternatif dengan sub-direktori target langsung di samping executable. Ketiga layout legacy datar, pustaka duduk di sebelah executable tanpa sub-direktori sama sekali. Keempat, hanya di Windows, direktori sistem, yang butuh kehati-hatian karena proses 32-bit harus mencari di SysWOW64 dan proses 64-bit di System32, dan di Windows 32-bit yang pertama tidak ada sehingga pencarian harus jatuh kembali. Hanya setelah semua itu loader diminta mencari dengan kemauannya sendiri
Sengaja tidak ada langkah direktori sistem di luar Windows. Jalur pencarian milik loader platform sendiri, digerakkan oleh konfigurasi runtime linker dan lingkungan path pustaka, sudah mencakup wilayah itu, dan menduplikasinya dalam Pascal berarti mengimplementasikan ulang aturan yang bervariasi antar distribusi. Mendiagnosis kegagalan di rantai Windows dibahas terpisah dalam men-deploy DLL PDFium dan mendiagnosis kegagalan muat
Dari mana nama sub-direktori berasal
Di Windows itu Win32 atau Win64, ditentukan oleh bitness proses yang berjalan alih-alih sistem operasinya, karena itulah yang menentukan binary mana yang bisa dimuat. Di tempat lain namanya dibangun dari makro target kompiler sehingga mesin yang membangun untuk dua arsitektur menghasilkan dua pohon yang terpisah jelas, dan sehingga folder yang memegang pustaka native duduk di sebelah folder yang memegang unit terkompilasi dengan nama yang sama
function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
if IsWin64 then
Result := 'Win64'
else
Result := 'Win32';
{$ELSE}
// Makro kompiler mengkapitalkan OS ("Linux", "Darwin") sementara
// direktori output unit paket tidak, sehingga keduanya sepakat hanya
// setelah pelipatan. Pada sistem berkas peka huruf besar-kecil,
// perbedaan itu adalah seluruh pencariannya
Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;
Mengapa satu huruf kapital merusak seluruh rantai
Makro kompiler mengeja sistem operasi target dengan kapital awal: Win64, Linux, Darwin. Paket Lazarus menulis output unitnya ke direktori yang dinamai dari variabel target miliknya sendiri, yang berupa huruf kecil: win64, linux, darwin. Dua ejaan untuk hal yang sama, dan tidak ada cara menyadarinya di Windows, di mana sistem berkas tidak membedakannya
Di Linux keduanya dua direktori yang berbeda. Deployment yang menaruh shared object di DLLs/x86_64-linux tak terlihat oleh loader yang mencari DLLs/x86_64-Linux, sehingga keempat langkah eksplisit rantai meleset dan kode jatuh ke membiarkan loader platform mencari. Kadang itu berhasil, jika pustaka kebetulan terpasang system-wide, dan kadang tidak, dan dengan cara apa pun pohon deployment yang disusun cermat tidak menyumbang apa pun. Kegagalannya tanpa pesan error karena tidak ada yang gagal: setiap langkah melaporkan dengan benar bahwa berkas tidak ada di tempat yang dilihatnya
Program probe, dikompilasi dan dijalankan
Kelas bug ini tidak bisa ditemukan dengan membaca, dan tidak bisa ditemukan dengan mengompilasi juga. Teknik lazim untuk memverifikasi cabang platform yang tidak pernah terkompilasi di mesin pengembangan adalah menyalin unit ke direktori sementara, mengganti namanya, mengganti conditional platform dengan simbol yang tidak pernah didefinisikan, dan mengompilasi salinannya; jika terkompilasi, uses clause dan signature panggilan pada jalur itu setidaknya konsisten dengan dirinya sendiri. Itu bekerja baik untuk unit yang mandiri
Itu tidak bekerja di sini. Unit binding utama sangat besar dan menarik LCL ke dalam, sehingga tidak bisa sekadar disalin dan dikompilasi dengan simbol Windows dimatikan. Jadi alih-alih itu, segelintir fungsi yang disentuh perubahan ditranskrip verbatim ke dalam program kecil yang mandiri, dan program itu dijalankan. Ia mencetak x86_64-Win64, dan ketidakcocokan itu terlihat dalam satu baris output. Mengompilasi program yang sama tidak akan memberi tahu Anda apa pun, karena string-nya sepenuhnya valid; hanya nilainya yang salah
program ProbeSubDir;
{$MODE DELPHI}
uses
SysUtils;
begin
// Cetak, jangan asersi. Intinya adalah melihat nilai yang benar-benar
// diekspansi makro pada toolchain ini
Writeln('raw: ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
{$I %FPCTARGETOS%}));
end.
Pelajaran umumnya: ketika perubahan lintas platform menyangkut nilai dari sesuatu alih-alih tipenya, verifikasi khusus kompilasi bukan verifikasi. Cetaklah. Himpunan lebih luas perbedaan lintas kompiler antara Delphi dan Free Pascal dikumpulkan dalam artikel jebakan lintas kompiler Delphi dan FPC
Biarkan platform menjelaskan kegagalan muatnya sendiri
Cabang Windows loader mengenumerasi dengan tangan alasan-alasan sebuah muat bisa gagal, karena distinksi yang berguna di sana, ketidakcocokan arsitektur, dependensi transitif yang hilang, path yang tidak terselesaikan, terpetakan ke kode error yang layak dinamai satu per satu. Di luar Windows unit loader portabel sudah mengembalikan string deskriptif yang mencakup wilayah yang sama, sehingga cabang non-Windows memakainya langsung alih-alih menurunkan ulang kategori dari nomor error yang berarti hal berbeda di sistem berbeda
Menahan dorongan menormalisasi keduanya menjadi satu pesan adalah hal yang disengaja. Kegagalan muat adalah masalah deployment, dan orang yang membaca pesannya membutuhkan kosakata milik platform untuk mencarinya
Tabrakan nama yang berrekursi
Satu jebakan lagi, kecil dan tajam. Unit loader portabel mengekspor procedure bernama UnloadLibrary, dan unit binding punya procedure bernama sama yang melakukan pencatatannya sendiri sebelum melepas handle. Di dalam procedure itu, panggilan tak terkualifikasi ke UnloadLibrary terselesaikan ke yang di unit saat ini, yang memanggil dirinya sendiri. Perbaikannya adalah mengkualifikasi panggilan dengan nama unit
Ini wujud yang sama dengan masalah penembayangan identifier yang mendominasi porting Free Pascal secara umum: unit Windows mengekspor fungsi minimum dan maximum bertipe integer yang menembayangi versi floating-point-nya, dan tipe sinkronisasi yang menembayangi kelas bernama sama, dan dalam setiap kasus penyelesaiannya bergantung pada urutan uses clause. Mengkualifikasi call site adalah perbaikan yang tidak bergantung pada seseorang menjaga urutan itu nanti
Checklist deployment
Tiga hal menyumbang sebagian besar kegagalan muat begitu aritmetika path sudah benar. Arsitektur harus cocok dengan proses, bukan mesin, sehingga aplikasi 32-bit di Windows 64-bit membutuhkan binary 32-bit. Build dengan V8 aktif punya nama berkas berbeda, sehingga deployment yang mencampur keduanya akan tampak benar dan tidak memuat apa pun. Dan hanya satu varian yang bisa berdiam di direktori sistem pada satu waktu, yang merupakan alasan baik untuk memilih layout sub-direktori eksplisit daripada memasang apa pun system-wide
Untuk Lazarus secara khusus, taruh pustaka native di bawah DLLs/<cpu>-<os> dengan huruf kecil, di sebelah executable, dan ia akan ditemukan oleh langkah pertama rantai pada setiap target. Contoh viewer yang menjalankan ini di Lazarus dijelaskan dalam artikel viewer Lazarus dan FPC, dan dukungan platform saat ini tercantum di halaman produk PDFium Delphi component