নিয়ম টিউটোরিয়াল

স্টারলার্ক হল একটি পাইথন-সদৃশ কনফিগারেশন ভাষা যা মূলত Bazel-এ ব্যবহারের জন্য তৈরি করা হয়েছে এবং অন্যান্য সরঞ্জাম দ্বারা গৃহীত হয়েছে। Bazel এর BUILD এবং .bzl ফাইলগুলি স্টারলার্কের একটি উপভাষায় লেখা হয় যা সঠিকভাবে "বিল্ড ল্যাঙ্গুয়েজ" নামে পরিচিত, যদিও এটিকে প্রায়শই সহজভাবে "স্টারলার্ক" হিসাবে উল্লেখ করা হয়, বিশেষ করে যখন জোর দেওয়া হয় যে বিল্ড ল্যাঙ্গুয়েজ এর বিপরীতে একটি বৈশিষ্ট্য প্রকাশ করা হয়। বাজেলের একটি অন্তর্নির্মিত বা "নেটিভ" অংশ। বেজেল অসংখ্য বিল্ড-সম্পর্কিত ফাংশন যেমন genrule glob java_binary , এবং আরও অনেকগুলি সহ মূল ভাষাকে বাড়িয়ে তোলে৷

আরও বিশদ বিবরণের জন্য Bazel এবং Starlark ডকুমেন্টেশন দেখুন, এবং নতুন নিয়ম সেটের জন্য একটি সূচনা বিন্দু হিসাবে নিয়ম SIG টেমপ্লেট দেখুন।

খালি নিয়ম

আপনার প্রথম নিয়ম তৈরি করতে, foo.bzl ফাইলটি তৈরি করুন:

def _foo_binary_impl(ctx):
    pass

foo_binary = rule(
    implementation = _foo_binary_impl,
)

আপনি যখন rule ফাংশন কল করেন, আপনাকে অবশ্যই একটি কলব্যাক ফাংশন সংজ্ঞায়িত করতে হবে। যুক্তি সেখানে যাবে, কিন্তু আপনি আপাতত ফাংশনটি খালি রাখতে পারেন। ctx আর্গুমেন্ট টার্গেট সম্পর্কে তথ্য প্রদান করে।

আপনি নিয়মটি লোড করতে পারেন এবং এটি একটি BUILD ফাইল থেকে ব্যবহার করতে পারেন।

একই ডিরেক্টরিতে একটি BUILD ফাইল তৈরি করুন:

load(":foo.bzl", "foo_binary")

foo_binary(name = "bin")

এখন, লক্ষ্য তৈরি করা যেতে পারে:

$ bazel build bin
INFO: Analyzed target //:bin (2 packages loaded, 17 targets configured).
INFO: Found 1 target...
Target //:bin up-to-date (nothing to build)

যদিও নিয়মটি কিছুই করে না, এটি ইতিমধ্যেই অন্যান্য নিয়মের মতো আচরণ করে: এটির একটি বাধ্যতামূলক নাম রয়েছে, এটি visibility , testonly , এবং tags মতো সাধারণ বৈশিষ্ট্যগুলিকে সমর্থন করে৷

মূল্যায়ন মডেল

আরও এগিয়ে যাওয়ার আগে, কোডটি কীভাবে মূল্যায়ন করা হয় তা বোঝা গুরুত্বপূর্ণ।

কিছু মুদ্রণ বিবৃতি সহ foo.bzl আপডেট করুন:

def _foo_binary_impl(ctx):
    print("analyzing", ctx.label)

foo_binary = rule(
    implementation = _foo_binary_impl,
)

print("bzl file evaluation")

এবং নির্মাণ:

load(":foo.bzl", "foo_binary")

print("BUILD file")
foo_binary(name = "bin1")
foo_binary(name = "bin2")

ctx.label বিশ্লেষণ করা লক্ষ্যের লেবেলের সাথে মিলে যায়। ctx অবজেক্টের অনেক দরকারী ক্ষেত্র এবং পদ্ধতি রয়েছে; আপনি API রেফারেন্সে একটি সম্পূর্ণ তালিকা খুঁজে পেতে পারেন।

কোড জিজ্ঞাসা করুন:

$ bazel query :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
//:bin2
//:bin1

কয়েকটি পর্যবেক্ষণ করুন:

  • "bzl ফাইল মূল্যায়ন" প্রথমে মুদ্রিত হয়। BUILD ফাইলের মূল্যায়ন করার আগে, Bazel লোড করা সমস্ত ফাইলের মূল্যায়ন করে। যদি একাধিক BUILD ফাইল foo.bzl লোড হয়, আপনি "bzl ফাইল মূল্যায়ন" এর শুধুমাত্র একটি ঘটনা দেখতে পাবেন কারণ Bazel মূল্যায়নের ফলাফল ক্যাশ করে।
  • কলব্যাক ফাংশন _foo_binary_impl বলা হয় না। Bazel ক্যোয়ারী BUILD ফাইল লোড করে, কিন্তু লক্ষ্য বিশ্লেষণ করে না।

