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. Chiến dịch này dựa trên một
ví dụ về dự án C++
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ẽ tìm hiểu cách:
- Thiết lập môi trường tạo bản dựng
- Định cấu hình chuỗi công cụ C++
- Tạo một quy tắc Starlark cung cấp thêm
cấu hình cho
cc_toolchain
để Bazel có thể tạo ứng dụng vớiclang
- Xác nhận kết quả mong đợi bằng cách chạy
bazel build --config=clang_config //main:hello-world
trên máy Linux - Xây dựng ứng dụng C++
Trước khi bắt đầu
Thiết lập môi trường tạo bản dựng
Hướng dẫn này giả định bạn đang sử dụng Linux và đã tạo thành công
C++ và đã cài đặt công cụ cũng như thư viện thích hợp.
Hướng dẫn này sử dụng clang version 9.0.1
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 như sau:
Nếu chưa thực hiện, tải xuống và cài đặt Bazel 0.23 trở lên.
Tải xuống ví dụ về dự án C++ từ GitHub rồi đặt vào một thư mục trống trên máy cục bộ của bạn.
Thêm mục tiêu
cc_binary
sau đây vào tệpmain/BUILD
:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
Tạo tệp
.bazelrc
ở gốc của thư mục không gian làm việc bằng những nội dung sau đây để cho phép 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 nhập build:{config_name} --flag=value
, cờ dòng lệnh
--config={config_name}
sẽ được liên kết với cờ cụ thể đó. Xem
tài liệu về cờ được sử dụng:
crosstool_top
!
cpu
và
host_crosstool_top
.
Khi bạn xây dựng mục tiêu của mình
với bazel build --config=clang_config //main:hello-world
, Bazel sử dụng
chuỗi công cụ tuỳ chỉnh từ
cc_toolchain_suite
//toolchain:clang_suite
. Bộ công cụ này có thể liệt kê
các
chuỗi công cụ cho nhiều CPU,
và đó là lý do chúng được phân biệt với 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ư trình bao bọc quy trình, chuỗi công cụ C++ mặc định có sẵn được chỉ định cho nền tảng lưu trữ để các công cụ này được xây dựng bằng cách sử dụng chuỗi công cụ đó thay vì hình ảnh đượ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 liên tục tạo ứng dụng và loại bỏ từng lỗi một như được mô tả dưới đây.
Chạy bản dựng bằng lệnh sau:
bazel build --config=clang_config //main:hello-world
Vì bạn đã chỉ định
--crosstool_top=//toolchain:clang_suite
trong.bazelrc
thì Bazel sẽ gặp 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
toolchain
cho gói và một tệpBUILD
trống trong thư mụctoolchain
.Chạy lại bản dựng. Vì gói
toolchain
chưa xác địnhclang_suite
mục tiêu, Bazel sẽ đưa ra lỗi sau:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILD
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 = "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 ra rằng cờ
--crosstool_top
trỏ đến một quy tắc không cung cấpToolchainInfo
cần thiết Google Cloud. 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
. Trongtoolchain/BUILD
, hãy thay thế nhóm tệp trống bằng:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )
Thuộc tính
toolchains
sẽ tự động liên kết--cpu
(và cũng có thể--compiler
khi được chỉ định) chocc_toolchain
. Bạn chưa xác định mọi mục tiêucc_toolchain
và Bazel sẽ phàn nàn về điều đó trong thời gian ngắn.Chạy lại bản dựng. Bazel đưa ra lỗi sau:
Rule '//toolchain:k8_toolchain' does not exist
Bây giờ, bạn cần xác định mục tiêu
cc_toolchain
cho mọi giá trị trong Thuộc tínhcc_toolchain_suite.toolchains
. Thêm đoạn mã sau vào phần 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 exist
Tiếp theo, hãy thêm ":k8_toolchain_config" nhắm mục tiêu đến 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'
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++ của bạn. Để 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 ra 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ế "k8_toolchain_config" với một phần khai báo
cc_toolchain_config
quy tắc:cc_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 để 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 điều đó, 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/clang
và/usr/bin/ld
là 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 đề đ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, 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 một 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
để xem 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 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 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ã nguồn sau đây 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
, trình duyệt đó sẽ cuối cùng là tạo dự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 giản này.
Những điểm chính cần ghi nhớ 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 lối tắt cho một cấu hình cụ thể bằng .bazelrc
tệp
– cc_toolchain_suite có thể liệt kê cc_toolchains
cho nhiều CPU và
trình biên dịch. Bạn có thể sử dụng 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ụ. 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ề
không gian làm việc tại đây. Công cụ của bạn có thể đến từ
không gian làm việc khác nhau và bạn sẽ phải cung cấp các tệp của họ
vào 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 đổi tool_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, hãy xem Cấu hình chuỗi công cụ C++