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

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 7.4 .

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

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

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

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

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

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

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

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

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

  2. रूट फ़ोल्डर में कोई खाली MODULE.bazel फ़ाइल जोड़ें.

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

    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    

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

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

    bazel build //main:hello-world
    

    MODULE.bazel में रजिस्टर किए गए किसी भी टूलचेन के बिना, बिल्ड सफल हो जाता है.

    यह देखने के लिए कि आगे क्या है, चलाएं:

    bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type'
    
    INFO: ToolchainResolution: Target platform @@platforms//host:host: Selected execution platform @@platforms//host:host, type @@bazel_tools//tools/cpp:toolchain_type -> toolchain @@bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8
    

    --platforms तय किए बिना, Bazel @bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8 का इस्तेमाल करके @platforms//host के लिए टारगेट बनाता है.

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

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

इसमें clang version 9.0.1 का भी अनुमान लगाया जाता है. हालांकि, clang के अलग-अलग वर्शन के बीच जानकारी में ज़्यादा बदलाव नहीं होना चाहिए.

  1. toolchain/BUILD को

    filegroup(name = "empty")
    
    cc_toolchain(
        name = "linux_x86_64_toolchain",
        toolchain_identifier = "linux_x86_64-toolchain",
        toolchain_config = ":linux_x86_64_toolchain_config",
        all_files = ":empty",
        compiler_files = ":empty",
        dwp_files = ":empty",
        linker_files = ":empty",
        objcopy_files = ":empty",
        strip_files = ":empty",
        supports_param_files = 0,
    )
    
    toolchain(
        name = "cc_toolchain_for_linux_x86_64",
        toolchain = ":linux_x86_64_toolchain",
        toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
        exec_compatible_with = [
            "@platforms//cpu:x86_64",
            "@platforms//os:linux",
        ],
        target_compatible_with = [
            "@platforms//cpu:x86_64",
            "@platforms//os:linux",
        ],
    )
    

    इसके बाद, ज़रूरी डिपेंडेंसी जोड़ें और टूलचेन को MODULE.bazel के साथ रजिस्टर करें

    bazel_dep(name = "platforms", version = "0.0.10")
    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64"
    )
    

    इस चरण में, cc_toolchain तय किया जाता है और इसे होस्ट कॉन्फ़िगरेशन के लिए toolchain टारगेट से जोड़ा जाता है.

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

    ERROR: toolchain/BUILD:4:13: in toolchain_config attribute of cc_toolchain rule //toolchain:linux_x86_64_toolchain: rule '//toolchain:linux_x86_64_toolchain_config' does not exist.
    
  3. toolchain/BUILD फ़ाइल में, खाली फ़ाइल ग्रुप को इस तरह सेट करें:

    package(default_visibility = ["//visibility:public"])
    
    filegroup(name = "linux_x86_64_toolchain_config")
    
  4. बिल्ड को फिर से चलाएं. Bazel, यह गड़बड़ी दिखाता है:

    '//toolchain:linux_x86_64_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'.
    

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

    साथ ही, "linux_x86_64_toolchain_config" फ़ाइल ग्रुप को cc_toolchain_config नियम के एलान से बदलें:

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

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

    ERROR: main/BUILD:3:10: Compiling main/hello-world.cc failed: absolute path inclusion(s) found in rule '//main:hello-world':
    the source file 'main/hello-world.cc' includes the following non-builtin files with absolute paths (if these are builtin files, make sure these paths are in your toolchain):
      '/usr/include/c++/13/ctime'
      '/usr/include/x86_64-linux-gnu/c++/13/bits/c++config.h'
      '/usr/include/x86_64-linux-gnu/c++/13/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-16/lib/clang/16/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,
    )
    
  7. 'बिल्ड करें' निर्देश को फिर से चलाएं. आपको इस तरह की गड़बड़ी दिखेगी:

    /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 एट्रिब्यूट का इस्तेमाल करना. यहां इस समस्या को हल करने के लिए, यह पक्का किया गया है कि टूलचेन का इस्तेमाल करने वाले किसी भी टारगेट को यह फ़्लैग बताने की ज़रूरत नहीं है.

    इस कोड को toolchain/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",    # NEW
        "flag_group", # NEW
        "flag_set",   # NEW
        "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],
    )
    
  8. bazel build //main:hello-world को चलाने पर, होस्ट के लिए बाइनरी को आखिर में सही तरीके से बनाया जाना चाहिए.

  9. toolchain/BUILD में, cc_toolchain_config, cc_toolchain, और toolchain टारगेट को कॉपी करें और linux_x86_64 को android_x86_64टारगेट के नामों से बदल दें.

    MODULE.bazel में, Android के लिए टूलचेन को रजिस्टर करें

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64",
        "//toolchain:cc_toolchain_for_android_x86_64"
    )
    
  10. Android के लिए बाइनरी बनाने के लिए, bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64 चलाएं.

आम तौर पर, Linux और Android के लिए C++ टूलचेन के अलग-अलग कॉन्फ़िगरेशन होने चाहिए. दोनों में अंतर के लिए, मौजूदा cc_toolchain_config में बदलाव किया जा सकता है या अलग-अलग प्लैटफ़ॉर्म के लिए अलग नियम (जैसे, CcToolchainConfigInfo प्रोवाइडर) बनाए जा सकते हैं.

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

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

सीखने लायक ज़रूरी बातें:

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

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

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