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

Sorun bildir Kaynağı göster

Bu eğiticide, bir proje için C++ araç zincirlerinin nasıl yapılandırılacağını açıklayan örnek bir senaryo kullanılmıştır.

Neler öğreneceksiniz?

Bu eğiticide şunları öğreneceksiniz:

  • Derleme ortamını ayarlama
  • Araç zinciri çözünürlüğünde hata ayıklamak için --toolchain_resolution_debug kullanın
  • C++ araç zincirini yapılandırma
  • Bazel'ın clang ile uygulama 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 komutunu çalıştırarak için C++ ikili programını oluşturun.
  • bazel build //main:hello-world --platforms=//:android_x86_64 komutunu çalıştırarak Android için ikili programı çapraz derleyin

Başlamadan önce

Bu eğitim, Linux kullandığınız, C++ uygulamalarını başarıyla oluşturduğunuz ve uygun araçlar ile kitaplıkları yüklediğiniz varsayılmaktadır. Eğitim, sisteminize yükleyebileceğiniz clang version 16 hizmetini kullanı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ünü indirip yükleyin.

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

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

    load("@rules_cc//cc:defs.bzl", "cc_binary")
    
    cc_binary(
        name = "hello-world",
        srcs = ["hello-world.cc"],
    )
    

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

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

    bazel build //main:hello-world
    

    Derleme, WORKSPACE içinde kayıtlı herhangi bir araç zinciri olmadan başarılı olur.

    Gelişmiş seçenekleri daha ayrıntılı olarak görmek için aşağıdaki komutu çalıştırın:

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

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

C++ araç zincirini yapılandırma

C++ araç zincirini yapılandırmak için uygulamayı tekrarlı şekilde derleyin ve aşağıda açıklandığı gibi her hatayı teker teker ortadan kaldırın.

Aynı zamanda clang version 9.0.1 olduğu da varsayılır, ancak clang'ın farklı versiyonları arasında ayrıntılar sadece biraz değişir.

  1. toolchain/BUILD adlı kişiyi şununla 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 araç zincirini WORKSPACE ile

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64"
    )
    

    Bu adım bir cc_toolchain tanımlar ve bunu ana makine yapılandırması için bir toolchain hedefine bağlar.

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

    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 aşağıdaki gibi boş bir dosya grubu tanımlayın:

    package(default_visibility = ["//visibility:public"])
    
    filegroup(name = "linux_x86_64_toolchain_config")
    
  4. Derlemeyi yeniden çalıştırın. Bazel şu 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'a 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ındaki toolchain/BUILD öğesine bir yük ifadesi ekleyin:

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

    "linux_x86_64_toolchain_config" dosya grubunu da bir cc_toolchain_config kuralı bildirimiyle değiştirin:

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. Derlemeyi yeniden ç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 oluşturmayı denemek için yeterli bilgiye sahip olsa da gerekli derleme işlemlerini tamamlamak için hangi araçları kullanacağını yine de bilmez. Bazel'a hangi araçların kullanılacağını bildirmek için Starlark kural uygulamasını değiştirirsiniz. Bunun için @bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl öğesinden 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 değerlerinin sisteminiz için doğru yollar olduğundan emin olun.

  6. Derlemeyi yeniden çalıştırın. Bazel şu 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'ın dahil edilen üstbilgileri nerede arayacağını bilmesi gerekiyor. Bu sorunu çözmenin, cc_binary öğesinin includes özelliğini kullanmak gibi birçok yolu vardır ancak bu yöntem cc_common.create_cc_toolchain_config_info cxx_builtin_include_directories parametresiyle araç zinciri düzeyinde çözülür. Farklı bir clang sürümü kullanıyorsanız dahil etme 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öndürülen değeri 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-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, bağlayıcıda C++ standart kitaplığının bulunmaması ve sembollerini bulamamasıdır. Bu sorunu çözmenin birçok yolu vardır. Örneğin, cc_binary öğesinin linkopts özelliğini kullanabilirsiniz. Burada sorun, araç zincirini kullanan herhangi bir hedefin bu işareti belirtmek zorunda kalmamasının sağlanmasıyla çözülür.

    Aşağıdaki kodu toolchain/cc_toolchain_config.bzl cihazı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 programın ana makine için başarıyla derlenmesi gerekir.

  9. toolchain/BUILD içinde cc_toolchain_config, cc_toolchain ve toolchain hedeflerini kopyalayıp hedef adlardaki linux_x86_64 değerini android_x86_64 ile değiştirin.

    WORKSPACE ürününde 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 programı derlemek üzere bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64 komutunu çalıştırın.

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

Çalışmanızı inceleyin

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

Ana fikirler şunlardır:

  • Bazel'ın platformda aynı kısıtlama değerleri için araç zincirine çözümlemesi için komut satırında eşleşen bir platforms işareti belirtmeniz gerekir. Dokümanlarda dile özgü yapılandırma işaretleri hakkında daha fazla bilgi bulunmaktadır.
  • Araçların nerede bulunduğunu araç zincirine bildirmelisiniz. Bu eğiticide, araçlara sistemden erişebileceğiniz basitleştirilmiş bir sürüm bulunmaktadır. Daha bağımsız bir yaklaşım istiyorsanız çalışma alanları hakkında bilgi edinebilirsiniz. Araçlarınız farklı bir çalışma alanından gelebilir ve bu dosyaların dosyalarını, compiler_files gibi özelliklere hedef bağımlılıklarla cc_toolchain için kullanılabilir hale getirmeniz gerekir. tool_paths öğesinin de değiştirilmesi gerekir.
  • Hangi işaretlerin farklı işlemlere (bağlantı oluşturma veya başka bir işlem türü) iletilmesi gerektiğini özelleştirmek için özellikler oluşturabilirsiniz.

Daha fazla bilgi

Daha fazla bilgi için C++ araç zinciri yapılandırması bölümüne bakın