تبعيات

يتم تحديد الاستهداف A بالاستناد إلى الاستهداف B إذا كان B مطلوبًا من A في وقت الإنشاء أو التنفيذ. ويؤدي الاعتماد على إلى إنشاء رسم بياني أثري موجّه (DAG) فوق الأهداف، ويُطلق عليه الرسم البياني للاعتمادية.

التبعيات المباشرة هي الأهداف الأخرى التي يمكن الوصول إليها عبر مسار 1 في الرسم البياني للاعتمادية. إنّ التبعيات العابرة للهدف هي أهداف يتم الاعتماد عليها عبر مسار بأي طول من خلال الرسم البياني.

في الواقع، في سياق الإصدارات، يتوفّر رسمان بيانيان للاعتمادية، والرسم البياني للاعتماديات الفعلية والرسم البياني للاعتماديات المُعلَن عنها. وفي معظم الأحيان، يكون الرسمان البيانيان مشابهين إلى حدّ كبير لدرجة أنّه لا يمكن إجراء هذا الفارق، إلا أنّه مفيد للمناقشة أدناه.

المهام التابعة الفعلية والمُعلنة

يعتمد الاستهداف X على الاستهداف الفعلي في الهدف Y إذا كان Y متوفرًا ومصمّمًا ومُحدّثًا حتى يتم إنشاء X بشكل صحيح. يعني التصميم أنه تم إنشاؤه أو معالجته أو تجميعه أو ربطه أو أرشفته أو ضغطه أو تنفيذه أو أي من أنواع المهام الأخرى التي تحدث بشكل روتيني أثناء الإصدار.

تتضمّن القيمة X المستهدَفة اعتمادية على الهدف Y إذا كانت هناك اعتمادية من X إلى Y في الحزمة X.

بالنسبة إلى الإصدارات الصحيحة، يجب أن يكون الرسم البياني للتبعيات الفعلية A فرعيًا على الرسم البياني للتبعيات المُعلنة D. وهذا يعني أنّ كل زوج من العُقد المتصلة مباشرةً x --> y في A يجب أيضًا أن يتصل مباشرةً في D. يمكن قول D هو تقريب للصيغة A.

يجب أن يعبّر مؤلّفو الملفات عن BUILD صراحةً عن جميع المهام التابعة المباشرة لكل قاعدة في نظام الإصدار.

يتسبب عدم اتّباع هذا المبدأ في حدوث سلوك غير محدّد: قد يتعذّر إتمام عملية الإصدار، ولكن أسوأ من ذلك، قد يعتمد الإصدار على بعض العمليات السابقة، أو على تبعيات مُعلَنة بشكل متبادل والهدف منها. يتحقّق Bazel من العناصر التابعة المفقودة ويبيّن الأخطاء، ولكن لا يمكن استكمال عملية الفحص هذه في جميع الحالات.

لست بحاجة إلى محاولة (ويجب عدم) محاولة سرد كل ما تم استيراده بشكل غير مباشر، حتى إذا كان مطلوبًا في A أثناء التنفيذ.

أثناء إنشاء الاستهداف X، تفحص أداة الإصدار إغلاق الاعتماديات الانتقالية بالكامل لـ X لضمان ظهور أي تغييرات في هذه الأهداف في النتيجة النهائية، وإعادة إنشاء الوسائط المتوسطة حسب الحاجة.

تؤدي الطبيعة الانتقالية للاعتماديات إلى حدوث خطأ شائع. في بعض الأحيان، قد يستخدم الرمز في ملف واحد رمزًا تم تقديمه من خلال تبعية غير مباشرة - وهي نقطة انتقالية ولكن غير مباشرة في الرسم البياني للاعتمادية المُعلنة. لا تظهر العناصر التابعة غير المباشرة في ملف BUILD. بما أنّ القاعدة لا تعتمد بشكل مباشر على مقدّم الخدمة، لا تتوفّر طريقة لتتبُّع التغييرات، كما هو موضّح في المثال الزمني التالي:

1- تتطابق العناصر التابعة المُعلنة مع المهام التابعة الفعلية.

أولاً، تنجح العملية. يستخدم الرمز في الحزمة a الرمز في الحزمة b. يستخدم الرمز في الحزمة b الرمز في الحزمة c، وبالتالي يعتمد a بشكلٍ مؤقت على c.

