Tutorial Bazel: Mengonfigurasi Toolchain C++

Laporkan masalah Lihat sumber Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Tutorial ini menggunakan contoh skenario untuk menjelaskan cara mengonfigurasi toolchain C++ untuk project.

Yang akan Anda pelajari

Dalam tutorial ini, Anda akan mempelajari cara:

  • Menyiapkan lingkungan build
  • Menggunakan --toolchain_resolution_debug untuk men-debug penyelesaian toolchain
  • Mengonfigurasi toolchain C++
  • Buat aturan Starlark yang memberikan konfigurasi tambahan untuk cc_toolchain sehingga Bazel dapat membangun aplikasi dengan clang
  • Bangun biner C++ dengan menjalankan bazel build //main:hello-world di komputer Linux
  • Kompilasi silang biner untuk Android dengan menjalankan bazel build //main:hello-world --platforms=//:android_x86_64

Sebelum memulai

Tutorial ini mengasumsikan Anda menggunakan Linux dan telah berhasil membuat aplikasi C++ serta menginstal alat dan library yang sesuai. Tutorial ini menggunakan clang version 19, yang dapat Anda instal di sistem Anda.

Menyiapkan lingkungan build

Siapkan lingkungan build Anda sebagai berikut:

  1. Jika Anda belum melakukannya, download dan instal Bazel 7.0.2 atau yang lebih baru.

  2. Tambahkan file MODULE.bazel kosong di folder root.

  3. Tambahkan target cc_binary berikut ke file main/BUILD:

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

    Karena Bazel menggunakan banyak alat internal yang ditulis dalam C++ selama build, seperti process-wrapper, toolchain C++ default yang sudah ada ditentukan untuk platform host. Hal ini memungkinkan alat internal ini dibangun menggunakan toolchain yang dibuat dalam tutorial ini. Oleh karena itu, target cc_binary juga dibangun dengan toolchain default.

  4. Jalankan build dengan perintah berikut:

    bazel build //main:hello-world
    

    Build berhasil tanpa toolchain apa pun yang terdaftar di MODULE.bazel.

    Untuk melihat lebih lanjut apa yang ada di balik layar, jalankan:

    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
    

    Tanpa menentukan --platforms, Bazel akan membangun target untuk @platforms//host menggunakan @bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8.

Mengonfigurasi toolchain C++

Untuk mengonfigurasi toolchain C++, bangun aplikasi berulang kali dan hilangkan setiap error satu per satu seperti yang dijelaskan di bawah.

