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

Sorun bildir Kaynağı görüntüle Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Bu eğitimde, bir proje için C++ araç zincirlerinin nasıl yapılandırılacağını açıklamak üzere örnek bir senaryo kullanılmaktadır. clang kullanılarak hatasız oluşturulan bir örnek C++ projesine dayanır.

Neler öğreneceksiniz?

Bu eğitimde şunları öğreneceksiniz:

  • Derleme ortamını ayarlama
  • C++ araç zincirini yapılandırma
  • cc_toolchain için ek yapılandırma sağlayan bir Starlark kuralı oluşturun. Böylece Bazel, uygulamayı clang ile derleyebilir.
  • Linux makinede bazel build --config=clang_config //main:hello-world komutunu çalıştırarak beklenen sonucu onaylayın.
  • C++ uygulamasını oluşturma

Başlamadan önce

Derleme ortamını ayarlama

Bu eğitimde, Linux kullandığınız ve C++ uygulamalarını başarıyla oluşturup uygun araçları ve kitaplıkları yüklediğiniz varsayılı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ünü indirip yükleyin.

  2. GitHub'dan örnek C++ projesini indirip yerel makinenizdeki 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. --config işaretinin kullanılmasını etkinleştirmek için aşağıdaki içeriklerle birlikte çalışma alanı dizininin kök dizininde bir .bazelrc dosyası oluşturun:

    # 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} söz konusu işaretle ilişkilendirilir. Kullanılan işaretlerle ilgili dokümanlara göz atın: crosstool_top, cpu ve host_crosstool_top.

bazel build --config=clang_config //main:hello-world ile hedefinizi oluşturduğunuzda Bazel, //toolchain:clang_suite cc_toolchain_suite'ten özel araç zincirinizi kullanır. Pakette farklı CPU'lar için farklı toolchain'ler listelenebilir. Bu nedenle, paket --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, bu araçların bu eğitimde oluşturulan araç zinciri yerine önceden var olan varsayılan C++ araç zinciri kullanılarak oluşturulması için ana makine platformu için önceden var olan varsayılan C++ araç zinciri belirtilir.

C++ araç zincirini yapılandırma

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

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

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

    .bazelrc dosyasında --crosstool_top=//toolchain:clang_suite belirttiğiniz için Bazel aşağıdaki hatayı verir:

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

    Çalışma alanı dizininde, paket için toolchain dizinini 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 grubu tanımlayın:

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

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

    Bazel, --crosstool_top işaretinin gerekli ToolchainInfo sağlayıcısını sağlamayan bir kurala işaret ettiğini tespit etti. Bu nedenle, --crosstool_top öğesini ToolchainInfo sağlayan bir kurala, yani cc_toolchain_suite kuralına yönlendirmeniz gerekir. toolchain/BUILD dosyasında boş dosya grubunu aşağıdakilerle değiştirin:

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

    toolchains özelliği, --cpu (ve belirtilmişse --compiler) değerlerini otomatik olarak cc_toolchain ile eşler. Henüz herhangi bir cc_toolchain hedef tanımlamadınız ve Bazel kısa süre içinde bu konuda hata verecek.

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

    Rule '//toolchain:k8_toolchain' does not exist
    

    Şimdi cc_toolchain_suite.toolchains özelliğindeki her değer için cc_toolchain hedeflerini tanımlamanız gerekir. toolchain/BUILD dosyasına aşağıdakileri ekleyin:

    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 aşağıdaki hatayı veriyor:

    Rule '//toolchain:k8_toolchain_config' does not exist
    

    Ardından, toolchain/BUILD dosyasına bir ":k8_toolchain_config" hedefi ekleyin:

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

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

    CcToolchainConfigInfo, C++ araç zincirlerinizi yapılandırmak için kullandığınız bir sağlayıcıdır. Bu hatayı düzeltmek için aşağıdaki içeriğe sahip bir toolchain/cc_toolchain_config.bzl dosyası oluşturarak Bazel'e CcToolchainConfigInfo sağlayan bir Starlark kuralı oluşturun:

    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 paket ifadesinin hemen altına toolchain/BUILD yükleme ifadesi ekleyin:

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

    Ayrıca "k8_toolchain_config" filegroup'u bir cc_toolchain_config kuralı bildirimiyle değiştirin:

    cc_toolchain_config(name = "k8_toolchain_config")
    
  7. Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı veriyor:

    .../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 oluşturmayı denemek için yeterli bilgiye sahiptir ancak gerekli derleme işlemlerini tamamlamak için hangi araçların kullanılacağını hâlâ bilmemektedir. Starlark kuralı uygulamasını, Bazel'e hangi araçların kullanılacağını söyleyecek şekilde değiştireceksiniz. Bunun için @bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl adresindeki tool_path() oluşturucusuna ihtiyacınız vardır:

    # 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'nin sisteminiz için doğru yollar olduğundan emin olun.

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

     ..../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'ın dahil edilen üstbilgileri nerede arayacağını bilmesi gerekir. Bunu çözmenin birden fazla yolu vardır. Örneğin, cc_binary öğesinin includes özelliği kullanılabilir. Ancak burada bu sorun, cc_common.create_cc_toolchain_config_info öğesinin cxx_builtin_include_directories parametresiyle araç zinciri düzeyinde çözülür. clang'nın farklı bir sürümünü kullanıyorsanız ekleme yolunun farklı olacağını unutmayın. Bu yollar, dağıtıma bağlı olarak da farklılık gösterebilir.

    toolchain/cc_toolchain_config.bzl içindeki dönüş değerini aşağıdaki gibi değiştirin:

     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ırın. Aşağıdakine 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++ standart kitaplığının olmaması ve sembollerinin bulunamamasıdır. Bu sorunu çözmenin birçok yolu vardır. Örneğin, cc_binary öğesinin linkopts özelliğini kullanabilirsiniz. Burada, araç zincirini kullanan herhangi bir hedefin bu işareti belirtmesi gerekmediğinden emin olunarak sorun çözülür.

    Aşağıdaki kodu cc_toolchain_config.bzl konumuna 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 komutunu çalıştırırsanız nihayet oluşturulur.

Çalışmanızı inceleme

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

Önemli noktalar: - Komut satırında --crosstool_top işaretini belirtmeniz gerekir. Bu işaret, cc_toolchain_suite konumunu göstermelidir. - .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ğitimin basitleştirilmiş versiyonunda, araçlara sistemden erişebilirsiniz. Daha bağımsız bir yaklaşımla ilgileniyorsanız çalışma alanları hakkında bilgi edinmek için burayı inceleyebilirsiniz. Araçlarınız farklı bir çalışma alanından geliyorsa dosyalarını, cc_toolchain için kullanılabilir hale getirmeniz gerekir. Bu işlemde, compiler_files gibi özelliklere yönelik hedef bağımlılıklar kullanılır. tool_paths de değiştirilmelidir. - 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 C++ toolchain yapılandırması başlıklı makaleyi inceleyin.