Hướng dẫn này sử dụng một tình huống mẫu để mô tả cách định cấu hình 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 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++ 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 bạn đang sử dụng Linux và đã xây dựng thành công C++
công cụ và cài đặt công cụ cũng như thư viện thích hợp. 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:
Hãy tải xuống và cài đặt Bazel nếu bạn chưa thực hiện việc này 7.0.2 trở lên.
Thêm một tệp
MODULE.bazel
trống ở thư mục gốc.Thêm mục tiêu
cc_binary
sau đây 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 tạo bản dựng, chẳng hạn như là
process-wrapper
, thì chuỗi công cụ C++ mặc định có sẵn sẽ được chỉ định cho nền tảng lưu trữ. Nhờ vậy, các công cụ nội bộ này có thể xây dựng bằng cách sử dụng của chuỗi công cụ đượ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
Bản dựng này hoạt động thành công mà không cần bất kỳ chuỗi công cụ nào được đăng ký trong
MODULE.bazel
.Để xem thêm về nội dung 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 @@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
Nếu không chỉ định
--platforms
, Bazel sẽ tạo mục tiêu cho@platforms//host
đang sử dụ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 một như được mô tả như sau.
Hàm 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/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 thêm các phần phụ thuộc thích hợp và đăng ký chuỗi công cụ với
MODULE.bazel
bằngbazel_dep(name = "platforms", version = "0.0.10") register_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ộttoolchain
đích 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 địnhlinux_x86_64_toolchain_config
mục tiêu, 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 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 đưa ra lỗi sau:
'//toolchain:linux_x86_64_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'.
CcToolchainConfigInfo
là trình cung cấp mà bạn dùng để định cấu hình C++ chuỗi công cụ. Để khắc phục lỗi này, hãy tạo một quy tắc Starlark cung cấpCcToolchainConfigInfo
đến Bazel bằng cách tạo một 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ạo trình cung cấp cần thiếtCcToolchainConfigInfo
. Để sử dụng quy tắccc_toolchain_config
, hãy thêm một tải chotoolchain/BUILD
ngay bên dưới câu lệnh của gói:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
Và thay thế "linux_x86_64_toolchain_config" nhóm tệp có một phần khai báo của 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 để thử tạo mã, nhưng vẫn không biết nên dùng công cụ nào để hoàn thành bản dựng theo yêu cầu hành động. Bạn sẽ sửa đổi quy trình triển khai quy tắc Starlark để cho Bazel biết những gì để sử dụng. Để làm được 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 )
Hãy đả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 đư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 nơi tìm kiếm các tiêu đề đi kèm. Có nhiều 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, vấn đề này được giải quyết ở cấp chuỗi công cụ vớicxx_builtin_include_directories
tham số củacc_common.create_cc_toolchain_config_info
. Xin lưu ý rằng nếu bạn đang sử dụng phiên bản khác củaclang
, đường dẫn bao gồm sẽ là khác. Các đường dẫn này cũng có thể khác nhau tuỳ thuộc vào cách thức phân phối.Sửa đổi giá trị trả về trong
toolchain/cc_toolchain_config.bzl
thành dạng như sau 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 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 tiêu chuẩn C++ thư viện và không thể 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
linkopts
củacc_binary
. Ở đây, vấn đề được giải quyết bằng cách giúp đả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 chuỗi cờ.Sao chép mã nguồn sau đây 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], )
Đang chạy
bazel build //main:hello-world
, cuối cùng tệp này sẽ tạo tệp nhị phân thành công cho máy chủ lưu trữ.Trong
toolchain/BUILD
, hãy sao chépcc_toolchain_config
,cc_toolchain
và Nhắm mục tiêutoolchain
và thay thếlinux_x86_64
bằngandroid_x86_64
trong 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.
Trong 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 tại cho phù hợp với sự khác biệt hoặc
tạo một quy tắc riêng (tức là nhà cung cấp CcToolchainConfigInfo
) cho
nền tảng.
Xem lại 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 ví dụ này.
Những điểm chính cần ghi nhớ là:
- Bạn cần chỉ định một cờ
platforms
phù hợp trong dòng lệnh cho Bazel để phân giải cho chuỗi công cụ cho cùng các giá trị ràng buộc trên chủ. Tài liệu này có thêm thông tin cụ thể về từng ngôn ngữ cờ cấu hình. - Bạn phải cho chuỗi công cụ biết vị trí của các công cụ. Nội dung của hướng dẫn này
có một phiên bản đơn giản giúp 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 cách tiếp cận độc lập hơn, bạn có thể đọc về
các phần phụ thuộc bên ngoài. Công cụ của bạn có thể đến từ
mô-đun khác nhau và bạn sẽ phải cung cấp các tệp của họ cho
cc_toolchain
có 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 những cờ sẽ được truyền đến các hành động khác nhau, có thể là liên kết hoặc bất kỳ loại hành động nào khác.
Tài liệu đọc thêm
Để biết thêm thông tin chi tiết, hãy xem Chuỗi công cụ C++ cấu hình