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

समस्या की शिकायत करें सोर्स देखें Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

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

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

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

  1. यहां दिए गए कमांड का इस्तेमाल करके, बिल्ड चलाएं:

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

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

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

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