নিয়ম

একটি নিয়ম একটি ক্রিয়াকলাপের একটি সিরিজকে সংজ্ঞায়িত করে যা Bazel একটি সেট আউটপুট তৈরি করতে ইনপুটগুলিতে সম্পাদন করে, যা নিয়মের বাস্তবায়ন ফাংশন দ্বারা প্রত্যাবর্তিত প্রদানকারীদের মধ্যে উল্লেখ করা হয়। উদাহরণস্বরূপ, একটি C++ বাইনারি নিয়ম হতে পারে:

  1. .cpp সোর্স ফাইল (ইনপুট) এর একটি সেট নিন।
  2. সোর্স ফাইলগুলিতে g++ চালান (ক্রিয়া)।
  3. রানটাইমে উপলব্ধ করার জন্য এক্সিকিউটেবল আউটপুট এবং অন্যান্য ফাইল সহ DefaultInfo প্রদানকারীকে ফেরত দিন।
  4. লক্ষ্য এবং তার নির্ভরতা থেকে সংগৃহীত C++-নির্দিষ্ট তথ্য সহ CcInfo প্রদানকারীকে ফেরত দিন।

Bazel এর দৃষ্টিকোণ থেকে, g++ এবং স্ট্যান্ডার্ড C++ লাইব্রেরিগুলিও এই নিয়মের ইনপুট। একটি নিয়ম লেখক হিসাবে, আপনাকে অবশ্যই একটি নিয়মে ব্যবহারকারী-প্রদত্ত ইনপুটগুলিই বিবেচনা করতে হবে না, তবে ক্রিয়াগুলি চালানোর জন্য প্রয়োজনীয় সমস্ত সরঞ্জাম এবং লাইব্রেরিগুলিও বিবেচনা করতে হবে৷

কোনো নিয়ম তৈরি বা পরিবর্তন করার আগে, নিশ্চিত করুন যে আপনি Bazel এর বিল্ড ফেজগুলির সাথে পরিচিত৷ একটি বিল্ডের তিনটি পর্যায় (লোডিং, বিশ্লেষণ এবং সম্পাদন) বোঝা গুরুত্বপূর্ণ। নিয়ম এবং ম্যাক্রোর মধ্যে পার্থক্য বোঝার জন্য ম্যাক্রো সম্পর্কে জানাও দরকারী। শুরু করতে, প্রথমে নিয়ম টিউটোরিয়াল পর্যালোচনা করুন। তারপর, একটি রেফারেন্স হিসাবে এই পৃষ্ঠা ব্যবহার করুন.

কয়েকটি নিয়ম Bazel নিজেই তৈরি করা হয়. এই নেটিভ নিয়মগুলি , যেমন cc_library এবং java_binary , নির্দিষ্ট ভাষার জন্য কিছু মূল সমর্থন প্রদান করে। আপনার নিজস্ব নিয়মগুলি সংজ্ঞায়িত করে, আপনি ভাষা এবং সরঞ্জামগুলির জন্য অনুরূপ সমর্থন যোগ করতে পারেন যা Bazel স্থানীয়ভাবে সমর্থন করে না।

ব্যাজেল স্টারলার্ক ভাষা ব্যবহার করে নিয়ম লেখার জন্য একটি এক্সটেনসিবিলিটি মডেল প্রদান করে। এই নিয়মগুলি .bzl ফাইলগুলিতে লেখা আছে, যা সরাসরি BUILD ফাইল থেকে লোড করা যেতে পারে।

আপনার নিজের নিয়ম সংজ্ঞায়িত করার সময়, আপনি সিদ্ধান্ত নিতে পারেন যে এটি কোন বৈশিষ্ট্যগুলি সমর্থন করে এবং এটি কীভাবে এর আউটপুট তৈরি করে।

নিয়মের implementation ফাংশন বিশ্লেষণ পর্যায়ে তার সঠিক আচরণ সংজ্ঞায়িত করে। এই ফাংশনটি কোন বাহ্যিক কমান্ড চালায় না। বরং, এটি এমন ক্রিয়াগুলি নিবন্ধন করে যা পরে প্রয়োগের পর্যায়ে ব্যবহার করা হবে নিয়মের আউটপুট তৈরি করতে, যদি তাদের প্রয়োজন হয়।

নিয়ম সৃষ্টি

একটি .bzl ফাইলে, একটি নতুন নিয়ম সংজ্ঞায়িত করতে নিয়ম ফাংশন ব্যবহার করুন এবং ফলাফলটিকে একটি গ্লোবাল ভেরিয়েবলে সংরক্ষণ করুন। rule কলটি বৈশিষ্ট্য এবং একটি বাস্তবায়ন ফাংশন নির্দিষ্ট করে:

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "deps": attr.label_list(),
        ...
    },
)

এটি example_library নামে এক ধরণের নিয়ম সংজ্ঞায়িত করে।

rule যদি একটি এক্সিকিউটেবল আউটপুট তৈরি করে ( executable=True সহ), বা বিশেষভাবে একটি টেস্ট এক্সিকিউটেবল ( test=True সাথে) তৈরি করে তাও রুলকে কল করতে হবে। পরেরটি হলে, নিয়মটি একটি পরীক্ষার নিয়ম , এবং নিয়মের নাম অবশ্যই _test এ শেষ হবে।

টার্গেট ইনস্ট্যান্টেশন

নিয়ম লোড এবং BUILD ফাইলে কল করা যেতে পারে:

load('//some/pkg:rules.bzl', 'example_library')

example_library(
    name = "example_target",
    deps = [":another_target"],
    ...
)

একটি বিল্ড নিয়মে প্রতিটি কল কোন মূল্য প্রদান করে না, তবে একটি লক্ষ্য নির্ধারণের পার্শ্ব প্রতিক্রিয়া রয়েছে৷ একে বলা হয় ইনস্ট্যান্টিয়েটিং দ্য রুল। এটি নতুন লক্ষ্যের জন্য একটি নাম এবং লক্ষ্যের বৈশিষ্ট্যগুলির জন্য মান নির্দিষ্ট করে।

স্টারলার্ক ফাংশন থেকেও নিয়মগুলি কল করা যেতে পারে এবং .bzl ফাইলগুলিতে লোড করা যেতে পারে। স্টারলার্ক ফাংশন যা নিয়মগুলিকে কল করে তাকে স্টারলার্ক ম্যাক্রো বলা হয়। স্টারলার্ক ম্যাক্রোগুলিকে শেষ পর্যন্ত BUILD ফাইল থেকে কল করতে হবে, এবং শুধুমাত্র লোডিং পর্বের সময় কল করা যেতে পারে, যখন BUILD ফাইলগুলিকে লক্ষ্যমাত্রা নির্ধারণের জন্য মূল্যায়ন করা হয়।

গুণাবলী

একটি বৈশিষ্ট্য একটি নিয়ম যুক্তি. বৈশিষ্ট্যগুলি একটি লক্ষ্যের বাস্তবায়নের জন্য নির্দিষ্ট মান প্রদান করতে পারে, অথবা তারা নির্ভরতার একটি গ্রাফ তৈরি করে অন্যান্য লক্ষ্যগুলি উল্লেখ করতে পারে।

নিয়ম-নির্দিষ্ট বৈশিষ্ট্যগুলি, যেমন srcs বা deps , rule attrs প্যারামিটারে অ্যাট্রিবিউটের নাম থেকে স্কিমা ( attr মডিউল ব্যবহার করে তৈরি) একটি মানচিত্র পাস করে সংজ্ঞায়িত করা হয়। সাধারণ গুণাবলী , যেমন name এবং visibility , সমস্ত নিয়মের সাথে নিহিতভাবে যোগ করা হয়। অতিরিক্ত বৈশিষ্ট্যগুলি বিশেষভাবে এক্সিকিউটেবল এবং পরীক্ষার নিয়মগুলিতে অন্তর্নিহিতভাবে যুক্ত করা হয়। কোনো নিয়মে পরোক্ষভাবে যোগ করা গুণাবলী attrs এ পাস করা অভিধানে অন্তর্ভুক্ত করা যাবে না।

নির্ভরতা বৈশিষ্ট্য

যে নিয়মগুলি সোর্স কোড প্রক্রিয়া করে তা সাধারণত বিভিন্ন ধরণের নির্ভরতা পরিচালনা করতে নিম্নলিখিত বৈশিষ্ট্যগুলিকে সংজ্ঞায়িত করে:

  • srcs একটি লক্ষ্যের ক্রিয়া দ্বারা প্রক্রিয়াকৃত উত্স ফাইলগুলিকে নির্দিষ্ট করে। প্রায়শই, অ্যাট্রিবিউট স্কিমা নির্দিষ্ট করে যে কোন ফাইল এক্সটেনশনগুলি সোর্স ফাইলের নিয়ম প্রক্রিয়ার জন্য প্রত্যাশিত। হেডার ফাইল সহ ভাষার জন্য নিয়মগুলি সাধারণত একটি লক্ষ্য এবং এর ভোক্তাদের দ্বারা প্রক্রিয়াকৃত হেডারগুলির জন্য একটি পৃথক hdrs বৈশিষ্ট্য নির্দিষ্ট করে।
  • deps একটি লক্ষ্যের জন্য কোড নির্ভরতা নির্দিষ্ট করে। অ্যাট্রিবিউট স্কিমা নির্দিষ্ট করা উচিত যে কোন প্রদানকারীদের এই নির্ভরতাগুলি প্রদান করতে হবে। (উদাহরণস্বরূপ, cc_library CcInfo ।)
  • data নির্দিষ্ট করে যে ফাইলগুলিকে রানটাইমে উপলব্ধ করা হবে যে কোনও এক্সিকিউটেবল যা একটি লক্ষ্যের উপর নির্ভর করে। এটি নির্বিচারে ফাইলগুলিকে নির্দিষ্ট করার অনুমতি দেওয়া উচিত।
