بنينا سابقًا حزمة تتضمن وحدة تنفيذية مصرفة Binary Crate ووحدة مكتبة مصرفة Library Crate، وقد تجد مع تطور مشروعك أن وحدة المكتبة المصرفة تزداد حجمًا وستحتاج إلى تقسيم حزمتك إلى عدد من وحدات مكتبة مصرفة. يقدّم كارجو Cargo ميزة تدعى مساحات العمل Workspaces التي تساعد على إدارة حزم متعددة مرتبطة تُطوَّر بالترادف tandem أي واحدًا بعد الآخر.
إنشاء مساحة عمل
مساحة العمل هي مجموعة من الحزم التي تتشارك ملف Cargo.lock ومجلد الخرج ذاتهما. سنستخدم شيفرة برمجية بسيطة لإنشاء مشروع باستخدام مساحة العمل، بهدف التركيز على بُنية مساحة العمل أكثر. هناك الكثير من الطرق لبناء مساحة العمل ولذا سنعمل وفق الطريقة الشائعة. سيكون لدينا مساحة عمل تحتوي على وحدة ثنائية أو تنفيذية واحدة ومكتبتين؛ إذ ستؤمن الوحدة الثنائية الوظيفة الأساسية، وستعتمد بدورها على مكتبتين: مكتبة تؤمن دالة add_one
، والثانية ستؤمن دالة add_two
. ستكون الوحدات المصرفة الثلاثة في مساحة العمل ذاتها. نبدأ بعمل مسار جديد لمساحة العمل:
$ mkdir add $ cd add
ننشئ بعد ذلك في مجلد الخرج add الملف Cargo.toml الذي سيضبط مساحة العمل كاملةً. لن يكون لهذا الملف قسم [package]
. بل سيبدأ بقسم [workspace]
الذي سيسمح لنا بإضافة أعضاء إلى مساحة العمل عن طريق تحديد المسار للحزمة باستخدام الوحدة الثنائية أو التنفيذية المصرفة، وفي هذه الحالة المسار هو "adder":
اسم الملف: Cargo.toml
[workspace] members = [ "adder", ]
ننشئ وحدة ثنائية مصرفة [adder]
عن طريق تنفيذ cargo new
في المجلد add:
$ cargo new adder Created binary (application) `adder` package
يمكننا الآن بناء مساحة العمل عن طريق تشغيل cargo build
. الملفات في مجلد add يجب أن تكون على النحو التالي:
├── Cargo.lock ├── Cargo.toml ├── adder │ ├── Cargo.toml │ └── src │ └── main.rs └── target
لمساحة العمل مجلد "target" واحد في بداية المستوى الذي ستوضع فيه أدوات التخطيط artifacts المصرفة، ولا تحتوي حزمة adder
على مجلد "target". حتى لو نفّذنا cargo build
داخل مجلد "adder"، ستكون أدوات التخطيط المصرفة في "add/target" بدلاً من "add/adder/target". يهيّئ كارجو المجلد target بالشكل هذا لأن الحزم المصرفة في مساحة العمل مهيئة لتعتمد على بعضها بعضًا. إذا كان لكل حزمة مصرفة مجلد "target" خاص بها، فهذا يعني أن كل حزمة مصرفة ستُعيد تصريف باقي الحزم المصرفة في مساحة العمل لوضع أدوات التخطيط في مجلد "target" الخاص بها، إلا أن الحزم تتجنب عملية إعادة البناء غير الضرورية بمشاركة مجلد "target" واحد.
إنشاء الحزمة الثانية في مساحة العمل
دعنا ننشئ حزمة عضو ثانية في مساحة العمل ونسميها add_one
. غيِّر ملف Cargo.toml الموجود في المستوى العلوي ليحدد المسار add_one في القائمة members
:
اسم الملف: Cargo.toml
[workspace] members = [ "adder", "add_one", ]
أنشئ بعد ذلك حزمة مكتبة مصرفة اسمها add_one
:
$ cargo new add_one --lib Created library `add_one` package
يجب أن يحتوي مجلد "add" الآن على المجلدات والملفات التالية:
├── Cargo.lock ├── Cargo.toml ├── add_one │ ├── Cargo.toml │ └── src │ └── lib.rs ├── adder │ ├── Cargo.toml │ └── src │ └── main.rs └── target
نضيف في الملف add_one/scr/lib.rs الدالة add_one
:
اسم الملف: add_one/src/lib.rs
pub fn add_one(x: i32) -> i32 { x + 1 }
الآن بإمكاننا أن نجعل كلًا من الحزمة adder
والوحدة الثنائية المصرفة تعتمدان على حزمة add_one
التي تحتوي مكتبتنا. أولاً، نضيف اعتمادية مسار add_one
إلى الملف adder/Cargo.toml.
[dependencies] add_one = { path = "../add_one" }
لا يفترض كارجو أن الحزم المصرفة تعتمد على بعضها في مساحة العمل، لذا نحتاج إلى توضيح علاقات الاعتمادية.
لنستخدم بعدها دالة add_one
(من الحزمة المصرفة add_one
) في الحزمة المصرفة adder
. افتح الملف adder/scr/main.rs وأضف سطر use
في الأعلى لإضافة حزمة المكتبة المصرفة add_one
الجديدة إلى النطاق. ثم عدِّل الدالة main
بحيث تستدعي الدالة add_one
كما في الشيفرة 7.
اسم الملف: adder/src/main.rs
use add_one; fn main() { let num = 10; println!("Hello, world! {num} plus one is {}!", add_one::add_one(num)); }
[الشيفرة 7: استخدام حزمة المكتبة المصرفة add_one
من الحزمة adder
]
دعنا نبني مساحة العمل بتنفيذ cargo build
في مجلد "add" العلوي.
$ cargo build Compiling add_one v0.1.0 (file:///projects/add/add_one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 0.68s
يمكننا تحديد أي حزمة نريد تشغيلها في مساحة العمل باستخدام الوسيط -p
واسم الحزمة مع cargo run
لتشغيل الحزمة الثنائية المصرفة من المجلد "add":
$ cargo run -p adder Finished dev [unoptimized + debuginfo] target(s) in 0.0s Running `target/debug/adder` Hello, world! 10 plus one is 11!
يشغل هذا الأمر الشيفرة الموجودة في الملف "adder/scr/main.rs"، والتي تعتمد على الحزمة المصرفة add_one
.
الاعتماد على حزمة خارجية في مساحة العمل
نلاحظ أن مساحة العمل تحتوي على ملف Cargo.lock واحد في المستوى الأعلى، بدلاً من أن يكون هناك ملف Cargo.lock في كل مسار حزمة مصرفة. يضمن ذلك أن كل حزمة مصرفة تستخدم الإصدار ذاته لكل الاعتماديات. إذا أضفنا حزمة rand
للملفين "adder/Cargo.toml" و "add_one/Cargo.toml"، سيحوِّل كارجو كلاهما إلى إصدار واحد من rand
، ثم سيسجل ذلك في Cargo.lock. جعل كل حزم المصرفة تستخدم نفس الاعتمادية يعني أن كل الحزم المصرفة ستكون متوافقة مع بعضها. دعنا نضيف الحزمة المصرفة rand
إلى قسم [dependencies]
في ملف add_one/Cargo.toml لكي نستخدم الحزمة المصرفة rand
في الحزمة المصرفة add_one
:
اسم الملف: add_one/Cargo.toml
[dependencies] rand = "0.8.5"
يمكننا الآن إضافة use rand;
إلى الملف add_one/src/lib.rs، وبناء كامل مساحة العمل عن طريق تنفيذ cargo build
في المجلد "add" الذي سيجلب ويصرِّف الحزمة المصرفة rand
. نحصل على تحذير واحد لأننا لم نُشر إلى حزمة rand
التي أضفناها إلى النطاق:
$ cargo build Updating crates.io index Downloaded rand v0.8.5 --snip-- Compiling rand v0.8.5 Compiling add_one v0.1.0 (file:///projects/add/add_one) warning: unused import: `rand` --> add_one/src/lib.rs:1:5 | 1 | use rand; | ^^^^ | = note: `#[warn(unused_imports)]` on by default warning: `add_one` (lib) generated 1 warning Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 10.18s
يحتوي ملف Cargo.lock في المستوى العلوي على معلومات عن الاعتمادية لكل من add_one
و rand
، ولكن وعلى الرغم من أننا نستخدم rand
في مكان ما ضمن مساحة العمل إلا أننا لا نستطيع استخدامها في الحزم المصرفة الأخرى إلا إذا اضفنا rand
إلى ملف Cargo.toml الخاص بها أيضاً. على سبيل المثال إذا أضفنا use rand;
إلى ملف adder/scr/main.rs من أجل الحزمة adder
سنحصل على خطأ:
$ cargo build --snip-- Compiling adder v0.1.0 (file:///projects/add/adder) error[E0432]: unresolved import `rand` --> adder/src/main.rs:2:5 | 2 | use rand; | ^^^^ no external crate `rand`
لحل هذه المشكلة، عدِّل ملف Cargo.toml لحزمة adder
وأشِر إلى أن rand
هي اعتمادية لها أيضاً. بناء الحزمة adder
سيضيف rand
إلى لائحة اعتماديات adder
في ملف cargo.lock، ولكن لن يجري أي تنزيل لنسخ إضافية من rand
. يضمن كارجو أن كل حزمة مصرفة في كل حزمة في مساحة العمل تستخدم نفس الإصدار من الحزمة rand
، وبالتالي ستقل المساحة التخزينية المستخدمة وسنضمن أن كل الحزم المصرفة في مساحة العمل ستكون متوافقة مع بعضها بعضًا.
إضافة اختبار إلى مساحة العمل
سنضيف اختبارًا للدالة add_one::add_one
داخل الحزمة المصرفة add_one
للمزيد من التحسينات:
اسم الملف: add_one/src/lib.rs
pub fn add_one(x: i32) -> i32 { x + 1 } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { assert_eq!(3, add_one(2)); } }
نفّذ الأمر cargo test
ضمن مجلد "add" العلوي، إذ سيؤدي تنفيذ cargo test
في مساحة عمل مهيكلة بهذا الشكل إلى تنفيذ الاختبارات الخاصة بالحزم المصرفة في مساحة العمل:
$ cargo test Compiling add_one v0.1.0 (file:///projects/add/add_one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished test [unoptimized + debuginfo] target(s) in 0.27s Running unittests src/lib.rs (target/debug/deps/add_one-f0253159197f7841) running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running unittests src/main.rs (target/debug/deps/adder-49979ff40686fa8e) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests add_one running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
يُظهر أول قسم في الخرج نجاح اختبار it_works
في الحزمة المصرفة add_one
، بينما يظهر القسم الثاني أنه لم يُعثر على أي اختبار في الحزمة المصرفة adder
، ويظهر القسم الأخير عدم العثور على اختبارات توثيق documentation tests في الحزمة المصرفة add_one
.
يمكن أيضاّ تنفيذ اختبارات لحزمة مصرفة محددة في مساحة عمل من المجلد العلوي باستخدام الراية -p
وتحديد اسم الحزمة المصرفة المراد اختبارها:
$ cargo test -p add_one Finished test [unoptimized + debuginfo] target(s) in 0.00s Running unittests src/lib.rs (target/debug/deps/add_one-b3235fea9a156f74) running 1 test test tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests add_one running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
يظهر الخرج أن cargo test
نفّذ فقط الاختبارات الموجودة في الحزمة المصرفة add_one
ولم ينفّذ الاختبارات الموجودة في الحزمة المصرفة adder
.
إذا أردت نشر الحزم المصرفة في مساحة العمل على crates.io، فيجب على كل حزمة مصرفة في مساحة العمل أن تُنشر على حدة. نستطيع نشر حزمة مصرفة معينة في مساحة العمل باستخدام الراية -p
وتحديد اسم الحزمة المصرفة المراد نشرها بصورةٍ مماثلة للأمر cargo test.
للتدرّب على العملية بصورةٍ أفضل، ضِف الحزمة المصرفة add_two
لمساحة العمل هذه بنفس طريقة الحزمة المصرفة add_one
.
ضع في الحسبان استخدام مساحة العمل كلما كبر مشروعك، فمن الأسهل فهم مكونات صغيرة ومنفردة على كتلة كبيرة من الشيفرة البرمجية. إضافةً إلى ذلك، إبقاء الحزم المصرفة في مساحة عمل واحدة يجعل التنسيق بين الحزم المصرفة أسهل إذا كانت تُعدَّل باستمرار في نفس الوقت.
تثبيت الملفات الثنائية binaries باستخدام cargo install
يسمح لك أمر cargo install
بتثبيت واستخدام الوحدات الثنائية المصرفة محليًا، وليس المقصود من ذلك استبدال حزم النظام، إذ أن الأمر موجود ليكون بمثابة طريقة ملائمة لمطوري رست لتثبيت الأدوات التي شاركها الآخرون على crates.io. لاحظ أنه يمكنك فقط تثبيت الحزم التي تحتوي أهداف ثنائية binary targets، والهدف الثنائي هو برنامج قابل للتشغيل يُنشأ إذا كانت الحزمة المصرفة تحتوي على ملف src/main.rs أو ملف آخر محدد على أنه ملف تنفيذي، على عكس هدف المكتبة library target الذي لا يمكن تشغيله لوحده، فهو موجود لضمِّه داخل برامج أخرى. تحتوي الحزم المصرفة عادةً على معلومات في ملف README وتدل هذه المعلومات فيما إذا كانت الوحدة المصرفة مكتبية أو تحتوي هدفًا ثنائيًا أو كلاهما.
تُخزَّن كل الوحدات الثنائية المصرفة المثبتة عند تنفيذ cargo install
في مجلد التثبيت الجذر الذي يدعى "bin". إذا ثبتّت رست باستخدام "rustup.rs" ولم يكن لديك أي إعدادات افتراضية فإن المجلد سيكون $HOME/.cargo/bin. تأكد أن هذا المجلد في $PATH
الخاص بك لتكون قادراً على تشغيل البرامج التي ثبتتها باستخدام cargo install
.
ذكرنا سابقًا أن هناك تنفيذ لأداة grep
بلغة رست اسمه ripgrep
للبحث عن الملفات، ولتثبيت ripgrep
يمكنك تنفيذ الأمر التالي:
$ cargo install ripgrep Updating crates.io index Downloaded ripgrep v13.0.0 Downloaded 1 crate (243.3 KB) in 0.88s Installing ripgrep v13.0.0 --snip-- Compiling ripgrep v13.0.0 Finished release [optimized + debuginfo] target(s) in 3m 10s Installing ~/.cargo/bin/rg Installed package `ripgrep v13.0.0` (executable `rg`)
يظهر السطر الثاني قبل الأخير من المخرجات مكان واسم الثنائية المثبتة، وهي rg
في حالة ripgrep
. إذا كان مجلد التثبيت موجودًا في $PATH
الخاص بك، فيمكنك تشغيل rg --help
والبدء باستخدام أداة أسرع مكتوبة بلغة رست للبحث عن الملفات.
توسيع استخدامات كارجو عن طريق أوامر مخصصة
كارجو مصمم بحيث يمكن توسيع استخداماته بأوامر فرعية دون الحاجة لتعديله. إذا كان هناك وحدة ثنائية binary ضمن $PATH
اسمها cargo-something
، فهذا يعني أنه يمكنك تشغيلها كما لو كانت أمر فرعي لكارجو عن طريق تنفيذ cargo something
. تستطيع استعراض الأوامر المخصصة بتنفيذ cargo --list
. قدرتك على استخدام cargo install
لتثبيت الإضافات وتشغيلها كما في أدوات الكارجو المضمّنة built-in هي ميزة ملائمة جداً بتصميم كارجو.
ترجمة -وبتصرف- لقسم من الفصل More About Cargo and Crates.io من كتاب The Rust Programming Language.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.