Chế độ hiển thị

Báo cáo vấn đề Xem nguồn Nightly/3}

Trang này đề cập đến hai hệ thống hiển thị của Bazel: chế độ hiển thị mục tiêuchế độ hiển thị tải.

Cả hai chế độ hiển thị này đều giúp các nhà phát triển khác phân biệt API công khai của thư viện và thông tin triển khai chi tiết, đồ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 trong khi từ chối API mới.

Khả năng 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 mục tiêu của bạn bên trong một thuộc tính như deps.

A mục tiêu sẽ hiển thị cho B mục tiêu nếu chúng ở trong cùng một gói, hoặc nếu A cấp chế độ 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 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 Các 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 đối với việc sử dụng phiên bản chính thức trong mã đã gửi.

Phương pháp chính để kiểm soát chế độ hiển thị là 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ả 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ừ 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ỳ quy cách 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. (Có thể 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 nó).

  • "//foo/bar:__subpackages__": Cấp quyền truy cập //foo/bar và 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ộc package_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__""//foo/bar:__subpackages__" được thay thế lần lượt bằng "//foo/bar""//foo/bar/...". Tương tự, "//visibility:public""//visibility:private" chỉ là "public""private".

Ví dụ: nếu //some/package:mytargetvisibility được đặt thành [":__subpackages__", "//tests:__pkg__"], thì nó có thể được 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 sử dụng, chứ 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 các danh sách bị đồ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à:

  1. Giá trị của thuộc tính visibility (nếu có); hoặc nếu không

  2. Giá trị của đối số default_visibility của câu lệnh package trong tệp BUILD của mục tiêu, nếu có nội dung khai báo như vậy; 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 nguyên mẫu hoặc trong các cơ sở mã nhỏ, nhưng nguy cơ vô tình tạo mục tiêu công khai sẽ gia tăng khi cơ sở mã tăng lên. Bạn nên nêu rõ ràng các 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 đã 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ị đích đến của tệp nguồn

Bạn có thể đặt rõ 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ẽ được 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ị sẽ phụ thuộc vào giá trị của cờ --incompatible_no_implicit_file_export:

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

  • Nếu không, hành vi cũ sẽ được áp dụng: Chế độ hiển thị giống với default_visibility của tệp BUILD hoặc ở chế độ 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 ghi phần 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 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ì 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ột 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 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ó 2 cờ để xoá hành vi cũ này:

  • --incompatible_enforce_config_setting_visibility cho phép kiểm tra chế độ hiển thị cho các mục tiêu này. Để hỗ trợ quá trình di chuyển, thao tá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).

  • --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 chế độ hiển thị riêng tư, giống như mọi mục tiêu quy tắc khác. Nếu bạn không đặt --incompatible_enforce_config_setting_visibility, thì tài sản này sẽ không hoạt động.

Tránh dựa vào hành vi cũ. Mọi config_setting 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.

Chế độ hiển thị mục tiêu của nhóm gói

Các mục tiêu package_group không có thuộc tính visibility. Các video này 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 nêu trong tệp BUILD nhưng vốn có trong mọi phiên bản của quy tắc đó. Ví dụ: quy tắc cc_library có thể tạo 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 một 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 mà 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. Như một phương án dự phòng, nếu phần phụ thuộc ngầm ẩn không hiển thị trong định nghĩa, thì phần phụ thuộc đó sẽ được kiểm tra tương ứng với 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ý giống như mọi phần phụ thuộc khác. Điều này có nghĩa là mục tiêu phụ thuộc vào (chẳng hạn như trình biên dịch C++) phải hiển thị cho 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ị khi 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 bên 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 đóng gói theo mục tiêu, chế độ 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 lại vào macro trong tệp .bzl. Nếu khô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 trong cùng một không gian làm việc sử dụng lại. Do đó, 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ó đích tệp nguồn tương ứng. Nếu có, thì không thể đảm bảo rằng khả năng hiển thị nội dung tải và phạm vi hiển thị mục tiêu trùng khớp. Điều này nghĩa 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 dưới dạng mã nguồn, chẳng hạn như để tạo hoặc kiểm thử tài liệu.

Đối với việc tạo nguyên 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. Giống 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

Để đặt chế độ hiển thị tải của tệp .bzl, hãy gọi hàm visibility() từ trong tệp. Đối số cho visibility() là một danh sách 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 quy cách 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 hàm) và tốt nhất là ngay sau câu lệnh load().

Không giống như chế độ hiển thị mục tiêu, chế độ hiển thị của tệp tải mặc định luôn là công khai. Bạn luôn có thể tải các tệp không gọi visibility() từ bất cứ đâ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

...

Phương pháp hiển thị tải

Phần này mô tả các mẹo để quản lý thông tin khai báo về 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 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 vô tình giữa chế độ hiển thị của các tệp .bzl. Việc này cũng dễ đọc hơn khi danh sách clients lớn.

Soạn thảo hiển thị

Đôi khi, tệp .bzl có thể cần hiển thị với một danh sách cho phép được bao gồm từ 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 không dùng macro được sử dụng rộng rãi. Bạn muốn gói này chỉ hiển thị cho những người dùng hiện tại và cho 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)

Loại bỏ trùng lặp bằng các 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 dựa trên package_group. Nếu muốn sử 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 di chuyển danh sách thông số kỹ thuật của gói vào tệp .bzl, nơi 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ố tiêu cực nào về 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 chia sẻ các biểu tượng 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 nào khác có thể thấy .bzl file của bạn, nhưng không cho phép bạn tải bất kỳ biểu tượng không bị gạch dưới nào.

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 hơn.

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

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 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 gốc của thư mục đó. Công cụ tìm lỗi mã nguồn này ra mắt trước tính năng chế độ hiển thị cho nội dung tải và không cần thiết trong những không gian làm việc mà các tệp .bzl khai báo chế độ hiển thị.