ماکروها

این صفحه اصول استفاده از ماکروها را پوشش می دهد و شامل موارد استفاده معمولی، اشکال زدایی و قراردادها می شود.

ماکرو تابعی است که از فایل BUILD فراخوانی می شود که می تواند قوانین را نمونه سازی کند. ماکروها عمدتاً برای کپسوله‌سازی و استفاده مجدد از کد قوانین موجود و سایر ماکروها استفاده می‌شوند. در پایان مرحله بارگذاری ، ماکروها دیگر وجود ندارند و Bazel تنها مجموعه مشخصی از قوانین نمونه را می بیند.

استفاده

مورد معمول استفاده برای یک ماکرو زمانی است که می خواهید از یک قانون استفاده مجدد کنید.

به عنوان مثال، genrule در یک فایل BUILD یک فایل را با استفاده از //:generator با آرگومان some_arg که در دستور هاردکد شده است تولید می کند:

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

اگر می خواهید فایل های بیشتری با آرگومان های مختلف تولید کنید، ممکن است بخواهید این کد را به یک تابع ماکرو استخراج کنید. اجازه دهید ماکرو file_generator را فراخوانی کنیم که دارای پارامترهای name و arg است. ژانر را با موارد زیر جایگزین کنید:

load("//path:generator.bzl", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)

file_generator(
    name = "file-two",
    arg = "some_arg_two",
)

file_generator(
    name = "file-three",
    arg = "some_arg_three",
)

در اینجا، نماد file_generator را از یک فایل .bzl . واقع در بسته //path بارگیری می کنید. با قرار دادن تعاریف تابع ماکرو در یک فایل .bzl جداگانه، فایل های BUILD خود را تمیز و شفاف نگه می دارید، فایل .bzl را می توان از هر بسته ای در فضای کاری بارگیری کرد.

در نهایت، در path/generator.bzl ، تعریف ماکرو را بنویسید تا تعریف ژانر اصلی را کپسوله و پارامتر کنید:

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location //:generator) %s > $@" % arg,
    tools = ["//:generator"],
    visibility = visibility,
  )

شما همچنین می توانید از ماکروها برای زنجیره قوانین با هم استفاده کنید. این مثال ژانرهای زنجیره ای را نشان می دهد، جایی که یک ژانر از خروجی های یک ژانر قبلی به عنوان ورودی استفاده می کند:

def chained_genrules(name, visibility=None):
  native.genrule(
    name = name + "-one",
    outs = [name + ".one"],
    cmd = "$(location :tool-one) $@",
    tools = [":tool-one"],
    visibility = ["//visibility:private"],
  )

  native.genrule(
    name = name + "-two",
    srcs = [name + ".one"],
    outs = [name + ".two"],
    cmd = "$(location :tool-two) $< $@",
    tools = [":tool-two"],
    visibility = visibility,
  )

مثال فقط یک مقدار قابل مشاهده را به ژانر دوم اختصاص می دهد. این به نویسندگان کلان اجازه می‌دهد تا خروجی‌های قوانین میانی را از وابستگی به سایر اهداف در فضای کاری پنهان کنند.

در حال گسترش ماکروها

وقتی می خواهید بررسی کنید که یک ماکرو چه کاری انجام می دهد، از دستور query با --output=build استفاده کنید تا فرم توسعه یافته را ببینید:

$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location //:generator) some_arg > $@",
)

نمونه سازی قوانین بومی

قوانین بومی (قوانینی که به دستور load() نیاز ندارند) را می توان از ماژول بومی نمونه سازی کرد:

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

اگر باید نام بسته را بدانید (به عنوان مثال، کدام فایل BUILD ماکرو را فراخوانی می کند)، از تابع () native.package_name استفاده کنید. توجه داشته باشید که native فقط در فایل‌های .bzl . قابل استفاده است و نه در فایل‌های WORKSPACE یا BUILD .

وضوح برچسب در ماکروها

از آنجایی که ماکروها در مرحله بارگذاری ارزیابی می‌شوند، رشته‌های برچسبی مانند "//foo:bar" که در یک ماکرو رخ می‌دهند، نسبت به فایل BUILD که در آن ماکرو استفاده می‌شود، تفسیر می‌شوند تا با فایل .bzl . که در آن قرار دارد. تعریف شده است. این رفتار عموماً برای ماکروهایی که قرار است در مخازن دیگر استفاده شوند، نامطلوب است، مثلاً به این دلیل که بخشی از مجموعه قوانین Starlark منتشر شده هستند.

