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

7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

आपको क्या सीखने को मिलेगा

इस ट्यूटोरियल में, आपको ये काम करने का तरीका पता चलेगा:

  • बिल्ड एनवायरमेंट को सेट अप करें
  • C++ टूलचेन को कॉन्फ़िगर करना
  • Starlark का ऐसा नियम बनाएं जो cc_toolchain के लिए अतिरिक्त कॉन्फ़िगरेशन उपलब्ध कराता हो, ताकि Ba हटाना, clang का इस्तेमाल करके ऐप्लिकेशन बना सके
  • Linux मशीन पर bazel build --config=clang_config //main:hello-world चलाकर, अनुमानित नतीजों की पुष्टि करें
  • C++ ऐप्लिकेशन बनाना

शुरू करने से पहले

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

इस ट्यूटोरियल में यह मान लिया गया है कि आप Linux का इस्तेमाल कर रहे हैं और आपने C++ ऐप्लिकेशन बना लिए हैं और ज़रूरी टूल और लाइब्रेरी इंस्टॉल कर ली हैं. ट्यूटोरियल में clang version 9.0.1 का इस्तेमाल किया गया है, जिसे अपने सिस्टम पर इंस्टॉल किया जा सकता है.

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

  1. अगर आपने पहले से ऐसा नहीं किया है, तो Bazel 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 फ़्लैग के हिसाब से अलग-अलग दिखाया जाता है.

Baज़ल, बिल्ड के दौरान C++ में लिखे कई इंटरनल टूल, जैसे कि प्रोसेस-रैपर का इस्तेमाल करता है. इसलिए, होस्ट प्लैटफ़ॉर्म के लिए पहले से मौजूद डिफ़ॉल्ट C++ टूलचेन के बारे में बताया जाता है. इससे, ये टूल इस ट्यूटोरियल में बनाए गए टूलचेन के बजाय, उस टूलचेन का इस्तेमाल करके बनाए जाते हैं.

C++ टूलचेन को कॉन्फ़िगर करना

C++ टूलचेन को कॉन्फ़िगर करने के लिए, ऐप्लिकेशन को बार-बार बनाएं और यहां बताए गए तरीके से हर गड़बड़ी को एक-एक करके ठीक करें.

  1. इस कमांड का इस्तेमाल करके बिल्ड चलाएं:

    bazel build --config=clang_config //main:hello-world
    

    आपने .bazelrc फ़ाइल में --crosstool_top=//toolchain:clang_suite को शामिल किया है, इसलिए Basel को यह गड़बड़ी दिखती है:

    No such package `toolchain`: BUILD file not found on package path.
    

    फ़ाइल फ़ोल्डर डायरेक्ट्री में, पैकेज के लिए toolchain डायरेक्ट्री बनाएं और toolchain डायरेक्ट्री में एक खाली BUILD फ़ाइल बनाएं.

  2. बिल्ड को फिर से चलाएं. toolchain पैकेज, अब तक clang_suite टारगेट को तय नहीं करता है. इसलिए, Basel को यह गड़बड़ी दिखती है:

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

    '//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'
    

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

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

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

    CcToolchainConfigInfo सेवा देने वाली कंपनी है. इसका इस्तेमाल C++ टूलचेन कॉन्फ़िगर करने के लिए किया जाता है. इस गड़बड़ी को ठीक करने के लिए, Starlark का नियम बनाएं. यह नियम इस कॉन्टेंट वाली toolchain/cc_toolchain_config.bzl फ़ाइल बनाकर, Basel को 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`
    

    इस समय, Basel के पास कोड बनाने की कोशिश करने के लिए काफ़ी जानकारी है, लेकिन उसे अब भी यह नहीं पता है कि ज़रूरी बिल्ड ऐक्शन को पूरा करने के लिए कौनसे टूल इस्तेमाल करने चाहिए. बेज़ेल को यह बताने के लिए कि कौनसे टूल इस्तेमाल करने हैं, आपको 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 आपके सिस्टम के लिए सही पाथ हों.

  8. बिल्ड को फिर से चलाएं. बेज़ल यह गड़बड़ी दिखाता है:

     ..../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,
     )
    
  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++ टूलचेन कॉन्फ़िगरेशन पर जाएं