Tutorial Bazel: Mengonfigurasi Toolchain C++

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.
Laporkan masalah Lihat sumber

Tutorial ini menggunakan contoh skenario untuk menjelaskan cara mengonfigurasi toolchain C++ untuk sebuah project. API ini didasarkan pada contoh project C++ yang mem-build bebas error menggunakan clang.

Yang akan Anda pelajari

Dalam tutorial ini, Anda akan mempelajari cara:

  • Menyiapkan lingkungan build
  • Mengonfigurasi toolchain C++
  • Buat aturan Starlark yang menyediakan konfigurasi tambahan untuk cc_toolchain sehingga Bazel dapat mem-build aplikasi dengan clang
  • Konfirmasikan hasil yang diharapkan dengan menjalankan bazel build --config=clang_config //main:hello-world di mesin Linux
  • Mem-build aplikasi C++

Sebelum memulai

Menyiapkan lingkungan build

Tutorial ini mengasumsikan bahwa Anda menggunakan Linux dan berhasil mem-build aplikasi C++ serta menginstal alat dan library yang sesuai. Tutorial ini menggunakan clang version 9.0.1, yang dapat Anda instal di sistem.

Siapkan lingkungan build Anda seperti berikut:

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

  2. Download contoh project C++ dari GitHub dan tempatkan di direktori kosong di komputer lokal Anda.

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

    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    
  4. Buat file .bazelrc di root direktori ruang kerja dengan konten berikut untuk memungkinkan penggunaan flag --config:

    # Use our custom-configured c++ toolchain.
    
    build:clang_config --crosstool_top=//toolchain:clang_suite
    
    # Use --cpu as a differentiator.
    
    build:clang_config --cpu=k8
    
    # Use the default Bazel C++ toolchain to build the tools used during the
    # build.
    
    build:clang_config --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
    

Untuk entri build:{config_name} --flag=value, flag command line --config={config_name} dikaitkan dengan flag tertentu tersebut. Lihat dokumentasi untuk flag yang digunakan: crosstool_top, cpu, dan host_crosstool_top.

Ketika Anda mem-build target dengan bazel build --config=clang_config //main:hello-world, Bazel menggunakan toolchain kustom dari cc_toolchain_suite //toolchain:clang_suite. Rangkaian tersebut dapat mencantumkan toolchain yang berbeda untuk CPU yang berbeda, sehingga itulah perbedaannya dengan flag --cpu=k8.

Karena Bazel menggunakan banyak alat internal yang ditulis dalam C++ selama build, seperti wrapper proses, toolchain C++ default yang sudah ada sebelumnya ditentukan untuk platform host, sehingga alat tersebut di-build menggunakan toolchain tersebut, bukan yang dibuat dalam tutorial ini.

