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

परिचय

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

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

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

डेवलपर, अपनी बिल्ड के नियमों के लिए, कस्टम प्रॉपर्टी या डिपेंडेंसी चुनने के लिए भी शर्तों का इस्तेमाल कर सकते हैं. उदाहरण के लिए: "Arm मशीन को टारगेट करने पर, का इस्तेमाल करेंsrc_arm.cc".

प्लैटफ़ॉर्म के टाइप

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

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

आम तौर पर, बिल्ड का प्लैटफ़ॉर्म से तीन तरह का संबंध होता है:

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

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

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

प्लैटफ़ॉर्म तय करना

डेवलपर, प्लैटफ़ॉर्म का इस्तेमाल आम तौर पर --platforms फ़्लैग के साथ, टारगेट की जाने वाली मशीनों को तय करने के लिए करते हैं:

$ bazel build //:my_linux_app --platforms=//myplatforms:linux_x86

आम तौर पर, संगठन अपने प्लैटफ़ॉर्म की परिभाषाएं खुद तय करते हैं, क्योंकि अलग-अलग संगठनों में बिल्ड मशीन सेटअप अलग-अलग होते हैं.

जब --platforms सेट नहीं होता है, तो यह डिफ़ॉल्ट रूप से @platforms//host पर सेट हो जाता है. इसे खास तौर पर, होस्ट मशीन के ओएस और सीपीयू की प्रॉपर्टी का पता लगाने के लिए तय किया जाता है, ताकि बिल्ड उसी मशीन को टारगेट करें जिस पर Bazel चलता है. बिल्ड के नियम, चुन सकते हैं इन प्रॉपर्टी को @platforms/os और @platforms/cpu की शर्तों के साथ.

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

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

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

शर्तें तय करना

शर्तों को constraint_setting और constraint_value बिल्ड के नियमों के साथ मॉडल किया जाता है.

constraint_setting, प्रॉपर्टी के टाइप का एलान करता है. उदाहरण के लिए:

constraint_setting(name = "cpu")

constraint_value, उस प्रॉपर्टी के लिए संभावित वैल्यू का एलान करता है:

constraint_value(
    name = "x86",
    constraint_setting = ":cpu"
)

प्लैटफ़ॉर्म तय करते समय या उन पर बिल्ड के नियमों को पसंद के मुताबिक बनाते समय, इन्हें लेबल के तौर पर रेफ़र किया जा सकता है. अगर ऊपर दिए गए उदाहरण, cpus/BUILD में तय किए गए हैं, तो शर्त को x86 के तौर पर रेफ़र किया जा सकता है.//cpus:x86

अगर विज़िबिलिटी की अनुमति है, तो constraint_setting के लिए अपनी वैल्यू तय करके, मौजूदा constraint_setting को बढ़ाया जा सकता है.

प्लैटफ़ॉर्म तय करना

platform बिल्ड का नियम प्लैटफ़ॉर्म को constraint_value के कलेक्शन के तौर पर तय करता है:

platform(
    name = "linux_x86",
    constraint_values = [
        "//oses:linux",
        "//cpus:x86",
    ],
)

इससे, ऐसी मशीन का मॉडल बनता है जिसमें //oses:linux और //cpus:x86, दोनों शर्तें होनी चाहिए.

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

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

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

ज़्यादा जटिल पाबंदियां तय करने के लिए, @platforms//:incompatible के साथ select() का इस्तेमाल करें. उदाहरण के लिए, इसका इस्तेमाल बुनियादी 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 आउटपुट फ़ॉर्मैट में, 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

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

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