इस ट्यूटोरियल में, किसी प्रोजेक्ट के लिए C++ टूलचेन को कॉन्फ़िगर करने का तरीका बताने के लिए, एक उदाहरण का इस्तेमाल किया गया है. यह clang
का इस्तेमाल करके, गड़बड़ी के बिना बने उदाहरण के तौर पर दिए गए C++ प्रोजेक्ट पर आधारित है.
आपको क्या सीखने को मिलेगा
इस ट्यूटोरियल में, आपको ये काम करने का तरीका पता चलेगा:
- बिल्ड एनवायरमेंट सेट अप करना
- C++ टूलचेन को कॉन्फ़िगर करना
- Starlark नियम बनाएं, जो
cc_toolchain
के लिए अतिरिक्त कॉन्फ़िगरेशन उपलब्ध कराता है, ताकि Bazelclang
की मदद से ऐप्लिकेशन बना सके - Linux मशीन पर
bazel build --config=clang_config //main:hello-world
को चलाकर, उम्मीद के मुताबिक नतीजे की पुष्टि करना - C++ ऐप्लिकेशन बनाएं
शुरू करने से पहले
बिल्ड एनवायरमेंट को सेट अप करें
इस ट्यूटोरियल में यह मान लिया गया है कि आप Linux का इस्तेमाल कर रहे हैं और आपने C++ ऐप्लिकेशन बना लिए हैं और ज़रूरी टूल और लाइब्रेरी इंस्टॉल कर ली हैं.
ट्यूटोरियल में clang version 9.0.1
का इस्तेमाल किया गया है, जिसे अपने सिस्टम पर इंस्टॉल किया जा सकता है.
अपना बिल्ड एनवायरमेंट इस तरह सेट अप करें:
अगर आपने पहले से ऐसा नहीं किया है, तो Bazel 0.23 या उसके बाद के वर्शन को डाउनलोड और इंस्टॉल करें.
GitHub से C++ प्रोजेक्ट का उदाहरण डाउनलोड करें और उसे अपने कंप्यूटर पर मौजूद खाली डायरेक्ट्री में रखें.
main/BUILD
फ़ाइल में, यहcc_binary
टारगेट जोड़ें:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
--config
फ़्लैग का इस्तेमाल चालू करने के लिए, यहां दिए गए कॉन्टेंट के साथ वर्कस्पेस डायरेक्ट्री के रूट में एक.bazelrc
फ़ाइल बनाएं:# Use our custom-configured c++ toolchain. build:clang_config --crosstool_top=//toolchain:clang_suite # Use --cpu as a differentiator. build:clang_config --cpu=k8 # Use the default Bazel C++ toolchain to build the tools used during the # build. build:clang_config --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
एंट्री build:{config_name} --flag=value
के लिए, कमांड लाइन फ़्लैग
--config={config_name}
उस खास फ़्लैग से जुड़ा होता है. इस्तेमाल किए गए फ़्लैग के लिए दस्तावेज़ देखें:
crosstool_top
,
cpu
, और
host_crosstool_top
.
जब bazel build --config=clang_config //main:hello-world
के साथ अपना टारगेट
बनाया जाता है, तो Basel, cc_toolchain_suite
//toolchain:clang_suite
के आपके कस्टम टूलचेन का इस्तेमाल
करता है. सुइट में अलग-अलग सीपीयू के लिए, अलग-अलग टूलचेन की सूची हो सकती है. इसलिए, इसे फ़्लैग --cpu=k8
के साथ अलग किया जाता है.
Bazel, बिल्ड के दौरान C++ में लिखे गए कई इंटरनल टूल का इस्तेमाल करता है. जैसे, प्रोसेस-रैपर. इसलिए, होस्ट प्लैटफ़ॉर्म के लिए पहले से मौजूद डिफ़ॉल्ट C++ टूलचेन तय किया जाता है, ताकि इन टूल को इस ट्यूटोरियल में बनाए गए टूलचेन के बजाय उस टूलचेन का इस्तेमाल करके बनाया जा सके.
C++ टूलचेन को कॉन्फ़िगर करना
C++ टूलचेन को कॉन्फ़िगर करने के लिए, ऐप्लिकेशन को बार-बार बनाएं और यहां बताए गए तरीके से हर गड़बड़ी को एक-एक करके ठीक करें.
इस कमांड का इस्तेमाल करके बिल्ड चलाएं:
bazel build --config=clang_config //main:hello-world
आपने
.bazelrc
फ़ाइल में--crosstool_top=//toolchain:clang_suite
तय किया है, इसलिए Bazel यह गड़बड़ी दिखाता है:No such package `toolchain`: BUILD file not found on package path.
वर्कस्पेस डायरेक्ट्री में, पैकेज के लिए
toolchain
डायरेक्ट्री औरtoolchain
डायरेक्ट्री में मौजूद एक खालीBUILD
फ़ाइल बनाएं.बिल्ड को फिर से चलाएं.
toolchain
पैकेज में अब तकclang_suite
टारगेट तय नहीं किया गया है. इसलिए, Bazel यह गड़बड़ी दिखाता है:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILD
toolchain/BUILD
फ़ाइल में, खाली फ़ाइल ग्रुप को इस तरह सेट करें:package(default_visibility = ["//visibility:public"]) filegroup(name = "clang_suite")
बिल्ड को फिर से चलाएं. Bazel, यह गड़बड़ी दिखाता है:
'//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'
Bazel को पता चला है कि
--crosstool_top
फ़्लैग, ऐसे नियम पर ले जाता है जो ज़रूरीToolchainInfo
प्रोवाइडर नहीं देता. इसलिए, आपको--crosstool_top
को उस नियम पर ले जाना होगा जोToolchainInfo
उपलब्ध कराता है. यहcc_toolchain_suite
नियम है.toolchain/BUILD
फ़ाइल में, खाली फ़ाइल ग्रुप को इनसे बदलें:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )
toolchains
एट्रिब्यूट,--cpu
(और तय होने पर--compiler
) की वैल्यू कोcc_toolchain
पर अपने-आप मैप करता है. आपने अब तक कोईcc_toolchain
टारगेट तय नहीं किया है और Bazel इसकी शिकायत जल्द ही करेगा.बिल्ड को फिर से चलाएं. Bazel, यह गड़बड़ी दिखाता है:
Rule '//toolchain:k8_toolchain' does not exist
अब आपको
cc_toolchain_suite.toolchains
एट्रिब्यूट की हर वैल्यू के लिए,cc_toolchain
टारगेट तय करने होंगे.toolchain/BUILD
फ़ाइल में ये चीज़ें जोड़ें:filegroup(name = "empty") cc_toolchain( name = "k8_toolchain", toolchain_identifier = "k8-toolchain", toolchain_config = ":k8_toolchain_config", all_files = ":empty", compiler_files = ":empty", dwp_files = ":empty", linker_files = ":empty", objcopy_files = ":empty", strip_files = ":empty", supports_param_files = 0, )
बिल्ड को फिर से चलाएं. Bazel, यह गड़बड़ी दिखाता है:
Rule '//toolchain:k8_toolchain_config' does not exist
इसके बाद,
toolchain/BUILD
फ़ाइल में ":k8_toolchain_config" टारगेट जोड़ें:filegroup(name = "k8_toolchain_config")
बिल्ड को फिर से चलाएं. बेज़ल यह गड़बड़ी दिखाता है:
'//toolchain:k8_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'
CcToolchainConfigInfo
एक सेवा देने वाली कंपनी है. इसका इस्तेमाल, C++ टूलचेन को कॉन्फ़िगर करने के लिए किया जाता है. इस गड़बड़ी को ठीक करने के लिए, Starlark नियम बनाएं. यह नियम,CcToolchainConfigInfo
को Bazel को उपलब्ध कराता है. इसके लिए, यहां दिए गए कॉन्टेंट वालीtoolchain/cc_toolchain_config.bzl
फ़ाइल बनाएं:def _impl(ctx): return cc_common.create_cc_toolchain_config_info( ctx = ctx, toolchain_identifier = "k8-toolchain", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", ) cc_toolchain_config = rule( implementation = _impl, attrs = {}, provides = [CcToolchainConfigInfo], )
cc_common.create_cc_toolchain_config_info()
ज़रूरी प्रोवाइडर बनाता हैCcToolchainConfigInfo
.cc_toolchain_config
नियम का इस्तेमाल करने के लिए, पैकेज स्टेटमेंट के ठीक नीचेtoolchain/BUILD
में लोड स्टेटमेंट जोड़ें:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
साथ ही, "k8_toolchain_config" फ़ाइल ग्रुप को
cc_toolchain_config
नियम के एलान से बदलें:cc_toolchain_config(name = "k8_toolchain_config")
बिल्ड को फिर से चलाएं. Bazel, यह गड़बड़ी दिखाता है:
.../BUILD:1:1: C++ compilation of rule '//:hello-world' failed (Exit 1) src/main/tools/linux-sandbox-pid1.cc:421: "execvp(toolchain/DUMMY_GCC_TOOL, 0x11f20e0)": No such file or directory Target //:hello-world failed to build`
इस समय, Bazel के पास कोड बनाने की कोशिश करने के लिए ज़रूरी जानकारी है. हालांकि, उसे अब भी यह नहीं पता है कि ज़रूरी बिल्ड ऐक्शन को पूरा करने के लिए, किन टूल का इस्तेमाल करना है. आपको Starlark नियम को लागू करने के तरीके में बदलाव करना होगा, ताकि Bazel को यह बताया जा सके कि किन टूल का इस्तेमाल करना है. इसके लिए, आपको
@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl
से tool_path() कन्स्ट्रक्टर की ज़रूरत होगी:# toolchain/cc_toolchain_config.bzl: # NEW load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "tool_path") def _impl(ctx): tool_paths = [ # NEW tool_path( name = "gcc", path = "/usr/bin/clang", ), tool_path( name = "ld", path = "/usr/bin/ld", ), tool_path( name = "ar", path = "/usr/bin/ar", ), tool_path( name = "cpp", path = "/bin/false", ), tool_path( name = "gcov", path = "/bin/false", ), tool_path( name = "nm", path = "/bin/false", ), tool_path( name = "objdump", path = "/bin/false", ), tool_path( name = "strip", path = "/bin/false", ), ] return cc_common.create_cc_toolchain_config_info( ctx = ctx, toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, # NEW )
पक्का करें कि
/usr/bin/clang
और/usr/bin/ld
आपके सिस्टम के लिए सही पाथ हैं.बिल्ड को फिर से चलाएं. Bazel, यह गड़बड़ी दिखाता है:
..../BUILD:3:1: undeclared inclusion(s) in rule '//main:hello-world': this rule is missing dependency declarations for the following files included by 'main/hello-world.cc': '/usr/include/c++/9/ctime' '/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h' '/usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h' ....
Basel को यह जानने की ज़रूरत है कि शामिल किए गए हेडर को कहां खोजना है. इस समस्या को हल करने के कई तरीके हैं. जैसे,
cc_binary
केincludes
एट्रिब्यूट का इस्तेमाल करना. हालांकि, यहांcc_common.create_cc_toolchain_config_info
केcxx_builtin_include_directories
पैरामीटर की मदद से, टूलचेन लेवल पर इस समस्या को हल किया गया है. ध्यान रखें कि अगरclang
के किसी दूसरे वर्शन का इस्तेमाल किया जा रहा है, तो शामिल करने का पाथ अलग होगा. डिस्ट्रिब्यूशन के हिसाब से, ये पाथ अलग-अलग भी हो सकते हैं.toolchain/cc_toolchain_config.bzl
की रिटर्न वैल्यू में बदलाव करें, ताकि वह कुछ ऐसा दिखे:return cc_common.create_cc_toolchain_config_info( ctx = ctx, cxx_builtin_include_directories = [ # NEW "/usr/lib/llvm-9/lib/clang/9.0.1/include", "/usr/include", ], toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, )
'बिल्ड करें' निर्देश को फिर से चलाएं. आपको इस तरह की गड़बड़ी दिखेगी:
/usr/bin/ld: bazel-out/k8-fastbuild/bin/main/_objs/hello-world/hello-world.o: in function `print_localtime()': hello-world.cc:(.text+0x68): undefined reference to `std::cout'
इसकी वजह यह है कि लिंकर में C++ स्टैंडर्ड लाइब्रेरी मौजूद नहीं है और उसे इसके सिंबल नहीं मिल रहे हैं. इसे ठीक करने के कई तरीके हैं, जैसे कि
cc_binary
केlinkopts
एट्रिब्यूट का इस्तेमाल करना. यहां इस समस्या को हल करने के लिए, यह पक्का किया गया है कि टूलचेन का इस्तेमाल करने वाले किसी भी टारगेट को यह फ़्लैग बताने की ज़रूरत नहीं है.नीचे दिए गए कोड को
cc_toolchain_config.bzl
में कॉपी करें:# NEW load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") # NEW load( "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag_set", "tool_path", ) all_link_actions = [ # NEW ACTION_NAMES.cpp_link_executable, ACTION_NAMES.cpp_link_dynamic_library, ACTION_NAMES.cpp_link_nodeps_dynamic_library, ] def _impl(ctx): tool_paths = [ tool_path( name = "gcc", path = "/usr/bin/clang", ), tool_path( name = "ld", path = "/usr/bin/ld", ), tool_path( name = "ar", path = "/bin/false", ), tool_path( name = "cpp", path = "/bin/false", ), tool_path( name = "gcov", path = "/bin/false", ), tool_path( name = "nm", path = "/bin/false", ), tool_path( name = "objdump", path = "/bin/false", ), tool_path( name = "strip", path = "/bin/false", ), ] features = [ # NEW feature( name = "default_linker_flags", enabled = True, flag_sets = [ flag_set( actions = all_link_actions, flag_groups = ([ flag_group( flags = [ "-lstdc++", ], ), ]), ), ], ), ] return cc_common.create_cc_toolchain_config_info( ctx = ctx, features = features, # NEW cxx_builtin_include_directories = [ "/usr/lib/llvm-9/lib/clang/9.0.1/include", "/usr/include", ], toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, ) cc_toolchain_config = rule( implementation = _impl, attrs = {}, provides = [CcToolchainConfigInfo], )
अगर
bazel build --config=clang_config //main:hello-world
चलाया जाता है, तो यह पूरी तरह से तैयार होना चाहिए.
अपने काम की समीक्षा करना
इस ट्यूटोरियल में, आपको C++ टूलचेन को कॉन्फ़िगर करने का तरीका बताया गया है. हालांकि, टूलचेन इस आसान उदाहरण से ज़्यादा बेहतर होते हैं.
अहम जानकारी:
- आपको कमांड लाइन में --crosstool_top
फ़्लैग की जानकारी देनी होगी, जो cc_toolchain_suite
पर ले जाना चाहिए
- .bazelrc
फ़ाइल का इस्तेमाल करके, किसी खास कॉन्फ़िगरेशन के लिए शॉर्टकट बनाया जा सकता है
- cc_toolchain_suite, अलग-अलग सीपीयू और compilers के लिए cc_toolchains
की सूची बना सकता है. अंतर करने के लिए, --cpu
जैसे कमांड लाइन फ़्लैग का इस्तेमाल किया जा सकता है.
- आपको टूलचेन को यह बताना होगा कि टूल कहां उपलब्ध हैं. इस ट्यूटोरियल में, सिस्टम से टूल ऐक्सेस करने का आसान तरीका बताया गया है. अगर आपको ज़्यादा बेहतर तरीके से काम करना है, तो यहां वर्कस्पेस के बारे में पढ़ें. आपके टूल किसी दूसरे वर्कस्पेस से हो सकते हैं. ऐसे में, आपको उनकी फ़ाइलों को cc_toolchain
के लिए उपलब्ध कराना होगा. साथ ही, compiler_files
जैसे एट्रिब्यूट पर टारगेट डिपेंडेंसी भी सेट करनी होगी. tool_paths
को भी बदलना होगा.
- आपके पास यह तय करने की सुविधा है कि अलग-अलग कार्रवाइयों के लिए कौनसे फ़्लैग भेजे जाने चाहिए.
जैसे, लिंक करना या कोई दूसरी कार्रवाई करना.
इसके बारे में और पढ़ें
ज़्यादा जानकारी के लिए, C++ टूलचेन कॉन्फ़िगरेशन पर जाएं