Bazel có thể tạo và kiểm thử mã trên nhiều loại phần cứng, hệ điều hành và cấu hình hệ thống, sử dụng nhiều phiên bản công cụ tạo khác nhau, chẳng hạn như trình liên kết và trình biên dịch. Để giúp quản lý sự phức tạp này, Bazel có khái niệm về các ràng buộc và nền tảng. Ràng buộc là một phương diện mà môi trường bản dựng hoặc sản xuất có thể khác nhau, chẳng hạn như cấu trúc CPU, sự hiện diện hoặc vắng mặt của GPU hoặc phiên bản của trình biên dịch được cài đặt trên hệ thống. Nền tảng là một tập hợp các lựa chọn được đặt tên cho cá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 mô hình hoá môi trường dưới dạng nền tảng giúp Bazel tự động chọn các chuỗi công cụ phù hợp cho các thao tác tạo. Bạn cũng có thể sử dụng nền tảng kết hợp với quy tắc config_setting để viết các thuộc tính có thể định cấu hình.
Bazel nhận ra 3 vai trò mà một nền tảng có thể đảm nhận:
- Máy chủ – nền tảng mà chính Bazel chạy.
- Thực thi – nền tảng mà các công cụ tạo thực thi các thao tác tạo để tạo ra kết quả đầu ra trung gian và cuối cùng.
- Mục tiêu – nền tảng mà kết quả đầu ra cuối cùng nằm và thực thi.
Bazel hỗ trợ các trường hợp tạo sau đây liên quan đến nền tảng:
Tạo một nền tảng (mặc định) – nền tảng máy chủ, thực thi và mục tiêu 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 máy chủ và thực thi giống nhau, nhưng nền tảng nhắm mục tiêu khác. Ví dụ: tạo một ứng dụng iOS trên macOS chạy trên MacBook Pro.
Tạo nhiều nền tảng – nền tảng máy chủ, thực thi và mục tiêu đều khác nhau.
Xác định các ràng buộc và nền tảng
Không gian của các lựa chọn có thể 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 phương diện mới, trong khi constraint_value tạo một giá trị mới cho một phương diện nhất định; cùng nhau, chúng xác định một cách hiệu quả một enum và các giá trị có thể có của enum đó. Ví dụ: đoạn mã sau đây của tệp BUILD giới thiệu một ràng buộc cho phiên bản glibc của hệ thống với 2 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 ràng buộc và giá trị của chúng trên nhiều gói trong không gian làm việc. Chúng được tham chiếu theo nhãn và tuân theo các chế độ kiểm soát khả năng hiển thị thông thường. Nếu khả năng hiển thị cho phép, bạn có thể mở rộng chế độ cài đặt ràng buộc hiện có bằng cách xác định giá trị của riêng mình cho chế độ cài đặt đó.
Quy tắc platform giới thiệu một nền tảng mới với một số lựa chọn về giá trị ràng buộc. Lệnh sau đây tạo một nền tảng có tên là linux_x86 và cho biết nền tảng này mô tả mọi môi trường chạy hệ điều hành Linux trên cấu trúc x86_64 với phiên bản glibc là 2.25. (Xem thêm thông tin về các ràng buộc tích hợp của Bazel bên dưới.)
platform(
name = "linux_x86",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
":glibc_2_25",
],
)
Các ràng buộc và nền tảng thường hữu ích
Để duy trì tính nhất quán của hệ sinh thái, nhóm Bazel duy trì một kho lưu trữ với các định nghĩa ràng buộc cho các cấu trúc CPU và hệ điều hành phổ biến nhất. Tất cả các định nghĩa này đều nằm trong https://github.com/bazelbuild/platforms.
Bazel đi kèm với định nghĩa nền tảng đặc biệt sau đây: @platforms//host (được đặt bí danh là @bazel_tools//tools:host_platform). Đây là giá trị nền tảng máy chủ được tự động phát hiện – đại diện cho nền tảng được tự động phát hiện 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 máy chủ và mục tiêu cho bản dựng bằng các cờ dòng lệnh sau:
--host_platform– mặc định là@bazel_tools//tools:host_platform- Mục tiêu này được đặt bí danh là
@platforms//host, được hỗ trợ bởi một quy tắc kho lưu trữ phát hiện hệ điều hành và CPU của máy chủ, đồng thời ghi mục tiêu nền tảng. - Ngoài ra còn có
@platforms//host:constraints.bzl, hiển thị một mảng có tên làHOST_CONSTRAINTS, có thể dùng trong các tệp BUILD và Starlark khác.
- Mục tiêu này được đặt bí danh là
--platforms– mặc định là nền tảng máy chủ- Điều này có nghĩa là khi không có cờ nào khác được đặt,
@platforms//hostlà nền tảng nhắm mục tiêu. - Nếu
--host_platformđược đặt và không phải là--platforms, thì giá trị của--host_platformlà cả nền tảng lưu trữ và nền tảng nhắm mục tiêu.
- Điều này có nghĩa là khi không có cờ nào khác được đặt,
Bỏ qua các mục tiêu không tương thích
Khi tạo cho một nền tảng nhắm mục tiêu cụ thể, bạn thường nên bỏ qua các mục tiêu sẽ 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ó khả năng sẽ tạo ra nhiều lỗi trình biên dịch khi tạo trên máy Linux bằng //.... Sử dụng thuộc tính
target_compatible_with
để cho Bazel biết các ràng buộc nền tảng nhắm mục tiêu mà mã của bạn có.
Cách sử dụng đơn giản nhất của thuộc tính này là hạn chế mục tiêu đối với một nền tảng duy nhất.
Mục tiêu sẽ không được tạo cho bất kỳ nền tảng nào không đáp ứng tất cả các ràng buộc. Ví dụ sau đây hạn chế win_driver_lib.cc đối với 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 là chỉ tương thích để tạo bằng Windows 64 bit và
không tương thích với mọi thứ khác. Tính không tương thích là tính chất 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 được coi là không tương thích.
Khi nào mục tiêu bị bỏ qua?
Các mục tiêu bị bỏ qua khi được coi là không tương thích và được đưa vào bản dựng như một phần của việc mở rộng mẫu mục tiêu. Ví dụ: 2 lời gọi sau đây bỏ qua mọi mục tiêu không tương thích được tìm thấy trong quá trình mở rộng mẫu mục tiêu.
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
Các kiểm thử không tương thích trong test_suite cũng bị bỏ qua 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 hoạt động giống như :all và .... Việc sử dụng --noexpand_test_suites sẽ ngăn quá trình mở rộng và khiến
test_suite các mục tiêu có 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ột 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 mục tiêu rõ ràng không tương thích sẽ bị bỏ qua một cách âm thầm nếu --skip_incompatible_explicit_targets được bật.
Các ràng buộc biểu cảm hơn
Để linh hoạt hơn trong việc thể hiện các ràng buộc, hãy sử dụng the
@platforms//:incompatible
constraint_value
mà không nền tảng nào đáp ứng.
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ụ: sử dụng quy tắc này để triển khai logic OR cơ bản. Lệnh sau đây đánh dấu một thư viện tương thích với macOS và Linux, nhưng không tương thích với các nền tảng 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"],
}),
)
Bạn có thể diễn giải nội dung trên như sau:
- Khi nhắm mục tiêu đến macOS, mục tiêu không có ràng buộc.
- Khi nhắm mục tiêu đến Linux, mục tiêu không có ràng buộc.
- Nếu không, mục tiêu sẽ có ràng buộc
@platforms//:incompatible. Vì@platforms//:incompatiblekhông thuộc bất kỳ nền tảng nào, nên mục tiêu được coi là không tương thích.
Để giúp các ràng buộc dễ đọc hơn, hãy sử dụng
skylib's
selects.with_or().
Bạn có thể thể hiện khả năng tương thích nghịch đảo theo cách tương tự. Ví dụ sau đây 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 bazel cquery's định dạng đầu ra Starlark để phân biệt các mục tiêu không tương thích với các mục tiêu tương thích.
Bạn có thể sử dụng tính năng này để lọc 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 quy định hạn chế về khả năng hiển thị restrictions.