example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = [".example"]),
        "hdrs": attr.label_list(allow_files = [".header"]),
        "deps": attr.label_list(providers = [ExampleInfo]),
        "data": attr.label_list(allow_files = True),
        ...
    },
)

এগুলি নির্ভরতা বৈশিষ্ট্যের উদাহরণ। যে কোনও বৈশিষ্ট্য যা একটি ইনপুট লেবেল নির্দিষ্ট করে (যা attr.label_list , attr.label , বা attr.label_keyed_string_dict দিয়ে সংজ্ঞায়িত করা হয়েছে) একটি নির্দিষ্ট ধরণের নির্ভরতা নির্দিষ্ট করে একটি লক্ষ্য এবং লক্ষ্যগুলির মধ্যে যার লেবেলগুলি (বা সংশ্লিষ্ট Label অবজেক্টগুলি) সেই অ্যাট্রিবিউটে তালিকাভুক্ত করা হয়েছে যখন লক্ষ্য নির্ধারণ করা হয়। এই লেবেলগুলির জন্য সংগ্রহস্থল, এবং সম্ভবত পথ, সংজ্ঞায়িত লক্ষ্যের সাপেক্ষে সমাধান করা হয়েছে।

example_library(
    name = "my_target",
    deps = [":other_target"],
)

example_library(
    name = "other_target",
    ...
)

এই উদাহরণে, other_target হল my_target এর একটি নির্ভরতা, এবং তাই other_target প্রথমে বিশ্লেষণ করা হয়। লক্ষ্যমাত্রার নির্ভরতা গ্রাফে একটি চক্র থাকলে এটি একটি ত্রুটি।

ব্যক্তিগত বৈশিষ্ট্য এবং অন্তর্নিহিত নির্ভরতা

একটি ডিফল্ট মান সহ একটি নির্ভরতা বৈশিষ্ট্য একটি অন্তর্নিহিত নির্ভরতা তৈরি করে। এটি অন্তর্নিহিত কারণ এটি লক্ষ্য গ্রাফের একটি অংশ যা ব্যবহারকারী একটি BUILD ফাইলে নির্দিষ্ট করে না। অন্তর্নিহিত নির্ভরতাগুলি একটি নিয়ম এবং একটি সরঞ্জামের (একটি বিল্ড-টাইম নির্ভরতা, যেমন একটি কম্পাইলার) এর মধ্যে সম্পর্ককে হার্ড-কোডিংয়ের জন্য দরকারী, যেহেতু বেশিরভাগ সময় একজন ব্যবহারকারী নিয়মটি কোন সরঞ্জামটি ব্যবহার করে তা নির্দিষ্ট করতে আগ্রহী হন না। নিয়মের বাস্তবায়ন ফাংশনের অভ্যন্তরে, এটি অন্যান্য নির্ভরতার মতোই বিবেচিত হয়।

আপনি যদি ব্যবহারকারীকে সেই মানটিকে ওভাররাইড করার অনুমতি না দিয়ে একটি অন্তর্নিহিত নির্ভরতা প্রদান করতে চান, তাহলে আপনি আন্ডারস্কোর ( _ ) দিয়ে শুরু হওয়া একটি নাম দিয়ে বৈশিষ্ট্যটিকে ব্যক্তিগত করতে পারেন। ব্যক্তিগত গুণাবলীর ডিফল্ট মান থাকতে হবে। এটি সাধারণত অন্তর্নিহিত নির্ভরতার জন্য ব্যক্তিগত বৈশিষ্ট্যগুলি ব্যবহার করাই বোধগম্য হয়।

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        ...
        "_compiler": attr.label(
            default = Label("//tools:example_compiler"),
            allow_single_file = True,
            executable = True,
            cfg = "exec",
        ),
    },
)

এই উদাহরণে, example_library টাইপের প্রতিটি টার্গেটের কম্পাইলার //tools:example_compiler এর উপর একটি অন্তর্নিহিত নির্ভরতা রয়েছে। এটি example_library এর ইমপ্লিমেন্টেশন ফাংশনকে এমন অ্যাকশন তৈরি করতে দেয় যা কম্পাইলারকে আমন্ত্রণ জানায়, যদিও ব্যবহারকারী তার লেবেলকে ইনপুট হিসেবে পাস করেনি। যেহেতু _compiler একটি ব্যক্তিগত বৈশিষ্ট্য, এটি অনুসরণ করে যে ctx.attr._compiler সর্বদা এই নিয়ম প্রকারের সমস্ত লক্ষ্যগুলিতে //tools:example_compiler নির্দেশ করবে। বিকল্পভাবে, আপনি আন্ডারস্কোর ছাড়াই অ্যাট্রিবিউট compiler নাম দিতে পারেন এবং ডিফল্ট মান রাখতে পারেন। এটি ব্যবহারকারীদের প্রয়োজনে একটি ভিন্ন কম্পাইলার প্রতিস্থাপন করতে দেয়, তবে এটির জন্য কম্পাইলারের লেবেল সম্পর্কে কোন সচেতনতার প্রয়োজন নেই।

অন্তর্নিহিত নির্ভরতাগুলি সাধারণত সেই সরঞ্জামগুলির জন্য ব্যবহৃত হয় যা নিয়ম বাস্তবায়নের মতো একই সংগ্রহস্থলে থাকে। যদি টুলটি এক্সিকিউশন প্ল্যাটফর্ম থেকে আসে বা পরিবর্তে একটি ভিন্ন সংগ্রহস্থল থেকে, নিয়মটি একটি টুলচেন থেকে সেই টুলটি প্রাপ্ত করা উচিত।

আউটপুট বৈশিষ্ট্য

আউটপুট বৈশিষ্ট্য , যেমন attr.output এবং attr.output_list , একটি আউটপুট ফাইল ঘোষণা করে যা লক্ষ্য তৈরি করে। এই দুটি উপায়ে নির্ভরতা বৈশিষ্ট্য থেকে পৃথক:

  • তারা অন্যত্র সংজ্ঞায়িত লক্ষ্যগুলি উল্লেখ করার পরিবর্তে আউটপুট ফাইল লক্ষ্যগুলিকে সংজ্ঞায়িত করে।
  • আউটপুট ফাইল লক্ষ্যমাত্রা অন্য উপায়ের পরিবর্তে, তাত্ক্ষণিক নিয়ম লক্ষ্যের উপর নির্ভর করে।

সাধারণত, আউটপুট বৈশিষ্ট্যগুলি শুধুমাত্র তখনই ব্যবহৃত হয় যখন একটি নিয়মকে ব্যবহারকারী-সংজ্ঞায়িত নামগুলির সাথে আউটপুট তৈরি করতে হয় যা লক্ষ্য নামের উপর ভিত্তি করে করা যায় না। যদি একটি নিয়মের একটি আউটপুট বৈশিষ্ট্য থাকে তবে এটি সাধারণত out বা outs নামে পরিচিত হয়।

আউটপুট অ্যাট্রিবিউট হল পূর্বঘোষিত আউটপুট তৈরি করার পছন্দের উপায়, যা বিশেষভাবে কমান্ড লাইনের উপর নির্ভর করে বা অনুরোধ করা যেতে পারে।

বাস্তবায়ন ফাংশন

প্রতিটি নিয়ম একটি implementation ফাংশন প্রয়োজন. এই ফাংশনগুলি বিশ্লেষণ পর্বে কঠোরভাবে সম্পাদিত হয় এবং লোডিং পর্যায়ে উত্পন্ন লক্ষ্যগুলির গ্রাফকে কার্যকরী পর্যায়ের সময় সম্পাদিত কর্মের গ্রাফে রূপান্তরিত করে। যেমন, বাস্তবায়ন ফাংশন আসলে ফাইল পড়তে বা লিখতে পারে না।

নিয়ম বাস্তবায়ন ফাংশন সাধারণত ব্যক্তিগত (একটি অগ্রণী আন্ডারস্কোর সহ নামকরণ করা হয়)। প্রচলিতভাবে, তারা তাদের নিয়ম হিসাবে একই নামকরণ করা হয়, কিন্তু _impl সাথে প্রত্যয়িত হয়।

ইমপ্লিমেন্টেশন ফাংশনগুলি ঠিক একটি প্যারামিটার নেয়: একটি নিয়ম প্রসঙ্গ , প্রচলিতভাবে ctx নামে। তারা প্রদানকারীদের একটি তালিকা ফেরত দেয়।

টার্গেট

নির্ভরতাগুলি বিশ্লেষণের সময় Target বস্তু হিসাবে উপস্থাপন করা হয়। এই বস্তুগুলিতে লক্ষ্যের বাস্তবায়ন ফাংশন কার্যকর করার সময় উত্পন্ন প্রদানকারী থাকে৷

ctx.attr এ প্রতিটি নির্ভরতা বৈশিষ্ট্যের নামের সাথে সঙ্গতিপূর্ণ ক্ষেত্র রয়েছে, যার মধ্যে Target বস্তু রয়েছে যা সেই বৈশিষ্ট্যের মাধ্যমে প্রতিটি সরাসরি নির্ভরতাকে প্রতিনিধিত্ব করে। label_list বৈশিষ্ট্যগুলির জন্য, এটি Targets একটি তালিকা। label বৈশিষ্ট্যগুলির জন্য, এটি একটি একক Target বা None

একটি লক্ষ্যের বাস্তবায়ন ফাংশন দ্বারা প্রদানকারী বস্তুর একটি তালিকা ফেরত দেওয়া হয়:

