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. Hướng dẫn này dựa trên một
dự án C++ mẫu
tạo bản dựng không có lỗi bằng clang.
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
- Đị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 - Xác nhận kết quả dự kiến bằng cách chạy
bazel build --config=clang_config //main:hello-worldtrên một máy Linux - Tạo ứng dụng C++
Trước khi bắt đầu
Thiết lập môi trường xây dựng
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 9.0.1, 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 như sau:
Tải xuống và cài đặt Bazel 0.23 trở lên (nếu bạn chưa thực hiện).
Tải dự án C++ mẫu xuống từ GitHub rồi đặt vào một thư mục trống trên máy cục bộ.
Thêm mục tiêu
cc_binarysau vào tệpmain/BUILD:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )Tạo một tệp
.bazelrcở thư mục gốc của không gian làm việc có nội dung sau để bật việc sử dụng cờ--config:# 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
Đối với mục build:{config_name} --flag=value, cờ dòng lệnh
--config={config_name} được liên kết với cờ cụ thể đó. Xem
tài liệu về các cờ được sử dụng:
crosstool_top,
cpu và
host_crosstool_top.
Khi bạn tạo mục tiêu
bằng bazel build --config=clang_config //main:hello-world, Bazel sẽ sử dụng
chuỗi công cụ tuỳ chỉnh của bạn từ
cc_toolchain_suite
//toolchain:clang_suite. Bộ này có thể liệt kê các
chuỗi công cụ khác nhau cho các CPU khác nhau,
đó là lý do bộ này được phân biệt bằng cờ --cpu=k8.
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 có sẵn được chỉ định cho nền tảng máy chủ để các công cụ này được tạo bằng chuỗi công cụ đó thay vì công cụ được tạo trong hướng dẫn này.
Đị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ả bên dưới.
Chạy bản dựng bằng lệnh sau:
bazel build --config=clang_config //main:hello-worldVì bạn đã chỉ định
--crosstool_top=//toolchain:clang_suitetrong tệp.bazelrc, nên Bazel sẽ đưa ra lỗi sau:No such package `toolchain`: BUILD file not found on package path.Trong thư mục không gian làm việc, hãy tạo thư mục
toolchaincho gói và một tệpBUILDtrống bên trong thư mụctoolchain.Chạy lại bản dựng. Vì gói
toolchainchưa xác định mục tiêuclang_suite, nên Bazel sẽ đưa ra lỗi sau:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILDTrong tệp
toolchain/BUILD, hãy xác định một filegroup trống như sau:package(default_visibility = ["//visibility:public"]) filegroup(name = "clang_suite")Chạy lại bản dựng. Bazel đưa ra lỗi sau:
'//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'Bazel phát hiện thấy cờ
--crosstool_toptrỏ đến một quy tắc không cung cấp trình cung cấpToolchainInfocần thiết. Vì vậy, bạn cần trỏ--crosstool_topđến một quy tắc cung cấpToolchainInfo– đó là quy tắccc_toolchain_suite. Trong tệptoolchain/BUILDfile, hãy thay thế filegroup trống bằng nội dung sau:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )Thuộc tính
toolchainstự động ánh xạ các giá trị--cpu(và cả--compilerkhi được chỉ định) đếncc_toolchain. Bạn chưa xác định bất kỳcc_toolchainmục tiêu nào và Bazel sẽ sớm phàn nàn về điều đó.Chạy lại bản dựng. Bazel đưa ra lỗi sau:
Rule '//toolchain:k8_toolchain' does not existBây giờ, bạn cần xác định các mục tiêu
cc_toolchaincho mọi giá trị trong thuộc tínhcc_toolchain_suite.toolchains. Thêm nội dung sau vào tệptoolchain/BUILD: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, )Chạy lại bản dựng. Bazel đưa ra lỗi sau:
Rule '//toolchain:k8_toolchain_config' does not existTiếp theo, hãy thêm mục tiêu ":k8_toolchain_config" vào tệp
toolchain/BUILD:filegroup(name = "k8_toolchain_config")Chạy lại bản dựng. Bazel đưa ra lỗi sau:
'//toolchain:k8_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'CcToolchainConfigInfolà 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ấ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 trình cung cấp cần thiếtCcToolchainConfigInfo. Để sử dụng quy tắccc_toolchain_config, hãy thêm một 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 "k8_toolchain_config" bằng một khai báo về quy tắc
cc_toolchain_configcc_toolchain_config(name = "k8_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 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 việc triển khai quy tắc Starlark để cho Bazel biết nên sử dụng công cụ nào. Để 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à các đườ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:
..../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 cần biết nơi 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. Hãy 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-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, )Chạy lại lệnh tạo 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 thư viện chuẩn C++ và không tìm thấy các ký hiệu của thư viện này. 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
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", "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], )Nếu bạn chạy
bazel build --config=clang_config //main:hello-world, thì cuối cùng, bản dựng sẽ được tạo.
Xem lại công việc
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 giản này.
Những điểm chính cần lưu ý là:
- Bạn cần chỉ định một cờ --crosstool_top trong dòng lệnh trỏ đến một cc_toolchain_suite
- Bạn có thể tạo một lối tắt cho một cấu hình cụ thể bằng tệp .bazelrc
- cc_toolchain_suite có thể liệt kê cc_toolchains cho các CPU và trình biên dịch khác nhau. Bạn có thể sử dụng các cờ dòng lệnh như --cpu để phân biệt.
- 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á để 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 độc lập hơn, bạn có thể đọc về
không gian làm việc tại đây. 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 sẽ phải cung cấp các tệp của chúng 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. tool_paths cũ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 Định cấu hình chuỗi công cụ C++