इस ट्यूटोरियल में, उदाहरण के तौर पर एक स्थिति का इस्तेमाल करके बताया गया है कि किसी प्रोजेक्ट के लिए C++ टूलचेन को कैसे कॉन्फ़िगर किया जाता है. यह C++ प्रोजेक्ट के उदाहरण पर आधारित है. यह प्रोजेक्ट, clang का इस्तेमाल करके बिना किसी गड़बड़ी के बनाया गया है.
आपको क्या सीखने को मिलेगा
इस ट्यूटोरियल में, आपको इनके बारे में जानकारी मिलेगी:
- बिल्ड एनवायरमेंट सेट अप करना
- C++ टूलचेन को कॉन्फ़िगर करना
- एक Starlark नियम बनाएं, जो
cc_toolchainके लिए अतिरिक्त कॉन्फ़िगरेशन उपलब्ध कराता हो, ताकि Bazel,clangकी मदद से ऐप्लिकेशन बना सके - 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"], ).bazelrcफ़्लैग का इस्तेमाल करने के लिए, वर्कस्पेस डायरेक्ट्री के रूट में.bazelrcफ़ाइल बनाएं. इस फ़ाइल में यह कॉन्टेंट शामिल करें:--config# 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 की मदद से टारगेट बनाने पर, Bazel cc_toolchain_suite
//toolchain:clang_suite से आपके कस्टम टूलचेन का इस्तेमाल करता है. सूट में अलग-अलग सीपीयू के लिए, अलग-अलग टूलचेन दिख सकती हैं. इसलिए, इसे --cpu=k8 फ़्लैग से अलग किया गया है.
Bazel, बिल्ड के दौरान C++ में लिखे गए कई इंटरनल टूल का इस्तेमाल करता है. जैसे, process-wrapper. इसलिए, होस्ट प्लैटफ़ॉर्म के लिए पहले से मौजूद डिफ़ॉल्ट C++ टूलचेन तय किया जाता है, ताकि इन टूल को इस ट्यूटोरियल में बनाए गए टूलचेन के बजाय, उस टूलचेन का इस्तेमाल करके बनाया जा सके.
C++ टूलचेन को कॉन्फ़िगर करना
C++ टूलचेन को कॉन्फ़िगर करने के लिए, ऐप्लिकेशन को बार-बार बनाएं और यहां बताए गए तरीके से, एक-एक करके हर गड़बड़ी को ठीक करें.
यहां दिए गए कमांड का इस्तेमाल करके, बिल्ड चलाएं:
bazel build --config=clang_config //main:hello-worldआपने
--crosstool_top=//toolchain:clang_suiteको.bazelrcफ़ाइल में शामिल किया है. इसलिए, 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/BUILDtoolchain/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एट्रिब्यूट की वैल्यू कोcc_toolchainएट्रिब्यूट की वैल्यू के साथ अपने-आप मैप करता है. साथ ही, अगर--compilerएट्रिब्यूट की वैल्यू दी गई है, तो उसे भी मैप करता है. आपने अब तक कोई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")फिर से बिल्ड करें. Bazel यह गड़बड़ी दिखाता है:
'//toolchain:k8_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'CcToolchainConfigInfoएक ऐसा प्रोवाइडर है जिसका इस्तेमाल C++ टूलचेन को कॉन्फ़िगर करने के लिए किया जाता है. इस गड़बड़ी को ठीक करने के लिए, एक Starlark नियम बनाएं. यह नियम, Bazel कोCcToolchainConfigInfoउपलब्ध कराता है. इसके लिए,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' ....Bazel को यह पता होना चाहिए कि शामिल किए गए हेडर कहां खोजने हैं. इस समस्या को हल करने के कई तरीके हैं. जैसे,
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 फ़्लैग तय करना होगा. यह --crosstool_top की ओर इशारा करना चाहिए
- .bazelrc फ़ाइल का इस्तेमाल करके, किसी कॉन्फ़िगरेशन के लिए शॉर्टकट बनाया जा सकता है
- cc_toolchain_suite, अलग-अलग सीपीयू और कंपाइलर के लिए cc_toolchains की सूची बना सकता है.cc_toolchain_suite अलग-अलग करने के लिए, कमांड लाइन फ़्लैग, जैसे कि --cpu का इस्तेमाल किया जा सकता है.
- आपको टूलचेन को यह बताना होगा कि टूल कहां मौजूद हैं. इस ट्यूटोरियल में, एक आसान वर्शन दिया गया है. इसमें सिस्टम से टूल ऐक्सेस किए जाते हैं. अगर आपको एक ही जगह पर सब कुछ मैनेज करने का तरीका अपनाना है, तो यहां वर्कस्पेस के बारे में पढ़ें. आपके टूल किसी दूसरे वर्कस्पेस से आ सकते हैं. ऐसे में, आपको उनकी फ़ाइलें cc_toolchain के लिए उपलब्ध करानी होंगी. साथ ही, आपको एट्रिब्यूट पर टारगेट डिपेंडेंसी सेट करनी होंगी, जैसे कि compiler_files. tool_paths को भी बदलना होगा.
- आपके पास ऐसी सुविधाएं बनाने का विकल्प होता है जिनकी मदद से यह तय किया जा सकता है कि अलग-अलग कार्रवाइयों के लिए कौनसे फ़्लैग पास किए जाने चाहिए. ये कार्रवाइयां, लिंक करने या किसी अन्य तरह की कार्रवाई से जुड़ी हो सकती हैं.
इस बारे में और पढ़ें
ज़्यादा जानकारी के लिए, C++ टूलचेन कॉन्फ़िगरेशन देखें