इस ट्यूटोरियल में, किसी प्रोजेक्ट के लिए 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"], )--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 की मदद से बनाते हैं, तो Bazel,
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/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(और--compilerकी वैल्यू भी, अगर तय की गई हो) की वैल्यू कोcc_toolchainपर अपने-आप मैप करता है. आपने अब तक कोई भीcc_toolchainटारगेट तय नहीं किया है. Bazel जल्द ही इसकी शिकायत करेगा.बिल्ड को फिर से चलाएं. Bazel यह गड़बड़ी दिखाता है:
Rule '//toolchain:k8_toolchain' does not existअब आपको
cc_toolchainटारगेट तय करने होंगे, जोcc_toolchain_suite.toolchainsएट्रिब्यूट में मौजूद हर वैल्यू के लिए हैं.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 के पास कोड बनाने की कोशिश करने के लिए ज़रूरी जानकारी है. हालांकि, उसे अब भी यह नहीं पता कि ज़रूरी बिल्ड ऐक्शन पूरे करने के लिए किन टूल का इस्तेमाल करना है. Bazel को यह बताने के लिए कि किन टूल का इस्तेमाल करना है, Starlark नियम के लागू करने के तरीके में बदलाव करें. इसके लिए, आपको
@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 फ़्लैग तय करना होगा. यह cc_toolchain_suite की ओर इशारा करना चाहिए
- .bazelrc फ़ाइल का इस्तेमाल करके, किसी खास कॉन्फ़िगरेशन के लिए शॉर्टकट बनाया जा सकता है
- cc_toolchain_suite में, अलग-अलग सीपीयू और कंपाइलर के लिए cc_toolchains की सूची हो सकती है. इन्हें अलग करने के लिए, --cpu जैसे कमांड लाइन फ़्लैग का इस्तेमाल किया जा सकता है.
- आपको टूलचेन को यह बताना होगा कि टूल कहां मौजूद हैं. इस ट्यूटोरियल में,
एक आसान वर्शन दिया गया है. इसमें सिस्टम से टूल ऐक्सेस किए जाते हैं. अगर आपको ज़्यादा सेल्फ-कंटेन्ड अप्रोच में दिलचस्पी है, तो यहां वर्कस्पेस के बारे में पढ़ें. आपके टूल, किसी दूसरे वर्कस्पेस से आ सकते हैं. ऐसे में, आपको उनके फ़ाइलें, cc_toolchain के लिए उपलब्ध करानी होंगी. इसके लिए, एट्रिब्यूट पर टारगेट डिपेंडेंसी का इस्तेमाल करना होगा. जैसे, compiler_files. tool_paths में भी बदलाव करना होगा.
- अलग-अलग कार्रवाइयों के लिए, कौनसे फ़्लैग पास किए जाने चाहिए, यह तय करने के लिए सुविधाएं बनाई जा सकती हैं. ये कार्रवाइयां, लिंकिंग या किसी अन्य तरह की कार्रवाई हो सकती हैं.
इस बारे में और पढ़ें
ज़्यादा जानकारी के लिए, C++ टूलचेन का कॉन्फ़िगरेशन देखें