برای به دست آوردن رفتاری مشابه با قوانین Starlark، رشته های برچسب را با سازنده Label بپیچید:

# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
  native.cc_library(
    name = name,
    deps = deps + select({
      # Due to the use of Label, this label is resolved within @my_ruleset,
      # regardless of its site of use.
      Label("//config:needs_foo"): [
        # Due to the use of Label, this label will resolve to the correct target
        # even if the canonical name of @dep_of_my_ruleset should be different
        # in the main workspace, such as due to repo mappings.
        Label("@dep_of_my_ruleset//tools:foo"),
      ],
      "//conditions:default": [],
    }),
    **kwargs,
  )

اشکال زدایی

  • bazel query --output=build //my/path:all به شما نشان می دهد که فایل BUILD پس از ارزیابی چگونه به نظر می رسد. همه ماکروها، گلوب ها، حلقه ها گسترش می یابند. محدودیت شناخته شده: عبارات select در حال حاضر در خروجی نشان داده نمی شوند.

  • می‌توانید خروجی را بر اساس generator_function (که تابع قوانین را ایجاد کرد) یا generator_name (ویژگی نام ماکرو) فیلتر کنید: bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'

  • برای اینکه بفهمید قانون foo دقیقاً کجا در یک فایل BUILD تولید می شود، می توانید ترفند زیر را امتحان کنید. این خط را نزدیک بالای فایل BUILD وارد کنید: cc_library(name = "foo") . بازل را اجرا کنید. هنگامی که قانون foo ایجاد می شود (به دلیل تضاد نام)، یک استثنا دریافت خواهید کرد که ردیابی کامل پشته را به شما نشان می دهد.

  • همچنین می توانید از چاپ برای اشکال زدایی استفاده کنید. این پیام را به عنوان یک خط ورود به سیستم DEBUG در مرحله بارگذاری نمایش می دهد. به جز در موارد نادر، قبل از ارسال کد به انبار، تماس‌های print را حذف کنید یا آنها را تحت یک پارامتر debugging که پیش‌فرض False است، مشروط کنید.

خطاها

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

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

کنوانسیون ها

  • همه توابع عمومی (توابعی که با خط زیر شروع نمی شوند) که قوانین را نمونه می کنند باید آرگومان name داشته باشند. این آرگومان نباید اختیاری باشد (مقدار پیش فرض ندهید).

  • توابع عمومی باید از یک docstring پیرو قراردادهای پایتون استفاده کنند.

  • در فایل های BUILD ، آرگومان name ماکروها باید آرگومان کلمه کلیدی باشد (نه آرگومان موقعیتی).

  • ویژگی name قوانین تولید شده توسط یک ماکرو باید آرگومان نام را به عنوان پیشوند شامل شود. به عنوان مثال، macro(name = "foo") می تواند یک cc_library foo و یک genrule foo_gen کند.

  • در بیشتر موارد، پارامترهای اختیاری باید مقدار پیش‌فرض None داشته باشند. None را نمی‌توان مستقیماً به قوانین بومی منتقل کرد، که با آن به‌گونه‌ای برخورد می‌کنند که انگار در هیچ استدلالی تصویب نکرده‌اید. بنابراین، برای این منظور نیازی به جایگزینی آن با 0 ، False یا [] نیست. در عوض، ماکرو باید قوانینی را که ایجاد می‌کند، رعایت کند، زیرا پیش‌فرض‌های آن‌ها ممکن است پیچیده باشند یا در طول زمان تغییر کنند. علاوه بر این، پارامتری که صراحتاً روی مقدار پیش‌فرض آن تنظیم شده است با پارامتری که هرگز تنظیم نشده است (یا روی None تنظیم نشده است) زمانی که از طریق زبان پرس و جو یا قسمت‌های داخلی سیستم ساخت مورد دسترسی قرار می‌گیرد متفاوت به نظر می‌رسد.

  • ماکروها باید آرگومان visibility اختیاری داشته باشند.