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

परिचय

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 को उसके लिए अपनी वैल्यू तय करके बढ़ाया जा सकता है.

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

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

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

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