a/BUILD b/BUILD
rule(
    name = "a",
    srcs = "a.in",
    deps = "//b:b",
)
      
rule(
    name = "b",
    srcs = "b.in",
    deps = "//c:c",
)
      
a / a.in b / b.in
import b;
b.foo();
    
import c;
function foo() {
  c.bar();
}
      
رسم بياني للاعتمادية مُعلَّق عليه أسهم يربط بين a وb وc
رسم بياني للاعتمادية
الرسم البياني للاعتمادية الفعلية الذي يطابق الرسم البياني للاعتمادية المُعلن عنه
                  مع الأسهم التي تربط بين a وb وc
الرسم البياني للاعتمادية الفعلية

اعتماديات Google التي تم اعتمادها بشكل زائد عن المهام التابعة الفعلية كل شيء على ما يرام.

2. إضافة تبعية غير معلنة

يظهر خطر كامن عندما يضيف شخص رمزًا إلى a ينشئ اعتمادية مباشرة مباشرة على c، ولكن ينسى تعريفه في ملف الإصدار a/BUILD.

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
رسم بياني للاعتمادية مُعلَّق عليه أسهم يربط بين a وb وc
رسم بياني للاعتمادية
رسم بياني للاعتمادية الفعلية يتضمن أسهمًا تربط بين a وb وc. السهم المتّصل
                  يشير أيضًا إلى حرف A إلى C أيضًا. لا يتطابق هذا
                  مع الرسم البياني للاعتمادية المعلَن عنه
الرسم البياني للاعتمادية الفعلية

لم تعُد المهام التابعة المُعلنة أعلى من المهام التابعة الفعلية. قد يبدو هذا الأمر جيدًا، لأنّ عمليات الإغلاق العابرة للرسمَين البيانيَين متساوية، ولكنها تحجب المشكلة: لدى a تبعية فعلية غير معلنة على c.

3- الاختلاف بين الرسوم البيانية المُعلَنة والاعتمادية الفعلية

ويتم الكشف عن الخطر عند إعادة تصميم المستخدم b بحيث لا يعتمد على c، عن طريق الخطأ a، بدون حدوث أي خطأ.

  b/BUILD
 
rule(
    name = "b",
    srcs = "b.in",
    deps = "//d:d",
)
      
  b / b.in
 
      import d;
      function foo() {
        d.baz();
      }
      
رسم بياني للاعتمادية المُعلَن به سهمان يربطان a وb.
                  لم يعد b يتصل بـ c، ما يؤدي إلى قطع الاتصال بشبكة c،
رسم بياني للاعتمادية
رسم بياني للاعتمادية الفعلية يعرض اتصالاً بـ b وc،
                  ولكن لم يعد b يتصل بـ c
الرسم البياني للاعتمادية الفعلية

أصبح الرسم البياني للاعتمادية المعلّق الآن تقريبًا للاعتماديات الفعلية، حتى عندما يكون مغلقًا بشكل مؤقت، ومن المحتمل أن يتعذّر الإصدار.

يمكن تجنُّب المشكلة من خلال التأكّد من أن الاعتمادية الفعلية من a إلى c التي تم تقديمها في الخطوة 2 تم الإعلان عنها بشكل صحيح في ملف BUILD.

أنواع المهام التابعة

تتضمّن معظم قواعد الإصدار ثلاث سمات لتحديد أنواع مختلفة من التبعيات العامة: srcs وdeps وdata. وهي موضّحة أدناه. لمزيد من التفاصيل، راجِع السمات الشائعة لجميع القواعد.

وتشمل العديد من القواعد أيضًا سمات إضافية لأنواع العناصر التابعة للقواعد، مثل compiler أو resources. وتتوفّر هذه التفاصيل في موسوعة الإصدار.

تبعيتان (srcs)

الملفات التي تم استهلاكها مباشرةً من خلال القاعدة أو القواعد التي تعرض ملفات المصدر.

تبعيتان (deps)

قاعدة تشير إلى وحدات مجمّعة بشكل منفصل تقدّم ملفات عناوين ورموزًا ومكتبات وبيانات وغير ذلك

تبعيتان (data)

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

