يجد مكون PDFium مكتبته الأصلية عبر سلسلة بحث ثابتة مرتبة بدل ترك الأمر إلى محمل نظام التشغيل، لأن شجرة نشر صريحة هي شجرة نشر يمكنك تنقيحها. وفي Windows تبحث تلك السلسلة عن دليل فرعي Win32 أو Win64 يشحنه المثبت أصلا. وفي الأهداف الأخرى تبني اسم الدليل الفرعي من ماكروهات الهدف في Free Pascal، بصيغة <cpu>-<os>، فتقرأ شجرة النشر تماما كشجرة الوحدات المترجمة. وقد أدخل ذلك القرار الأخير عيبا يستحق المقال كله، لأن السبب كان حرفا كبيرا والعرض كان صمتا
السلسلة، بالترتيب
أربعة مواضع تجرب بتتابع، ثم محمل المنصة كملاذ أخير. أولا التخطيط المفضل، دليل DLLs بجانب الملف التنفيذي يحوي دليلا فرعيا لكل هدف. ثانيا تخطيط بديل والدليل الفرعي للهدف بجانب الملف التنفيذي مباشرة. ثالثا التخطيط القديم المسطح، والمكتبة تجاور الملف التنفيذي بلا دليل فرعي إطلاقا. رابعا، في Windows فقط، دليل النظام، الذي يحتاج عناية لأن عملية ذات 32 بت يجب أن تبحث في SysWOW64 وعملية ذات 64 بت في System32، وفي Windows ذات 32 بت لا يوجد الأول فيعاد البحث. وبعد ذلك كله فقط يطلب من المحمل أن يبحث بنفسه
لا توجد عمدا خطوة دليل نظام خارج Windows. مسار بحث محمل المنصة الخاص، المدفوع بإعداد رابط زمن التشغيل وبيئة مسار المكتبات، يغطي تلك الأرض أصلا، وتكراره في Pascal يعني إعادة تنفيذ قواعد تختلف بحسب التوزيعة. وتشخيص الإخفاقات في سلسلة Windows مشمول على حدة في نشر DLL لـ PDFium وتشخيص إخفاقات التحميل
من أين يأتي اسم الدليل الفرعي
في Windows هو Win32 أو Win64، يقرر بعرض بتات العملية الجارية لا لنظام التشغيل، لأن ذلك هو ما يحدد أي ثنائي يمكن تحميله. وفي كل مكان آخر يبنى الاسم من ماكروهات هدف المترجم كي تنتج آلة تبني لمعماريين شجرتين منفصلتين بوضوح، ولكي يجاور مجلد المكتبة الأصلية مجلد الوحدات المترجمة بالاسم نفسه
function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
if IsWin64 then
Result := 'Win64'
else
Result := 'Win32';
{$ELSE}
// ماكروهات المترجم تجعل الحرف الأول من نظام التشغيل كبيرا («Linux» و«Darwin»)
// بينما دليل خرج وحدات الحزمة لا يفعل، فلا يتفق الاثنان إلا بعد
// الطي. وعلى نظام ملفات حساس لحالة الأحرف يكون ذلك الاختلاف
// هو البحث كله
Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;
لماذا كسر حرف كبير السلسلة كلها
تهجئ ماكرو المترجم نظام التشغيل الهدف بحرف أولي كبير: Win64 وLinux وDarwin. وتكتب حزمة Lazarus خرج وحداتها في دليل مسمى من متغير هدفها الخاص، وهو أحرف صغيرة: win64 وlinux وdarwin. تهجئتان للشيء نفسه، ولا سبيل لملاحظتها في Windows حيث لا يميز نظام الملفات بينهما
في Linux هما دليلان مختلفان. نشر يضع الكائن المشترك في DLLs/x86_64-linux غير مرئي لمحمل يبحث عن DLLs/x86_64-Linux، فتخطئ الخطوات الصريحة الأربع للسلسلة كلها ويسقط الكود إلى مطالبة محمل المنصة بالبحث. أحيانا يعمل ذلك، إذا صادف أن ثبتت المكتبة على مستوى النظام، وأحيانا لا يعمل، وفي الحالين لا تسهم شجرة النشر المرتبة بعناية بأي شيء. وليس للإخفاق رسالة خطأ لأن شيئا لم يفشل: كل خطوة أفادت صائبة بأن الملف ليس حيث بحثت
برنامج المجس، مترجما ومشغلا
لا يمكن إيجاد هذا الصنف من العيوب بالقراءة، ولا بالترجمة هو الآخر. والتقنية الاعتيادية للتحقق من فرع منصة لا يترجم قط على آلة التطوير نسخ الوحدة إلى دليل مؤقت، وإعادة تسميتها، واستبدال الشرط المنصاتي برمز لا يعرف أبدا، وترجمة النسخة؛ فإن ترجمت كانت عبارة uses وتواقيع النداء على ذلك المسار على الأقل متسقة ذاتيا. ويعمل ذلك جيدا لوحدة مكتفة بذاتها
وهو لا يعمل هنا. وحدة الربط الرئيسية ضخمة جدا وتسحب LCL، فلا يمكن ببساطة نسخها وترجمتها مع إطفاء رمز Windows. فبدلا من ذلك نقلت حفنة الدوال التي لمسها التغيير حرفيا إلى برنامج صغير مكتف بذاته، وشُغّل ذلك البرنامج. طبع x86_64-Win64، وبدا عدم التطابق في سطر واحد من الخرج. وترجمة البرنامج نفسه ما كانت لتخبرك شيئا، لأن السلسلة صالحة تماما؛ قيمتها وحدها الخاطئة
program ProbeSubDir;
{$MODE DELPHI}
uses
SysUtils;
begin
// اطبع ولا توكد. الغاية النظر في القيمة التي يتمدد إليها ماكرو
// فعلا على سلسلة الأدوات هذه
Writeln('raw: ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
{$I %FPCTARGETOS%}));
end.
الدرس العام: عندما يخص تغيير عبر المنصات قيمة شيء لا نوعه، فالتحقق بالترجمة وحدها ليس تحققا. اطبعه. ومجموعة الاختلافات الأوسع عبر المترجمين بين Delphi وFree Pascal مجمعة في مقال مزالق المترجمين المتقاطعين بين Delphi وFPC
دع المنصة تشرح إخفاقات تحميلها بنفسها
يعدد فرع Windows في المحمل يدويا أسباب إخفاق تحميل، لأن التمييزات المفيدة هناك، عدم تطابق معمارية، أو اعتمادية متعدية مفقودة، أو مسار لا يُحل، تقابل رموز خطأ تستحق التسمية فردا فردا. وخارج Windows تعيد وحدة المحمل المحمولة أصلا سلسلة وصفية تغطي الأرض نفسها، فيستخدمها فرع غير Windows مباشرة بدل إعادة اشتقاق فئات من رقم خطأ يعني أشياء مختلفة على أنظمة مختلفة
مقاومة الرغبة في تطبيع الاثنين في رسالة واحدة مقصودة. إخفاق التحميل مشكلة نشر، والشخص الذي يقرأ الرسالة يحتاج مفردات المنصة نفسها ليبحث عنها
تصادم أسماء يستدعي نفسه تكراريا
فخ آخر، صغير وحاد. تصدر وحدة المحمل المحمولة إجراء يسمى UnloadLibrary، وللوحدة الرابطة إجراء بالاسم نفسه يقوم بدفتر حساباته الخاص قبل تحرير المقبض. وداخل ذلك الإجراء، يتحد نداء غير مؤهل لـ UnloadLibrary على نظيره في الوحدة الحالية، الذي يستدعي نفسه. والإصلاح تأهيل النداء باسم الوحدة
هذا هو الشكل نفسه لمشاكل حجب المعرفات التي تسيطر على عمليات نقل Free Pascal عموما: تصدر وحدة Windows دالتي حصر يونيكودية وحصر نقطة عائمة، ونوع مزامنة يحجب الصنف بالاسم نفسه، وفي كل حالة يعتمد الحل على ترتيب عبارة uses. وتأهيل موقع النداء هو الإصلاح الذي لا يعتمد على أن يحافظ أحدهم على ذلك الترتيب لاحقا
قائمة فحص النشر
ثلاثة أشياء تفسر معظم إخفاقات التحميل مرة تصح حساب المسارات. يجب أن تطابق المعمارية العملية لا الآلة، فتطبيق ذو 32 بت على Windows ذات 64 بت يحتاج الثنائي ذا 32 بت. وبناء V8 الممكن يملك اسم ملف مختلفا، فنشر يمزجهما سيبدو صحيحا ولا يحمل شيئا. ويمكن لصيغة واحدة فقط أن تعيش في دليل نظام في وقت واحد، وهو سبب وجيه لتفضيل تخطيط الدليل الفرعي الصريح على تثبيت أي شيء على مستوى النظام
ولـ Lazarus تحديدا، ضع المكتبة الأصلية تحت DLLs/<cpu>-<os> بأحرف صغيرة، بجانب الملف التنفيذي، وستوجد بالخطوة الأولى من السلسلة على كل هدف. وعينة العارض التي تجرب هذا على Lazarus موصوفة في مقال عارض Lazarus وFPC، ودعم المنصات الحالي مدرج في صفحة منتج PDFium Delphi component