इस ट्यूटोरियल में, उदाहरण के तौर पर एक स्थिति का इस्तेमाल करके बताया गया है कि किसी प्रोजेक्ट के लिए 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/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
एट्रिब्यूट की वैल्यू को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++ टूलचेन कॉन्फ़िगरेशन देखें