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

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

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

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

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

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

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

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

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

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

प्लैटफ़ॉर्म के लिए चुने जा सकने वाले विकल्पों की जगह, constraint_setting और constraint_value नियमों का इस्तेमाल करके BUILD फ़ाइलों में तय की जाती है. 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 के तौर पर भी जाना जाता है. यह एक ऐसे रेपो नियम पर आधारित है जो होस्ट ओएस और सीपीयू का पता लगाता है और प्लैटफ़ॉर्म टारगेट लिखता है.
    • @platforms//host:constraints.bzl भी मौजूद है. यह HOST_CONSTRAINTS नाम का एक अरे दिखाता है. इसका इस्तेमाल अन्य BUILD और Starlark फ़ाइलों में किया जा सकता है.
  • --platforms - डिफ़ॉल्ट रूप से होस्ट प्लैटफ़ॉर्म पर सेट होता है
    • इसका मतलब है कि जब कोई अन्य फ़्लैग सेट नहीं किया जाता है, तो @platforms//host टारगेट प्लैटफ़ॉर्म होता है.
    • अगर --host_platform सेट है और --platforms नहीं, तो --host_platform की वैल्यू, होस्ट और टारगेट प्लैटफ़ॉर्म दोनों होती है.

मेल न खाने वाले टारगेट छोड़ना

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

इस एट्रिब्यूट का सबसे आसान इस्तेमाल, किसी टारगेट को एक प्लैटफ़ॉर्म तक सीमित करता है. जिस प्लैटफ़ॉर्म पर सभी कंस्ट्रेंट पूरे नहीं होते उसके लिए टारगेट बिल्ड नहीं किया जाएगा. यहां दिए गए उदाहरण में, 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

test_suite में मेल न खाने वाले टेस्ट भी इसी तरह छोड़ दिए जाते हैं. ऐसा तब होता है, जब कमांड लाइन पर test_suite के साथ --expand_test_suites तय किया जाता है. दूसरे शब्दों में, कमांड लाइन पर 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's 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 का इस्तेमाल करके, मेल न खाने वाले टारगेट का पता लगाना

मेल न खाने वाले टारगेट को, काम करने वाले टारगेट से अलग करने के लिए, bazel cquery's Starlark output format में IncompatiblePlatformProvider का इस्तेमाल किया जा सकता है.

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

$ cat example.cquery

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


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

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

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