كيفية بناء عدة ملفات ثنائية باستخدام Cargo دون فقدان الأداء؟
\n\nهل سبق لك أن واجهت موقفًا حيث ينمو مشروع Rust الخاص بك إلى درجة لا يستطيع فيها ملف ثنائي واحد التعامل مع المهام؟ ربما تكتب أداة CLI يجب أن تحتوي على ملفات تنفيذية منفصلة لمهام مختلفة: واحد لتحليل البيانات، وآخر لتوليد التقارير، وثالث للتفاعل مع API. أو ربما تقوم بإنشاء تطبيق خادم حيث تحتاج إلى فصل المراقبة ومعالجة الطلبات والمهام الخلفية.
\n\nالسؤال "How can I build multiple binaries with Cargo?" هو أحد أكثر الأسئلة شيوعًا على Stack Overflow بين مطوري Rust. وهذا ليس من قبيل الصدفة. عندما يتجاوز المشروع حدود "Hello, World!" البسيطة، تصبح القدرة على تنظيم بناء عدة ملفات ثنائية بمهارة مهارة بالغة الأهمية. ولكن كيف تفعل ذلك دون فقدان الأداء؟ كيف لا تغرق في تكرار الكود ولا تبطئ عملية الترجمة؟
\n\nفي هذه المقالة، لن نجيب على هذا السؤال فحسب - بل سنوضح كيف يمكن تحويل بناء عدة ملفات ثنائية من صداع إلى أداة قوية للتحسين. ستتعرف على أفضل الممارسات والأداء وحتى الحيل السرية لـ Cargo التي يستخدمها مطورو Rust ذوو الخبرة.
\n\nلماذا عدة ملفات ثنائية؟ مشكلة الملف التنفيذي الواحد
\n\nتخيل: لقد كتبت مكتبة رائعة للعمل مع البيانات. ثم أضفت واجهة CLI. ثم خادم ويب. ثم جدولة مهام. كل ذلك في مشروع واحد. النتيجة؟ كتلة واحدة:
\n\n- \n
- تستغرق الترجمة وقتًا طويلاً، حتى لو قمت بتغيير سطر واحد فقط في CLI. \n
- تتطلب إعادة تشغيل التطبيق بأكمله عند تحديث مكون واحد. \n
- يصعب اختبارها ونشرها. \n
الحل هو تقسيم المشروع إلى عدة ملفات ثنائية. ولكن كيف تفعل ذلك بشكل صحيح في Rust؟
\n\nالأساسيات: Cargo.toml وقسم [[bin]]
\n\nأبسط طريقة هي الإعلان عن عدة أهداف ثنائية في ملف Cargo.toml الخاص بك. دعنا نستعرض مثالًا أساسيًا.
// Cargo.toml\n[package]\nname = "my_project"\nversion = "0.1.0"\nedition = "2021"\n\n[[bin]]\nname = "analyzer"\npath = "src/bin/analyzer.rs"\n\n[[bin]]\nname = "reporter"\npath = "src/bin/reporter.rs"\n\n[[bin]]\nname = "server"\npath = "src/bin/server.rs"\n\n[dependencies]\nserde = { version = "1.0", features = ["derive"] }\ntokio = { version = "1", features = ["full"] }\n\n\nالآن في مجلد src/bin/ لديك ثلاثة ملفات: analyzer.rs و reporter.rs و server.rs. كل منها هو ملف ثنائي كامل مع دالة main() الخاصة به.
لبناء كل شيء مرة واحدة:
\ncargo build --release\n\nأو ملف ثنائي محدد:
\ncargo build --release --bin analyzer\n\nبسيط؟ نعم. ولكن في هذا يكمن المأزق. إذا لم تقم بتحسين البناء، فسيتم ترجمة كل ملف ثنائي من جديد، حتى لو كانت تستخدم نفس التبعيات. وهذا طريق مباشر لفقدان الأداء.
\n\nتحسين أداء البناء: ملفات تعريف Cargo
\n\nسر البناء الفعال لعدة ملفات ثنائية يكمن في ملفات تعريف Cargo. هذه أداة قوية تسمح لك بضبط معلمات الترجمة لسيناريوهات مختلفة.
\n\nملفات التعريف الأساسية: dev و release
\n\nافتراضيًا، يستخدم Cargo ملفي تعريف: dev (للتطوير) و release (للإنتاج). ولكن يمكنك إنشاء ملفات التعريف الخاصة بك أو تعديل الملفات الموجودة.
// Cargo.toml\n[profile.release]\nopt-level = 3 # أقصى تحسين للكود\nlto = "fat" # تحسين وقت الربط (دمج جميع الكريتات)\ncodegen-units = 1 # وحدة واحدة لتوليد الكود لتحسين أفضل\nstrip = true # إزالة معلومات التصحيح\n\n\nهذه الإعدادات مهمة بشكل خاص للملفات الثنائية التي يجب أن تعمل بسرعة. ولكن هناك فارق بسيط: كلما كان التحسين أكثر عدوانية