يُجري نظام الإصدار اختبارات في دليل معزول، حيث لا تتوفّر سوى الملفات المدرَجة على أنها data. وبالتالي، إذا كان البرنامج الثنائي/المكتبة/الاختبار يحتاج إلى بعض الملفات لتشغيلها، حدِّدها (أو قاعدة إصدار تحتوي عليها) في data. مثلاً:

# I need a config file from a directory named env:
java_binary(
    name = "setenv",
    ...
    data = [":env/default_env.txt"],
)

# I need test data from another directory
sh_test(
    name = "regtest",
    srcs = ["regtest.sh"],
    data = [
        "//data:file1.txt",
        "//data:file2.txt",
        ...
    ],
)

تتوفّر هذه الملفات باستخدام المسار النسبي path/to/data/file. في الاختبارات، يمكنك الرجوع إلى هذه الملفات من خلال الانضمام إلى مسارات دليل ملفات المصدر والمسار التجريبي في مساحة العمل، على سبيل المثال، ${TEST_SRCDIR}/workspace/path/to/data/file.

استخدام التصنيفات للرجوع إلى الأدلة

أثناء الاطّلاع على ملفات BUILD، قد تلاحظ أن بعض تصنيفات data تشير إلى الأدلة. تنتهي هذه التصنيفات بـ /. أو / كما في هذه الأمثلة، والتي يجب عدم استخدامها:

إجراء غير مقترَح: — data = ["//data/regression:unittest/."]

إجراء غير مقترَح: — data = ["testdata/."]

إجراء غير مقترَح: — data = ["testdata/"]

يبدو هذا الأمر مناسبًا، خاصة للاختبارات لأنها تسمح للاختبار باستخدام كل ملفات البيانات في الدليل.

ولكن حاوِل عدم تنفيذ هذا الإجراء. لضمان عمليات إعادة الإنشاء التدريجية الصحيحة (وإعادة تنفيذ الاختبارات) بعد إجراء تغيير، يجب أن يكون نظام الإصدار على دراية بالمجموعة الكاملة من الملفات التي يتم إدخالها في الإصدار (أو الاختبار). عند تحديد دليل، لا ينفِّذ نظام الإصدار عملية إعادة إنشاء إلا عند تغيير الدليل نفسه (بسبب إضافة ملفات أو حذفها)، ولكن لن يتمكن من رصد التعديلات على الملفات الفردية لأن هذه التغييرات لا تؤثر في الدليل المُضمَّن. وبدلاً من تحديد الأدلة كإدخالات إلى نظام الإصدار، يجب عدّ مجموعة الملفات المتضمّنة فيها، إما بشكل صريح أو باستخدام الدالة glob(). (استخدِم ** لفرض glob() على التكرار.)

مقترَحdata = glob(["testdata/**"])

للأسف، هناك بعض السيناريوهات التي يجب فيها استخدام تصنيفات الدليل. على سبيل المثال، إذا كان الدليل testdata يحتوي على ملفات لا تتوافق أسماؤها مع بنية التصنيف، يؤدي تعداد الملفات الصريح أو استخدام الدالة glob() إلى حدوث خطأ غير صالح في التصنيفات. يجب استخدام تصنيفات الدليل في هذه الحالة، ولكن احذر من المخاطر المرتبطة بعمليات إعادة التصميم غير الصحيحة الموضّحة أعلاه.

إذا احتجت إلى استخدام تصنيفات الدليل، تذكّر أنّه لا يمكنك الرجوع إلى الحزمة الرئيسية التي تتضمّن مسار ../ نسبيًا. وبدلاً من ذلك، استخدِم مسارًا مطلقًا مثل //data/regression:unittest/..

أي قاعدة خارجية، مثل اختبار، تحتاج إلى استخدام ملفات متعدّدة، يجب أن تذكر صراحةً اعتمادها عليها جميعًا. يمكنك استخدام filegroup() لتجميع الملفات معًا في ملف BUILD:

filegroup(
        name = 'my_data',
        srcs = glob(['my_unittest_data/*'])
)

يمكنك بعد ذلك الإشارة إلى التصنيف my_data على أنه اعتمادية البيانات في الاختبار.

ملف (BUILD) مستوى العرض