Nền tảng

Báo cáo vấn đề Xem nguồn Hằng đêm · 7.3 · 7.2 · 7.1 · 7 · 6,5

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ác cấu hình hệ thống, sử dụng nhiều phiên bản khác nhau của công cụ xây dựng như trình liên kết và trình biên dịch. Để giúp giải quyết sự phức tạp này, Bazel đưa ra một khái niệm quy tắc ràng buộcnền tảng. Quy tắc ràng buộc là một phương diện trong đó bản dựng hoặc môi trường sản xuất có thể khác nhau, chẳng hạn như kiến trúc CPU, sự hiện diện 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ên cho những điều kiện ràng buộc này, đại diện cho 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 phù hợp chuỗi công cụ cho các thao tác trong bản dựng. Nền tảng cũng có thể được sử dụng kết hợp với 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 đó.
  • Thực thi – một nền tảng mà trên đó các công cụ xây dựng thực thi hành động tạo bản dựng để tạo ra đầu ra 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ữ, thực thi và nền tảng đích đều 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 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.

  • 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 là khác.

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ể cho nền tảng được xác định bằng cách sử dụng phương pháp constraint_settingconstraint_value quy tắc trong tệp BUILD. constraint_setting tạo một phương diện mới, trong khi constraint_value tạo ra giá trị mới cho một phương diện nhất định; chúng với nhau xác định hiệu quả một enum và các giá trị có thể có của nó. Ví dụ: như sau đoạn mã 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",
)

Các hạn chế và giá trị của chúng có thể được xác định trên các gói khác nhau trong Workspace. Các tệp này được tham chiếu theo nhãn và tuân theo 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 hạn chế hiện có bằng cách xác định giá trị của riêng bạn.

Quy tắc platform giới thiệu một nền tảng mới với các lựa chọn nhất định về giá trị ràng buộc. Chiến lược phát hành đĩa đơn sau đây sẽ tạo ra một nền tảng có tên linux_x86 và cho biết rằng nền tảng này mô tả bất kỳ môi trường chạy hệ điều hành Linux trên kiến trúc x86_64 có glibc phiên bản 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ữ bằng các định nghĩa ràng buộc cho những cấu trúc CPU và hoạt động phổ biến nhất hệ thống. Tất cả các kênh này đều nằm trong https://github.com/bazelbuild/platforms.

Bazel cung cấp định nghĩa nền tảng đặc biệt sau đây: @platforms//host (đặt biệt hiệu là @bazel_tools//tools:host_platform). Đây là giá trị nền tảng máy chủ được phát hiện tự động - biểu thị 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 lệnh sau cờ hiệu dòng lệnh:

  • --host_platform – mặc định là @bazel_tools//tools:host_platform
    • Mục tiêu này được đặt bí danh là @platforms//host và được một kho lưu trữ hỗ trợ phát hiện hệ điều hành và CPU 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. Bạn có thể dùng các mảng này trong các phiên bản BUILD khác và Tệp Starlark.
  • --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, giá trị của --host_platform vừa là nền tảng lưu trữ vừa là nền tảng đích.

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ể, người ta thường muố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ụ: thiết bị Windows của bạn trình điều khiển có thể sẽ tạo ra nhiều lỗi trình biên dịch khi tạo trên một Máy Linux có //.... Sử dụng 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 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 xây dựng cho bất kỳ nền tảng nào không đáp ứng tất cả hạn chế. Ví dụ sau đây giới hạn win_driver_lib.cc ở 64 bit Windows.

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 bằng Windows 64 bit và không tương thích với tất cả yếu tố khác. Sự không tương thích mang tính bắc cầu. Mục tiêu bất kỳ phụ thuộc bắc cầu vào mục tiêu không tương thích được tự xem xét 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 được coi là không tương thích và được đưa vào tạo như một phần của việc mở rộng mẫu mục tiêu. Ví dụ: hai URL sau sẽ bỏ qua mọi mục tiêu không tương thích được tìm thấy trong mở rộng mẫu mục tiêu.

$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all

Các chương trình kiểm thử không tương thích trong một test_suite là tương tự 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.... Việc sử dụng --noexpand_test_suites sẽ ngăn chặn việc mở rộng và nguyên nhân test_suite nhắm mục tiêu 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 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ẽ tự động bị bỏ qua nếu Đã 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 phương thức @platforms//:incompatible constraint_value mà không nền tảng nào đáp ứng được.

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. Để 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 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"],
    }),
)

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. Bởi vì @platforms//:incompatible không thuộc bất kỳ nền tảng nào, mục tiêu là bị coi là không tương thích.

Để làm cho các quy tắc ràng buộc dễ đọc hơn, hãy sử dụng của skylib selects.with_or().

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 podcast Starlark đầu ra của bazel cquery định dạng để phân biệt mục tiêu không tương thích khỏ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 là chưa đượ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

Mục tiêu không tương thích bỏ qua chế độ hiển thị hạn chế.