Nền tảng

Báo cáo vấn đề Xem nguồn Nightly .

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 quản lý sự phức tạp này, Bazel đưa ra một khái niệm về các quy tắc ràng buộcnền tảng. Hạn chế là một phương diện mà trong đó môi trường bản dựng hoặc môi trường phát hành chính thức 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 các chuỗi công cụ phù hợp để xây dựng các hành độ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 công nhận 3 vai trò mà nền tảng có thể phục vụ:

  • Máy chủ lưu trữ – nền tảng mà Bazel tự chạy trên đó.
  • Execution (Thực thi) – một nền tảng mà trên đó các công cụ tạo bản dựng sẽ thực thi các thao tác tạo bản dựng để tạo ra kết quả trung gian và cuối cùng.
  • Target (Mục tiêu) – một nền tảng chứa và thực thi đầu ra 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ữ, nền tảng thực thi và nền tảng đích giống nhau. Ví dụ: xây dựng 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à nền tảng thực thi giống nhau, nhưng nền tảng đích thì khác nhau. Ví dụ: tạo một ứng dụng iOS trên macOS chạy trên MacBook Pro.

  • 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 quy tắc ràng buộc và nền tảng

Không gian của các lựa chọn có thể có cho nền tảng được xác định bằng cách sử dụng quy tắc constraint_settingconstraint_value trong các 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ác phương diện 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ó của phương diện đó. 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 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",
)

Các hạn chế và giá trị của các hạn chế đó có thể được xác định trên các gói khác nhau trong không gian làm việc. Các tệp 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 mình cho chế độ đó.

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ị ràng buộc. Sau đây là một nền tảng có tên linux_x86 và 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 2.25. (Xem phần dưới đây để biết thêm thông tin về các quy tắc ràng buộc tích hợp sẵn 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 phương pháp này đều có 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 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ờ dòng lệnh sau:

  • --host_platform – giá trị 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 cho thấy một mảng có tên là HOST_CONSTRAINTS có thể dùng trong các tệp BUILD và Starlark khác.
  • --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 là cả nền tảng lưu trữ và nền tảng mục tiêu.

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 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ủa bạn có thể sẽ tạo ra nhiều lỗi trình biên dịch khi xây 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ế nào của nền tảng mục tiêu.

Cách sử dụng đơn giản nhất thuộc tính này để hạn chế 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 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 với bản dựng với Windows 64 bit và không tương thích với tất cả các phiên bản khác. Sự 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 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 việc mở rộng mẫu mục tiêu. Ví dụ: 2 lệnh gọi sau đây sẽ bỏ qua mọi mục tiêu không tương thích được tìm thấy trong việc 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 một test_suite cũng bị bỏ qua theo cách 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 hoạt động giống như :all.... 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ụ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 tường minh không tương thích sẽ tự động bị bỏ qua nếu bạn bật --skip_incompatible_explicit_targets.

Các quy tắc ràng buộc về biểu thức khác

Để 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à không nền tảng nào đáp ứng.

Hãy 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ụ: hãy 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"],
    }),
)

Nội dung trên có thể được hiểu như sau:

  1. Khi nhắm mục tiêu đến macOS, mục tiêu không có hạn chế nào.
  2. Khi nhắm mục tiêu Linux, mục tiêu không có hạn chế nào.
  3. 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 cho 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 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 nội dung 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 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ể sử dụng chỉ số 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 sẽ bỏ qua các hạn chế về khả năng hiển thị.