return [ExampleInfo(headers = depset(...))]

সেগুলি একটি কী হিসাবে প্রদানকারীর ধরন সহ সূচক স্বরলিপি ( [] ) ব্যবহার করে অ্যাক্সেস করা যেতে পারে। এগুলি স্টারলার্ক-এ সংজ্ঞায়িত কাস্টম প্রদানকারী বা স্টারলার্ক গ্লোবাল ভেরিয়েবল হিসাবে উপলব্ধ স্থানীয় নিয়মগুলির জন্য প্রদানকারী হতে পারে।

উদাহরণস্বরূপ, যদি একটি নিয়ম একটি hdrs অ্যাট্রিবিউটের মাধ্যমে হেডার ফাইল গ্রহণ করে এবং লক্ষ্য এবং এর ভোক্তাদের সংকলন ক্রিয়াগুলিতে সেগুলি সরবরাহ করে তবে এটি সেগুলিকে এভাবে সংগ্রহ করতে পারে:

def _example_library_impl(ctx):
    ...
    transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]

উত্তরাধিকার শৈলীর জন্য যেখানে প্রদানকারী বস্তুর তালিকার পরিবর্তে লক্ষ্যের বাস্তবায়ন ফাংশন থেকে একটি struct ফেরত দেওয়া হয়:

return struct(example_info = struct(headers = depset(...)))

প্রদানকারীদের Target বস্তুর সংশ্লিষ্ট ক্ষেত্র থেকে পুনরুদ্ধার করা যেতে পারে:

transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]

এই শৈলী দৃঢ়ভাবে নিরুৎসাহিত করা হয় এবং নিয়ম এটি থেকে দূরে স্থানান্তর করা উচিত.

নথি পত্র

ফাইল File বস্তু দ্বারা প্রতিনিধিত্ব করা হয়. যেহেতু Bazel বিশ্লেষণ পর্যায়ে ফাইল I/O সঞ্চালন করে না, এই বস্তুগুলি সরাসরি ফাইলের বিষয়বস্তু পড়তে বা লিখতে ব্যবহার করা যাবে না। বরং, এগুলি অ্যাকশন-এমিটিং ফাংশনে প্রেরণ করা হয় ( ctx.actions দেখুন) অ্যাকশন গ্রাফের টুকরো তৈরি করতে।

একটি File হয় একটি উৎস ফাইল বা একটি উত্পন্ন ফাইল হতে পারে. প্রতিটি জেনারেট করা ফাইল অবশ্যই একটি অ্যাকশনের আউটপুট হতে হবে। উৎস ফাইল কোনো কর্মের আউটপুট হতে পারে না.

প্রতিটি নির্ভরতা বৈশিষ্ট্যের জন্য, ctx.files এর সংশ্লিষ্ট ক্ষেত্রে সেই বৈশিষ্ট্যের মাধ্যমে সমস্ত নির্ভরতার ডিফল্ট আউটপুটগুলির একটি তালিকা রয়েছে:

def _example_library_impl(ctx):
    ...
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    ...

ctx.file এ নির্ভরশীলতার বৈশিষ্ট্যগুলির জন্য একটি একক File বা None রয়েছে যার allow_single_file=True সেট করে। ctx.executable ctx.file মতোই আচরণ করে, কিন্তু শুধুমাত্র নির্ভরতা বৈশিষ্ট্যের জন্য ক্ষেত্র রয়েছে যার স্পেস executable=True সেট করে।

আউটপুট ঘোষণা

বিশ্লেষণ পর্বের সময়, একটি নিয়মের বাস্তবায়ন ফাংশন আউটপুট তৈরি করতে পারে। যেহেতু লোডিং পর্বের সময় সমস্ত লেবেল জানতে হবে, এই অতিরিক্ত আউটপুটগুলির কোনও লেবেল নেই৷ ctx.actions.declare_file এবং ctx.actions.declare_directory ব্যবহার করে আউটপুটের জন্য File অবজেক্ট তৈরি করা যেতে পারে। প্রায়শই, আউটপুটগুলির নাম টার্গেটের নামের উপর ভিত্তি করে, ctx.label.name :

def _example_library_impl(ctx):
  ...
  output_file = ctx.actions.declare_file(ctx.label.name + ".output")
  ...

পূর্বঘোষিত আউটপুটগুলির জন্য, যেমন আউটপুট বৈশিষ্ট্যগুলির জন্য তৈরি করা হয়েছে, পরিবর্তে File অবজেক্টগুলি ctx.outputs এর সংশ্লিষ্ট ক্ষেত্রগুলি থেকে পুনরুদ্ধার করা যেতে পারে।

কর্ম

একটি ক্রিয়া বর্ণনা করে কিভাবে ইনপুটগুলির একটি সেট থেকে আউটপুটগুলির একটি সেট তৈরি করা যায়, উদাহরণস্বরূপ "hello.c তে gcc চালান এবং hello.o পান"। যখন একটি ক্রিয়া তৈরি করা হয়, তখন Bazel কমান্ডটি অবিলম্বে চালায় না। এটি নির্ভরতার একটি গ্রাফে এটি নিবন্ধন করে, কারণ একটি ক্রিয়া অন্য একটি কর্মের আউটপুটের উপর নির্ভর করতে পারে। উদাহরণস্বরূপ, সি-তে, কম্পাইলারের পরে লিঙ্কারকে কল করতে হবে।

সাধারণ-উদ্দেশ্য ফাংশন যা ক্রিয়া তৈরি করে সেগুলি ctx.actions এ সংজ্ঞায়িত করা হয়েছে:

  • ctx.actions.run , একটি এক্সিকিউটেবল চালানোর জন্য।
  • ctx.actions.run_shell , একটি শেল কমান্ড চালানোর জন্য।
  • ctx.actions.write , একটি ফাইলে একটি স্ট্রিং লিখতে।
  • ctx.actions.expand_template , একটি টেমপ্লেট থেকে একটি ফাইল তৈরি করতে।

ctx.actions.args দক্ষতার সাথে কর্মের জন্য আর্গুমেন্ট সংগ্রহ করতে ব্যবহার করা যেতে পারে। এটি কার্যকর করার সময় পর্যন্ত সমতল ডিপসেট এড়ায়:

def _example_library_impl(ctx):
    ...

    transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    inputs = depset(srcs, transitive=[headers])
    output_file = ctx.actions.declare_file(ctx.label.name + ".output")

    args = ctx.actions.args()
    args.add_joined("-h", headers, join_with=",")
    args.add_joined("-s", srcs, join_with=",")
    args.add("-o", output_file)

    ctx.actions.run(
        mnemonic = "ExampleCompile",
        executable = ctx.executable._compiler,
        arguments = [args],
        inputs = inputs,
        outputs = [output_file],
    )
    ...

ক্রিয়াগুলি ইনপুট ফাইলগুলির একটি তালিকা বা ডিপসেট নেয় এবং আউটপুট ফাইলগুলির একটি (খালি নয়) তালিকা তৈরি করে। ইনপুট এবং আউটপুট ফাইলের সেট বিশ্লেষণ পর্বের সময় জানা আবশ্যক। এটি নির্ভরতা থেকে প্রদানকারী সহ গুণাবলীর মানের উপর নির্ভর করতে পারে, তবে এটি কার্যকর করার ফলাফলের উপর নির্ভর করতে পারে না। উদাহরণস্বরূপ, যদি আপনার ক্রিয়াটি আনজিপ কমান্ড চালায়, তাহলে আপনাকে অবশ্যই নির্দিষ্ট করতে হবে যে কোন ফাইলগুলি আপনি স্ফীত হবে (আনজিপ চালানোর আগে)। যে ক্রিয়াগুলি অভ্যন্তরীণভাবে একটি পরিবর্তনশীল সংখ্যক ফাইল তৈরি করে সেগুলিকে একটি একক ফাইলে (যেমন একটি জিপ, টার, বা অন্যান্য সংরক্ষণাগার বিন্যাস) মোড়ানো হতে পারে।

অ্যাকশন তাদের ইনপুট সব তালিকাভুক্ত করা আবশ্যক. তালিকাভুক্ত ইনপুট যা ব্যবহার করা হয় না অনুমোদিত, কিন্তু অদক্ষ।

ক্রিয়াগুলিকে অবশ্যই তাদের সমস্ত আউটপুট তৈরি করতে হবে। তারা অন্য ফাইল লিখতে পারে, কিন্তু আউটপুটে নেই এমন কিছু ভোক্তাদের কাছে উপলব্ধ হবে না। সমস্ত ঘোষিত আউটপুট কিছু ক্রিয়া দ্বারা লিখতে হবে।

অ্যাকশনগুলি বিশুদ্ধ ফাংশনের সাথে তুলনীয়: তাদের শুধুমাত্র প্রদত্ত ইনপুটগুলির উপর নির্ভর করা উচিত এবং কম্পিউটারের তথ্য, ব্যবহারকারীর নাম, ঘড়ি, নেটওয়ার্ক বা I/O ডিভাইসগুলি (ইনপুট পড়া এবং লেখার আউটপুট ব্যতীত) অ্যাক্সেস করা এড়ানো উচিত। এটি গুরুত্বপূর্ণ কারণ আউটপুট ক্যাশে করা হবে এবং পুনরায় ব্যবহার করা হবে।

নির্ভরতাগুলি Bazel দ্বারা সমাধান করা হয়, যা সিদ্ধান্ত নেবে কোন ক্রিয়াগুলি কার্যকর করা হবে৷ নির্ভরতা গ্রাফে একটি চক্র থাকলে এটি একটি ত্রুটি। একটি ক্রিয়া তৈরি করা গ্যারান্টি দেয় না যে এটি কার্যকর করা হবে, এটি নির্ভর করে এর আউটপুটগুলি বিল্ডের জন্য প্রয়োজন কিনা।

