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, sử dụng nhiều phiên bản công cụ xây dựng khác nhau 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 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à môi trường xây dựng hoặc môi trường sản xuất có thể khác nhau, chẳng hạn như cấu trúc CPU, có hoặc 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 các 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 mô hình hoá 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 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 3 vai trò mà một nền tảng có thể thực hiện:
- Máy chủ lưu trữ – nền tảng mà chính Bazel chạy trên đó.
- Thực thi – một nền tảng mà trên đó các công cụ xây dựng thực thi các hành động xây dựng để tạo ra đầu ra trung gian và cuối cùng.
- Mục tiêu – một nền tảng lưu trữ và thực thi kết quả cuối cùng.
Bazel hỗ trợ các kịch bản 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à mục tiêu giống nhau. Ví dụ: tạo 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à nền tảng thực thi giống nhau, nhưng nền tảng 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.
Bản dựng nhiều nền tảng – máy chủ lưu trữ, nền tảng thực thi và nền tảng mục tiêu đều khác nhau.
Xác định các quy tắc ràng buộc và nền tảng
Không gian lựa chọn có thể có cho các nền tảng được xác định bằng cách sử dụng 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, các phương diện này
xác định 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
đưa ra một quy tắc 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 quy tắc ràng buộc và giá trị của các quy tắc đó trên nhiều gói trong không gian làm việc. Các thành phần này được tham chiếu theo nhãn và tuân theo các chế độ điều khiển chế độ hiển thị thông thường. Nếu chế độ hiển thị cho phép, bạn có thể mở rộng chế độ cài đặt quy tắc 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 với một số lựa chọn nhất định về giá trị quy tắc ràng buộc. Nội dung sau đây sẽ tạo một nền tảng có tên 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 phần bên dưới để biết thêm về các quy tắc ràng buộc 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 thường hữu ích
Để duy trì hệ sinh thái nhất quán, đội ngũ Bazel duy trì một kho lưu trữ kèm theo các định nghĩa ràng buộc cho các hệ điều hành và kiến trúc CPU phổ biến nhất. Tất cả các nền tảng này đều nằm trong https://github.com/bazelbuild/platforms.
Bazel được phân phối kèm theo định nghĩa nền tảng đặc biệt sau: @platforms//host
(bí danh là @bazel_tools//tools:host_platform
). Đây là giá trị nền tảng máy chủ đượ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 máy chủ lưu trữ và nền tảng mục tiêu cho một 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 repo phát hiện CPU và hệ điều hành 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ênHOST_CONSTRAINTS
. Bạn có thể sử dụng mảng này 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 lưu trữ- Điều này có nghĩa là khi không có cờ nào khác được đặt,
@platforms//host
là nền tảng mục tiêu. - Nếu bạn đặt
--host_platform
chứ không phải--platforms
, thì giá trị của--host_platform
sẽ là cả máy chủ lưu trữ và nền tảng 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 bản dựng cho một nền tảng 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ó thể 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 mã của bạn có những hạn chế nào đối với nền tảng mục tiêu.
Cách sử dụng đơn giản nhất của thuộc tính này là giới hạn một mục tiêu ở 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 mọi quy tắ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 để xây dựng bằng Windows 64 bit và không tương thích với mọi nền tảng khác. Tính không tương thích là 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 sẽ 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 quá trình mở rộng mẫu mục tiêu. Ví dụ: hai lệnh gọi sau đây sẽ bỏ qua mọi mục tiêu không tương thích 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
Tương tự, các kiểm thử không tương thích trong test_suite
sẽ 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 việc mở rộng và làm cho các mục tiêu test_suite
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 tự động nếu bạn bật --skip_incompatible_explicit_targets
.
Các quy tắc ràng buộc biểu cảm hơn
Để thể hiện các quy tắc ràng buộc một cách linh hoạt hơn, hãy sử dụng @platforms//:incompatible
constraint_value
mà không có 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 tắc hạn chế phức tạp hơn. Ví dụ: sử dụng hàm này để triển khai logic OR cơ bản. Phần sau đây đánh dấu một thư viện tương thích với macOS và Linux, nhưng không có trên 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"],
}),
)
Bạn có thể diễn giải nội dung trên như sau:
- Khi nhắm đến macOS, mục tiêu không có quy tắc ràng buộc nào.
- Khi nhắm đến Linux, mục tiêu không có quy tắc ràng buộc nào.
- 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 này được coi là không tương thích.
Để các quy tắc ràng buộc 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 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 định dạng đầu ra Starlark của bazel cquery
để 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ể dùng thông tin này để 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ế về chế độ hiển thị.