وابستگی ها

اگر B در زمان ساخت یا اجرا مورد نیاز A باشد، هدف A به هدف B بستگی دارد . رابطه وابسته یک گراف غیر چرخه جهت دار (DAG) را روی اهداف القا می کند و به آن نمودار وابستگی می گویند.

وابستگی های مستقیم یک هدف، آن دسته از اهداف دیگری هستند که با مسیری به طول 1 در نمودار وابستگی قابل دسترسی هستند. وابستگی های گذرای یک هدف، اهدافی هستند که از طریق مسیری با هر طولی در نمودار به آنها بستگی دارد.

در واقع، در زمینه ساخت‌ها، دو نمودار وابستگی وجود دارد، نمودار وابستگی‌های واقعی و نمودار وابستگی‌های اعلام‌شده . اغلب اوقات، این دو نمودار به قدری شبیه هستند که نیازی به ایجاد این تمایز نیست، اما برای بحث زیر مفید است.

وابستگی های واقعی و اعلام شده

هدف X در واقع به هدف Y وابسته است اگر Y باید وجود داشته باشد، ساخته شود و به روز باشد تا X به درستی ساخته شود. ساخته شده می تواند به معنای تولید، پردازش، کامپایل، پیوند، بایگانی، فشرده، اجرا شده یا هر نوع دیگری از وظایفی باشد که به طور معمول در طول ساخت انجام می شود.

اگر یک یال وابستگی از X به Y در بسته X وجود داشته باشد، یک هدف X یک وابستگی به هدف Y دارد.

برای ساخت‌های صحیح، نمودار وابستگی‌های واقعی 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 مطابقت دارد.
نمودار وابستگی واقعی

وابستگی های اعلام شده بیش از حد وابستگی های واقعی است. همه چیز خوب است.

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 متصل نمی شود، که اتصال a به 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 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 را به عنوان وابستگی داده در آزمایش خود ارجاع دهید.

فایل ها را بسازید قابلیت مشاهده