Bu eğitimde, bir proje için C++ araç zincirlerinin nasıl yapılandırılacağı örnek bir senaryoyla açıklanmaktadır. 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
- Bazel'in uygulamayı
clang
ile derleyebilmesi içincc_toolchain
için ek yapılandırma sağlayan bir Starlark kuralı oluşturun bazel build --config=clang_config //main:hello-world
dosyasını Linux makinesinde ç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:
Henüz yapmadıysanız Bazel 0.23 veya sonraki bir sürümü indirip yükleyin.
GitHub'dan örnek C++ projesini indirip yerel makinenizdeki boş bir dizine yerleştirin.
main/BUILD
dosyasına aşağıdakicc_binary
hedefini ekleyin:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
--config
işaretinin kullanımını etkinleştirmek için çalışma alanı dizininin kökünde aşağıdaki içerikleri içeren 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şaretler için dokümanlara bakın:
crosstool_top
,
cpu
ve
host_crosstool_top
.
bazel build --config=clang_config //main:hello-world
ile hedefinizi derlediğinizde Bazel, cc_toolchain_suite
//toolchain:clang_suite
öğesinden özel araç zincirinizi kullanır. Paket, farklı CPU'lar için farklı araç zincirleri listeleyebilir. 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.
Aşağıdaki komutu kullanarak 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
dizini vetoolchain
dizininin içinde boş birBUILD
dosyası oluşturun.Derlemeyi tekrar çalıştırın.
toolchain
paketi henüzclang_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")
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 gerekliToolchainInfo
sağlayıcıyı sağlamayan bir kuralı işaret ettiğini keşfetti. Bu nedenle,--crosstool_top
'üToolchainInfo
sağlayan bir kurala (cc_toolchain_suite
kuralı) yönlendirmeniz gerekir.toolchain/BUILD
dosyasında boş filegroup öğesini aşağıdakiyle değiştirin:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )
toolchains
özelliği,--cpu
(ve belirtilirse--compiler
) değerlerinicc_toolchain
ile otomatik olarak eşler. Henüzcc_toolchain
hedefi tanımlamadınız ve Bazel kısa süre içinde bu konuda şikayette bulunacaktır.Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı verir:
Rule '//toolchain:k8_toolchain' does not exist
Artık
cc_toolchain_suite.toolchains
özelliğindeki her değer içincc_toolchain
hedefleri 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, )
Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı verir:
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")
Derlemeyi tekrar çalıştırın. Bazel aşağıdaki hatayı verir:
'//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 birtoolchain/cc_toolchain_config.bzl
dosyası oluşturarak Bazel'eCcToolchainConfigInfo
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çintoolchains/BUILD
'e bir load 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")
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
kaynaklı tool_path() yapıcısına 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
'un sisteminiz için doğru yollar olduğundan emin olun.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, dahil edilen üstbilgileri nerede arayacağını bilmesi gerekir. Bu sorunu çözmenin birden fazla yolu vardır (ör.
cc_binary
öğesininincludes
özelliğini kullanmak). Ancak burada bu sorun,cc_common.create_cc_toolchain_config_info
öğesinincxx_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ğını unutmayın. 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-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, )
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
öğesininlinkopts
ö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], )
bazel build --config=clang_config //main:hello-world
dosyasını çalıştırırsanız sonunda derlenir.
Ç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ç setinin, araçların nerede olduğunu bilmesi 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 buradan çalışma alanları hakkında bilgi edinebilirsiniz. Araçlar farklı bir Workspace'ten gelebilir. Bu durumda, dosyalarını cc_toolchain
'e compiler_files
gibi özelliklerle hedef bağımlılıkları olacak şekilde sunmanı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.