Bazel có thể tạo và kiểm thử mã trên nhiều phần cứng, hệ điều hành và cấu hình hệ thống bằng cách sử dụng nhiều phiên bản công cụ xây dựng, chẳng hạn như trình liên kết và trình biên dịch. Để giúp xử lý sự phức tạp này, Bazel đã đưa ra khái niệm về các quy tắc ràng buộc và nền tảng. Quy tắc ràng buộc là một phương diện mà trong đó môi trường xây dựng hoặc môi trường thực tế có thể khác nhau, chẳng hạn như cấu trúc CPU, sự hiện diện hay không có GPU, hoặc phiên bản của trình biên dịch do hệ thống cài đặt. Nền tảng là một tập hợp các lựa chọn được đặt tên cho những quy tắc ràng buộc này, đại diện cho các tài nguyên cụ thể có sẵn trong một số môi trường.
Việc lập mô hình môi trường dưới dạng nền tảng giúp Bazel tự động chọn chuỗi công cụ phù hợp cho các hành động tạo bản dựng. Bạn cũng có thể sử dụng các nền tảng kết hợp với quy tắc config_setting để ghi các thuộc tính có thể định cấu hình.
Bazel nhận ra ba vai trò mà một nền tảng có thể phục vụ:
- Host (Máy chủ lưu trữ) – nền tảng mà Bazel tự chạy.
- Thực thi – một nền tảng mà trên đó các công cụ bản dựng thực thi các hành động tạo bản dựng để tạo ra kết quả trung gian và đầu ra cuối cùng.
- Đích – một nền tảng nơi lưu trữ và thực thi đầu ra cuối cùng.
Bazel hỗ trợ các trường hợp xây dựng sau đây liên quan đến nền tảng:
Bản dựng một nền tảng (mặc định) – nền tảng lưu trữ, thực thi và đích giống nhau. Ví dụ: tạo một tệp thực thi Linux trên Ubuntu chạy trên CPU Intel x64.
Bản dựng biên dịch chéo – nền tảng lưu trữ và thực thi giống nhau, nhưng nền tảng mục tiêu là khác nhau. Ví dụ: tạo một ứng dụng iOS trên macOS chạy trên MacBook Pro.
Các bản dựng đa nền tảng – nền tảng lưu trữ, thực thi và nền tảng đích đều khác nhau.
Xác định các điều kiện ràng buộc và nền tảng
Không gian cho các lựa chọn khả thi cho nền tảng được xác định bằng cách sử dụng các quy tắc constraint_setting
và constraint_value
trong tệp BUILD
. constraint_setting
tạo một nhóm mới, trong khi constraint_value
tạo một giá trị mới cho một nhóm nhất định; hai tác vụ này cùng nhau xác định một cách hiệu quả một enum và các giá trị có thể có. Ví dụ: đoạn mã sau đây của tệp BUILD
giới thiệu một quy tắc ràng buộc cho phiên bản glibc của hệ thống với hai giá trị có thể có.
constraint_setting(name = "glibc_version")
constraint_value(
name = "glibc_2_25",
constraint_setting = ":glibc_version",
)
constraint_value(
name = "glibc_2_26",
constraint_setting = ":glibc_version",
)
Bạn có thể xác định các quy tắc ràng buộc và giá trị của chúng trên các gói khác nhau trong không gian làm việc. Các mẫu này được tham chiếu theo nhãn và tuân theo các chế độ kiểm soát chế độ hiển thị thông thường. Nếu chế độ hiển thị cho phép, bạn có thể mở rộng một chế độ cài đặt ràng buộc hiện có bằng cách xác định giá trị của riêng bạn cho chế độ cài đặt đó.
Quy tắc platform
giới thiệu một nền tảng mới có một số lựa chọn nhất định về các giá trị ràng buộc. Nội dung sau đây tạo một nền tảng có tên linux_x86
và nói rằng mô tả mọi môi trường chạy hệ điều hành Linux trên cấu trúc x86_64 có phiên bản glibc 2.25. (Xem phần bên dưới để biết thêm về các hạn chế tích hợp của Bazel.)
platform(
name = "linux_x86",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
":glibc_2_25",
],
)
Những quy tắc ràng buộc và nền tảng nhìn chung hữu ích
Để đảm bảo tính nhất quán cho hệ sinh thái, đội ngũ Bazel duy trì một kho lưu trữ với các định nghĩa ràng buộc cho các kiến trúc CPU và hệ điều hành phổ biến nhất. Tất cả các khoá này đều nằm tại https://github.com/bazelbuild/platforms.
Bazel xuất xưởng cùng với định nghĩa nền tảng đặc biệt sau đây: @local_config_platform//:host
. Đây là giá trị nền tảng lưu trữ được phát hiện tự động – đại diện cho nền tảng được phát hiện tự động cho hệ thống mà Bazel đang chạy.
Chỉ định nền tảng cho bản dựng
Bạn có thể chỉ định nền tảng lưu trữ và nền tảng đích cho một bản dựng bằng cách sử dụng các cờ dòng lệnh sau:
--host_platform
– mặc định là@bazel_tools//platforms:host_platform
--platforms
– mặc định là@bazel_tools//platforms:target_platform
Bỏ qua các mục tiêu không tương thích
Khi xây dựng cho một nền tảng mục tiêu cụ thể, bạn nên bỏ qua các mục tiêu không bao giờ hoạt động trên nền tảng đó. Ví dụ: trình điều khiển thiết bị Windows của bạn có thể sẽ tạo ra nhiều lỗi trình biên dịch khi tạo bản dựng trên máy Linux có //...
. Sử dụng thuộc tính target_compatible_with
để cho Bazel biết mã của bạn có những hạn chế đối với nền tảng mục tiêu nào.
Cách sử dụng đơn giản nhất của thuộc tính này là giới hạn mục tiêu trong một nền tảng duy nhất.
Mục tiêu sẽ không được tạo cho bất cứ nền tảng nào không đáp ứng được tất cả các ràng buộc. Ví dụ sau đây giới hạn win_driver_lib.cc
ở Windows 64 bit.
cc_library(
name = "win_driver_lib",
srcs = ["win_driver_lib.cc"],
target_compatible_with = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
],
)
:win_driver_lib
chỉ tương thích với các bản dựng Windows 64 bit và không tương thích với những thiết bị khác. Không tương thích mang tính bắc cầu. Mọi mục tiêu phụ thuộc bắc cầu vào một mục tiêu không tương thích đều bị coi là không tương thích.
Khi nào các mục tiêu bị bỏ qua?
Các mục tiêu bị bỏ qua khi bị coi là không tương thích và được đưa vào bản dựng trong quá trình mở rộng mẫu mục tiêu. Ví dụ: 2 lệnh gọi sau đây bỏ qua mọi mục tiêu không tương thích được tìm thấy trong bản mở rộng mẫu mục tiêu.
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
Các hoạt động kiểm thử không tương thích trong test_suite
cũng bị bỏ qua tương tự nếu test_suite
được chỉ định trên dòng lệnh bằng --expand_test_suites
.
Nói cách khác, các mục tiêu test_suite
trên dòng lệnh sẽ hoạt động giống như :all
và ...
. Việc sử dụng --noexpand_test_suites
sẽ ngăn việc mở rộng và khiến các mục tiêu test_suite
chứa các kiểm thử không tương thích cũng không tương thích.
Việc chỉ định rõ ràng mục tiêu không tương thích trên dòng lệnh sẽ dẫn đến thông báo lỗi và bản dựng không thành công.
$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully
Các quy tắc ràng buộc có tính biểu đạt cao hơn
Để linh hoạt hơn trong việc thể hiện các quy tắc ràng buộc, hãy sử dụng @platforms//:incompatible
constraint_value
mà nền tảng không thoả mãn.
Sử dụng select()
kết hợp với @platforms//:incompatible
để thể hiện các quy định hạn chế phức tạp hơn. Ví dụ: hãy sử dụng hàm này để triển khai logic OR cơ bản. Những ký tự sau đây đánh dấu một thư viện tương thích với macOS và Linux, ngoài ra không tương thích với nền tảng nào khác.
cc_library(
name = "unixish_lib",
srcs = ["unixish_lib.cc"],
target_compatible_with = select({
"@platforms//os:osx": [],
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)
Điều trên có thể được hiểu như sau:
- Khi nhắm mục tiêu macOS, mục tiêu không có hạn chế.
- Khi nhắm mục tiêu Linux, mục tiêu không có hạn chế.
- Nếu không, mục tiêu sẽ có quy tắc ràng buộc
@platforms//:incompatible
. Vì@platforms//:incompatible
không thuộc bất kỳ nền tảng nào, nên mục tiêu được xem là không tương thích.
Để các quy tắc ràng buộc của bạn dễ đọc hơn, hãy sử dụng selects.with_or()
của skylib.
Bạn có thể biểu thị khả năng tương thích ngược theo cách tương tự. Ví dụ sau mô tả một thư viện tương thích với mọi thứ, ngoại trừ ARM.
cc_library(
name = "non_arm_lib",
srcs = ["non_arm_lib.cc"],
target_compatible_with = select({
"@platforms//cpu:arm": ["@platforms//:incompatible"],
"//conditions:default": [],
],
)
Phát hiện các mục tiêu không tương thích bằng bazel cquery
Bạn có thể sử dụng IncompatiblePlatformProvider
trong định dạng đầu ra Starlark của bazel cquery
để phân biệt mục tiêu không tương thích với mục tiêu tương thích.
Chỉ số này có thể được sử dụng để lọc ra các mục tiêu không tương thích. Ví dụ bên dưới sẽ chỉ in nhãn cho các mục tiêu tương thích. Các mục tiêu không tương thích sẽ không được in.
$ cat example.cquery
def format(target):
if "IncompatiblePlatformProvider" not in providers(target):
return target.label
return ""
$ bazel cquery //... --output=starlark --starlark:file=example.cquery
Vấn đề đã biết
Các mục tiêu không tương thích bỏ qua các hạn chế hiển thị.