Chế độ hiển thị

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Báo cáo sự cố Xem nguồn

Trang này trình bày hai hệ thống hiển thị của Bazel: chế độ hiển thị mục tiêukhả năng hiển thị tải.

Cả hai loại chế độ hiển thị đều giúp 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 của thư viện, đồ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 API công khai để cho phép người dùng hiện tại đồng thời từ chối người dùng 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à 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.

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 đó, các gói là đơn vị chi tiết để quyết định xem 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 đều không thành công trong quá trình phân tích.

Lưu ý rằng việc cấp quyền hiển thị cho một gói không tự cấp cho chế độ hiển thị 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 nội dung Khái niệm và thuật ngữ.

Để tạo nguyên mẫu, bạn có thể tắt chế độ 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 trong trường hợp sử dụng 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 chế độ hiển thị của mục tiêu.

Thông số hiển thị

Tất cả mục tiêu quy tắc đều có thuộc tính visibility nhận danh sách nhãn. Mỗi nhãn có một trong các dạng sau. Ngoại trừ biểu mẫu cuối cùng, đây chỉ là các phần 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 vào 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 bất kỳ quyền truy cập bổ sung 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 phải 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 cấp đó.

  • "//some_pkg:my_package_group": Cấp quyền truy cập vào tất cả các gói thuộc package_group nhất định.

    • Các nhóm gói sử dụng cú pháp khác để chỉ định các gói. Trong một nhóm gói, các biểu mẫu "//foo/bar:__pkg__""//foo/bar:__subpackages__" lần lượt được thay thế bằng "//foo/bar""//foo/bar/...". Tương tự, "//visibility:public""//visibility:private" chỉ là "public""private".

Ví dụ: nếu //some/package:mytarget của visibility đã được đặt thành [":__subpackages__", "//tests:__pkg__"], thì mục tiêu đó có thể được sử dụng bởi bất kỳ mục tiêu nào là một phần của 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ị 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 sẽ làm tăng khả năng đọc và ngăn danh sách không được đồng bộ hoá.

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à:

  1. Giá trị của thuộc tính visibility, nếu được đặt; hoặc nếu không

  2. Giá trị của đối số default_visibility của đối số package trong tệp BUILD của mục tiêu nếu phần khai báo đó tồn tại; hoặc nếu không

  3. //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 bản dựng nguyên mẫu hoặc trong các cơ sở mã nhỏ, nhưng nguy cơ vô tình tạo ra các 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õ những mục tiêu nào 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 tệp đã tạo

Mục tiêu tệp được tạo có mức độ hiển thị giống 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 chế độ hiển thị rõ ràng cho 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, đối số này sẽ hiển thị công khai. 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, chế độ hiển thị phụ thuộc vào giá trị của cờ --incompatible_no_implicit_file_export:

  • Nếu cờ được đặt, chế độ hiển thị sẽ ở chế độ riêng tư.

  • Nếu không, chế độ hiển thị cũ sẽ được áp dụng: Chế độ hiển thị này giống với default_visibility của tệp BUILD hoặc ở chế độ riêng tư nếu bạn 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 nội dung khai báo exports_files bất cứ khi nào mục tiêu tệp nguồn không cần chế độ hiển thị 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 thay vì hiển thị tệp nguồn. Ví dụ: thay vì gọi exports_files trên tệp .java, hãy gói tệp này trong mục tiêu java_library không riêng tư. Nhìn chung, mục tiêu quy tắc chỉ nên tham chiếu trực tiếp 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 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 tính năng kiểm tra chế độ hiển thị cho các mục tiêu này. Để hỗ trợ quá trình di chuyển, việc này cũng khiến mọi config_setting không chỉ định visibility sẽ được coi là công khai (bất kể default_visibility cấp gói là gì).

  • --incompatible_config_setting_private_default_visibility khiến config_setting không chỉ định visibility để tuân thủ default_visibility của gói và dự phòng về chế độ hiển thị riêng tư, giống như mọi mục tiêu quy tắc khác. Đây là thao tác không hoạt độ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 được dự định sử dụng bên ngoài gói hiện tại phải có visibility rõ ràng nếu gói chưa chỉ định default_visibility phù hợp.

