بستر، زمینه

Bazel می تواند کد را روی انواع سخت افزار، سیستم عامل و پیکربندی سیستم، با استفاده از نسخه های مختلف ابزارهای ساخت مانند لینکرها و کامپایلرها بسازد و آزمایش کند. برای کمک به مدیریت این پیچیدگی، Bazel مفهومی از محدودیت‌ها و پلتفرم‌ها دارد. محدودیت ابعادی است که در آن محیط های ساخت یا تولید ممکن است متفاوت باشند، مانند معماری CPU، وجود یا عدم وجود یک GPU، یا نسخه یک کامپایلر نصب شده در سیستم. پلتفرم مجموعه ای نامگذاری شده از انتخاب ها برای این محدودیت ها است که نشان دهنده منابع خاصی است که در برخی از محیط ها موجود است.

مدل‌سازی محیط به‌عنوان یک پلتفرم به Bazel کمک می‌کند تا به طور خودکار زنجیره‌های ابزار مناسب را برای اقدامات ساخت انتخاب کند. پلتفرم ها همچنین می توانند در ترکیب با قانون config_setting برای نوشتن ویژگی های قابل تنظیم استفاده شوند .

بازل سه نقش را که یک پلتفرم ممکن است ایفا کند تشخیص می دهد:

  • میزبان - پلتفرمی که خود بازل روی آن اجرا می شود.
  • Execution - پلتفرمی است که ابزارهای ساخت بر روی آن اقدامات ساخت را برای تولید خروجی های میانی و نهایی اجرا می کنند.
  • Target - پلتفرمی که خروجی نهایی روی آن قرار دارد و اجرا می شود.

Bazel از سناریوهای ساخت زیر در مورد پلتفرم ها پشتیبانی می کند:

  • ساخت‌های تک پلتفرمی (پیش‌فرض) - پلتفرم‌های میزبان، اجرا و هدف یکسان هستند. به عنوان مثال، ساخت یک لینوکس اجرایی بر روی اوبونتو که بر روی CPU اینتل x64 اجرا می شود.

  • ساخت های متقابل کامپایل - پلت فرم های میزبان و اجرا یکسان هستند، اما پلت فرم هدف متفاوت است. به عنوان مثال، ساختن یک برنامه iOS در macOS که روی مک بوک پرو اجرا می شود.

  • ساخت‌های چند پلتفرمی - پلتفرم‌های میزبان، اجرا و هدف همگی متفاوت هستند.

تعریف محدودیت ها و پلتفرم ها

فضای انتخاب های ممکن برای پلتفرم ها با استفاده از قوانین constraint_setting و constraint_value در فایل های BUILD تعریف می شود. constraint_setting یک بعد جدید ایجاد می کند، در حالی که constraint_value یک مقدار جدید برای یک بعد معین ایجاد می کند. آنها با هم به طور موثر یک enum و مقادیر ممکن آن را تعریف می کنند. به عنوان مثال، قطعه زیر از یک فایل BUILD یک محدودیت برای نسخه glibc سیستم با دو مقدار ممکن معرفی می کند.

constraint_setting(name = "glibc_version")

constraint_value(
    name = "glibc_2_25",
    constraint_setting = ":glibc_version",
)

constraint_value(
    name = "glibc_2_26",
    constraint_setting = ":glibc_version",
)

محدودیت ها و مقادیر آنها ممکن است در بسته های مختلف در فضای کاری تعریف شود. آنها با برچسب ارجاع داده می شوند و تحت کنترل های دید معمول هستند. اگر دید اجازه می دهد، می توانید یک تنظیم محدودیت موجود را با تعریف مقدار خود برای آن گسترش دهید.

قانون platform یک پلتفرم جدید با انتخاب های خاصی از مقادیر محدودیت معرفی می کند. زیر پلتفرمی به نام linux_x86 ایجاد می کند و می گوید که هر محیطی را توصیف می کند که یک سیستم عامل لینوکس را بر روی معماری x86_64 با نسخه glibc 2.25 اجرا می کند. (برای اطلاعات بیشتر در مورد محدودیت های داخلی Bazel به زیر مراجعه کنید.)

platform(
    name = "linux_x86",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":glibc_2_25",
    ],
)

به طور کلی محدودیت ها و پلت فرم های مفید

برای ثابت نگه داشتن اکوسیستم، تیم Bazel یک مخزن با تعاریف محدودیت برای محبوب ترین معماری ها و سیستم عامل های CPU نگهداری می کند. همه اینها در https://github.com/bazelbuild/platforms قرار دارند.

Bazel با تعریف پلت فرم ویژه زیر ارسال می شود: @local_config_platform//:host . این مقدار پلت فرم میزبان شناسایی شده خودکار است - نشان دهنده پلتفرم شناسایی خودکار برای سیستمی است که Bazel روی آن اجرا می شود.

تعیین پلتفرم برای ساخت

