Bazel Eğitimi: C++ Araç Zincirlerini Yapılandırma

Sorun bildirme Kaynağı görüntüleme Nightly · 7.4 . 7.3 · 7,2 · 7,1 · 7,0 · 6,5

Bu eğitimde, C++'ın nasıl yapılandırılacağını açıklayan örnek bir senaryo kullanılmaktadır. araç zincirleri olarak düşünebilirsiniz. Bu örnek, clang kullanılarak hatasız şekilde derlenen örnek bir C++ projesine dayanır.

Neler öğreneceksiniz?

Bu eğitimde şunları öğreneceksiniz:

  • Derleme ortamını ayarlama
  • C++ araç zincirini yapılandırma
  • Ek özellikler sağlayan bir Starlark kuralı Bazel'in uygulamayı derleyebilmesi için cc_toolchain yapılandırması clang ile
  • bazel build --config=clang_config //main:hello-world dosyasını Linux makinede çalıştırarak beklenen sonucu onaylama
  • C++ uygulamasını derleme

Başlamadan önce

Derleme ortamını ayarlama

Bu eğitimde, Linux kullanıyor ve C++ uygulamalarını başarıyla derleyip uygun araçları ve kitaplıkları yüklediğiniz varsayılmaktadır. Eğitimde, sisteminize yükleyebileceğiniz clang version 9.0.1 kullanılır.

Derleme ortamınızı aşağıdaki gibi ayarlayın:

  1. Henüz yapmadıysanız Bazel 0.23 veya sonraki bir sürümü indirip yükleyin.

  2. İndirin: örnek C++ projesi dosyayı yerel makinenizde boş bir dizine yerleştirin.

  3. main/BUILD dosyasına aşağıdaki cc_binary hedefini ekleyin:

    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    
  4. Çalışma alanı dizininin kökünde, şununla bir .bazelrc dosyası oluşturun: --config işaretinin kullanımını etkinleştirmek için aşağıdaki içerikleri inceleyin:

    # 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
    

build:{config_name} --flag=value girişi için komut satırı işareti --config={config_name} bu işaretle ilişkilendirilmiş. Kullanılan işaretler için dokümanlara bakın: crosstool_top, cpu ve host_crosstool_top.

Hedefinizi oluştururken bazel build --config=clang_config //main:hello-world kullanan Bazel, özel araç zincirini veya cc_toolchain_suite //toolchain:clang_suite. Paket, farklı araç zincirlerini kullanıma sunduk. Bu nedenle --cpu=k8 işaretiyle ayırt edilir.

Bazel, derleme sırasında C++ ile yazılmış birçok dahili araç (ör. process-wrapper) kullandığından, ana platform için önceden var olan varsayılan C++ araç zinciri belirtilir. Böylece bu araçlar, bu eğitimde oluşturulan araç zinciri yerine bu araç zinciri kullanılarak derlenir.

C++ araç zincirini yapılandırma