লক্ষ্যগুলি বিশ্লেষণ করতে, cquery ("কনফিগার করা প্রশ্ন") বা build কমান্ড ব্যবহার করুন:

$ bazel build :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin1
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin2
INFO: Analyzed 2 targets (0 packages loaded, 0 targets configured).
INFO: Found 2 targets...

আপনি দেখতে পাচ্ছেন, _foo_binary_impl এখন দুইবার বলা হয় - প্রতিটি টার্গেটের জন্য একবার।

কিছু পাঠক লক্ষ্য করবেন যে "bzl ফাইল মূল্যায়ন" আবার প্রিন্ট করা হয়েছে, যদিও foo.bzl-এর মূল্যায়ন কল টু bazel query পরে ক্যাশে করা হয়েছে। ব্যাজেল কোডটি পুনরায় মূল্যায়ন করে না, এটি শুধুমাত্র মুদ্রণ ইভেন্টগুলিকে পুনরায় প্লে করে। ক্যাশে অবস্থা নির্বিশেষে, আপনি একই আউটপুট পাবেন।

একটি ফাইল তৈরি করা হচ্ছে

আপনার নিয়মকে আরও উপযোগী করতে, একটি ফাইল তৈরি করতে এটি আপডেট করুন। প্রথমে, ফাইলটি ঘোষণা করুন এবং এটির একটি নাম দিন। এই উদাহরণে, লক্ষ্য হিসাবে একই নামের একটি ফাইল তৈরি করুন:

ctx.actions.declare_file(ctx.label.name)

আপনি যদি bazel build :all এখন চালান, আপনি একটি ত্রুটি পাবেন:

The following files have no generating action:
bin2

যখনই আপনি একটি ফাইল ঘোষণা করেন, আপনাকে ব্যাজেলকে বলতে হবে কিভাবে একটি অ্যাকশন তৈরি করে এটি তৈরি করতে হয়। প্রদত্ত বিষয়বস্তু সহ একটি ফাইল তৈরি করতে ctx.actions.write ব্যবহার করুন।

def _foo_binary_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = "Hello\n",
    )

কোডটি বৈধ, কিন্তু এটি কিছুই করবে না:

$ bazel build bin1
Target //:bin1 up-to-date (nothing to build)

ctx.actions.write ফাংশন একটি অ্যাকশন নিবন্ধন করেছে, যা Bazel কে শিখিয়েছে কিভাবে ফাইল তৈরি করতে হয়। কিন্তু Bazel ফাইলটি তৈরি করবে না যতক্ষণ না এটি আসলে অনুরোধ করা হয়। তাই শেষ জিনিসটি Bazel কে বলতে হবে যে ফাইলটি নিয়মের একটি আউটপুট, এবং নিয়ম বাস্তবায়নের মধ্যে ব্যবহৃত একটি অস্থায়ী ফাইল নয়।

def _foo_binary_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = "Hello!\n",
    )
    return [DefaultInfo(files = depset([out]))]

DefaultInfo এবং depset ফাংশন পরে দেখুন। আপাতত, ধরে নিন যে শেষ লাইনটি একটি নিয়মের আউটপুট বেছে নেওয়ার উপায়।

এখন, Bazel চালান:

$ bazel build bin1
INFO: Found 1 target...
Target //:bin1 up-to-date:
  bazel-bin/bin1

$ cat bazel-bin/bin1
Hello!

আপনি সফলভাবে একটি ফাইল তৈরি করেছেন!

গুণাবলী

নিয়মটিকে আরও উপযোগী করতে, attr মডিউল ব্যবহার করে নতুন বৈশিষ্ট্য যোগ করুন এবং নিয়মের সংজ্ঞা আপডেট করুন।

username নামে একটি স্ট্রিং বৈশিষ্ট্য যোগ করুন:

foo_binary = rule(
    implementation = _foo_binary_impl,
    attrs = {
        "username": attr.string(),
    },
)

পরবর্তী, এটি BUILD ফাইলে সেট করুন:

foo_binary(
    name = "bin",
    username = "Alice",
)

কলব্যাক ফাংশনে মান অ্যাক্সেস করতে, ctx.attr.username ব্যবহার করুন। উদাহরণ স্বরূপ:

def _foo_binary_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = "Hello {}!\n".format(ctx.attr.username),
    )
    return [DefaultInfo(files = depset([out]))]

মনে রাখবেন যে আপনি বৈশিষ্ট্যটিকে বাধ্যতামূলক করতে পারেন বা একটি ডিফল্ট মান সেট করতে পারেন। attr.string এর ডকুমেন্টেশন দেখুন। আপনি অন্যান্য ধরণের বৈশিষ্ট্যগুলিও ব্যবহার করতে পারেন, যেমন বুলিয়ান বা পূর্ণসংখ্যার তালিকা

নির্ভরতা

