प्‍लेटफ़ॉर्म

Bazel, अलग-अलग हार्डवेयर, ऑपरेटिंग सिस्टम, और सिस्टम कॉन्फ़िगरेशन पर कोड को बना और टेस्ट कर सकता है. इसके लिए, यह बिल्ड टूल के कई अलग-अलग वर्शन का इस्तेमाल करता है. जैसे, लिंक करने वाले टूल और कंपाइलर. इस जटिलता को मैनेज करने के लिए, Bazel में constraints और platforms का कॉन्सेप्ट है. बाधा एक ऐसा डाइमेंशन है जिसमें बिल्ड या प्रोडक्शन एनवायरमेंट अलग-अलग हो सकते हैं. जैसे, सीपीयू आर्किटेक्चर, जीपीयू का होना या न होना या सिस्टम में इंस्टॉल किए गए कंपाइलर का वर्शन. प्लैटफ़ॉर्म, इन शर्तों के लिए विकल्पों का एक ऐसा कलेक्शन होता है जिसे नाम दिया गया हो. यह उन खास संसाधनों को दिखाता है जो किसी एनवायरमेंट में उपलब्ध हैं.

एनवायरमेंट को प्लैटफ़ॉर्म के तौर पर मॉडल करने से, Bazel को बिल्ड ऐक्शन के लिए सही टूलचेन अपने-आप चुनने में मदद मिलती है. config_setting नियम के साथ प्लैटफ़ॉर्म का इस्तेमाल करके, कॉन्फ़िगर किए जा सकने वाले एट्रिब्यूट लिखे जा सकते हैं.

Bazel, तीन भूमिकाओं को पहचानता है जो कोई प्लैटफ़ॉर्म निभा सकता है:

  • होस्ट - वह प्लैटफ़ॉर्म जिस पर Bazel खुद चलता है.
  • Execution - यह एक ऐसा प्लैटफ़ॉर्म है जिस पर बिल्ड टूल, बिल्ड ऐक्शन को लागू करते हैं, ताकि इंटरमीडिएट और फ़ाइनल आउटपुट जनरेट किए जा सकें.
  • टारगेट - एक ऐसा प्लैटफ़ॉर्म जहां फ़ाइनल आउटपुट मौजूद होता है और उसे लागू किया जाता है.

Bazel, प्लैटफ़ॉर्म के हिसाब से इन बिल्ड के साथ काम करता है:

  • सिंगल-प्लैटफ़ॉर्म बिल्ड (डिफ़ॉल्ट) - होस्ट, एक्ज़ीक्यूशन, और टारगेट प्लैटफ़ॉर्म एक ही होते हैं. उदाहरण के लिए, Intel x64 CPU पर चल रहे Ubuntu पर Linux एक्ज़ीक्यूटेबल बनाना.

  • क्रॉस-कंपाइलेशन बिल्ड - होस्ट और एक्ज़ीक्यूशन प्लैटफ़ॉर्म एक ही होते हैं, लेकिन टारगेट प्लैटफ़ॉर्म अलग होता है. उदाहरण के लिए, MacBook Pro पर macOS का इस्तेमाल करके iOS ऐप्लिकेशन बनाना.

  • एक से ज़्यादा प्लैटफ़ॉर्म पर काम करने वाले बिल्ड - होस्ट, एक्ज़ीक्यूशन, और टारगेट प्लैटफ़ॉर्म, सभी अलग-अलग होते हैं.

शर्तें और प्लैटफ़ॉर्म तय करना

BUILD फ़ाइलों में constraint_setting और constraint_value नियमों का इस्तेमाल करके, प्लैटफ़ॉर्म के लिए उपलब्ध विकल्पों की जानकारी दी जाती है. constraint_setting एक नया डाइमेंशन बनाता है, जबकि constraint_value किसी दिए गए डाइमेंशन के लिए नई वैल्यू बनाता है; ये दोनों मिलकर असरदार तरीके से एक एनम और उसकी संभावित वैल्यू तय करते हैं. उदाहरण के लिए, 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 आर्किटेक्चर पर Linux ऑपरेटिंग सिस्टम चलाने वाले किसी भी एनवायरमेंट के बारे में बताता है. साथ ही, इसमें glibc का वर्शन 2.25 है. (Bazel की पहले से मौजूद पाबंदियों के बारे में ज़्यादा जानने के लिए, यहां देखें.)

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

आम तौर पर काम आने वाली शर्तें और प्लैटफ़ॉर्म

इकोसिस्टम को एक जैसा बनाए रखने के लिए, Bazel टीम एक ऐसी रिपॉज़िटरी मैनेज करती है जिसमें सबसे लोकप्रिय सीपीयू आर्किटेक्चर और ऑपरेटिंग सिस्टम के लिए, कंस्ट्रेंट की परिभाषाएं होती हैं. ये सभी https://github.com/bazelbuild/platforms पर मौजूद हैं.

Bazel में, प्लैटफ़ॉर्म की यह खास परिभाषा शामिल होती है: @platforms//host (इसे @bazel_tools//tools:host_platform के तौर पर भी जाना जाता है). यह होस्ट प्लैटफ़ॉर्म की अपने-आप पता लगाई गई वैल्यू है. यह उस सिस्टम के लिए अपने-आप पता लगाए गए प्लैटफ़ॉर्म को दिखाती है जिस पर Bazel चल रहा है.

बिल्ड के लिए प्लैटफ़ॉर्म तय करना

