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

Sorun bildir Kaynağı görüntüle Nightly · 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.

Neler öğreneceksiniz?

Bu eğitimde şunları öğreneceksiniz:

  • Derleme ortamını ayarlama
  • Araç zinciri çözümünde hata ayıklamak için --toolchain_resolution_debug kullanma
  • C++ araç zincirini yapılandırma
  • Bazel'in uygulamayı clang ile oluşturabilmesi için cc_toolchain ile ilgili ek yapılandırma sağlayan bir Starlark kuralı oluşturun.
  • Linux makinede bazel build //main:hello-world komutunu çalıştırarak C++ ikilisini oluşturun.
  • bazel build //main:hello-world --platforms=//:android_x86_64 komutunu çalıştırarak Android için ikili dosyayı çapraz derleyin.

Başlamadan önce

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 19 kullanılıyor.

Derleme ortamını ayarlama

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

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

  2. Kök klasöre boş bir MODULE.bazel dosyası ekleyin.

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

    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    

    Bazel, derleme sırasında process-wrapper gibi C++ ile yazılmış birçok dahili araç kullandığından, ana makine platformu için önceden var olan varsayılan C++ araç zinciri belirtilir. Bu sayede, bu dahili araçlar, bu eğiticide oluşturulan araç zincirini kullanarak geliştirme yapabilir. Bu nedenle, cc_binary hedefi de varsayılan araç zinciriyle oluşturulur.

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

    bazel build //main:hello-world
    

    Derleme, MODULE.bazel'da kayıtlı herhangi bir araç zinciri olmadan başarılı olur.

    İşleyişin ayrıntılarını görmek için şu komutu çalıştırın:

    bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type'
    
    INFO: ToolchainResolution: Target platform @@platforms//host:host: Selected execution platform @@platforms//host:host, type @@bazel_tools//tools/cpp:toolchain_type -> toolchain @@bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8
    

    --platforms belirtilmeden Bazel, @platforms//host için hedefi @bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8 kullanarak oluşturur.

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.

Ayrıca, ayrıntılar clang'in farklı sürümleri arasında yalnızca biraz değişse de clang version 9.0.1 olduğu varsayılır.

  1. toolchain/BUILD ile ekle

    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",
        ],
    )
    

    Ardından uygun bağımlılıkları ekleyin ve araç zincirini MODULE.bazel ile kaydedin.

    bazel_dep(name = "platforms", version = "0.0.10")
    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64"
    )
    

    Bu adımda bir cc_toolchain tanımlanır ve ana makine yapılandırması için bir toolchain hedefine bağlanır.

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

    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. toolchain/BUILD dosyasında boş bir dosya grubu tanımlayın:

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

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

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

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

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. 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 kaynağından tool_path() oluşturucusu gerekir:

    # 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",  # Compiler is referenced by the name "gcc" for historic reasons.
                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. Derleyicinin, geçmişe dayanan nedenlerden dolayı "gcc" adıyla referans verildiğini unutmayın.

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

    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'ın, dahil edilen üstbilgileri nerede arayacağını bilmesi gerekir. Bunu çözmenin birden fazla yolu vardır (ör. cc_binary öğesinin includes özelliğini kullanma). 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-19/lib/clang/19/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. 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, linkopts özelliğini cc_binary ile birlikte kullanabilirsiniz. Burada, araç zincirini kullanan herhangi bir hedefin bu işareti belirtmesi gerekmediğinden sorun çözülmüştür.

    Aşağıdaki kodu toolchain/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",    # 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",  # Compiler is referenced by the name "gcc" for historic reasons.
                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-19/lib/clang/19/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],
    )
    

    Bu kodun GNU C++ kitaplığı libstdc++'yı kullandığını unutmayın. LLVM C++ kitaplığını kullanmak istiyorsanız "-lstdc++" yerine "-lc++" kullanın.

  8. bazel build //main:hello-world çalıştırıldığında, ana makine için ikili dosya başarıyla oluşturulmalıdır.

  9. toolchain/BUILD içinde cc_toolchain_config, cc_toolchain ve toolchain hedeflerini kopyalayın ve hedef adlarında linux_x86_64 yerine android_x86_64 yazın.

    MODULE.bazel içinde, Android için araç zincirini kaydedin.

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64",
        "//toolchain:cc_toolchain_for_android_x86_64"
    )
    
  10. Android için ikili dosyayı oluşturmak üzere bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64 komutunu çalıştırın.

Uygulamada, Linux ve Android'in farklı C++ araç zinciri yapılandırmaları olmalıdır. Farklılıklar için mevcut cc_toolchain_config öğesini değiştirebilir veya ayrı platformlar için ayrı kurallar (ör. CcToolchainConfigInfo sağlayıcı) oluşturabilirsiniz.

Çalışmanızı inceleme

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

Önemli noktalar:

  • Bazel'in platformdaki aynı kısıtlama değerleri için araç zincirini çözebilmesi amacıyla komut satırında eşleşen bir platforms işareti belirtmeniz gerekir. Dokümanda dile özgü yapılandırma işaretleri hakkında daha fazla bilgi yer almaktadır.
  • Araç zincirine araçların nerede bulunduğunu bildirmeniz gerekir. Bu eğitimin basitleştirilmiş bir sürümünde, araçlara sistemden erişebilirsiniz. Daha bağımsız bir yaklaşımla ilgileniyorsanız harici bağımlılıklar hakkında bilgi edinebilirsiniz. Araçlarınız farklı bir modülden gelebilir ve dosyalarını cc_toolchain için kullanılabilir hale getirmeniz gerekir. Bu dosyalar, compiler_files gibi özelliklere yönelik hedef bağımlılıklar içerir. tool_paths de değiştirilmelidir.
  • Bağlantı oluşturma veya başka bir işlem türü olsun, hangi işaretlerin farklı işlemlere iletileceğini özelleştirmek için özellikler oluşturabilirsiniz.

Daha fazla bilgi

Daha fazla bilgi için C++ toolchain configuration (C++ araç zinciri yapılandırması) başlıklı makaleyi inceleyin.