परिचय
Bazel, अलग-अलग हार्डवेयर, ऑपरेटिंग सिस्टम, और सिस्टम कॉन्फ़िगरेशन पर कोड बना सकता है और उसकी जांच कर सकता है. इसमें बिल्ड टूल के अलग-अलग वर्शन शामिल हो सकते हैं. जैसे, लिंक करने वाले टूल और कंपाइलर. इस जटिलता को मैनेज करने के लिए, Bazel में constraints और platforms के कॉन्सेप्ट मौजूद हैं.
बाधा, बिल्ड या प्रोडक्शन मशीन की एक खास प्रॉपर्टी होती है. आम तौर पर, सीपीयू आर्किटेक्चर, जीपीयू की मौजूदगी या गैर-मौजूदगी या स्थानीय तौर पर इंस्टॉल किए गए कंपाइलर का वर्शन जैसी पाबंदियां होती हैं. हालांकि, कंस्ट्रेंट कुछ भी हो सकते हैं. इससे बिल्ड प्रोसेस को मैनेज करते समय, मशीनों के बीच अंतर किया जा सकता है.
प्लैटफ़ॉर्म, शर्तों का एक ऐसा कलेक्शन होता है जो पूरी मशीन के बारे में बताता है. 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 टाइप बनाना होगा.
ज़रूरी शर्तें पूरी न करने वाले टारगेट को स्किप किया जा रहा है
किसी खास टारगेट प्लैटफ़ॉर्म के लिए बिल्ड करते समय, अक्सर उन टारगेट को छोड़ना बेहतर होता है जो उस प्लैटफ़ॉर्म पर कभी काम नहीं करेंगे. उदाहरण के लिए, 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"],
}),
)
ऊपर दी गई जानकारी को इस तरह समझा जा सकता है:
- macOS को टारगेट करते समय, टारगेट पर कोई पाबंदी नहीं होती.
- Linux को टारगेट करते समय, टारगेट पर कोई पाबंदी नहीं होती.
- ऐसा न होने पर, टारगेट पर
@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
आम तौर पर होने वाली समस्याएं
असंगत टारगेट, दिखने से जुड़ी पाबंदियों को अनदेखा करते हैं.