Tutorial Bazel: Mengonfigurasi Toolchain C++

Laporkan masalah Lihat sumber Per malam · 7,2 · 7,1 · 7,0 · 6,5 · 6,4

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

Yang akan Anda pelajari

Dalam tutorial ini, Anda akan mempelajari cara:

  • Menyiapkan lingkungan build
  • Menggunakan --toolchain_resolution_debug untuk men-debug resolusi toolchain
  • Mengonfigurasi toolchain C++
  • Buat aturan Starlark yang memberikan konfigurasi untuk cc_toolchain agar Bazel dapat membangun aplikasi dengan clang
  • Membangun 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 bahwa Anda menggunakan Linux dan telah berhasil membuat aplikasi C++ dan menginstal alat dan library yang sesuai. Tutorial ini menggunakan clang version 16, yang dapat diinstal di sistem Anda.

Menyiapkan lingkungan build

Siapkan lingkungan build Anda seperti berikut:

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

  2. Tambahkan file WORKSPACE kosong di folder root.

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

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

    Karena Bazel menggunakan banyak {i>tool<i} internal yang ditulis dalam C++ selama {i>build<i}, seperti sebagai process-wrapper, toolchain C++ default yang sudah ada sebelumnya ditentukan untuk platform host. Hal ini memungkinkan alat internal ini membangun menggunakan toolchain dari yang dibuat dalam tutorial ini. Oleh karena itu, target cc_binary juga dibuat dengan toolchain default.

  4. Jalankan build dengan perintah berikut:

    bazel build //main:hello-world
    

    Build akan berhasil tanpa toolchain yang terdaftar di WORKSPACE.

    Untuk melihat 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 @@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
    

    Tanpa menetapkan --platforms, Bazel membuat target untuk @local_config_platform//: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 tiap {i>error<i} satu per satu seperti yang dijelaskan di bawah ini.

Tutorial ini juga mengasumsikan clang version 9.0.1, meskipun detailnya hanya akan berubah. sedikit di antara versi {i>clang<i} yang berbeda.

  1. Tambahkan 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 daftarkan toolchain dengan WORKSPACE dengan

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64"
    )
    

    Langkah ini menentukan cc_toolchain dan mengikatnya ke target toolchain untuk konfigurasi {i>host<i}.

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

    package(default_visibility = ["//visibility:public"])
    
    filegroup(name = "linux_x86_64_toolchain_config")
    
  4. Jalankan build lagi. Bazel akan 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 kepada Bazel dengan membuat 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 beban menjadi toolchain/BUILD tepat di bawah pernyataan paket:

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

    Dan ganti file "linux_x86_64_ toolchain_config" filegroup dengan deklarasi dari aturan cc_toolchain_config:

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. Jalankan build lagi. Bazel akan 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 titik ini, Bazel memiliki cukup informasi untuk mencoba membuat kode tetapi mereka masih belum tahu alat apa yang digunakan untuk menyelesaikan build yang dibutuhkan tindakan. Anda akan memodifikasi implementasi aturan Starlark untuk memberi tahu Bazel apa yang peralatan yang tepat untuk digunakan. Untuk itu, Anda membutuhkan 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",
                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.

  6. Jalankan build lagi. Bazel akan 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 tahu di mana harus mencari {i>header<i} yang disertakan. Ada beberapa cara untuk menyelesaikan masalah ini, seperti menggunakan atribut includes cc_binary, tetapi di sini, masalah ini dipecahkan di level toolchain dengan cxx_builtin_include_directories dari cc_common.create_cc_toolchain_config_info. Berhati-hatilah jika Anda menggunakan versi clang yang berbeda, jalur yang disertakan akan menjadi berbeda. Jalur ini mungkin juga berbeda tergantung pada distribusinya.

    Ubah nilai yang ditampilkan di toolchain/cc_toolchain_config.bzl untuk mencarinya seperti ini:

    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. Jalankan perintah build lagi, 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 standar C++ {i>library<i} dan tidak dapat menemukan simbolnya. Ada banyak cara untuk menyelesaikan ini, seperti menggunakan atribut linkopts dari cc_binary. Di sini masalah diselesaikan dengan memastikan bahwa setiap target yang menggunakan {i>chain<i} tidak perlu menentukan penanda 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",
                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. Dengan menjalankan bazel build //main:hello-world, seharusnya biner berhasil di-build untuk host.

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

    Di WORKSPACE, 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 guna membangun biner untuk Android.

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

Tinjau tugas Anda

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

Poin-poin penting adalah:

  • Anda harus menentukan flag platforms yang cocok pada command line agar Bazel dapat ditetapkan ke toolchain untuk nilai batasan yang sama pada platform. Dokumentasi ini menyimpan informasi selengkapnya tentang tanda konfigurasi khusus bahasa.
  • Anda harus memberi tahu toolchain terkait tempat alat tersebut berada. Dalam tutorial ini terdapat versi sederhana di mana Anda dapat mengakses alat dari sistem. Jika Anda tertarik dengan pendekatan yang lebih mandiri, Anda dapat membaca ruang kerja. Alat Anda bisa berasal dari ruang kerja yang berbeda dan Anda harus membuat file-file mereka tersedia ke cc_toolchain dengan dependensi target pada atribut, seperti compiler_files. tool_paths juga harus diubah.
  • Anda dapat membuat fitur untuk menyesuaikan tanda yang harus diteruskan tindakan yang berbeda, baik itu menautkan atau jenis tindakan lainnya.

Bacaan lebih lanjut

Untuk detail selengkapnya, lihat Konfigurasi toolchain C++