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

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

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

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

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

  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 टारगेट तय नहीं किया गया है. इसलिए, Bazel यह गड़बड़ी दिखाता है:

    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 नियम बनाएं. यह नियम, 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")
    

    इसके बाद, "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 को यह बताया जा सके कि किन टूल का इस्तेमाल करना है. इसके लिए, आपको @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",  # Compiler is referenced by the name "gcc" for historic reasons.
                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, आपके सिस्टम के लिए सही पाथ हों. ध्यान दें कि पुरानी वजहों से, कंपाइलर को "gcc" नाम से रेफ़र किया जाता है.

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

    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'
      ...
    

    Bazel को यह पता होना चाहिए कि शामिल किए गए हेडर कहां खोजने हैं. इस समस्या को हल करने के कई तरीके हैं. जैसे, cc_binary एट्रिब्यूट का इस्तेमाल करना. हालांकि, यहां इसे टूलचेन लेवल पर हल किया गया है. इसके लिए, cc_common.create_cc_toolchain_config_info के cxx_builtin_include_directories पैरामीटर का इस्तेमाल किया गया है.includes ध्यान दें कि अगर clang के किसी दूसरे वर्शन का इस्तेमाल किया जा रहा है, तो शामिल करने का पाथ अलग होगा. ये पाथ, डिस्ट्रिब्यूशन के हिसाब से भी अलग-अलग हो सकते हैं.

    toolchain/cc_toolchain_config.bzl में मौजूद रिटर्न वैल्यू में बदलाव करके, उसे इस तरह दिखाएं:

    return cc_common.create_cc_toolchain_config_info(
        ctx = ctx,
        cxx_builtin_include_directories = [ # NEW
            "/usr/lib/llvm-19/lib/clang/19/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",  # Compiler is referenced by the name "gcc" for historic reasons.
                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-19/lib/clang/19/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],
    )
    

    ध्यान दें कि यह कोड, GNU C++ लाइब्रेरी libstdc++ का इस्तेमाल करता है. अगर आपको LLVM C++ लाइब्रेरी का इस्तेमाल करना है, तो "-lstdc++" के बजाय "-lc++" का इस्तेमाल करें.

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