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

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

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

आप इन चीज़ों के बारे में जानेंगे

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

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

वेब कंटेनर इंस्टॉल करने से पहले

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

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

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

  1. अगर आपने पहले से ऐसा नहीं किया है, तो Basel 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"],
    )
    

    Basel, बिल्ड के दौरान 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 तय किए बिना, Basel ने @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 टारगेट को तय नहीं कर सका. इसलिए, 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. बिल्ड को फिर से चलाएं. बेज़ल यह गड़बड़ी दिखाता है:

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

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

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

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. बिल्ड को फिर से चलाएं. बेज़ल यह गड़बड़ी दिखाता है:

    .../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`
    

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

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

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

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