Khả năng 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. Quảng cáo này 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ó phần phụ thuộc ngầm ẩn – các phần phụ thuộc không được viết rõ ràng trong tệp BUILD nhưng vốn có mọi bản sao 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 đến một mục tiêu thực thi đại diện cho một trình biên dịch C++.

Hiện tại, vì mục đích hiển thị, các phần phụ thuộc ngầm ẩn này được coi như mọi phần phụ thuộc khác. Điều này có nghĩa là mục tiêu đang phụ thuộc (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 phiên bản 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.

Bạn có thể thay đổi hành vi này bằng cách đặt --incompatible_visibility_private_attributes_at_definition. Khi được bật, mục tiêu được đề cập chỉ cần hiển thị với quy tắc khai báo rằng đó là phần phụ thuộc ngầm ẩn. Nghĩa là gói này phải hiển thị với gói chứa tệp .bzl mà trong đó quy tắc đượ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 về quy tắc cc_library.

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 hay không.

Giống như cách chế độ hiển thị mục tiêu bảo vệ mã nguồn được đóng gói theo mục tiêu, chế độ hiển thị tải sẽ 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 lại vào macro trong tệp .bzl. Nếu không có khả năng bảo vệ khả năng 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, do đó, việc sửa đổi macro sẽ phá vỡ 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ó, không có gì đảm bảo rằng chế độ hiển thị tải và chế độ hiển thị mục tiêu sẽ 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, việc này có thể gây ra vấn đề cho các quy tắc muốn sử dụng các tệp .bzl làm mã nguồn, chẳng hạn như mã tạo tài liệu hoặc kiểm thử.

Để tạo mẫu, bạn có thể tắt chế độ thực thi chế độ hiển thị tải bằng cách đặt --check_bzl_visibility=false. Tương tự như đối 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.

Khả năng hiển thị tải có sẵn kể từ Bazel 6.0.

Khai báo khả năng hiển thị tải

Để đặt 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ố của 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 của gói phủ định.

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 có thể tải từ bất kỳ đâu 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 mục đích 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 tải chế độ hiển thị

Phần này mô tả các mẹo quản lý phần khai báo về khả năng hiển thị tải.

Xem xét khả năng hiển thị

Khi nhiều tệp .bzl phải có cùng chế độ hiển thị, bạn nên kèm theo các 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 tránh được tình trạng sai lệch giữa các chế độ hiển thị của các tệp .bzl. Tên danh sách này cũng dễ đọc hơn khi danh sách clients lớn.

Chế độ hiển thị tổng hợp

Đôi khi, một tệp .bzl có thể cần phải 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 ngừng sử dụng macro được sử dụng rộng rãi. Bạn chỉ muốn hiển thị ứng dụng cho 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 thế này:

# //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)

Nhân bản với 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 muốn sử dụng lại cùng một danh sách cho phép cho chế độ hiển thị mục tiêu và chế độ hiển thị tải, tốt nhất bạn nên di chuyển danh sách các thông số kỹ thuật của gói vào tệp .bzl. Trong đó, cả hai loại nội dung khai báo có thể tham chiếu đến tệp này. Dựa trên ví dụ về khả năng nhìn thấy, hãy viết như sau:

# //mylib/BUILD

load(":internal_defs", "clients")

package_group(
    name = "my_pkg_grp",
    packages = clients,
)

Tính năng này chỉ hoạt động nếu danh sách không chứa bất kỳ thông số kỹ thuật nào về gói phủ định.

Bảo vệ từng ký hiệu

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 biểu tượng 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 nhóm tệp đáng tin cậy có giới hạn. 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 chặn bất kỳ biểu tượng nào không được gạch dưới để tả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

Trình tìm lỗi mã nguồn trình tạo bzl-Visibility

Có một Trình tìm lỗi mã nguồn bản dựng sẽ cung cấp cảnh báo nếu người dùng tải một tệp từ một thư mục có tên internal hoặc private, khi tệp của người dùng đó không nằm chính trong thư mục mẹ của thư mục đó. Công cụ tìm lỗi mã nguồn này trả trước tính năng hiển thị trạng thái tải và không cần thiết trong các không gian làm việc mà tệp .bzl khai báo chế độ hiển thị.