Phần 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ẽ tìm hiểu cách:
- Thiết lập môi trường tạo bản dựng
- Sử dụng
--toolchain_resolution_debug
để gỡ lỗi phân giải chuỗi công cụ - Định cấu hình chuỗi công cụ C++
- Tạo quy tắc Starlark cung cấp cấu hình bổ sung cho
cc_toolchain
để Bazel có thể xây dựng ứng dụng bằngclang
- Tạo tệp nhị phân C++ cho bằng cách chạy
bazel build //main:hello-world
trê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ông cụ và thư viện thích hợp.
Phần hướng dẫn 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 tạo bản dựng
Thiết lập môi trường tạo bản dựng như sau:
Nếu bạn chưa thực hiện, hãy tải và cài đặt Bazel 7.0.2 trở lên.
Thêm một tệp
WORKSPACE
trống vào thư mục gốc.Thêm mục tiêu
cc_binary
sau đây vào tệpmain/BUILD
:load("@rules_cc//cc:defs.bzl", "cc_binary") 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 tạo bản dựng, chẳng hạn như
process-wrapper
, nên chuỗi công cụ C++ mặc định sẵn có sẽ được chỉ định cho nền tảng lưu trữ. Việc 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ụ đã tạo trong hướng dẫn này. Do đó, mục tiêucc_binary
cũng được tạo 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-world
Quá trình tạo bản dựng thành công mà không cần bất kỳ chuỗi công cụ nào được đăng ký trong
WORKSPACE
.Để xem thêm tính năng nâng cao, hãy chạy:
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
Nếu không chỉ định
--platforms
, Bazel sẽ tạo mục tiêu cho@local_config_platform//:host
bằ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 liên tục tạo ứng dụng và loại bỏ từng lỗi như mô tả sau đây.
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ỉ nên thay đổi một chút giữa các phiên bản clang khác nhau.
Thêm
toolchain/BUILD
bằngfilegroup(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 đó, hãy đăng ký chuỗi công cụ bằng
WORKSPACE
bằngregister_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64" )
Bước này xác định một
cc_toolchain
và liên kết nó với một mục tiêutoolchain
cho cấu hình máy chủ lưu trữ.Chạy lại bản dựng. Vì gói
toolchain
chưa xác định mục tiêulinux_x86_64_toolchain_config
, nên Bazel sẽ gửi 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 nhóm tệp 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 gửi thông báo lỗi sau đây:
'//toolchain:linux_x86_64_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'.
CcToolchainConfigInfo
là một trì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ấpCcToolchainConfigInfo
cho Bazel bằng cách tạo tệptoolchain/cc_toolchain_config.bzl
có 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ạoCcToolchainConfigInfo
của trình cung cấp cần thiết. Để sử dụng quy tắccc_toolchain_config
, hãy thêm câu lệnh tải vàotoolchain/BUILD
ngay bên dưới câu lệnh gói:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
Đồng thời thay thế nhóm tệp "linux_x86_64_toolchain_config" bằng phần khai báo quy tắc
cc_toolchain_config
:cc_toolchain_config(name = "linux_x86_64_toolchain_config")
Chạy lại bản dựng. Bazel gửi thông báo lỗi sau đây:
.../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 để thử tạo mã nhưng vẫn chưa biết nên sử dụng công cụ nào để hoàn tất các thao tác tạo bản dựng bắt buộc. Bạn sẽ sửa đổi phương thức triển khai quy tắc Starlark để cho Bazel biết nên sử dụng công cụ nào. Để làm được việc đó, bạn cần hàm khởi tạo tool_path() trong
@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
/usr/bin/clang
và/usr/bin/ld
là đường dẫn chính xác cho hệ thống của bạn.Chạy lại bản dựng. Bazel gửi thông báo lỗi sau đây:
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 nơi để tìm các tiêu đề đi kèm. 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
includes
củacc_binary
, nhưng ở đây, bạn có thể giải quyết vấn đề này ở cấp chuỗi công cụ bằng tham sốcxx_builtin_include_directories
củacc_common.create_cc_toolchain_config_info
. Hãy lưu ý rằng nếu bạn đang sử dụng một phiên bản khác củaclang
, đường dẫn bao gồm sẽ khác. Những đường dẫn này cũng có thể khác nhau tuỳ thuộc vào sự 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 bản dựng, bạn sẽ thấy lỗi như sau:
/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'
Lý do là trình liên kết bị thiếu thư viện tiêu chuẩn C++ và không tìm được ký hiệu của mình. 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
linkopts
củacc_binary
. Ở đây, vấn đề đượ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], )
Chạy
bazel build //main:hello-world
, cuối cùng nó cũng sẽ tạo thành công tệp nhị phân cho máy chủ lưu trữ.Trong
toolchain/BUILD
, hãy sao chép các mục tiêucc_toolchain_config
,cc_toolchain
vàtoolchain
rồi thay thếlinux_x86_64
bằngandroid_x86_64
trong tên mục tiêu.Trong
WORKSPACE
, 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 nên 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 một quy tắc riêng (tức là nhà cung cấp CcToolchainConfigInfo
) cho các nền tảng riêng biệt.
Kiểm tra bài tập của bạn
Trong hướng dẫn này, bạn đã tìm hiểu cách định cấu hình chuỗi công cụ C++ cơ bản, nhưng chuỗi công cụ mạnh mẽ hơn so với ví dụ đơn giản này.
Những điểm chính cần ghi nhớ là:
- Bạn cần chỉ định một cờ
platforms
trùng khớp trong dòng lệnh để Bazel phân giải cho chuỗi công cụ đối với các giá trị ràng buộc tương tự trên nền tảng. Tài liệu này có thêm thông tin về cờ cấu hình dành riêng cho từng ngôn ngữ. - Bạn phải cho chuỗi công cụ biết vị trí của các công cụ này. Trong hướng dẫn này, có một phiên bản đơn giản để bạn truy cập vào các công cụ từ hệ thống. Nếu muốn có một phương pháp độc lập hơn, bạn có thể đọc bài viết về không gian làm việc. Các công cụ của bạn có thể đến từ một không gian làm việc khác và bạn phải cung cấp các tệp đó cho
cc_toolchain
vớ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
. Bạn cũng cần thay đổitool_paths
. - 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 bài viết Cấu hình chuỗi công cụ C++