बेज़ेल ट्यूटोरियल: 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. फ़ाइल फ़ोल्डर की डायरेक्ट्री के रूट में .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.

target bazel build --config=clang_config //main:hello-world के साथ, Bazel आपके कस्टम टूलचेन का इस्तेमाल cc_toolchain_suite //toolchain:clang_suite के साथ करता है. यह सुइट अलग-अलग सीपीयू की सूची अलग-अलग सीपीयू के लिए बना सकता है. इसलिए, फ़्लैग --cpu=k8 से इसे अलग दिखाया गया है.

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

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

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

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

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

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

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

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

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

    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'
    

    बैज़ल ने पता लगाया कि --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 नियम का इस्तेमाल करने के लिए, पैकेज स्टेटमेंट के ठीक नीचे 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 में कोड बनाने की कोशिश करने के लिए काफ़ी जानकारी मौजूद है, लेकिन इसे अब भी पता नहीं है कि बिल्ड की ज़रूरी कार्रवाइयों को पूरा करने के लिए कौनसे टूल का इस्तेमाल किया जाना चाहिए. आप बाज़ल को यह बताने के लिए स्टारलार्क नियम लागू करेंगे कि किन टूल का इस्तेमाल करना है. इसके लिए, आपको@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl से टूल_पाथ() कंस्ट्रक्टर की ज़रूरत होगी:

    # 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++ टूलचेन कॉन्फ़िगरेशन देखें