Mengonfigurasi toolchain C++

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

  1. Jalankan build dengan perintah berikut:

    bazel build --config=clang_config //main:hello-world
    

    Karena Anda menentukan --crosstool_top=//toolchain:clang_suite dalam file .bazelrc, Bazel akan menampilkan error berikut:

    No such package `toolchain`: BUILD file not found on package path.
    

    Dalam direktori ruang kerja, buat direktori toolchain untuk paket dan file BUILD kosong di dalam direktori toolchain.

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

    No such target '//toolchain:clang_suite': target 'clang_suite' not declared
    in package 'toolchain' defined by .../toolchain/BUILD
    

    Di file toolchain/BUILD, tentukan grup file kosong sebagai berikut:

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

    '//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'
    

    Bazel menemukan bahwa flag --crosstool_top mengarah ke aturan yang tidak menyediakan penyedia ToolchainInfo yang diperlukan. Jadi, Anda harus mengarahkan --crosstool_top ke aturan yang menyediakan ToolchainInfo, yaitu aturan cc_toolchain_suite. Dalam file toolchain/BUILD, ganti grup file kosong dengan baris berikut:

    cc_toolchain_suite(
        name = "clang_suite",
        toolchains = {
            "k8": ":k8_toolchain",
        },
    )
    

    Atribut toolchains secara otomatis memetakan nilai --cpu (dan juga --compiler jika ditentukan) ke cc_toolchain. Anda belum menentukan target cc_toolchain apa pun dan Bazel akan mengeluhkannya secara singkat.

  4. Jalankan lagi build. Bazel menampilkan error berikut:

    Rule '//toolchain:k8_toolchain' does not exist
    

    Sekarang Anda perlu menentukan target cc_toolchain untuk setiap nilai dalam atribut cc_toolchain_suite.toolchains. Tambahkan kode berikut ke file toolchain/BUILD:

    filegroup(name = "empty")
    
    cc_toolchain(
        name = "k8_toolchain",
        toolchain_identifier = "k8-toolchain",
        toolchain_config = ":k8_toolchain_config",
        all_files = ":empty",
        compiler_files = ":empty",
        dwp_files = ":empty",
        linker_files = ":empty",
        objcopy_files = ":empty",
        strip_files = ":empty",
        supports_param_files = 0,
    )
    
  5. Jalankan lagi build. Bazel menampilkan error berikut:

    Rule '//toolchain:k8_toolchain_config' does not exist
    

    Selanjutnya, tambahkan target ":k8_toolchain_config" ke file toolchain/BUILD:

    filegroup(name = "k8_toolchain_config")
    
  6. Jalankan lagi build. Bazel menampilkan error berikut:

    '//toolchain:k8_toolchain_config' does not have mandatory providers:
    'CcToolchainConfigInfo'
    

    CcToolchainConfigInfo adalah penyedia yang digunakan 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 pemuatan ke toolchains/BUILD:

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

    Dan ganti grup file "k8_toolchain_config" dengan deklarasi aturan cc_toolchain_config:

    cc_toolchain_config(name = "k8_toolchain_config")
    
  7. Jalankan lagi build. 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 mem-build kode tetapi masih tidak tahu alat apa yang harus digunakan untuk menyelesaikan tindakan build yang diperlukan. Anda akan memodifikasi 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",
                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 tepat untuk sistem Anda.

  8. Jalankan lagi build. Bazel menampilkan error berikut:

     ..../BUILD:3:1: undeclared inclusion(s) in rule '//main:hello-world':
     this rule is missing dependency declarations for the following files included by 'main/hello-world.cc':
     '/usr/include/c++/9/ctime'
     '/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h'
     '/usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h'
     ....
    

    Bazel perlu mengetahui tempat untuk menelusuri header yang disertakan. Ada beberapa cara untuk mengatasi masalah ini, seperti menggunakan atribut includes dari cc_binary, tetapi di sini masalah ini diatasi di level toolchain dengan parameter cxx_builtin_include_directories cc_common.create_cc_toolchain_config_info. Berhati-hatilah jika Anda menggunakan versi clang yang berbeda, jalur penyertaan akan berbeda. Jalur ini mungkin juga berbeda, bergantung pada distribusinya.

    Ubah nilai return 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-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,
     )
    
  9. 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 penaut tidak memiliki library standar C++ dan tidak dapat menemukan simbolnya. Ada banyak cara untuk melakukannya, seperti menggunakan atribut linkopts dari cc_binary. Di sini, masalah dapat diatasi dengan memastikan bahwa setiap target yang menggunakan toolchain tidak harus menentukan flag ini.

    Salin kode berikut ke 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",
          "flag_group",
          "flag_set",
          "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],
      )
    
  10. Jika Anda menjalankan bazel build --config=clang_config //main:hello-world, prosesnya akan selesai.

Meninjau tugas

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

Poin penting yang perlu diambil adalah: - Anda harus menentukan flag --crosstool_top pada command line yang harus mengarah ke cc_toolchain_suite - Anda dapat membuat pintasan untuk konfigurasi tertentu menggunakan file .bazelrc - cc_toolchain_suite dapat mencantumkan cc_toolchains untuk CPU dan compiler yang berbeda. Anda dapat menggunakan tanda command line seperti --cpu untuk membedakannya. - 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 mandiri, Anda dapat membaca tentang ruang kerja di sini. Alat Anda dapat berasal dari ruang kerja yang berbeda dan Anda harus membuat filenya tersedia ke cc_toolchain dengan dependensi target pada atribut, seperti compiler_files. tool_paths juga perlu diubah. - Anda dapat membuat fitur untuk menyesuaikan flag mana yang harus diteruskan ke tindakan yang berbeda, baik dengan menautkan maupun jenis tindakan lainnya.

Bacaan lebih lanjut

Untuk mengetahui detail selengkapnya, lihat Konfigurasi toolchain C++