Trang này giới thiệu 2 hệ thống 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à chi tiết 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 chế độ 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 đồng thời từ chối các API mới.
Chế độ hiển thị mục tiêu
Chế độ 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à 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
.
A
mục tiêu sẽ hiển thị với B
mục tiêu 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 đó, 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 tạo B
sẽ không thành công trong quá trình phân tích.
Lưu ý rằng việc cấp chế độ hiển thị cho một gói không tự cấp chế độ hiển thị cho các gói con đó. Để biết thêm thông tin chi tiết về gói và các gói con, hãy xem Khái niệm và thuật ngữ.
Để tạo nguyên mẫu, 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 để sử dụng trong quá trình sản xuất 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 chế độ 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
nhận danh sách các 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 phần giữ chỗ theo 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 vào tất cả các gói. (Không thể 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; 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 thể kết hợp với bất kỳ quy cách nào khác.)"//foo/bar:__pkg__"
: Cấp quyền truy cập vào//foo/bar
(nhưng không phải cho các gói con)."//foo/bar:__subpackages__"
: Cấp quyền truy cập vào//foo/bar
và tất cả các gói con trực tiếp và gián tiếp của gói này."//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.- Các nhóm gói sử dụng một cú pháp khác để chỉ định gói. Trong một nhóm gói, các biểu mẫu
"//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"
.
- Các nhóm gói sử dụng một cú pháp khác để chỉ định gói. Trong một nhóm gói, các biểu mẫu
Ví dụ: nếu //some/package:mytarget
có visibility
được đặt 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 được sử dụng 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ị nhiều mục tiêu cho cùng một nhóm 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 từng mục tiêu. Điều này giúp dễ đọc hơn và ngăn danh sách không đồng bộ hoá.
Chế độ hiển thị mục tiêu của 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ặc thuộc tính khácGiá trị của đối số
default_visibility
của câu lệnhpackage
trong tệpBUILD
của mục tiêu, nếu có nội dung khai báo như vậy; hoặc//visibility:private
.
Phương pháp hay nhất: Tránh đặt default_visibility
ở chế độ công khai. Việc này có thể thuận tiện cho việc tạo mẫu hoặc tạo cơ sở mã nhỏ, nhưng nguy cơ vô tình tạo mục tiêu công khai sẽ tăng lên khi cơ sở mã tăng lên. Tốt hơn là bạn nên nêu rõ các mục tiêu thuộc giao diện công khai của 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 của tệp đã tạo
Mục tiêu tệp được 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 của tệp nguồn
Bạn có thể thiết lập 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 chuyển đến exports_files
, chế độ hiển thị sẽ hiển thị ở chế độ công khai.
Không được 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
, chế độ hiển thị phụ thuộc vào giá trị của cờ --incompatible_no_implicit_file_export
:
Nếu cờ này được đặt thì chế độ hiển thị sẽ ở chế độ riêng tư.
Khác, hành vi cũ sẽ được áp dụng: Chế độ hiển thị giống với
default_visibility
của tệpBUILD
hoặc ở chế độ riêng tư nếu không được chỉ định chế độ hiển thị mặc định.
Tránh dựa vào hành vi cũ. Luôn ghi nội dung khai báo exports_files
bất cứ khi nào một mục tiêu tệp nguồn cần chế độ hiển thị không ở chế độ riêng tư.
Phương pháp hay nhất: Khi có thể, hãy ưu tiên hiển thị mục tiêu quy tắc hơn là tệp nguồn. Ví dụ: thay vì gọi exports_files
trên một tệp .java
, hãy gói tệp này trong một mục tiêu java_library
không riêng tư. Nhìn chung, các mục tiêu quy tắc chỉ nên tham chiếu trực tiếp 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ị chế độ cài đặt cấu hình
Trước đây, Bazel chưa thực thi chế độ hiển thị cho các mục tiêu config_setting
được tham chiếu trong các khoá của select()
. Có hai cờ để xoá hành vi cũ này:
--incompatible_enforce_config_setting_visibility
bật chế độ kiểm tra chế độ hiển thị cho các mục tiêu này. Để hỗ trợ di chuyển, phương thức này cũng khiến mọiconfig_setting
không chỉ địnhvisibility
được coi là công khai (bất kểdefault_visibility
ở cấp gói).--incompatible_config_setting_private_default_visibility
gây raconfig_setting
không chỉ địnhvisibility
để tuân thủdefault_visibility
của gói và dự phòng trong chế độ hiển thị riêng tư, giống như mọi mục tiêu quy tắc khác. Thuộc tính này sẽ không hoạt động nếu không đặt--incompatible_enforce_config_setting_visibility
.
Tránh dựa vào hành vi cũ. Mọi config_setting
mà bạn dự định 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 của nhóm gói
Mục tiêu package_group
không có thuộc tính visibility
. Chúng luôn hiển thị công khai.
Mức độ 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 viết trong tệp BUILD
nhưng vốn có trong mọi bản sao của quy tắc đó. Ví dụ: một 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 có thể 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 sẽ được kiểm tra đối với 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ể ở chế độ 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
. Để dự phòng, nếu phần phụ thuộc ngầm ẩn không hiển thị trong phần định nghĩa, thì phần phụ thuộc đó sẽ được kiểm tra theo 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ạn tắt chính sách này, các phần phụ thuộc ngầm ẩn sẽ được xử lý như mọi phần phụ thuộc khác.
Điều này có nghĩa là mục tiêu được phụ thuộc vào (chẳng hạn như trình biên dịch C++) 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 quy tắc cho một số gói nhất định, hãy dùng chế độ hiển thị tải.
Chế độ hiển thị tải
Chế độ hiển thị tải kiểm soát việc tệp .bzl
có thể được tải từ các tệp BUILD
hoặc .bzl
khác ngoài gói hiện tại hay không.
Tương tự như chế độ hiển thị mục tiêu bảo vệ mã nguồn được mục tiêu đóng gói, chế độ hiển thị tải bảo vệ logic bản dựng được các tệp .bzl
đóng gói. 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 lại vào macro trong tệp .bzl
. Nếu không có biện pháp bảo vệ chế độ hiển thị tải, nhóm có thể thấy macro đượ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. Do đó, việc sửa đổi macro sẽ làm hỏng các 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ó đích của tệp nguồn tương ứng.
Nếu có, 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ều này đôi khi có thể gây ra sự cố 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 hoặc kiểm thử tài liệu.
Để tạo mẫu, 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
. Tương tự như với --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
Để thiết lập chế độ hiển thị tải của tệp .bzl
, hãy gọi hàm visibility()
từ bên trong tệp.
Đối số cho visibility()
là danh sách các thông số kỹ thuật của gói, giống như thuộc tính packages
của package_group
. Tuy nhiên, visibility()
không chấp nhận thông số kỹ thuật phủ định của gói.
Lệnh gọi đến visibility()
chỉ được thực hiện một lần cho mỗi tệp, ở cấp cao nhất (không phải bên trong một 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 ở chế độ công khai. Các tệp
không gọi visibility()
luôn 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 mọi tệp .bzl
mới 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
...
Phương pháp hiển thị tải
Phần này mô tả các mẹo quản lý nội dung khai báo chế độ hiển thị tải.
Phân tích khả năng hiển thị
Khi nhiều tệp .bzl
có cùng một chế độ hiển thị, bạn nên đưa thông số kỹ thuật của gói vào 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 tình trạng sai lệch ngẫu nhiên giữa độ hiển thị của nhiều tệp .bzl
. Giá trị này cũng dễ đọc hơn khi danh sách clients
lớn.
Khả năng hiển thị khi kết hợp
Đôi khi, một tệp .bzl
có thể cần hiển thị với một 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 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 hiển thị với người dùng hiện tại và các gói thuộc sở hữu của nhóm của bạn. 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)
Loại bỏ 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 về mặt package_group
. Nếu muốn dùng lại cùng một danh sách cho phép cho cả chế độ hiển thị mục tiêu và chế độ hiển thị tải, tốt nhất bạn nên chuyển danh sách thông số kỹ thuật của gói vào một tệp .bzl, trong đó cả hai loại nội dung khai báo đều có thể tham chiếu đến tệp đó. Dựa trên ví dụ trong phần Phân tích khả năng 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,
)
Cách này chỉ hiệu quả nếu danh sách không chứa thông số kỹ thuật phủ định nào cho gói.
Bảo vệ các biểu tượng riêng lẻ
Không thể tải bất kỳ biểu tượng 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 biểu tượng riêng tư nhưng không cho phép bạn chia sẻ các biểu tượng này với một nhóm hạn chế các tệp đáng tin cậy. Mặt khác, chế độ hiển thị tải cho phép bạn kiểm soát những gói khác có thể thấy .bzl file
của bạn, nhưng không cho phép bạn ngăn việc tải mọi biểu tượng không có dấu gạch dưới.
May mắn thay, 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
Tìm lỗi mã nguồn của Trình tạo bản dựng bzl-visible
Có một Trình tìm lỗi mã nguồn của trình tạo bản dựng đưa ra cảnh báo nếu người dùng tải một tệp từ thư mục có tên internal
hoặc private
, khi tệp của người dùng không nằm trong thư mục mẹ của thư mục đó. Trình tìm lỗi mã nguồn này có trước tính năng hiển thị tải và không cần thiết trong
không gian làm việc nơi các tệp .bzl
khai báo chế độ hiển thị.