مقال تقني

تحميل مكتبة PDFium الأصلية على أي هدف

يجد مكون PDFium مكتبته الأصلية عبر سلسلة بحث ثابتة مرتبة بدل ترك الأمر إلى محمل نظام التشغيل، لأن شجرة نشر صريحة هي شجرة نشر يمكنك تنقيحها. وفي Windows تبحث تلك السلسلة عن دليل فرعي Win32 أو Win64 يشحنه المثبت أصلا. وفي الأهداف الأخرى تبني اسم الدليل الفرعي من ماكروهات الهدف في Free Pascal، بصيغة <cpu>-<os>، فتقرأ شجرة النشر تماما كشجرة الوحدات المترجمة. وقد أدخل ذلك القرار الأخير عيبا يستحق المقال كله، لأن السبب كان حرفا كبيرا والعرض كان صمتا

السلسلة، بالترتيب

أربعة مواضع تجرب بتتابع، ثم محمل المنصة كملاذ أخير. أولا التخطيط المفضل، دليل DLLs بجانب الملف التنفيذي يحوي دليلا فرعيا لكل هدف. ثانيا تخطيط بديل والدليل الفرعي للهدف بجانب الملف التنفيذي مباشرة. ثالثا التخطيط القديم المسطح، والمكتبة تجاور الملف التنفيذي بلا دليل فرعي إطلاقا. رابعا، في Windows فقط، دليل النظام، الذي يحتاج عناية لأن عملية ذات 32 بت يجب أن تبحث في SysWOW64 وعملية ذات 64 بت في System32، وفي Windows ذات 32 بت لا يوجد الأول فيعاد البحث. وبعد ذلك كله فقط يطلب من المحمل أن يبحث بنفسه

مخطط سلسلة بحث مكتبة PDFium الأصلية في Delphi، من الدليل الفرعي DLLs للهدف عبر التخطيطات البديلة والمسطحة ودليل نظام Windows إلى محمل المنصة
أربعة مواضع صريحة تُستقصى بالترتيب قبل مطالبة محمل نظام التشغيل بالبحث بنفسه

لا توجد عمدا خطوة دليل نظام خارج 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، فتخطئ الخطوات الصريحة الأربع للسلسلة كلها ويسقط الكود إلى مطالبة محمل المنصة بالبحث. أحيانا يعمل ذلك، إذا صادف أن ثبتت المكتبة على مستوى النظام، وأحيانا لا يعمل، وفي الحالين لا تسهم شجرة النشر المرتبة بعناية بأي شيء. وليس للإخفاق رسالة خطأ لأن شيئا لم يفشل: كل خطوة أفادت صائبة بأن الملف ليس حيث بحثت

كيف يكسر حرف كبير واحد في ماكرو الهدف FPC بحث DLL لـ PDFium على Linux: يبحث المحمل في DLLs/x86_64-Linux بينما المجلد المنشور هو DLLs/x86_64-linux، وهو لا يطابق إلا على Windows غير الحساس لحالة الأحرف
الهدف نفسه بهجأتين يطابق في Windows ويفوت بصمت على نظام ملفات حساس لحالة الأحرف

برنامج المجس، مترجما ومشغلا

لا يمكن إيجاد هذا الصنف من العيوب بالقراءة، ولا بالترجمة هو الآخر. والتقنية الاعتيادية للتحقق من فرع منصة لا يترجم قط على آلة التطوير نسخ الوحدة إلى دليل مؤقت، وإعادة تسميتها، واستبدال الشرط المنصاتي برمز لا يعرف أبدا، وترجمة النسخة؛ فإن ترجمت كانت عبارة 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. وتأهيل موقع النداء هو الإصلاح الذي لا يعتمد على أن يحافظ أحدهم على ذلك الترتيب لاحقا

تصادم اسم UnloadLibrary في مكون PDFium: نداء غير مؤهل في وحدة ربط Delphi يستدعي نفسه تكراريا، بينما نداء مؤهل باسم الوحدة يصل إلى وحدة المحمل المحمولة ويحرر المقبض
تأهيل موقع النداء يمرر التحرير عبر وحدة المحمل بدل الاستدعاء التكراري داخل وحدة الربط

قائمة فحص النشر

ثلاثة أشياء تفسر معظم إخفاقات التحميل مرة تصح حساب المسارات. يجب أن تطابق المعمارية العملية لا الآلة، فتطبيق ذو 32 بت على Windows ذات 64 بت يحتاج الثنائي ذا 32 بت. وبناء V8 الممكن يملك اسم ملف مختلفا، فنشر يمزجهما سيبدو صحيحا ولا يحمل شيئا. ويمكن لصيغة واحدة فقط أن تعيش في دليل نظام في وقت واحد، وهو سبب وجيه لتفضيل تخطيط الدليل الفرعي الصريح على تثبيت أي شيء على مستوى النظام

ولـ Lazarus تحديدا، ضع المكتبة الأصلية تحت DLLs/<cpu>-<os> بأحرف صغيرة، بجانب الملف التنفيذي، وستوجد بالخطوة الأولى من السلسلة على كل هدف. وعينة العارض التي تجرب هذا على Lazarus موصوفة في مقال عارض Lazarus وFPC، ودعم المنصات الحالي مدرج في صفحة منتج PDFium Delphi component