Trang này trình bày về 2 hệ thống chế độ hiển thị của Bazel: chế độ hiển thị mục tiêu và chế độ hiển thị tải.
Cả hai loại chế độ hiển thị đều giúp các nhà phát triển khác phân biệt giữa API công khai của thư viện và thông tin chi tiết về cách triển khai, đồng thời giúp thực thi cấu trúc khi không gian làm việc của bạn phát triển. Bạn cũng có thể sử dụng khả năng hiển thị khi ngừng sử dụng một API công khai để cho phép người dùng hiện tại trong khi từ chối người dùng mới.
Khả năng hiển thị mục tiêu
Khả năng hiển thị mục tiêu kiểm soát những người có thể phụ thuộc vào mục tiêu của bạn, tức là những người có thể sử dụng nhãn của mục tiêu bên trong một thuộc tính như deps.
Mục tiêu A hiển thị với mục tiêu B nếu chúng nằm trong cùng một gói hoặc nếu A cấp quyền hiển thị cho gói của B. Do đó, các gói là đơn vị chi tiết để quyết định có cho phép truy cập hay không. Nếu B phụ thuộc vào A
nhưng A không hiển thị với B, thì mọi nỗ lực xây dựng B đều không thành công trong quá trình
phân tích.
Xin lưu ý rằng việc cấp quyền hiển thị cho một gói không tự cấp quyền hiển thị cho các gói con của gói đó. Để biết thêm thông tin chi tiết về gói và gói con, hãy xem phần Khái niệm và thuật ngữ.
Để tạo mẫu thử nghiệm, bạn có thể tắt tính năng thực thi chế độ hiển thị mục tiêu bằng cách đặt cờ --check_visibility=false. Bạn không nên thực hiện việc này khi sử dụng trong phiên bản chính thức trong mã đã gửi.
Cách chính để kiểm soát chế độ hiển thị là sử dụng thuộc tính
visibility trên
các mục tiêu quy tắc. Phần này mô tả định dạng của thuộc tính này và cách xác định khả năng hiển thị của mục tiêu.
Thông số kỹ thuật về chế độ hiển thị
Tất cả các mục tiêu quy tắc đều có thuộc tính visibility lấy danh sách nhãn. Mỗi nhãn có một trong các dạng sau. Ngoại trừ dạng cuối cùng, đây chỉ là các trình giữ chỗ cú pháp không tương ứng với bất kỳ mục tiêu thực tế nào.
"//visibility:public": Cấp quyền truy cập cho tất cả các gói. (Không được kết hợp với bất kỳ thông số kỹ thuật nào khác.)"//visibility:private": Không cấp thêm quyền truy cập nào; chỉ các mục tiêu trong gói này mới có thể sử dụng mục tiêu này. (Không được kết hợp với bất kỳ thông số kỹ thuật nào khác.)"//foo/bar:__pkg__": Cấp quyền truy cập vào//foo/bar(nhưng không cấp quyền truy cập vào các gói con của gói đó)."//foo/bar:__subpackages__": Cấp quyền truy cập vào//foo/barvà tất cả các gói con trực tiếp và gián tiếp của gói đó."//some_pkg:my_package_group": Cấp quyền truy cập vào tất cả các gói thuộcpackage_groupđã cho.- Nhóm gói sử dụng một
cú pháp khác để
chỉ định các gói. Trong một nhóm gói, các dạng
"//foo/bar:__pkg__"và"//foo/bar:__subpackages__"lần lượt được thay thế bằng"//foo/bar"và"//foo/bar/...". Tương tự,"//visibility:public"và"//visibility:private"chỉ là"public"và"private".
- Nhóm gói sử dụng một
cú pháp khác để
chỉ định các gói. Trong một nhóm gói, các dạng
Ví dụ: nếu //some/package:mytarget được đặt visibility thành
[":__subpackages__", "//tests:__pkg__"], thì mục tiêu này có thể được sử dụng bởi bất kỳ mục tiêu nào
thuộc cây nguồn //some/package/..., cũng như các mục tiêu được xác định
trong //tests/BUILD, nhưng không phải bởi các mục tiêu được xác định trong //tests/integration/BUILD.
Phương pháp hay nhất: Để hiển thị một số mục tiêu cho cùng một tập hợp gói, hãy sử dụng package_group thay vì lặp lại danh sách trong thuộc tính visibility của mỗi mục tiêu. Điều này giúp tăng khả năng đọc và ngăn danh sách bị lệch.
Chế độ hiển thị mục tiêu quy tắc
Chế độ hiển thị của mục tiêu quy tắc là:
Giá trị của thuộc tính
visibility(nếu được đặt); hoặcGiá trị của đối số
default_visibilitycủa câu lệnhpackagetrong tệpBUILDcủa mục tiêu (nếu có khai báo như vậy); hoặc//visibility:private.
Phương pháp hay nhất: Tránh đặt default_visibility thành công khai. Việc này có thể thuận tiện cho việc tạo mẫu thử nghiệm hoặc trong các cơ sở mã nhỏ, nhưng nguy cơ vô tình tạo các mục tiêu công khai sẽ tăng lên khi cơ sở mã phát triển. Bạn nên nêu rõ những mục tiêu nào thuộc giao diện công khai của một gói.
Ví dụ:
Tệp //frobber/bin/BUILD:
# This target is visible to everyone
cc_binary(
name = "executable",
visibility = ["//visibility:public"],
deps = [":library"],
)
# This target is visible only to targets declared in the same package
cc_library(
name = "library",
# No visibility -- defaults to private since no
# package(default_visibility = ...) was used.
)
# This target is visible to targets in package //object and //noun
cc_library(
name = "subject",
visibility = [
"//noun:__pkg__",
"//object:__pkg__",
],
)
# See package group "//frobber:friends" (below) for who can
# access this target.
cc_library(
name = "thingy",
visibility = ["//frobber:friends"],
)
Tệp //frobber/BUILD:
# This is the package group declaration to which target
# //frobber/bin:thingy refers.
#
# Our friends are packages //frobber, //fribber and any
# subpackage of //fribber.
package_group(
name = "friends",
packages = [
"//fribber/...",
"//frobber",
],
)
Chế độ hiển thị mục tiêu tệp đã tạo
Mục tiêu tệp đã tạo có cùng chế độ hiển thị với mục tiêu quy tắc tạo ra mục tiêu đó.
Chế độ hiển thị mục tiêu tệp nguồn
Bạn có thể đặt rõ ràng chế độ hiển thị của mục tiêu tệp nguồn bằng cách gọi
exports_files. Khi không có đối số visibility nào được truyền đến exports_files, đối số này sẽ đặt chế độ hiển thị thành công khai.
Bạn không được sử dụng exports_files để ghi đè chế độ hiển thị của tệp đã tạo.
Đối với các mục tiêu tệp nguồn không xuất hiện trong lệnh gọi đến exports_files, khả năng hiển thị sẽ phụ thuộc vào giá trị của cờ
--incompatible_no_implicit_file_export:
Nếu cờ được đặt, thì chế độ hiển thị sẽ là riêng tư.
Nếu không, hành vi cũ sẽ được áp dụng: Chế độ hiển thị giống như
default_visibilitycủa tệpBUILDhoặc riêng tư nếu không chỉ định chế độ hiển thị mặc định.
Tránh dựa vào hành vi cũ. Luôn viết một khai báo exports_files bất cứ khi nào mục tiêu tệp nguồn cần chế độ hiển thị không phải là riêng tư.
Phương pháp hay nhất: Khi có thể, bạn nên hiển thị mục tiêu quy tắc thay vì tệp nguồn. Ví dụ: thay vì gọi exports_files trên tệp .java, hãy gói tệp đó trong mục tiêu java_library không phải là riêng tư. Nói chung, các mục tiêu quy tắc chỉ nên trực tiếp tham chiếu đến các tệp nguồn nằm trong cùng một gói.
Ví dụ:
Tệp //frobber/data/BUILD:
exports_files(["readme.txt"])
Tệp //frobber/bin/BUILD:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
Chế độ hiển thị cài đặt cấu hình
Trước đây, Bazel không thực thi chế độ hiển thị cho
config_setting các mục tiêu được
tham chiếu trong các khoá của select(). Có 2 cờ để xoá hành vi cũ này:
--incompatible_enforce_config_setting_visibilitybật tính năng kiểm tra khả năng hiển thị cho các mục tiêu này. Để hỗ trợ quá trình di chuyển, cờ này cũng khiến mọiconfig_settingkhông chỉ địnhvisibilityđược coi là công khai (bất kểdefault_visibilityở cấp gói).--incompatible_config_setting_private_default_visibilitykhiến cácconfig_settingkhông chỉ địnhvisibilitytuân theodefault_visibilitycủa gói và quay lại chế độ hiển thị riêng tư, giống như bất kỳ mục tiêu quy tắc nào khác. Đây là một thao tác không có tác dụng nếu bạn không đặt--incompatible_enforce_config_setting_visibility.
Tránh dựa vào hành vi cũ. Bất kỳ config_setting nào dự định được
sử dụng bên ngoài gói hiện tại đều phải có visibility rõ ràng, nếu gói chưa
chỉ định default_visibility phù hợp.
Chế độ hiển thị mục tiêu nhóm gói
Các mục tiêu package_group không có thuộc tính visibility. Chúng luôn hiển thị công khai.
Chế độ hiển thị của các phần phụ thuộc ngầm ẩn
Một số quy tắc có các phần phụ thuộc ngầm ẩn —
các phần phụ thuộc không được nêu rõ trong tệp BUILD nhưng vốn có trong
mọi thực thể của quy tắc đó. Ví dụ: quy tắc cc_library có thể tạo một phần phụ thuộc ngầm ẩn từ mỗi mục tiêu quy tắc của quy tắc đó đến một mục tiêu thực thi đại diện cho trình biên dịch C++.
Chế độ hiển thị của phần phụ thuộc ngầm ẩn như vậy được kiểm tra liên quan đến gói chứa tệp .bzl trong đó quy tắc (hoặc khía cạnh) được xác định. Trong ví dụ của chúng tôi, trình biên dịch C++ có thể là riêng tư miễn là trình biên dịch đó nằm trong cùng một gói với định nghĩa của quy tắc cc_library. Nếu không hiển thị được phần phụ thuộc ngầm ẩn từ định nghĩa, thì phần phụ thuộc đó sẽ được kiểm tra liên quan đến mục tiêu cc_library.
Bạn có thể thay đổi hành vi này bằng cách tắt
--incompatible_visibility_private_attributes_at_definition.
Khi bị tắt, các phần phụ thuộc ngầm ẩn được xử lý như bất kỳ phần phụ thuộc nào khác.
Điều này có nghĩa là mục tiêu đang được phụ thuộc vào (chẳng hạn như trình biên dịch C++ của chúng tôi) phải hiển thị với mọi thực thể của quy tắc. Trong thực tế, điều này thường có nghĩa là mục tiêu phải có chế độ hiển thị công khai.
Nếu bạn muốn hạn chế việc sử dụng một quy tắc cho một số gói nhất định, hãy sử dụng chế độ hiển thị tải thay thế.
Chế độ hiển thị tải
Chế độ hiển thị tải kiểm soát việc có thể tải tệp .bzl từ các tệp BUILD hoặc .bzl khác hay không.
Tương tự như cách khả năng hiển thị mục tiêu bảo vệ mã nguồn được đóng gói bởi các mục tiêu, khả năng hiển thị tải bảo vệ logic bản dựng được đóng gói bởi các tệp .bzl. Ví dụ: tác giả tệp BUILD có thể muốn đưa một số định nghĩa mục tiêu lặp đi lặp lại vào một macro trong tệp .bzl. Nếu không có sự bảo vệ của chế độ hiển thị tải, họ có thể thấy macro của mình được các cộng tác viên khác sử dụng lại trong cùng một không gian làm việc, để việc sửa đổi macro sẽ làm hỏng bản dựng của các nhóm khác.
Xin lưu ý rằng tệp .bzl có thể có hoặc không có mục tiêu tệp nguồn tương ứng.
Nếu có, thì không có gì đảm bảo rằng chế độ hiển thị tải và chế độ hiển thị mục tiêu trùng khớp. Tức là, cùng một tệp BUILD có thể tải tệp
.bzl nhưng không liệt kê tệp đó trong srcs của filegroup,
hoặc ngược lại. Đôi khi, điều này có thể gây ra vấn đề cho các quy tắc muốn sử dụng tệp .bzl làm mã nguồn, chẳng hạn như để tạo tài liệu hoặc kiểm thử.
Để tạo mẫu thử nghiệm, bạn có thể tắt tính năng thực thi chế độ hiển thị tải bằng cách đặt --check_bzl_visibility=false. Giống như --check_visibility=false, bạn không nên thực hiện việc này đối với mã đã gửi.
Chế độ hiển thị tải có sẵn kể từ Bazel 6.0.
Khai báo chế độ hiển thị tải
Để đặt chế độ hiển thị tải của tệp .bzl, hãy gọi hàm
visibility() trong tệp.
Đối số cho visibility() là danh sách các thông số kỹ thuật về gói, giống như
thuộc tính packages của
package_group. Tuy nhiên, visibility() không chấp nhận các thông số kỹ thuật về gói phủ định.
Lệnh gọi đến visibility() chỉ được xảy ra một lần cho mỗi tệp, ở cấp cao nhất (không nằm trong hàm) và tốt nhất là ngay sau các câu lệnh load().
Không giống như chế độ hiển thị mục tiêu, chế độ hiển thị tải mặc định luôn là công khai. Các tệp không gọi visibility() luôn có thể tải được từ mọi nơi trong không gian làm việc. Bạn nên thêm visibility("private") vào đầu bất kỳ
tệp .bzl mới nào không dành riêng cho việc sử dụng bên ngoài gói.
Ví dụ:
# //mylib/internal_defs.bzl
# Available to subpackages and to mylib's tests.
visibility(["//mylib/...", "//tests/mylib/..."])
def helper(...):
...
# //mylib/rules.bzl
load(":internal_defs.bzl", "helper")
# Set visibility explicitly, even though public is the default.
# Note the [] can be omitted when there's only one entry.
visibility("public")
myrule = rule(
...
)
# //someclient/BUILD
load("//mylib:rules.bzl", "myrule") # ok
load("//mylib:internal_defs.bzl", "helper") # error
...
Các phương pháp về chế độ hiển thị tải
Phần này mô tả các mẹo để quản lý khai báo chế độ hiển thị tải.
Phân tích chế độ hiển thị
Khi nhiều tệp .bzl phải có cùng khả năng hiển thị, bạn có thể phân tích các thông số kỹ thuật về gói của chúng thành một danh sách chung. Ví dụ:
# //mylib/internal_defs.bzl
visibility("private")
clients = [
"//foo",
"//bar/baz/...",
...
]
# //mylib/feature_A.bzl
load(":internal_defs.bzl", "clients")
visibility(clients)
...
# //mylib/feature_B.bzl
load(":internal_defs.bzl", "clients")
visibility(clients)
...
Điều này giúp ngăn chặn sự sai lệch ngẫu nhiên giữa các chế độ hiển thị của tệp .bzl khác nhau. Danh sách này cũng dễ đọc hơn khi danh sách clients lớn.
Kết hợp chế độ hiển thị
Đôi khi, tệp .bzl có thể cần hiển thị với danh sách cho phép bao gồm nhiều danh sách cho phép nhỏ hơn. Điều này tương tự như cách a
package_group có thể kết hợp các package_group khác thông qua thuộc tính
includes.
Giả sử bạn đang ngừng sử dụng một macro được sử dụng rộng rãi. Bạn chỉ muốn macro đó hiển thị với người dùng hiện tại và các gói do nhóm của bạn sở hữu. Bạn có thể viết:
# //mylib/macros.bzl
load(":internal_defs.bzl", "our_packages")
load("//some_big_client:defs.bzl", "their_remaining_uses)
# List concatenation. Duplicates are fine.
visibility(our_packages + their_remaining_uses)
Khử trùng lặp bằng nhóm gói
Không giống như chế độ hiển thị mục tiêu, bạn không thể xác định chế độ hiển thị tải theo package_group. Nếu bạn muốn sử dụng lại cùng một danh sách cho phép cho cả khả năng hiển thị mục tiêu và khả năng hiển thị tải, thì tốt nhất là chuyển danh sách các thông số kỹ thuật về gói vào tệp .bzl, nơi cả hai loại khai báo đều có thể tham chiếu đến danh sách đó. Dựa trên ví dụ trong phần Phân tích chế độ hiển thị
ở trên, bạn có thể viết:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
Điều này chỉ hoạt động nếu danh sách không chứa bất kỳ thông số kỹ thuật về gói phủ định nào.
Bảo vệ các ký hiệu riêng lẻ
Bạn không thể tải bất kỳ ký hiệu Starlark nào có tên bắt đầu bằng dấu gạch dưới từ một tệp khác. Điều này giúp bạn dễ dàng tạo các ký hiệu riêng tư, nhưng không cho phép bạn chia sẻ các ký hiệu này với một tập hợp giới hạn các tệp đáng tin cậy. Mặt khác, khả năng hiển thị tải cho phép bạn kiểm soát những gói khác có thể nhìn thấy .bzl file của bạn, nhưng không cho phép bạn ngăn tải bất kỳ ký hiệu nào không có dấu gạch dưới.
May mắn là bạn có thể kết hợp hai tính năng này để kiểm soát chi tiết.
# //mylib/internal_defs.bzl
# Can't be public, because internal_helper shouldn't be exposed to the world.
visibility("private")
# Can't be underscore-prefixed, because this is
# needed by other .bzl files in mylib.
def internal_helper(...):
...
def public_util(...):
...
# //mylib/defs.bzl
load(":internal_defs", "internal_helper", _public_util="public_util")
visibility("public")
# internal_helper, as a loaded symbol, is available for use in this file but
# can't be imported by clients who load this file.
...
# Re-export public_util from this file by assigning it to a global variable.
# We needed to import it under a different name ("_public_util") in order for
# this assignment to be legal.
public_util = _public_util
bzl-visibility Buildifier lint
Có một Buildifier lint
cung cấp cảnh báo nếu người dùng tải một tệp từ thư mục có tên là internal
hoặc private, khi tệp của người dùng không nằm bên dưới thư mục mẹ của
thư mục đó. Lint này có trước tính năng hiển thị tải và không cần thiết trong các không gian làm việc nơi các tệp .bzl khai báo chế độ hiển thị.