প্রদানকারী

প্রদানকারীরা এমন তথ্যের টুকরো যা একটি নিয়ম তার উপর নির্ভর করে এমন অন্যান্য নিয়মকে প্রকাশ করে। এই ডেটাতে আউটপুট ফাইল, লাইব্রেরি, একটি টুলের কমান্ড লাইনে পাস করার পরামিতি বা লক্ষ্যের ভোক্তাদের জানা উচিত এমন কিছু অন্তর্ভুক্ত থাকতে পারে।

যেহেতু একটি নিয়মের বাস্তবায়ন ফাংশন শুধুমাত্র তাত্ক্ষণিক লক্ষ্যের তাত্ক্ষণিক নির্ভরতা থেকে প্রদানকারীকে পড়তে পারে, তাই নিয়মগুলির প্রয়োজন একটি লক্ষ্যের নির্ভরতা থেকে যে কোনো তথ্য ফরোয়ার্ড করতে হবে যা একটি লক্ষ্যের ভোক্তাদের দ্বারা জানা প্রয়োজন, সাধারণত এটি একটি depset জমা করে।

একটি লক্ষ্যের প্রদানকারী বাস্তবায়ন ফাংশন দ্বারা প্রত্যাবর্তিত Provider বস্তুর একটি তালিকা দ্বারা নির্দিষ্ট করা হয়।

পুরানো বাস্তবায়ন ফাংশনগুলিও একটি উত্তরাধিকার শৈলীতে লেখা যেতে পারে যেখানে বাস্তবায়ন ফাংশন প্রদানকারী বস্তুর তালিকার পরিবর্তে একটি struct প্রদান করে। এই শৈলী দৃঢ়ভাবে নিরুৎসাহিত করা হয় এবং নিয়ম এটি থেকে দূরে স্থানান্তর করা উচিত.

ডিফল্ট আউটপুট

একটি লক্ষ্যের ডিফল্ট আউটপুট হল সেই আউটপুট যা ডিফল্টরূপে অনুরোধ করা হয় যখন লক্ষ্যটিকে কমান্ড লাইনে নির্মাণের জন্য অনুরোধ করা হয়। উদাহরণস্বরূপ, একটি java_library target //pkg:foo foo.jar এর একটি ডিফল্ট আউটপুট হিসাবে foo.jar রয়েছে, যাতে এটি bazel build //pkg:foo কমান্ড দ্বারা নির্মিত হবে।

ডিফল্ট আউটপুটগুলি DefaultInfo এর files প্যারামিটার দ্বারা নির্দিষ্ট করা হয়:

def _example_library_impl(ctx):
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        ...
    ]

যদি DefaultInfo একটি নিয়ম বাস্তবায়নের মাধ্যমে ফেরত না দেওয়া হয় বা files প্যারামিটার নির্দিষ্ট করা না থাকে, তাহলে DefaultInfo.files সমস্ত পূর্বঘোষিত আউটপুট (সাধারণত, আউটপুট বৈশিষ্ট্য দ্বারা তৈরি) ডিফল্ট করে।

ক্রিয়া সম্পাদনকারী নিয়মগুলিকে ডিফল্ট আউটপুট প্রদান করা উচিত, এমনকি যদি সেই আউটপুটগুলি সরাসরি ব্যবহার করার আশা না করা হয়। অনুরোধকৃত আউটপুটগুলির গ্রাফে নেই এমন ক্রিয়াগুলি ছাঁটাই করা হয়। যদি একটি আউটপুট শুধুমাত্র একটি টার্গেটের ভোক্তাদের দ্বারা ব্যবহার করা হয়, তাহলে সেই ক্রিয়াগুলি সঞ্চালিত হবে না যখন লক্ষ্যটি বিচ্ছিন্নভাবে নির্মিত হয়। এটি ডিবাগিংকে আরও কঠিন করে তোলে কারণ শুধুমাত্র ব্যর্থ লক্ষ্য পুনর্নির্মাণ ব্যর্থতা পুনরুত্পাদন করবে না।

রানফাইলস

রানফাইলগুলি রানটাইমে একটি টার্গেট দ্বারা ব্যবহৃত ফাইলগুলির একটি সেট (বিল্ড টাইমের বিপরীতে)। এক্সিকিউশন পর্বের সময়, Bazel একটি ডিরেক্টরি ট্রি তৈরি করে যাতে রানফাইলের দিকে নির্দেশ করে সিমলিংক থাকে। এটি বাইনারির জন্য পরিবেশকে পর্যায়ভুক্ত করে যাতে এটি রানটাইমের সময় রানফাইলগুলি অ্যাক্সেস করতে পারে।

Runfiles নিয়ম তৈরির সময় ম্যানুয়ালি যোগ করা যেতে পারে। runfiles অবজেক্টগুলি Rule context, ctx.runfilesrunfiles পদ্ধতি দ্বারা তৈরি করা যেতে পারে এবং DefaultInfo তে runfiles প্যারামিটারে পাস করা যেতে পারে। এক্সিকিউটেবল নিয়মের এক্সিকিউটেবল আউটপুট রানফাইলে অস্পষ্টভাবে যোগ করা হয়।

কিছু নিয়ম বৈশিষ্ট্যগুলি নির্দিষ্ট করে, সাধারণত data নামে পরিচিত, যার আউটপুট একটি লক্ষ্যের রানফাইলে যোগ করা হয়। রানফাইলগুলিকে data থেকেও একত্রিত করা উচিত, সেইসাথে যেকোন বৈশিষ্ট্য থেকে যা চূড়ান্ত সম্পাদনের জন্য কোড প্রদান করতে পারে, সাধারণত srcs (যাতে সংশ্লিষ্ট data সহ filegroup লক্ষ্য থাকতে পারে) এবং deps

def _example_library_impl(ctx):
    ...
    runfiles = ctx.runfiles(files = ctx.files.data)
    transitive_runfiles = []
    for runfiles_attr in (
        ctx.attr.srcs,
        ctx.attr.hdrs,
        ctx.attr.deps,
        ctx.attr.data,
    ):
        for target in runfiles_attr:
            transitive_runfiles.append(target[DefaultInfo].default_runfiles)
    runfiles = runfiles.merge_all(transitive_runfiles)
    return [
        DefaultInfo(..., runfiles = runfiles),
        ...
    ]

কাস্টম প্রদানকারী

নিয়ম-নির্দিষ্ট তথ্য জানাতে provider ফাংশন ব্যবহার করে প্রদানকারীদের সংজ্ঞায়িত করা যেতে পারে:

ExampleInfo = provider(
    "Info needed to compile/link Example code.",
    fields={
        "headers": "depset of header Files from transitive dependencies.",
        "files_to_link": "depset of Files from compilation.",
    })

নিয়ম বাস্তবায়ন ফাংশন তারপর প্রদানকারী দৃষ্টান্ত নির্মাণ এবং ফেরত দিতে পারে:

def _example_library_impl(ctx):
  ...
  return [
      ...
      ExampleInfo(
          headers = headers,
          files_to_link = depset(
              [output_file],
              transitive = [
                  dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
              ],
          ),
      )
  ]
প্রদানকারীদের কাস্টম আরম্ভ করা

কাস্টম প্রিপ্রসেসিং এবং বৈধতা যুক্তি সহ একটি প্রদানকারীর ইনস্ট্যান্টেশন রক্ষা করা সম্ভব। এটি নিশ্চিত করতে ব্যবহার করা যেতে পারে যে সমস্ত প্রদানকারীর দৃষ্টান্তগুলি নির্দিষ্ট পরিবর্তনগুলি মেনে চলে, অথবা একটি উদাহরণ পাওয়ার জন্য ব্যবহারকারীদের একটি ক্লিনার API দিতে।

এটি provider ফাংশনে একটি init কলব্যাক পাস করে করা হয়। যদি এই কলব্যাক দেওয়া হয়, তাহলে provider() রিটার্নের ধরন দুটি মানের মধ্যে পরিবর্তিত হয়: প্রদানকারীর প্রতীক যা সাধারণ রিটার্ন মান যখন init ব্যবহার করা হয় না এবং একটি "raw constructor"।

এই ক্ষেত্রে, যখন প্রদানকারী চিহ্নটিকে কল করা হয়, সরাসরি একটি নতুন উদাহরণ ফেরত দেওয়ার পরিবর্তে, এটি init কলব্যাকের সাথে আর্গুমেন্টগুলিকে ফরোয়ার্ড করবে। কলব্যাকের রিটার্ন মান অবশ্যই মানগুলির জন্য একটি dict ম্যাপিং ক্ষেত্রের নাম (স্ট্রিং) হতে হবে; এটি নতুন উদাহরণের ক্ষেত্রগুলি শুরু করতে ব্যবহৃত হয়। নোট করুন যে কলব্যাকের কোনো স্বাক্ষর থাকতে পারে, এবং যদি আর্গুমেন্টগুলি স্বাক্ষরের সাথে মেলে না তাহলে একটি ত্রুটি রিপোর্ট করা হয় যেন কলব্যাকটি সরাসরি আহ্বান করা হয়েছিল।

বিপরীতে, কাঁচা কনস্ট্রাক্টর, init কলব্যাককে বাইপাস করবে।

নিম্নলিখিত উদাহরণটি এর আর্গুমেন্টগুলিকে প্রিপ্রসেস এবং যাচাই করতে init ব্যবহার করে:

# //pkg:exampleinfo.bzl

_core_headers = [...]  # private constant representing standard library files

# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
    if not files_to_link and not allow_empty_files_to_link:
        fail("files_to_link may not be empty")
    all_headers = depset(_core_headers, transitive = headers)
    return {'files_to_link': files_to_link, 'headers': all_headers}

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init)

export ExampleInfo

একটি নিয়ম বাস্তবায়ন তারপর প্রদানকারীকে নিম্নরূপ সূচনা করতে পারে:

    ExampleInfo(
        files_to_link=my_files_to_link,  # may not be empty
        headers = my_headers,  # will automatically include the core headers
    )

কাঁচা কনস্ট্রাক্টরটি বিকল্প পাবলিক ফ্যাক্টরি ফাংশনগুলিকে সংজ্ঞায়িত করতে ব্যবহার করা যেতে পারে যা init লজিকের মধ্য দিয়ে যায় না। উদাহরণস্বরূপ, exampleinfo.bzl এ আমরা সংজ্ঞায়িত করতে পারি:

def make_barebones_exampleinfo(headers):
    """Returns an ExampleInfo with no files_to_link and only the specified headers."""
    return _new_exampleinfo(files_to_link = depset(), headers = all_headers)

সাধারণত, কাঁচা কনস্ট্রাক্টর একটি ভেরিয়েবলের সাথে আবদ্ধ থাকে যার নাম একটি আন্ডারস্কোর ( _new_exampleinfo উপরে) দিয়ে শুরু হয়, যাতে ব্যবহারকারী কোড এটি লোড করতে না পারে এবং নির্বিচারে প্রদানকারী উদাহরণ তৈরি করতে পারে না।

init এর আরেকটি ব্যবহার হল ব্যবহারকারীকে সরবরাহকারী চিহ্নটিকে সম্পূর্ণভাবে কল করা থেকে বিরত রাখা এবং পরিবর্তে একটি ফ্যাক্টরি ফাংশন ব্যবহার করতে বাধ্য করা:

def _exampleinfo_init_banned(*args, **kwargs):
    fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init_banned)

def make_exampleinfo(...):
    ...
    return _new_exampleinfo(...)

এক্সিকিউটেবল নিয়ম এবং পরীক্ষার নিয়ম

এক্সিকিউটেবল নিয়মগুলি লক্ষ্যগুলিকে সংজ্ঞায়িত করে যা একটি bazel run কমান্ড দ্বারা আহ্বান করা যেতে পারে। পরীক্ষার নিয়ম হল একটি বিশেষ ধরনের এক্সিকিউটেবল নিয়ম যার লক্ষ্যগুলি একটি bazel test কমান্ড দ্বারাও আহ্বান করা যেতে পারে। এক্সিকিউটেবল এবং টেস্ট নিয়মগুলি কল টু rule -এ সংশ্লিষ্ট executable বা test আর্গুমেন্ট True এ সেট করে তৈরি করা হয়:

example_binary = rule(
   implementation = _example_binary_impl,
   executable = True,
   ...
)

example_test = rule(
   implementation = _example_binary_impl,
   test = True,
   ...
)

পরীক্ষার নিয়মে এমন নাম থাকতে হবে যা _test এ শেষ হয়। (পরীক্ষার লক্ষ্যের নামগুলিও প্রায়শই নিয়ম অনুসারে _test এ শেষ হয়, তবে এটির প্রয়োজন নেই।) অ-পরীক্ষার নিয়মে এই প্রত্যয় থাকা উচিত নয়।

উভয় ধরণের নিয়ম অবশ্যই একটি এক্সিকিউটেবল আউটপুট ফাইল তৈরি করতে হবে (যা পূর্ব ঘোষিত হতে পারে বা নাও হতে পারে) যা run বা test কমান্ড দ্বারা আহ্বান করা হবে। Bazel কে এই এক্সিকিউটেবল হিসাবে কোন নিয়মের আউটপুট ব্যবহার করতে হবে তা বলতে, এটিকে একটি ফেরত DefaultInfo প্রদানকারীর executable আর্গুমেন্ট হিসাবে পাস করুন। সেই executable নিয়মের ডিফল্ট আউটপুটে যোগ করা হয় (তাই আপনাকে executable এবং files উভয়েই এটি পাস করতে হবে না)। এটি রানফাইলগুলিতেও অন্তর্নিহিতভাবে যোগ করা হয়েছে:

def _example_binary_impl(ctx):
    executable = ctx.actions.declare_file(ctx.label.name)
    ...
    return [
        DefaultInfo(executable = executable, ...),
        ...
    ]

যে ক্রিয়াটি এই ফাইলটি তৈরি করে তাকে অবশ্যই ফাইলটিতে এক্সিকিউটেবল বিট সেট করতে হবে। একটি ctx.actions.run বা ctx.actions.run_shell অ্যাকশনের জন্য এটি অন্তর্নিহিত টুল দ্বারা করা উচিত যা অ্যাকশন দ্বারা আহ্বান করা হয়েছে। একটি ctx.actions.write অ্যাকশনের জন্য, is_executable=True পাস করুন।

লিগ্যাসি আচরণ হিসাবে, এক্সিকিউটেবল নিয়মগুলির একটি বিশেষ ctx.outputs.executable পূর্ব ঘোষিত আউটপুট রয়েছে। এই ফাইলটি ডিফল্ট এক্সিকিউটেবল হিসাবে কাজ করে যদি আপনি DefaultInfo ব্যবহার করে একটি নির্দিষ্ট না করেন; এটা অন্যথায় ব্যবহার করা উচিত নয়. এই আউটপুট প্রক্রিয়াটি বাতিল করা হয়েছে কারণ এটি বিশ্লেষণের সময় এক্সিকিউটেবল ফাইলের নাম কাস্টমাইজ করা সমর্থন করে না।

একটি এক্সিকিউটেবল নিয়ম এবং একটি পরীক্ষার নিয়মের উদাহরণ দেখুন।

এক্সিকিউটেবল নিয়ম এবং পরীক্ষার নিয়মগুলিতে সমস্ত নিয়মের জন্য যোগ করা ছাড়াও অতিরিক্ত বৈশিষ্ট্যগুলি অন্তর্নিহিতভাবে সংজ্ঞায়িত করা হয়েছে। অন্তর্নিহিত-সংযোজিত বৈশিষ্ট্যগুলির ডিফল্টগুলি পরিবর্তন করা যায় না, যদিও এটি একটি স্টারলার্ক ম্যাক্রোতে একটি ব্যক্তিগত নিয়ম মোড়ানোর মাধ্যমে কাজ করা যেতে পারে যা ডিফল্টটিকে পরিবর্তন করে:

def example_test(size="small", **kwargs):
  _example_test(size=size, **kwargs)

_example_test = rule(
 ...
)

রানফাইলস অবস্থান

যখন একটি এক্সিকিউটেবল টার্গেট bazel run (বা test ) দিয়ে চালানো হয়, রানফাইলস ডিরেক্টরির রুট এক্সিকিউটেবলের সংলগ্ন থাকে। পথগুলি নিম্নরূপ সম্পর্কিত:

# Given executable_file and runfile_file:
runfiles_root = executable_file.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
    runfiles_root, workspace_name, runfile_path)

রানফাইলস ডিরেক্টরির অধীনে একটি File পাথ File.short_path এর সাথে মিলে যায়।

ব্যাজেল দ্বারা সরাসরি সম্পাদিত বাইনারি bazel ডিরেক্টরির runfiles সংলগ্ন। যাইহোক, রানফাইল থেকে কল করা বাইনারিগুলি একই অনুমান করতে পারে না। এটি প্রশমিত করার জন্য, প্রতিটি বাইনারিকে একটি পরিবেশ বা কমান্ড লাইন আর্গুমেন্ট/ফ্ল্যাগ ব্যবহার করে একটি প্যারামিটার হিসাবে তার রানফাইল রুট গ্রহণ করার একটি উপায় প্রদান করা উচিত। এটি বাইনারিগুলিকে সঠিক ক্যানোনিকাল রানফাইল রুটটি বাইনারিগুলিতে প্রেরণ করতে দেয় যা এটি কল করে। যদি এটি সেট করা না থাকে, একটি বাইনারি অনুমান করতে পারে যে এটিই প্রথম বাইনারি বলা হয়েছিল এবং একটি সংলগ্ন রানফাইল ডিরেক্টরি সন্ধান করতে পারে।

উন্নত বিষয়

আউটপুট ফাইল অনুরোধ করা হচ্ছে

একটি একক লক্ষ্যে একাধিক আউটপুট ফাইল থাকতে পারে। যখন একটি bazel build কমান্ড চালানো হয়, কমান্ডে দেওয়া লক্ষ্যগুলির কিছু আউটপুট অনুরোধ করা হয় বলে মনে করা হয়। Bazel শুধুমাত্র এই অনুরোধ করা ফাইলগুলি এবং ফাইলগুলি তৈরি করে যা তারা প্রত্যক্ষ বা পরোক্ষভাবে নির্ভর করে। (অ্যাকশন গ্রাফের পরিপ্রেক্ষিতে, Bazel শুধুমাত্র সেই ক্রিয়াগুলি সম্পাদন করে যা অনুরোধ করা ফাইলগুলির ট্রানজিটিভ নির্ভরতা হিসাবে পৌঁছানো যায়৷)

