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. Ini didasarkan pada contoh project C++ yang membangun 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 memberikan konfigurasi untuk cc_toolchain agar Bazel dapat membangun aplikasi dengan clang
  • Konfirmasikan hasil yang diharapkan dengan menjalankan bazel build --config=clang_config //main:hello-world di komputer Linux
  • Membangun aplikasi C++

Sebelum memulai

Menyiapkan lingkungan build

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 9.0.1, yang dapat diinstal di sistem Anda.

Siapkan lingkungan build Anda seperti berikut:

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

  2. Unduh contoh project C++ dari GitHub dan menempatkannya di direktori kosong pada mesin 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 tersebut. Lihat untuk flag yang digunakan: crosstool_top, cpu dan host_crosstool_top.

Saat Anda membangun target dengan bazel build --config=clang_config //main:hello-world, Bazel menggunakan toolchain kustom dari cc_toolchain_suite //toolchain:clang_suite. Suite ini mungkin mencantumkan berbagai toolchain untuk CPU yang berbeda, dan itulah mengapa ini dibedakan dengan flag --cpu=k8.

Karena Bazel menggunakan banyak alat internal yang ditulis dalam C++ selama build, seperti {i>process-wrapper<i}, toolchain C++ default yang sudah ada sebelumnya ditentukan untuk platform host, sehingga alat ini dibangun menggunakan toolchain itu yang dibuat dalam tutorial ini.

Mengonfigurasi toolchain C++

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

  1. Jalankan build dengan perintah berikut:

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

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

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

    Di direktori workspace, buat direktori toolchain untuk paket tersebut dan file BUILD kosong di dalam direktori toolchain.

  2. Jalankan build lagi. Karena paket toolchain belum menentukan Target clang_suite, Bazel akan 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 build lagi. Bazel akan menampilkan error berikut:

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

    Bazel menemukan bahwa flag --crosstool_top menunjuk ke aturan yang tidak memberikan ToolchainInfo yang diperlukan penyedia layanan. Jadi, Anda perlu mengarahkan --crosstool_top ke aturan yang menyediakan ToolchainInfo - itu adalah aturan cc_toolchain_suite. Di kolom toolchain/BUILD, ganti grup file kosong dengan kode berikut:

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

    Atribut toolchains secara otomatis memetakan --cpu (dan juga --compiler jika ditentukan) menjadi cc_toolchain. Anda belum menentukan target cc_toolchain dan Bazel akan mengeluhkannya sebentar lagi.

  4. Jalankan build lagi. Bazel akan menampilkan error berikut:

    Rule '//toolchain:k8_toolchain' does not exist
    

    Sekarang Anda harus 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 build lagi. Bazel akan menampilkan error berikut:

    Rule '//toolchain:k8_toolchain_config' does not exist
    

    Selanjutnya, tambahkan ":k8_ toolchain_config" menargetkan ke file toolchain/BUILD:

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

    '//toolchain:k8_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 "k8_ toolchain_config" grup file dengan deklarasi cc_toolchain_config aturan:

    cc_toolchain_config(name = "k8_toolchain_config")
    
  7. 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.

  8. Jalankan build lagi. Bazel akan 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 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-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 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 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, seharusnya akhirnya dibangun.

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 perlu menentukan flag --crosstool_top pada command line yang seharusnya arahkan ke cc_toolchain_suite - Anda dapat membuat pintasan untuk konfigurasi tertentu menggunakan .bazelrc file - cc_ toolchain_suite mungkin mencantumkan cc_toolchains untuk CPU yang berbeda dan compiler. Anda dapat menggunakan flag command line seperti --cpu untuk membedakannya. - Anda harus memberi tahu toolchain 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 di sini. 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 flag yang harus diteruskan tindakan yang berbeda, baik itu menautkan atau jenis tindakan lainnya.

Bacaan lebih lanjut

Untuk detail selengkapnya, lihat Konfigurasi toolchain C++