Hướng dẫn này sử dụng một tình huống ví dụ để mô tả cách định cấu hình chuỗi công cụ C++ cho một dự án.
Kiến thức bạn sẽ học được
Trong hướng dẫn này, bạn sẽ học cách:
- Thiết lập môi trường xây dựng
- Sử dụng
--toolchain_resolution_debugđể gỡ lỗi quá trình phân giải chuỗi công cụ - Định cấu hình chuỗi công cụ C++
- Tạo một quy tắc Starlark cung cấp cấu hình bổ sung cho
cc_toolchainđể Bazel có thể tạo ứng dụng bằngclang - Tạo tệp nhị phân C++ cho bằng cách chạy
bazel build //main:hello-worldtrên máy Linux - Biên dịch chéo tệp nhị phân cho Android bằng cách chạy
bazel build //main:hello-world --platforms=//:android_x86_64
Trước khi bắt đầu
Hướng dẫn này giả định rằng bạn đang sử dụng Linux và đã tạo thành công các ứng dụng C++
cũng như cài đặt các công cụ và thư viện thích hợp. Hướng dẫn này
sử dụng clang version 16, mà bạn có thể cài đặt trên hệ thống của mình.
Thiết lập môi trường xây dựng
Thiết lập môi trường xây dựng như sau:
Tải xuống và cài đặt Bazel 7.0.2 trở lên (nếu bạn chưa thực hiện).
Thêm một tệp
MODULE.bazeltrống vào thư mục gốc.Thêm mục tiêu
cc_binarysau vào tệpmain/BUILD:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )Vì Bazel sử dụng nhiều công cụ nội bộ được viết bằng C++ trong quá trình xây dựng, chẳng hạn như
process-wrapper, nên chuỗi công cụ C++ mặc định có sẵn được chỉ định cho nền tảng máy chủ. Điều này cho phép các công cụ nội bộ này xây dựng bằng chuỗi công cụ đó của công cụ được tạo trong hướng dẫn này. Do đó, mục tiêucc_binarycũng được xây dựng bằng chuỗi công cụ mặc định.Chạy bản dựng bằng lệnh sau:
bazel build //main:hello-worldBản dựng thành công mà không có chuỗi công cụ nào được đăng ký trong
MODULE.bazel.Để xem thêm thông tin chi tiết, hãy chạy:
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-k8Nếu không chỉ định
--platforms, Bazel sẽ tạo mục tiêu cho@platforms//hostbằng@bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8.
Định cấu hình chuỗi công cụ C++
Để định cấu hình chuỗi công cụ C++, hãy tạo ứng dụng nhiều lần và loại bỏ từng lỗi một như mô tả sau.
Hướng dẫn này cũng giả định clang version 9.0.1, mặc dù thông tin chi tiết chỉ thay đổi
một chút giữa các phiên bản clang khác nhau.
Thêm
toolchain/BUILDvớifilegroup(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", ], )Sau đó, thêm các phần phụ thuộc thích hợp và đăng ký chuỗi công cụ bằng
MODULE.bazelvớibazel_dep(name = "platforms", version = "0.0.10") register_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64" )Bước này xác định
cc_toolchainvà liên kết nó với mục tiêutoolchaincho cấu hình máy chủ.Chạy lại bản dựng. Vì gói
toolchainchưa xác định mục tiêulinux_x86_64_toolchain_confignên Bazel sẽ đưa ra lỗi sau: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.Trong tệp
toolchain/BUILD, hãy xác định một filegroup trống như sau:package(default_visibility = ["//visibility:public"]) filegroup(name = "linux_x86_64_toolchain_config")Chạy lại bản dựng. Bazel đưa ra lỗi sau:
'//toolchain:linux_x86_64_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'.CcToolchainConfigInfolà một nhà cung cấp mà bạn dùng để định cấu hình chuỗi công cụ C++. Để khắc phục lỗi này, hãy tạo một quy tắc Starlark cung cấpCcToolchainConfigInfocho Bazel bằng cách tạo một tệptoolchain/cc_toolchain_config.bzlcó nội dung sau: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()tạo nhà cung cấp cần thiếtCcToolchainConfigInfo. Để sử dụng quy tắccc_toolchain_config, hãy thêm câu lệnh tải vàotoolchain/BUILDngay bên dưới câu lệnh gói:load(":cc_toolchain_config.bzl", "cc_toolchain_config")Và thay thế filegroup "linux_x86_64_toolchain_config" bằng một khai báo về quy tắc
cc_toolchain_config:cc_toolchain_config(name = "linux_x86_64_toolchain_config")Chạy lại bản dựng. Bazel đưa ra lỗi sau:
.../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`Tại thời điểm này, Bazel có đủ thông tin để cố gắng tạo mã nhưng vẫn không biết công cụ nào cần sử dụng để hoàn tất các thao tác xây dựng bắt buộc. Bạn sẽ sửa đổi việc triển khai quy tắc Starlark để cho Bazel biết công cụ nào cần sử dụng. Để làm việc đó, bạn cần hàm khởi tạo
tool_path()từ@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl:# 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 )Đảm bảo rằng
/usr/bin/clangvà/usr/bin/ldlà đường dẫn chính xác cho hệ thống của bạn.Chạy lại bản dựng. Bazel đưa ra lỗi sau:
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 cần biết vị trí tìm kiếm các tiêu đề được đưa vào. Có nhiều cách để giải quyết vấn đề này, chẳng hạn như sử dụng thuộc tính
includescủacc_binary, nhưng ở đây, vấn đề này được giải quyết ở cấp chuỗi công cụ bằng tham sốcxx_builtin_include_directoriescủacc_common.create_cc_toolchain_config_info. Xin lưu ý rằng nếu bạn đang sử dụng một phiên bảnclangkhác, thì đường dẫn bao gồm sẽ khác. Các đường dẫn này cũng có thể khác nhau tuỳ thuộc vào bản phân phối.Sửa đổi giá trị trả về trong
toolchain/cc_toolchain_config.bzlđể có dạng như sau: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, )Chạy lại lệnh xây dựng, bạn sẽ thấy một lỗi như:
/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'Nguyên nhân là do trình liên kết thiếu thư viện tiêu chuẩn C++ và không tìm thấy các ký hiệu của thư viện đó. Có nhiều cách để giải quyết vấn đề này, chẳng hạn như sử dụng thuộc tính
linkoptscủacc_binary. Ở đây, vấn đề này được giải quyết bằng cách đảm bảo rằng mọi mục tiêu sử dụng chuỗi công cụ đều không phải chỉ định cờ này.Sao chép mã sau vào
toolchain/cc_toolchain_config.bzl:# 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], )Khi chạy
bazel build //main:hello-world, cuối cùng, mã này sẽ tạo tệp nhị phân thành công cho máy chủ.Trong
toolchain/BUILD, hãy sao chép các mục tiêucc_toolchain_config,cc_toolchain, vàtoolchain, đồng thời thay thếlinux_x86_64bằngandroid_x86_64trong tên mục tiêu.Trong
MODULE.bazel, hãy đăng ký chuỗi công cụ cho Androidregister_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64", "//toolchain:cc_toolchain_for_android_x86_64" )Chạy
bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64để tạo tệp nhị phân cho Android.
Trên thực tế, Linux và Android phải có các cấu hình chuỗi công cụ C++ khác nhau. Bạn
có thể sửa đổi cc_toolchain_config hiện có cho các điểm khác biệt hoặc
tạo các quy tắc riêng (tức là nhà cung cấp CcToolchainConfigInfo) cho các
nền tảng riêng biệt.
Xem lại công việc của bạn
Trong hướng dẫn này, bạn đã học cách định cấu hình một chuỗi công cụ C++ cơ bản, nhưng các chuỗi công cụ mạnh mẽ hơn ví dụ này.
Những điểm chính cần lưu ý là:
- Bạn cần chỉ định một cờ
platformsphù hợp trong dòng lệnh cho Bazel để phân giải thành chuỗi công cụ cho cùng các giá trị ràng buộc trên nền tảng. Tài liệu này chứa thêm thông tin về các cờ cấu hình dành riêng cho ngôn ngữ. - Bạn phải cho chuỗi công cụ biết vị trí của các công cụ. Trong hướng dẫn này
có một phiên bản đơn giản hoá, trong đó bạn truy cập vào các công cụ từ hệ thống. Nếu
bạn quan tâm đến một phương pháp tự chứa nhiều hơn, bạn có thể đọc về
các phần phụ thuộc bên ngoài. Các công cụ của bạn có thể đến từ một
mô-đun khác và bạn sẽ phải cung cấp các tệp của chúng cho
cc_toolchainvới các phần phụ thuộc mục tiêu trên các thuộc tính, chẳng hạn nhưcompiler_files.tool_pathscũng cần thay đổi. - Bạn có thể tạo các tính năng để tuỳ chỉnh cờ nào sẽ được truyền đến các thao tác khác nhau, có thể là liên kết hoặc bất kỳ loại thao tác nào khác.
Tài liệu đọc thêm
Để biết thêm thông tin chi tiết, hãy xem phần Cấu hình chuỗi công cụ C++