ডিফল্ট আউটপুট ছাড়াও, যেকোনো পূর্বঘোষিত আউটপুট কমান্ড লাইনে স্পষ্টভাবে অনুরোধ করা যেতে পারে। নিয়ম আউটপুট বৈশিষ্ট্যের মাধ্যমে পূর্বঘোষিত আউটপুট নির্দিষ্ট করতে পারে। সেই ক্ষেত্রে, ব্যবহারকারী যখন নিয়মটি ইনস্ট্যান্টিয়েট করে তখন আউটপুটগুলির জন্য স্পষ্টভাবে লেবেলগুলি বেছে নেয়। আউটপুট অ্যাট্রিবিউটের জন্য File অবজেক্ট পেতে, ctx.outputs এর সংশ্লিষ্ট অ্যাট্রিবিউট ব্যবহার করুন। নিয়মগুলি লক্ষ্যের নামের উপর ভিত্তি করে পূর্বঘোষিত আউটপুটগুলিকে স্পষ্টভাবে সংজ্ঞায়িত করতে পারে, তবে এই বৈশিষ্ট্যটি অবমূল্যায়ন করা হয়েছে।

ডিফল্ট আউটপুট ছাড়াও, আউটপুট গ্রুপ রয়েছে, যা আউটপুট ফাইলগুলির সংগ্রহ যা একসাথে অনুরোধ করা যেতে পারে। এগুলি --output_groups দিয়ে অনুরোধ করা যেতে পারে। উদাহরণস্বরূপ, যদি একটি টার্গেট //pkg:mytarget একটি নিয়ম প্রকারের হয় যার একটি debug_files আউটপুট গ্রুপ থাকে, তাহলে এই ফাইলগুলি bazel build //pkg:mytarget --output_groups=debug_files চালানোর মাধ্যমে তৈরি করা যেতে পারে। যেহেতু পূর্বঘোষিত আউটপুটগুলিতে লেবেল নেই, সেগুলি শুধুমাত্র ডিফল্ট আউটপুট বা একটি আউটপুট গ্রুপে উপস্থিত হওয়ার মাধ্যমে অনুরোধ করা যেতে পারে।

আউটপুট গ্রুপগুলি OutputGroupInfo প্রদানকারীর সাথে নির্দিষ্ট করা যেতে পারে। মনে রাখবেন যে অনেক বিল্ট-ইন প্রদানকারীর বিপরীতে, OutputGroupInfo সেই নামের সাথে আউটপুট গোষ্ঠীগুলিকে সংজ্ঞায়িত করতে নির্বিচারে নাম সহ প্যারামিটার নিতে পারে:

def _example_library_impl(ctx):
    ...
    debug_file = ctx.actions.declare_file(name + ".pdb")
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        OutputGroupInfo(
            debug_files = depset([debug_file]),
            all_files = depset([output_file, debug_file]),
        ),
        ...
    ]

এছাড়াও বেশিরভাগ প্রদানকারীর বিপরীতে, OutputGroupInfo একটি দিক এবং নিয়ম লক্ষ্য উভয়ের দ্বারা ফেরত দেওয়া যেতে পারে যেখানে সেই দিকটি প্রয়োগ করা হয়, যতক্ষণ না তারা একই আউটপুট গোষ্ঠীগুলিকে সংজ্ঞায়িত করে না। এই ক্ষেত্রে, ফলাফল প্রদানকারী একত্রিত করা হয়.

লক্ষ্য করুন যে OutputGroupInfo সাধারণত নির্দিষ্ট ধরণের ফাইলগুলিকে লক্ষ্য থেকে তার ভোক্তাদের ক্রিয়াকলাপে বোঝানোর জন্য ব্যবহার করা উচিত নয়। এর পরিবর্তে নিয়ম-নির্দিষ্ট প্রদানকারীদের সংজ্ঞায়িত করুন।

কনফিগারেশন

কল্পনা করুন যে আপনি একটি ভিন্ন আর্কিটেকচারের জন্য একটি C++ বাইনারি তৈরি করতে চান। বিল্ড জটিল হতে পারে এবং একাধিক ধাপ জড়িত হতে পারে। কিছু মধ্যবর্তী বাইনারি, যেমন কম্পাইলার এবং কোড জেনারেটর, এক্সিকিউশন প্ল্যাটফর্মে চালাতে হবে (যা আপনার হোস্ট বা দূরবর্তী নির্বাহক হতে পারে)। টার্গেট আর্কিটেকচারের জন্য চূড়ান্ত আউটপুটের মতো কিছু বাইনারি তৈরি করতে হবে।

এই কারণে, ব্যাজেলের "কনফিগারেশন" এবং ট্রানজিশনের একটি ধারণা রয়েছে। শীর্ষস্থানীয় লক্ষ্যগুলি (কমান্ড লাইনে অনুরোধ করা হয়েছে) "টার্গেট" কনফিগারেশনে তৈরি করা হয়, যখন কার্যকরী প্ল্যাটফর্মে চালানো উচিত এমন সরঞ্জামগুলি একটি "এক্সিক" কনফিগারেশনে তৈরি করা হয়। নিয়মগুলি কনফিগারেশনের উপর ভিত্তি করে বিভিন্ন অ্যাকশন তৈরি করতে পারে, যেমন কম্পাইলারে পাস করা cpu আর্কিটেকচার পরিবর্তন করা। কিছু ক্ষেত্রে, বিভিন্ন কনফিগারেশনের জন্য একই লাইব্রেরির প্রয়োজন হতে পারে। যদি এটি ঘটে তবে এটি বিশ্লেষণ করা হবে এবং সম্ভাব্য একাধিকবার নির্মিত হবে।

ডিফল্টরূপে, Bazel একটি টার্গেটের নির্ভরতা তৈরি করে একই কনফিগারেশনে টার্গেট নিজেই, অন্য কথায় ট্রানজিশন ছাড়াই। যখন একটি নির্ভরতা একটি টুল যা লক্ষ্য তৈরি করতে সাহায্য করার জন্য প্রয়োজন, সংশ্লিষ্ট বৈশিষ্ট্যটি একটি exec কনফিগারেশনে একটি রূপান্তর নির্দিষ্ট করা উচিত। এটি কার্যকরী প্ল্যাটফর্মের জন্য সরঞ্জাম এবং এর সমস্ত নির্ভরতা তৈরি করে।

প্রতিটি নির্ভরতা বৈশিষ্ট্যের জন্য, আপনি নির্ভরতা একই কনফিগারেশনে তৈরি করা উচিত বা একটি exec কনফিগারেশনে রূপান্তর করা উচিত কিনা তা সিদ্ধান্ত নিতে cfg ব্যবহার করতে পারেন। যদি একটি নির্ভরতা বৈশিষ্ট্যের পতাকা executable=True থাকে, cfg অবশ্যই স্পষ্টভাবে সেট করতে হবে। এটি ভুল কনফিগারেশনের জন্য দুর্ঘটনাক্রমে একটি টুল তৈরি করা থেকে রক্ষা করার জন্য। উদাহরণ দেখুন

সাধারণভাবে, উত্স, নির্ভরশীল লাইব্রেরি, এবং এক্সিকিউটেবল যা রানটাইমে প্রয়োজন হবে একই কনফিগারেশন ব্যবহার করতে পারে।

যে সরঞ্জামগুলি বিল্ডের অংশ হিসাবে কার্যকর করা হয় (যেমন কম্পাইলার বা কোড জেনারেটর) একটি exec কনফিগারেশনের জন্য তৈরি করা উচিত। এই ক্ষেত্রে, অ্যাট্রিবিউটে cfg="exec" উল্লেখ করুন।

অন্যথায়, রানটাইমে ব্যবহৃত এক্সিকিউটেবলগুলি (যেমন একটি পরীক্ষার অংশ) লক্ষ্য কনফিগারেশনের জন্য তৈরি করা উচিত। এই ক্ষেত্রে, অ্যাট্রিবিউটে cfg="target" উল্লেখ করুন।

cfg="target" আসলে কিছুই করে না: নিয়ম ডিজাইনারদের তাদের উদ্দেশ্য সম্পর্কে স্পষ্ট হতে সাহায্য করার জন্য এটি সম্পূর্ণরূপে একটি সুবিধার মান। যখন executable=False , যার মানে cfg ঐচ্ছিক, শুধুমাত্র তখনই এটি সেট করুন যখন এটি সত্যিই পঠনযোগ্যতাকে সাহায্য করে।

এছাড়াও আপনি cfg=my_transition ব্যবহার করতে পারেন ব্যবহারকারী-সংজ্ঞায়িত ট্রানজিশন ব্যবহার করতে, যা নিয়ম লেখকদের কনফিগারেশন পরিবর্তন করার ক্ষেত্রে প্রচুর নমনীয়তা দেয়, বিল্ড গ্রাফটিকে আরও বড় এবং কম বোধগম্য করার অসুবিধা সহ।

দ্রষ্টব্য : ঐতিহাসিকভাবে, ব্যাজেলের এক্সিকিউশন প্ল্যাটফর্মের ধারণা ছিল না, এবং পরিবর্তে সমস্ত বিল্ড অ্যাকশন হোস্ট মেশিনে চালানোর জন্য বিবেচনা করা হত। এই কারণে, একটি একক "হোস্ট" কনফিগারেশন আছে, এবং একটি "হোস্ট" রূপান্তর যা হোস্ট কনফিগারেশনে নির্ভরতা তৈরি করতে ব্যবহার করা যেতে পারে। অনেক নিয়ম এখনও তাদের টুলের জন্য "হোস্ট" ট্রানজিশন ব্যবহার করে, কিন্তু এটি বর্তমানে অবহেলিত এবং যেখানে সম্ভব "exec" ট্রানজিশন ব্যবহার করার জন্য স্থানান্তরিত করা হচ্ছে।