নির্ভরতা বৈশিষ্ট্য, যেমন attr.label এবং attr.label_list , লক্ষ্য থেকে একটি নির্ভরতা ঘোষণা করে যেটি লক্ষ্যের বৈশিষ্ট্যটির মালিক যার লেবেলটি বৈশিষ্ট্যের মানতে প্রদর্শিত হয়। এই ধরনের বৈশিষ্ট্য লক্ষ্য গ্রাফের ভিত্তি গঠন করে।

BUILD ফাইলে, টার্গেট লেবেল একটি স্ট্রিং অবজেক্ট হিসাবে প্রদর্শিত হয়, যেমন //pkg:name । ইমপ্লিমেন্টেশন ফাংশনে, টার্গেটটি Target অবজেক্ট হিসাবে অ্যাক্সেসযোগ্য হবে। উদাহরণস্বরূপ, Target.files ব্যবহার করে লক্ষ্য দ্বারা ফেরত ফাইলগুলি দেখুন।

একাধিক ফাইল

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

"srcs": attr.label_list(allow_files = [".java"]),

ফাইলের তালিকা ctx.files.<attribute name> । উদাহরণস্বরূপ, srcs অ্যাট্রিবিউটের ফাইলগুলির তালিকার মাধ্যমে অ্যাক্সেস করা যেতে পারে

ctx.files.srcs

বিক্ষিপ্ত নথি

আপনার যদি শুধুমাত্র একটি ফাইলের প্রয়োজন হয়, allow_single_file ব্যবহার করুন :

"src": attr.label(allow_single_file = [".java"])

এই ফাইলটি তারপর ctx.file.<attribute name> :

ctx.file.src

একটি টেমপ্লেট দিয়ে একটি ফাইল তৈরি করুন

আপনি একটি নিয়ম তৈরি করতে পারেন যা একটি টেমপ্লেটের উপর ভিত্তি করে একটি .cc ফাইল তৈরি করে। এছাড়াও, আপনি নিয়ম বাস্তবায়ন ফাংশনে নির্মিত একটি স্ট্রিং আউটপুট করতে ctx.actions.write ব্যবহার করতে পারেন, কিন্তু এতে দুটি সমস্যা রয়েছে। প্রথমত, টেমপ্লেটটি বড় হওয়ার সাথে সাথে এটিকে একটি পৃথক ফাইলে রাখা এবং বিশ্লেষণের পর্যায়ে বড় স্ট্রিং তৈরি করা এড়াতে এটি আরও মেমরি দক্ষ হয়ে ওঠে। দ্বিতীয়ত, একটি পৃথক ফাইল ব্যবহার করা ব্যবহারকারীর জন্য আরও সুবিধাজনক। পরিবর্তে, ctx.actions.expand_template ব্যবহার করুন, যা একটি টেমপ্লেট ফাইলে প্রতিস্থাপন করে।

টেমপ্লেট ফাইলের উপর নির্ভরতা ঘোষণা করতে একটি template বৈশিষ্ট্য তৈরি করুন:

def _hello_world_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name + ".cc")
    ctx.actions.expand_template(
        output = out,
        template = ctx.file.template,
        substitutions = {"{NAME}": ctx.attr.username},
    )
    return [DefaultInfo(files = depset([out]))]

hello_world = rule(
    implementation = _hello_world_impl,
    attrs = {
        "username": attr.string(default = "unknown person"),
        "template": attr.label(
            allow_single_file = [".cc.tpl"],
            mandatory = True,
        ),
    },
)

ব্যবহারকারীরা এই মত নিয়ম ব্যবহার করতে পারেন:

hello_world(
    name = "hello",
    username = "Alice",
    template = "file.cc.tpl",
)

cc_binary(
    name = "hello_bin",
    srcs = [":hello"],
)

আপনি যদি শেষ-ব্যবহারকারীর কাছে টেমপ্লেটটি প্রকাশ করতে না চান এবং সর্বদা একই ব্যবহার করতে চান তবে আপনি একটি ডিফল্ট মান সেট করতে পারেন এবং বৈশিষ্ট্যটিকে ব্যক্তিগত করতে পারেন:

    "_template": attr.label(
        allow_single_file = True,
        default = "file.cc.tpl",
    ),

একটি আন্ডারস্কোর দিয়ে শুরু হওয়া বৈশিষ্ট্যগুলি ব্যক্তিগত এবং একটি BUILD ফাইলে সেট করা যায় না৷ টেমপ্লেটটি এখন একটি অন্তর্নিহিত নির্ভরতা : প্রতিটি hello_world টার্গেটের এই ফাইলের উপর নির্ভরশীলতা রয়েছে। BUILD ফাইলটি আপডেট করে এবং exports_files ব্যবহার করে এই ফাইলটিকে অন্য প্যাকেজগুলিতে দৃশ্যমান করতে ভুলবেন না:

exports_files(["file.cc.tpl"])

সামনে যাচ্ছি