इन कमांड-लाइन फ़्लैग का इस्तेमाल करके, किसी बिल्ड के लिए होस्ट और टारगेट प्लैटफ़ॉर्म तय किए जा सकते हैं:

  • --host_platform - डिफ़ॉल्ट रूप से @bazel_tools//tools:host_platform पर सेट होता है
    • इस टारगेट को @platforms//host के तौर पर भी जाना जाता है. यह एक repo rule पर आधारित होता है. यह नियम, होस्ट ओएस और सीपीयू का पता लगाता है और प्लैटफ़ॉर्म टारगेट लिखता है.
    • इसके अलावा, @platforms//host:constraints.bzl भी है. यह HOST_CONSTRAINTS नाम के एक अरे को दिखाता है. इसका इस्तेमाल अन्य BUILD और Starlark फ़ाइलों में किया जा सकता है.
  • --platforms - डिफ़ॉल्ट रूप से होस्ट प्लैटफ़ॉर्म पर सेट होता है
    • इसका मतलब है कि जब कोई अन्य फ़्लैग सेट नहीं किया जाता है, तब @platforms//host टारगेट प्लैटफ़ॉर्म होता है.
    • अगर --host_platform सेट है और --platforms नहीं, तो --host_platform की वैल्यू, होस्ट और टारगेट प्लैटफ़ॉर्म, दोनों के लिए होती है.

ज़रूरी शर्तें पूरी न करने वाले टारगेट को स्किप किया जा रहा है

किसी खास टारगेट प्लैटफ़ॉर्म के लिए ऐप्लिकेशन बनाते समय, अक्सर उन टारगेट को छोड़ना बेहतर होता है जो उस प्लैटफ़ॉर्म पर कभी काम नहीं करेंगे. उदाहरण के लिए, Windows डिवाइस ड्राइवर को //... के साथ Linux मशीन पर बनाने के दौरान, कंपाइलर से जुड़ी कई गड़बड़ियां हो सकती हैं. target_compatible_with एट्रिब्यूट का इस्तेमाल करके, Bazel को बताएं कि आपके कोड पर टारगेट प्लैटफ़ॉर्म की कौनसी पाबंदियां लागू हैं.

इस एट्रिब्यूट का सबसे आसान इस्तेमाल, किसी टारगेट को एक प्लैटफ़ॉर्म तक सीमित कर देता है. टारगेट को ऐसे किसी भी प्लैटफ़ॉर्म के लिए नहीं बनाया जाएगा जो सभी शर्तों को पूरा नहीं करता है. यहां दिए गए उदाहरण में, win_driver_lib.cc को 64-बिट Windows तक सीमित किया गया है.

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-बिट Windows के साथ इस्तेमाल किया जा सकता है. इसे किसी और के साथ इस्तेमाल नहीं किया जा सकता. साथ काम न करने की समस्या एक से दूसरे डिवाइस में भी हो सकती है. ऐसे सभी टारगेट जो किसी ऐसे टारगेट पर ट्रांज़िटिव तरीके से निर्भर होते हैं जो कंपैटिबल नहीं है उन्हें भी कंपैटिबल नहीं माना जाता.

टारगेट कब स्किप किए जाते हैं?

जब टारगेट को काम न करने वाला माना जाता है, तब उन्हें स्किप कर दिया जाता है. साथ ही, उन्हें टारगेट पैटर्न के विस्तार के हिस्से के तौर पर बिल्ड में शामिल किया जाता है. उदाहरण के लिए, नीचे दिए गए दो इनवोकेशन, टारगेट पैटर्न के एक्सपैंशन में मिले किसी भी ऐसे टारगेट को छोड़ देते हैं जो काम नहीं करता.

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

अगर कमांड लाइन पर --expand_test_suites के साथ test_suite को सेट किया जाता है, तो 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

अगर --skip_incompatible_explicit_targets चालू है, तो साफ़ तौर पर बताए गए ऐसे टारगेट को चुपचाप स्किप कर दिया जाता है जो काम नहीं करते.

ज़्यादा एक्सप्रेशन वाले निर्देश

पाबंदियों को ज़्यादा आसानी से लागू करने के लिए, @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. Linux को टारगेट करते समय, टारगेट पर कोई पाबंदी नहीं होती.
  3. ऐसा न होने पर, टारगेट पर @platforms//:incompatible की पाबंदी लागू होती है. @platforms//:incompatible किसी भी प्लैटफ़ॉर्म का हिस्सा नहीं है. इसलिए, टारगेट को प्लैटफ़ॉर्म के साथ काम न करने वाला माना जाता है.

अपनी पाबंदियों को ज़्यादा आसानी से समझने के लिए, skylib के selects.with_or() का इस्तेमाल करें.

इसी तरह, यह बताया जा सकता है कि कोई सुविधा, किसी वर्शन के साथ काम नहीं करती. यहां दिए गए उदाहरण में, ऐसी लाइब्रेरी के बारे में बताया गया है जो 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 का इस्तेमाल, bazel cquery के Starlark आउटपुट फ़ॉर्मैट में किया जा सकता है. इससे, काम न करने वाले टारगेट को काम करने वाले टारगेट से अलग किया जा सकता है.

इसका इस्तेमाल, काम न करने वाले टारगेट को फ़िल्टर करने के लिए किया जा सकता है. नीचे दिए गए उदाहरण में, सिर्फ़ उन टारगेट के लेबल प्रिंट किए जाएंगे जो इसके साथ काम करते हैं. काम न करने वाले टारगेट प्रिंट नहीं किए जाते.

$ cat example.cquery

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


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

आम तौर पर होने वाली समस्याएं

असंगत टारगेट दिखने से जुड़ी पाबंदियों को अनदेखा करते हैं.