"হোস্ট" এবং "exec" কনফিগারেশনের মধ্যে অনেক পার্থক্য রয়েছে:

  • "হোস্ট" টার্মিনাল, "exec" নয়: একবার নির্ভরতা "হোস্ট" কনফিগারেশনে থাকলে, আর কোনো পরিবর্তনের অনুমতি নেই। একবার আপনি "exec" কনফিগারেশনে থাকলে আপনি আরও কনফিগারেশন ট্রানজিশন করা চালিয়ে যেতে পারেন।
  • "হোস্ট" একচেটিয়া, "exec" নয়: শুধুমাত্র একটি "হোস্ট" কনফিগারেশন আছে, কিন্তু প্রতিটি এক্সিকিউশন প্ল্যাটফর্মের জন্য একটি ভিন্ন "exec" কনফিগারেশন থাকতে পারে।
  • "হোস্ট" অনুমান করে যে আপনি বাজেলের মতো একই মেশিনে বা উল্লেখযোগ্যভাবে অনুরূপ মেশিনে সরঞ্জামগুলি চালান। এটি আর সত্য নয়: আপনি আপনার স্থানীয় মেশিনে বা রিমোট এক্সিকিউটরে বিল্ড অ্যাকশন চালাতে পারেন এবং রিমোট এক্সিকিউটর আপনার স্থানীয় মেশিনের মতো একই সিপিইউ এবং ওএসের কোনো গ্যারান্টি নেই।

উভয় "exec" এবং "হোস্ট" কনফিগারেশন একই বিকল্প পরিবর্তনগুলি প্রয়োগ করে, (উদাহরণস্বরূপ, --compilation_mode --host_compilation_mode --cpu --host_cpu , ইত্যাদি)। পার্থক্য হল "হোস্ট" কনফিগারেশন অন্য সব পতাকার ডিফল্ট মান দিয়ে শুরু হয়, যেখানে "exec" কনফিগারেশন টার্গেট কনফিগারেশনের উপর ভিত্তি করে পতাকার বর্তমান মান দিয়ে শুরু হয়।

কনফিগারেশন টুকরা

নিয়মগুলি cpp , java এবং jvm এর মতো কনফিগারেশন টুকরা অ্যাক্সেস করতে পারে। যাইহোক, অ্যাক্সেস ত্রুটি এড়াতে সমস্ত প্রয়োজনীয় টুকরো ঘোষণা করা আবশ্যক:

def _impl(ctx):
    # Using ctx.fragments.cpp leads to an error since it was not declared.
    x = ctx.fragments.java
    ...

my_rule = rule(
    implementation = _impl,
    fragments = ["java"],      # Required fragments of the target configuration
    host_fragments = ["java"], # Required fragments of the host configuration
    ...
)

ctx.fragments শুধুমাত্র লক্ষ্য কনফিগারেশনের জন্য কনফিগারেশন টুকরা প্রদান করে। আপনি যদি হোস্ট কনফিগারেশনের জন্য টুকরা অ্যাক্সেস করতে চান তবে পরিবর্তে ctx.host_fragments ব্যবহার করুন।

সাধারণত, রানফাইলস ট্রিতে একটি ফাইলের আপেক্ষিক পাথ সোর্স ট্রি বা জেনারেটেড আউটপুট ট্রিতে সেই ফাইলের আপেক্ষিক পাথের সমান। যদি কোনো কারণে এগুলি আলাদা হতে হয়, আপনি root_symlinks বা symlinks আর্গুমেন্ট নির্দিষ্ট করতে পারেন। root_symlinks হল ফাইলগুলির জন্য একটি অভিধান ম্যাপিং পাথ, যেখানে পাথগুলি রানফাইলস ডিরেক্টরির রুটের সাথে আপেক্ষিক। symlinks অভিধান একই, কিন্তু পাথগুলি কার্যক্ষেত্রের নামের সাথে নিহিতভাবে উপসর্গযুক্ত।

    ...
    runfiles = ctx.runfiles(
        root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
        symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
    )
    # Creates something like:
    # sometarget.runfiles/
    #     some/
    #         path/
    #             here.foo -> some_data_file2
    #     <workspace_name>/
    #         some/
    #             path/
    #                 here.bar -> some_data_file3

symlinks বা root_symlinks ব্যবহার করা হলে, রানফাইলস ট্রিতে দুটি ভিন্ন ফাইলকে একই পথে ম্যাপ না করার বিষয়ে সতর্ক থাকুন। এটি বিরোধ বর্ণনা করার ত্রুটির সাথে বিল্ডটিকে ব্যর্থ করে দেবে। ঠিক করতে, সংঘর্ষটি সরাতে আপনাকে আপনার ctx.runfiles আর্গুমেন্ট পরিবর্তন করতে হবে। এই চেকিং আপনার নিয়ম ব্যবহার করে যেকোন টার্গেটের জন্য করা হবে, সেইসাথে সেই টার্গেটের উপর নির্ভরশীল যে কোন ধরনের টার্গেট। এটি বিশেষত ঝুঁকিপূর্ণ যদি আপনার টুলটি অন্য টুল দ্বারা ট্রানজিটিভভাবে ব্যবহার করার সম্ভাবনা থাকে; একটি টুলের রানফাইল এবং এর সমস্ত নির্ভরতা জুড়ে symlink নাম অবশ্যই অনন্য হতে হবে।

কোড কভারেজ

যখন coverage কমান্ড চালানো হয়, বিল্ডকে নির্দিষ্ট লক্ষ্যগুলির জন্য কভারেজ উপকরণ যোগ করতে হতে পারে। বিল্ডটি ইনস্ট্রুমেন্টেড সোর্স ফাইলগুলির তালিকাও সংগ্রহ করে। বিবেচিত লক্ষ্যগুলির উপসেট পতাকা --instrumentation_filter দ্বারা নিয়ন্ত্রিত হয়। পরীক্ষার লক্ষ্যগুলি বাদ দেওয়া হয়, যদি না --instrument_test_targets নির্দিষ্ট করা হয়৷

যদি একটি নিয়ম বাস্তবায়ন বিল্ড টাইমে কভারেজ ইনস্ট্রুমেন্টেশন যোগ করে, তবে এটির বাস্তবায়ন ফাংশনে এটির জন্য অ্যাকাউন্ট করতে হবে। ctx.coverage_instrumented কভারেজ মোডে সত্য রিটার্ন করে যদি একটি টার্গেটের সোর্স ইনস্ট্রুমেন্ট করা উচিত:

# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
  # Do something to turn on coverage for this compile action

লজিক যা সর্বদা কভারেজ মোডে থাকা দরকার (একটি টার্গেটের উত্স বিশেষভাবে যন্ত্রযুক্ত হোক বা না হোক) ctx.configuration.coverage_enabled- এ শর্তযুক্ত করা যেতে পারে।

যদি নিয়মটি সংকলনের আগে সরাসরি তার নির্ভরতা থেকে উত্সগুলি অন্তর্ভুক্ত করে (যেমন শিরোনাম ফাইল), তাহলে নির্ভরতাগুলির উত্সগুলিকে উপকরণ করা উচিত হলে এটিকে কম্পাইল-টাইম ইনস্ট্রুমেন্টেশন চালু করতে হবে:

# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
    (ctx.coverage_instrumented() or
     any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
    # Do something to turn on coverage for this compile action

Coverage_common.instrumented_files_info ব্যবহার করে নির্মিত InstrumentedFilesInfo প্রদানকারীর সাথে coverage_common.instrumented_files_info জন্য কোন বৈশিষ্ট্যগুলি প্রাসঙ্গিক সে সম্পর্কেও নিয়মগুলিকে তথ্য প্রদান করা উচিত। instrumented_files_info এর dependency_attributes প্যারামিটারে deps মতো কোড নির্ভরতা এবং ডেটার মতো data নির্ভরতা সহ সমস্ত রানটাইম নির্ভরতা বৈশিষ্ট্যের তালিকা করা উচিত। source_attributes প্যারামিটারে নিয়মের সোর্স ফাইলের বৈশিষ্ট্যগুলি তালিকাভুক্ত করা উচিত যদি কভারেজ উপকরণ যোগ করা যেতে পারে:

def _example_library_impl(ctx):
    ...
    return [
        ...
        coverage_common.instrumented_files_info(
            ctx,
            dependency_attributes = ["deps", "data"],
            # Omitted if coverage is not supported for this rule:
            source_attributes = ["srcs", "hdrs"],
        )
        ...
    ]

যদি InstrumentedFilesInfo ফেরত না দেওয়া হয়, প্রতিটি নন-টুল নির্ভরতা বৈশিষ্ট্যের সাথে একটি ডিফল্ট একটি তৈরি করা হয় যা cfg কে "host" বা "exec" এ অ্যাট্রিবিউট স্কিমাতে সেট করে না) dependency_attributes এ। (এটি আদর্শ আচরণ নয়, কারণ এটি source_attributes এর পরিবর্তে srcs এর মতো বৈশিষ্ট্যগুলিকে dependency_attributes রাখে, তবে এটি নির্ভরতা চেইনের সমস্ত নিয়মের জন্য স্পষ্ট কভারেজ কনফিগারেশনের প্রয়োজন এড়ায়।)

বৈধতা কর্ম

কখনও কখনও আপনাকে বিল্ড সম্পর্কে কিছু যাচাই করতে হবে, এবং সেই যাচাইকরণের জন্য প্রয়োজনীয় তথ্য শুধুমাত্র আর্টিফ্যাক্টগুলিতে (সোর্স ফাইল বা জেনারেট করা ফাইল) পাওয়া যায়। যেহেতু এই তথ্যটি আর্টিফ্যাক্টে রয়েছে, নিয়মগুলি বিশ্লেষণের সময় এই বৈধতা করতে পারে না কারণ নিয়মগুলি ফাইল পড়তে পারে না৷ পরিবর্তে, কর্মগুলি কার্যকর করার সময় এই বৈধতা অবশ্যই করবে৷ যখন বৈধতা ব্যর্থ হয়, তখন ক্রিয়া ব্যর্থ হবে, এবং তাই বিল্ড হবে।