Hal ini juga mengasumsikan clang version 9.0.1, meskipun detailnya hanya akan berubah sedikit di antara berbagai versi clang.

  1. Menambahkan toolchain/BUILD dengan

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

    Kemudian, tambahkan dependensi yang sesuai dan daftarkan toolchain dengan MODULE.bazel dengan

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

    Langkah ini menentukan cc_toolchain dan mengikatnya ke target toolchain untuk konfigurasi host.

  2. Jalankan build lagi. Karena paket toolchain belum menentukan target linux_x86_64_toolchain_config, Bazel akan menampilkan error berikut:

    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. Dalam file toolchain/BUILD, tentukan filegroup kosong sebagai berikut:

    package(default_visibility = ["//visibility:public"])
    
    filegroup(name = "linux_x86_64_toolchain_config")
    
  4. Jalankan build lagi. Bazel menampilkan error berikut:

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

    CcToolchainConfigInfo adalah penyedia yang Anda gunakan untuk mengonfigurasi toolchain C++ Anda. Untuk memperbaiki error ini, buat aturan Starlark yang menyediakan CcToolchainConfigInfo ke Bazel dengan membuat file toolchain/cc_toolchain_config.bzl dengan konten berikut:

    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() membuat penyedia yang diperlukan CcToolchainConfigInfo. Untuk menggunakan aturan cc_toolchain_config, tambahkan pernyataan load ke toolchain/BUILD tepat di bawah pernyataan paket:

    load(":cc_toolchain_config.bzl", "cc_toolchain_config")
    

    Lalu, ganti grup file "linux_x86_64_toolchain_config" dengan deklarasi aturan cc_toolchain_config:

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. Jalankan build lagi. Bazel menampilkan error berikut:

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

    Pada tahap ini, Bazel memiliki cukup informasi untuk mencoba membangun kode, tetapi Bazel masih belum mengetahui alat apa yang harus digunakan untuk menyelesaikan tindakan build yang diperlukan. Anda akan mengubah implementasi aturan Starlark untuk memberi tahu Bazel alat apa yang akan digunakan. Untuk itu, Anda memerlukan konstruktor tool_path() dari @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",  # 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
        )
    

    Pastikan /usr/bin/clang dan /usr/bin/ld adalah jalur yang benar untuk sistem Anda. Perhatikan bahwa compiler dirujuk dengan nama "gcc" karena alasan historis.

  6. Jalankan build lagi. Bazel menampilkan error berikut:

    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 perlu mengetahui tempat untuk menelusuri header yang disertakan. Ada beberapa cara untuk mengatasi hal ini, seperti menggunakan atribut includes dari cc_binary, tetapi di sini hal ini diselesaikan di tingkat toolchain dengan parameter cxx_builtin_include_directories dari cc_common.create_cc_toolchain_config_info. Perhatikan bahwa jika Anda menggunakan clang versi yang berbeda, jalur include akan berbeda. Jalur ini juga dapat berbeda, bergantung pada distribusinya.

    Ubah nilai yang ditampilkan di toolchain/cc_toolchain_config.bzl agar terlihat seperti ini:

    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. Jalankan kembali perintah build, Anda akan melihat error seperti:

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

    Alasannya adalah karena linker tidak memiliki library standar C++ dan tidak dapat menemukan simbolnya. Ada banyak cara untuk mengatasi hal ini, seperti menggunakan atribut linkopts dari cc_binary. Di sini, masalah diselesaikan dengan memastikan bahwa target yang menggunakan toolchain tidak perlu menentukan flag ini.

    Salin kode berikut ke 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],
    )
    

    Perhatikan bahwa kode ini menggunakan library GNU C++ libstdc++. Jika Anda ingin menggunakan library LLVM C++, gunakan "-lc++" dan bukan "-lstdc++".

  8. Dengan menjalankan bazel build //main:hello-world, biner akan berhasil di-build untuk host.

  9. Di toolchain/BUILD, salin target cc_toolchain_config, cc_toolchain, dan toolchain, lalu ganti linux_x86_64 dengan android_x86_64 di nama target.

    Di MODULE.bazel, daftarkan toolchain untuk android

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64",
        "//toolchain:cc_toolchain_for_android_x86_64"
    )
    
  10. Jalankan bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64 untuk membuat biner untuk Android.

Dalam praktiknya, Linux dan Android harus memiliki konfigurasi toolchain C++ yang berbeda. Anda dapat mengubah cc_toolchain_config yang ada untuk perbedaan atau membuat aturan terpisah (yaitu penyedia CcToolchainConfigInfo) untuk platform terpisah.

Meninjau tugas Anda

Dalam tutorial ini, Anda telah mempelajari cara mengonfigurasi toolchain C++ dasar, tetapi toolchain lebih canggih daripada contoh ini.

Poin-poin pentingnya adalah:

  • Anda harus menentukan tanda platforms yang cocok di command line agar Bazel dapat menyelesaikan toolchain untuk nilai batasan yang sama di platform. Dokumentasi berisi informasi selengkapnya tentang flag konfigurasi khusus bahasa.
  • Anda harus memberi tahu toolchain tempat alat berada. Dalam tutorial ini, ada versi sederhana tempat Anda mengakses alat dari sistem. Jika Anda tertarik dengan pendekatan yang lebih mandiri, Anda dapat membaca tentang dependensi eksternal. Alat Anda dapat berasal dari modul yang berbeda dan Anda harus menyediakan file-nya untuk cc_toolchain dengan dependensi target pada atribut, seperti compiler_files. tool_paths juga perlu diubah.
  • Anda dapat membuat fitur untuk menyesuaikan tanda yang harus diteruskan ke tindakan yang berbeda, baik itu penautan atau jenis tindakan lainnya.

Bacaan lebih lanjut

Untuk mengetahui detail selengkapnya, lihat Konfigurasi toolchain C++