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

Sorun bildir Kaynağı göster Nightly · 8.0 · 7.4 · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bu eğitimde, bir proje için C++ araç zincirlerinin nasıl yapılandırılacağı örnek bir senaryoyla açıklanmaktadır.

Neler öğreneceksiniz?

Bu eğitimde şunları öğreneceksiniz:

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

Başlamadan önce

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 16 kullanılır.

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, bu dahili araçların bu eğitimde oluşturulan araç zincirini kullanarak derlemesine olanak tanır. Bu nedenle, cc_binary hedefi de varsayılan araç zinciriyle oluşturulur.

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

    bazel build //main:hello-world
    

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

    İşleyişin ayrıntılarını daha ayrıntılı 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
    

    Bazel, --platforms belirtmeden @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 derleyin ve her hatayı aşağıda açıklandığı şekilde tek tek ortadan kaldırın.

Ayrıntılar clang'ın farklı sürümleri arasında yalnızca biraz değişse de clang version 9.0.1 de varsayılır.

  1. toolchain/BUILD ekleme

    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ç setini MODULE.bazel ile kaydettirin.

    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 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 grubunu aşağıdaki gibi 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ı verir:

    '//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ştururCcToolchainConfigInfo. cc_toolchain_config kuralını kullanmak için paket ifadesi hemen altındaki toolchain/BUILD alanına bir yükleme ifadesi ekleyin:

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

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

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. Derlemeyi tekrar çalıştırın. Bazel aşağıdaki 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 kaynağındaki tool_path() oluşturucuya 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.

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

    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'in, dahil edilen üstbilgileri nerede arayacağını bilmesi gerekir. 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. clang'nin farklı bir sürümünü kullanıyorsanız dahil etme yolunun farklı olacağına dikkat edin. Bu yollar, dağıtıma bağlı olarak da farklı olabilir.

    toolchain/cc_toolchain_config.bzl içindeki döndürülen değeri şu şekilde değiştirin:

    return cc_common.create_cc_toolchain_config_info(
        ctx = ctx,
        cxx_builtin_include_directories = [ # NEW
            "/usr/lib/llvm-16/lib/clang/16/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ı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, C++ standart kitaplığının bağlayıcıda bulunmaması ve bağlayıcının bu kitaplığın simgelerini bulamamasıdır. Bu sorunu çözmenin birçok yolu vardır. Örneğin, cc_binary öğesinin linkopts özelliğini kullanabilirsiniz. Burada, araç zincirini kullanan tüm hedeflerin bu işareti belirtmesi gerekmediğinden emin olarak bu sorun çözüldü.

    Aşağıdaki kodu toolchain/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",    # 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",
                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],
    )
    
  8. bazel build //main:hello-world çalıştırıldığında, ikili dosya ana makine için başarıyla derlenir.

  9. toolchain/BUILD'te 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'te 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ı derlemek ü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'yi değiştirebilir veya ayrı platformlar için ayrı kurallar (ör. CcToolchainConfigInfo sağlayıcısı) 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 şunlardır:

  • Bazel'in platformdaki aynı kısıtlama değerleri için araç zincirine çözüm üretmesi amacıyla komut satırında eşleşen bir platforms işareti belirtmeniz gerekir. Dile özgü yapılandırma işaretleri hakkında daha fazla bilgi dokümanda yer almaktadır.
  • Araçların nerede olduğunu araç zincirine bildirmeniz gerekir. Bu eğitimde, sistemdeki araçlara eriştiğiniz basitleştirilmiş bir sürüm bulunmaktadır. 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 işlem için compiler_files gibi özelliklerde hedef bağımlılıklar kullanmanız gerekir. 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 C++ araç zinciri yapılandırması başlıklı makaleyi inceleyin.