চালানো যেতে পারে এমন বৈধতার উদাহরণ হল স্ট্যাটিক বিশ্লেষণ, লিন্টিং, নির্ভরতা এবং সামঞ্জস্য পরীক্ষা এবং শৈলী পরীক্ষা।

বৈধকরণ ক্রিয়াগুলি বিভিন্ন ক্রিয়াকলাপের অংশগুলিকে সরিয়ে দিয়ে বিল্ড কার্যক্ষমতা উন্নত করতে সাহায্য করতে পারে যা শিল্পকর্মগুলিকে আলাদা ক্রিয়ায় তৈরি করার জন্য প্রয়োজন হয় না। উদাহরণস্বরূপ, যদি একটি একক অ্যাকশন যা কম্পাইলেশন এবং লিন্টিং করে তাকে একটি কম্পাইলেশন অ্যাকশন এবং লিন্টিং অ্যাকশনে আলাদা করা যায়, তাহলে লিন্টিং অ্যাকশনটিকে একটি বৈধতা অ্যাকশন হিসেবে চালানো যেতে পারে এবং অন্যান্য অ্যাকশনের সাথে সমান্তরালভাবে চালানো যেতে পারে।

এই "বৈধকরণ ক্রিয়াগুলি" প্রায়শই এমন কিছু তৈরি করে না যা বিল্ডে অন্য কোথাও ব্যবহার করা হয়, কারণ তাদের শুধুমাত্র তাদের ইনপুটগুলি সম্পর্কে কিছু দাবি করতে হবে। যদিও এটি একটি সমস্যা উপস্থাপন করে: যদি একটি বৈধতা ক্রিয়া এমন কিছু তৈরি না করে যা বিল্ডের অন্য কোথাও ব্যবহার করা হয়, তাহলে একটি নিয়ম কীভাবে ক্রিয়াকলাপ চালাতে পারে? Historically, the approach was to have the validation action output an empty file, and artificially add that output to the inputs of some other important action in the build:

This works, because Bazel will always run the validation action when the compile action is run, but this has significant drawbacks:

  1. The validation action is in the critical path of the build. Because Bazel thinks the empty output is required to run the compile action, it will run the validation action first, even though the compile action will ignore the input. This reduces parallelism and slows down builds.

  2. If other actions in the build might run instead of the compile action, then the empty outputs of validation actions need to be added to those actions as well ( java_library 's source jar output, for example). This is also a problem if new actions that might run instead of the compile action are added later, and the empty validation output is accidentally left off.

The solution to these problems is to use the Validations Output Group.

Validations Output Group

The Validations Output Group is an output group designed to hold the otherwise unused outputs of validation actions, so that they don't need to be artificially added to the inputs of other actions.

This group is special in that its outputs are always requested, regardless of the value of the --output_groups flag, and regardless of how the target is depended upon (for example, on the command line, as a dependency, or through implicit outputs of the target). Note that normal caching and incrementality still apply: if the inputs to the validation action have not changed and the validation action previously succeeded, then the validation action will not be run.

Using this output group still requires that validation actions output some file, even an empty one. This might require wrapping some tools that normally don't create outputs so that a file is created.

A target's validation actions are not run in three cases:

  • When the target is depended upon as a tool
  • When the target is depended upon as an implicit dependency (for example, an attribute that starts with "_")
  • When the target is built in the host or exec configuration.

It is assumed that these targets have their own separate builds and tests that would uncover any validation failures.

Using the Validations Output Group

The Validations Output Group is named _validation and is used like any other output group:

def _rule_with_validation_impl(ctx):

  ctx.actions.write(ctx.outputs.main, "main output\n")

  ctx.actions.write(ctx.outputs.implicit, "implicit output\n")

  validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
  ctx.actions.run(
      outputs = [validation_output],
      executable = ctx.executable._validation_tool,
      arguments = [validation_output.path])

  return [
    DefaultInfo(files = depset([ctx.outputs.main])),
    OutputGroupInfo(_validation = depset([validation_output])),
  ]


rule_with_validation = rule(
  implementation = _rule_with_validation_impl,
  outputs = {
    "main": "%{name}.main",
    "implicit": "%{name}.implicit",
  },
  attrs = {
    "_validation_tool": attr.label(
        default = Label("//validation_actions:validation_tool"),
        executable = True,
        cfg = "exec"),
  }
)

Notice that the validation output file is not added to the DefaultInfo or the inputs to any other action. The validation action for a target of this rule kind will still run if the target is depended upon by label, or any of the target's implicit outputs are directly or indirectly depended upon.

It is usually important that the outputs of validation actions only go into the validation output group, and are not added to the inputs of other actions, as this could defeat parallelism gains. Note however that Bazel does not currently have any special checking to enforce this. Therefore, you should test that validation action outputs are not added to the inputs of any actions in the tests for Starlark rules. উদাহরণ স্বরূপ:

load("@bazel_skylib//lib:unittest.bzl", "analysistest")

def _validation_outputs_test_impl(ctx):
  env = analysistest.begin(ctx)

  actions = analysistest.target_actions(env)
  target = analysistest.target_under_test(env)
  validation_outputs = target.output_groups._validation.to_list()
  for action in actions:
    for validation_output in validation_outputs:
      if validation_output in action.inputs.to_list():
        analysistest.fail(env,
            "%s is a validation action output, but is an input to action %s" % (
                validation_output, action))

  return analysistest.end(env)

validation_outputs_test = analysistest.make(_validation_outputs_test_impl)

Validation Actions Flag

Running validation actions is controlled by the --run_validations command line flag, which defaults to true.

Deprecated features

Deprecated predeclared outputs

There are two deprecated ways of using predeclared outputs:

  • The outputs parameter of rule specifies a mapping between output attribute names and string templates for generating predeclared output labels. Prefer using non-predeclared outputs and explicitly adding outputs to DefaultInfo.files . Use the rule target's label as input for rules which consume the output instead of a predeclared output's label.

  • For executable rules , ctx.outputs.executable refers to a predeclared executable output with the same name as the rule target. Prefer declaring the output explicitly, for example with ctx.actions.declare_file(ctx.label.name) , and ensure that the command that generates the executable sets its permissions to allow execution. Explicitly pass the executable output to the executable parameter of DefaultInfo .

Runfiles features to avoid

ctx.runfiles and the runfiles type have a complex set of features, many of which are kept for legacy reasons. The following recommendations help reduce complexity:

  • Avoid use of the collect_data and collect_default modes of ctx.runfiles . These modes implicitly collect runfiles across certain hardcoded dependency edges in confusing ways. Instead, add files using the files or transitive_files parameters of ctx.runfiles , or by merging in runfiles from dependencies with runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles) .

  • Avoid use of the data_runfiles and default_runfiles of the DefaultInfo constructor. Specify DefaultInfo(runfiles = ...) instead. The distinction between "default" and "data" runfiles is maintained for legacy reasons. For example, some rules put their default outputs in data_runfiles , but not default_runfiles . Instead of using data_runfiles , rules should both include default outputs and merge in default_runfiles from attributes which provide runfiles (often data ).

  • When retrieving runfiles from DefaultInfo (generally only for merging runfiles between the current rule and its dependencies), use DefaultInfo.default_runfiles , not DefaultInfo.data_runfiles .

Migrating from legacy providers

Historically, Bazel providers were simple fields on the Target object. They were accessed using the dot operator, and they were created by putting the field in a struct returned by the rule's implementation function.

This style is deprecated and should not be used in new code; see below for information that may help you migrate. The new provider mechanism avoids name clashes. It also supports data hiding, by requiring any code accessing a provider instance to retrieve it using the provider symbol.

For the moment, legacy providers are still supported. A rule can return both legacy and modern providers as follows:

def _old_rule_impl(ctx):
  ...
  legacy_data = struct(x="foo", ...)
  modern_data = MyInfo(y="bar", ...)
  # When any legacy providers are returned, the top-level returned value is a
  # struct.
  return struct(
      # One key = value entry for each legacy provider.
      legacy_info = legacy_data,
      ...
      # Additional modern providers:
      providers = [modern_data, ...])

If dep is the resulting Target object for an instance of this rule, the providers and their contents can be retrieved as dep.legacy_info.x and dep[MyInfo].y .

In addition to providers , the returned struct can also take several other fields that have special meaning (and thus do not create a corresponding legacy provider):

  • The fields files , runfiles , data_runfiles , default_runfiles , and executable correspond to the same-named fields of DefaultInfo . It is not allowed to specify any of these fields while also returning a DefaultInfo provider.

  • The field output_groups takes a struct value and corresponds to an OutputGroupInfo .

In provides declarations of rules, and in providers declarations of dependency attributes, legacy providers are passed in as strings and modern providers are passed in by their *Info symbol. Be sure to change from strings to symbols when migrating. For complex or large rule sets where it is difficult to update all rules atomically, you may have an easier time if you follow this sequence of steps:

  1. Modify the rules that produce the legacy provider to produce both the legacy and modern providers, using the above syntax. For rules that declare they return the legacy provider, update that declaration to include both the legacy and modern providers.

  2. Modify the rules that consume the legacy provider to instead consume the modern provider. If any attribute declarations require the legacy provider, also update them to instead require the modern provider. Optionally, you can interleave this work with step 1 by having consumers accept/require either provider: Test for the presence of the legacy provider using hasattr(target, 'foo') , or the new provider using FooInfo in target .

  3. Fully remove the legacy provider from all rules.