Bazel ट्यूटोरियल: C++ टूलचेन कॉन्फ़िगर करें

इस ट्यूटोरियल में उदाहरण के तौर पर यह बताया गया है कि किसी प्रोजेक्ट के लिए 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 का इस्तेमाल करता है, जिसे अपने सिस्टम पर इंस्टॉल किया जा सकता है.

अपने बिल्ड एनवायरमेंट को इस तरह सेट अप करें:

  1. अगर आपने पहले से ऐसा नहीं किया है, तो Bzel 0.23 या इसके बाद का वर्शन डाउनलोड करके इंस्टॉल करें.

  2. GitHub से C++ प्रोजेक्ट का उदाहरण डाउनलोड करें और उसे अपनी लोकल मशीन पर किसी खाली डायरेक्ट्री में रखें.

  3. main/BUILD फ़ाइल में, यह cc_binary टारगेट जोड़ें:

    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    
  4. --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++ टूलचेन को कॉन्फ़िगर करने के लिए, बार-बार ऐप्लिकेशन बनाएं और जैसा कि नीचे बताया गया है, हर गड़बड़ी को एक-एक करके खत्म करें.

  1. नीचे दिए गए निर्देश के साथ बिल्ड चलाएं:

    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 फ़ाइल बनाएं.

  2. बिल्ड को फिर से चलाएं. 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")
    
  3. बिल्ड को फिर से चलाएं. 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 जल्द ही शिकायत करेगा.

  4. बिल्ड को फिर से चलाएं. 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,
    )
    
  5. बिल्ड को फिर से चलाएं. Bazel यह गड़बड़ी दिखाता है:

    Rule '//toolchain:k8_toolchain_config' does not exist
    

    इसके बाद, toolchain/BUILD फ़ाइल में ":k8_toolchain_config" टारगेट जोड़ें:

    filegroup(name = "k8_toolchain_config")
    
  6. बिल्ड को फिर से चलाएं. Bazel यह गड़बड़ी दिखाता है:

    '//toolchain:k8_toolchain_config' does not have mandatory providers:
    'CcToolchainConfigInfo'
    

    CcToolchainConfigInfo एक सेवा देने वाली कंपनी है, जिसका इस्तेमाल आप अपने C++ टूलचेन को कॉन्फ़िगर करने के लिए करते हैं. इस गड़बड़ी को ठीक करने के लिए, Starlark नियम बनाएं. इस नियम के तहत, इस कॉन्टेंट वाली toolchain/cc_toolchain_config.bzl फ़ाइल बनाकर, Bazel को CcToolchainConfigInfo मिलेगा:

    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 नियम का इस्तेमाल करने के लिए, toolchains/BUILD में लोड स्टेटमेंट जोड़ें:

    load(":cc_toolchain_config.bzl", "cc_toolchain_config")
    

    इसके अलावा, "k8_toolchain_config" फ़ाइल ग्रुप को cc_toolchain_config नियम के एलान से बदलें:

    cc_toolchain_config(name = "k8_toolchain_config")
    
  7. बिल्ड को फिर से चलाएं. 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 से टूल_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 आपके सिस्टम के लिए सही पाथ हैं.

  8. बिल्ड को फिर से चलाएं. 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,
     )
    
  9. बिल्ड निर्देश को फिर से चलाएं, आपको इस तरह की गड़बड़ी दिखेगी:

    /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],
      )
    
  10. अगर आप bazel build --config=clang_config //main:hello-world चलाते हैं, तो यह आखिर में बननी चाहिए.

अपने काम की समीक्षा करें

इस ट्यूटोरियल में, आपने एक बुनियादी C++ टूलचेन को कॉन्फ़िगर करने का तरीका सीखा, लेकिन टूलचेन इस आसान उदाहरण से ज़्यादा असरदार हैं.

ज़रूरी बातें: - आपको कमांड लाइन में --crosstool_top फ़्लैग तय करना होगा, जो cc_toolchain_suite पर ले जाने वाला होना चाहिए - .bazelrc फ़ाइल का इस्तेमाल करके किसी खास कॉन्फ़िगरेशन के लिए शॉर्टकट बनाया जा सकता है - cc_toolchain_suite अलग-अलग सीपीयू और कंपाइलर के लिए cc_toolchains शामिल कर सकता है. अंतर करने के लिए, --cpu जैसे कमांड लाइन फ़्लैग का इस्तेमाल किया जा सकता है. - आपको टूलचेन को यह बताना होगा कि टूल कहां मौजूद हैं. इस ट्यूटोरियल में, एक आसान वर्शन दिया गया है, जिसमें सिस्टम से टूल ऐक्सेस किए जा सकते हैं. अगर आपको अपने काम को बेहतर तरीके से पूरा करना है, तो फ़ाइल फ़ोल्डर के बारे में यहां पढ़ें. आपके टूल किसी दूसरे फ़ाइल फ़ोल्डर से आ सकते हैं. ऐसा हो सकता है कि आपको उनकी फ़ाइलें, compiler_files जैसे एट्रिब्यूट के हिसाब से टारगेट डिपेंडेंसी के साथ cc_toolchain पर उपलब्ध करानी हों. tool_paths को भी बदलना होगा. - आपके पास यह तय करने के लिए सुविधाएं बनाने का विकल्प है कि अलग-अलग कार्रवाइयों के लिए कौनसे फ़्लैग भेजे जाएं. यह लिंक करने या दूसरी तरह की कोई कार्रवाई हो सकती है.

इसके बारे में और पढ़ें

ज़्यादा जानकारी के लिए, C++ टूलचेन कॉन्फ़िगरेशन देखें