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

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

इस ट्यूटोरियल में उदाहरण के तौर पर यह बताया गया है कि किसी प्रोजेक्ट के लिए 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. रूट फ़ोल्डर में खाली WORKSPACE फ़ाइल जोड़ें.

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

    load("@rules_cc//cc:defs.bzl", "cc_binary")
    
    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    

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

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

    bazel build //main:hello-world
    

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

    आगे की जानकारी देखने के लिए, चलाएं:

    bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type'
    
    INFO: ToolchainResolution: Target platform @@local_config_platform//:host: Selected execution platform @@local_config_platform//: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 का इस्तेमाल करके, @local_config_platform//:host के लिए टारगेट बनाता है

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

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

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

  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",
        ],
    )
    

    इसके बाद, WORKSPACE से टूलचेन को रजिस्टर करें

    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 से टूल_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. बिल्ड को फिर से चलाएं. 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 के 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से बदलें.

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

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

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