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

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

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

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

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

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

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

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

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

पाबंदियां और प्लैटफ़ॉर्म तय करना

BUILD फ़ाइलों में constraint_setting और constraint_value नियमों का इस्तेमाल करके, प्लैटफ़ॉर्म के लिए उपलब्ध विकल्पों की जानकारी दी जाती है. constraint_setting से नया डाइमेंशन बनता है, जबकि constraint_value से किसी डाइमेंशन के लिए नई वैल्यू बनती है. ये दोनों मिलकर, किसी enum और उसकी संभावित वैल्यू को असरदार तरीके से तय करते हैं. उदाहरण के लिए, 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 की वैल्यू, होस्ट और टारगेट प्लैटफ़ॉर्म, दोनों के लिए होती है.

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

किसी खास टारगेट प्लैटफ़ॉर्म के लिए कॉन्टेंट बनाते समय, अक्सर उन टारगेट को छोड़ना बेहतर होता है जो उस प्लैटफ़ॉर्म पर कभी काम नहीं करेंगे. उदाहरण के लिए, //... के साथ Linux मशीन पर बिल्ड करते समय, आपके Windows डिवाइस ड्राइवर में कंपाइलर से जुड़ी कई गड़बड़ियां हो सकती हैं. 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

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

bazel cquery के Starlark आउटपुट फ़ॉर्मैट में 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

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

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