C++ araç zincirini yapılandırmak için uygulamayı tekrar tekrar derleyin ve aşağıda açıklandığı gibi her hatayı tek tek giderin.

  1. Aşağıdaki komutu kullanarak derlemeyi çalıştırın:

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

    --crosstool_top=//toolchain:clang_suite .bazelrc dosyasını kaydederken Bazel şu hatayı verir:

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

    Çalışma alanı dizininde, paket için toolchain dizini ve toolchain dizininin içinde boş bir BUILD dosyası oluşturun.

  2. Derlemeyi tekrar çalıştırın. toolchain paketi henüz clang_suite hedefini tanımlamadığından Bazel aşağıdaki hatayı verir:

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

    toolchain/BUILD dosyasında boş bir dosya grubunu aşağıdaki gibi tanımlayın:

    package(default_visibility = ["//visibility:public"])
    
    filegroup(name = "clang_suite")
    
  3. Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı verir:

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

    Bazel, --crosstool_top işaretinin gereken ToolchainInfo sağlanmıyor sağlar. Bu nedenle, --crosstool_topToolchainInfo sağlayan bir kurala (cc_toolchain_suite kuralı) yönlendirmeniz gerekir. toolchain/BUILD dosyası varsa boş dosya grubunu aşağıdakiyle değiştirin:

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

    toolchains özelliği, --cpu (ve belirtilirse --compiler) değerlerini cc_toolchain ile otomatik olarak eşler. Henüz göndermediniz tüm cc_toolchain hedefleri tanımladı. Bazel bundan şikayet ediyor yakında.

  4. Derlemeyi tekrar çalıştırın. Bazel şu hatayı verir:

    Rule '//toolchain:k8_toolchain' does not exist
    

    Artık cc_toolchain_suite.toolchains özelliğindeki her değer için cc_toolchain hedefleri tanımlamanız gerekir. Aşağıdakileri toolchain/BUILD dosyası:

    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. Derlemeyi tekrar çalıştırın. Bazel şu hatayı verir:

    Rule '//toolchain:k8_toolchain_config' does not exist
    

    Sonra, bir ":k8_toolchain_config" ekleyin toolchain/BUILD dosyasını hedefle:

    filegroup(name = "k8_toolchain_config")
    
  6. Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı verir:

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

    CcToolchainConfigInfo, yapılandırmak için kullandığınız bir sağlayıcıdır çok önemlidir. Bu hatayı düzeltmek için bir Starlark kuralı oluşturun oluşturarak Bazel'e CcToolchainConfigInfo sağlayan bir Şu içeriğe sahip toolchain/cc_toolchain_config.bzl dosyası:

    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(), gerekli sağlayıcıyı oluşturur CcToolchainConfigInfo. cc_toolchain_config kuralını kullanmak için toolchains/BUILD'e bir yükleme ifadesi ekleyin:

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

    Ardından, "k8_toolchain_config" dosya grubunu bir cc_toolchain_config kuralı beyanıyla değiştirin:

    cc_toolchain_config(name = "k8_toolchain_config")
    
  7. Derlemeyi tekrar çalıştırın. Bazel şu hatayı verir:

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

    Bu noktada Bazel, kodu derlemeye çalışmak için yeterli bilgiye sahiptir ancak gerekli derleme işlemlerini tamamlamak için hangi araçların kullanılacağını hâlâ bilmez. Bazel'e hangi araçları kullanacağını bildirmek için Starlark kural uygulamasını değiştireceksiniz. Bunun için @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
        )
    

    /usr/bin/clang ve /usr/bin/ld yollarının doğru olduğundan emin olun bir uygulamadır.

  8. Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı verir:

     ..../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'in eklenen başlıkları nerede arayacağını bilmesi gerekiyor. Bu sorunu çözmenin birden fazla yolu vardır (ör. cc_binary öğesinin includes özelliğini kullanmak). Ancak burada bu sorun, cc_common.create_cc_toolchain_config_info öğesinin cxx_builtin_include_directories parametresi kullanılarak araç zinciri düzeyinde çözülmüştür. Bu nedenle, farklı bir clang sürümünü kullanıyorsanız dahil etme yolu yardımcı olur. Bu yollar ayrıca dağıtıma bağlı olarak farklı olabilir.

    toolchain/cc_toolchain_config.bzl bölümündeki döndürülen değeri değiştirerek aşağıdaki gibidir:

     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. Derleme komutunu tekrar çalıştırdığınızda şuna benzer bir hata görürsünüz:

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

    Bunun nedeni, bağlayıcıda C++ standardının eksik olmasıdır. simgelerini bulamıyor. Bu sorunu çözmenin birçok yolu vardır. Örneğin, cc_binary öğesinin linkopts özelliğini kullanabilirsiniz. Burada, araç setini kullanan tüm hedeflerin bu işareti belirtmesi gerekmediğinden emin olarak bu sorun çözüldü.

    Aşağıdaki kodu cc_toolchain_config.bzl alanına kopyalayın:

      # 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. bazel build --config=clang_config //main:hello-world kullanıyorsanız bahsedeceğim.

Çalışmanızı inceleme

Bu eğitimde, temel bir C++ araç zincirinin nasıl yapılandırılacağını öğrendiniz ancak araç zincirleri bu basit örnekten daha güçlüdür.

Önemli noktalar: - Komut satırında, cc_toolchain_suite değerini işaretleyen bir --crosstool_top işareti belirtmeniz gerekir. - .bazelrc dosyasını kullanarak belirli bir yapılandırma için kısayol oluşturabilirsiniz. - cc_toolchain_suite, farklı CPU'lar ve derleyiciler için cc_toolchains değerini listeleyebilir. Farklılaştırmak için --cpu gibi komut satırı işaretlerini kullanabilirsiniz. - Araç zincirine, araçların nerede bulunduğunu bildirmeniz gerekir. Bu eğiticide araçlara sistemden erişebileceğiniz basitleştirilmiş bir sürümü vardır. Eğer daha bağımsız bir yaklaşım istiyorsanız çalışma alanlarınızı burada bulabilirsiniz. Araçlarınız nereden ve onlara ait dosyaları kullanılabilir hale getirmeniz, cc_toolchain ile compiler_files. tool_paths değerinin de değiştirilmesi gerekir. - Bağlantı oluşturma veya başka bir işlem türü olsun, farklı işlemlere hangi işaretlerin iletileceğini özelleştirmek için özellikler oluşturabilirsiniz.

Daha fazla bilgi

Daha fazla bilgi için bkz. C++ araç zinciri yapılandırması