اذهب إلى المحتوى

مساحة عمل كارجو Cargo Workspaces في لغة رست وتحميل حزمة من crates.io


Naser Dakhel

بنينا سابقًا حزمة تتضمن وحدة تنفيذية مصرفة 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.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...