می‌توانید با استفاده از پرچم‌های خط فرمان زیر، پلتفرم‌های میزبان و هدف را برای یک بیلد مشخص کنید:

  • --host_platform - پیش‌فرض به @bazel_tools//platforms:host_platform
  • --platforms - پیش‌فرض به @bazel_tools//platforms:target_platform

پرش از اهداف ناسازگار

هنگام ساختن برای یک پلت فرم هدف خاص، اغلب مطلوب است که از اهدافی که هرگز روی آن پلتفرم کار نمی کنند، چشم پوشی کنید. به عنوان مثال، درایور دستگاه ویندوز شما احتمالاً هنگام ساخت بر روی یک ماشین لینوکس با //... خطاهای کامپایلر زیادی ایجاد می کند. از ویژگی target_compatible_with استفاده کنید تا به Bazel بگویید کد شما دارای چه محدودیت های پلتفرم هدف است.

ساده ترین استفاده از این ویژگی یک هدف را به یک پلتفرم محدود می کند. هدف برای هیچ پلتفرمی که تمام محدودیت ها را برآورده نمی کند ساخته نخواهد شد. مثال زیر win_driver_lib.cc را به ویندوز 64 بیتی محدود می کند.

cc_library(
    name = "win_driver_lib",
    srcs = ["win_driver_lib.cc"],
    target_compatible_with = [
        "@platforms//cpu:x86_64",
        "@platforms//os:windows",
    ],
)

:win_driver_lib فقط برای ساختن با ویندوز 64 بیتی سازگار است و با همه موارد دیگر سازگار نیست. ناسازگاری گذرا است. هر هدفی که به طور موقت به یک هدف ناسازگار وابسته باشد، خود ناسازگار در نظر گرفته می شود.

چه زمانی اهداف نادیده گرفته می شوند؟

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

$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all

اگر test_suite در خط فرمان با --expand_test_suites مشخص شده باشد، آزمایش‌های ناسازگار در یک test_suite به همین ترتیب نادیده گرفته می‌شوند. به عبارت دیگر، اهداف test_suite در خط فرمان مانند :all و ... رفتار می کنند. استفاده از --noexpand_test_suites از گسترش جلوگیری می کند و باعث می شود که اهداف test_suite با تست های ناسازگار نیز ناسازگار باشند.

مشخص کردن صریح یک هدف ناسازگار در خط فرمان منجر به یک پیام خطا و یک ساخت ناموفق می شود.

$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully

محدودیت های بیانی بیشتر

برای انعطاف بیشتر در بیان محدودیت ها، از @platforms//:incompatible constraint_value استفاده کنید که هیچ پلتفرمی آن را برآورده نمی کند.

از select() در ترکیب با @platforms//:incompatible برای بیان محدودیت های پیچیده تر استفاده کنید. به عنوان مثال، از آن برای پیاده سازی منطق اولیه OR استفاده کنید. موارد زیر یک کتابخانه سازگار با macOS و Linux را نشان می‌دهد، اما هیچ پلتفرم دیگری را ندارد.

cc_library(
    name = "unixish_lib",
    srcs = ["unixish_lib.cc"],
    target_compatible_with = select({
        "@platforms//os:osx": [],
        "@platforms//os:linux": [],
        "//conditions:default": ["@platforms//:incompatible"],
    }),
)

موارد فوق را می توان به صورت زیر تفسیر کرد:

  1. هنگام هدف قرار دادن macOS، هدف هیچ محدودیتی ندارد.
  2. هنگام هدف قرار دادن لینوکس، هدف هیچ محدودیتی ندارد.
  3. در غیر این صورت، هدف دارای @platforms//:incompatible است. از آنجایی که @platforms//:incompatible بخشی از هیچ پلتفرمی نیست، هدف ناسازگار تلقی می شود.

برای خوانایی بیشتر محدودیت های خود، از selects.with_or() selects.with_or skylib استفاده کنید.

شما می توانید سازگاری معکوس را به روشی مشابه بیان کنید. مثال زیر کتابخانه ای را توصیف می کند که با همه چیز به جز ARM سازگار است.

cc_library(
    name = "non_arm_lib",
    srcs = ["non_arm_lib.cc"],
    target_compatible_with = select({
        "@platforms//cpu:arm": ["@platforms//:incompatible"],
        "//conditions:default": [],
    ],
)

شناسایی اهداف ناسازگار با استفاده از bazel cquery

می‌توانید از IncompatiblePlatformProvider در قالب خروجی Starlark در bazel cquery cquery برای تشخیص اهداف ناسازگار از اهداف سازگار استفاده کنید.

این می تواند برای فیلتر کردن اهداف ناسازگار استفاده شود. مثال زیر فقط برچسب‌هایی را برای اهدافی چاپ می‌کند که سازگار هستند. اهداف ناسازگار چاپ نمی شوند.

$ cat example.cquery

def format(target):
  if "IncompatiblePlatformProvider" not in providers(target):
    return target.label
  return ""


$ bazel cquery //... --output=starlark --starlark:file=example.cquery

مشکلات شناخته شده

اهداف ناسازگار محدودیت های دید را نادیده می گیرند .