ระดับการเข้าถึง

รายงานปัญหา ดูแหล่งที่มา รุ่น Nightly · 7.4 7.3 · 7.2 · 7.1 · 7.0 · 6.5

หน้านี้อธิบายระบบระดับการเข้าถึง 2 รูปแบบของ Bazel ได้แก่ ระดับการเข้าถึงเป้าหมายและระดับการเข้าถึงการโหลด

ระดับการเข้าถึงทั้ง 2 ประเภทนี้ช่วยให้นักพัฒนาซอฟต์แวร์รายอื่นๆ แยกความแตกต่างระหว่าง API สาธารณะของไลบรารีกับรายละเอียดการใช้งานได้ และช่วยบังคับใช้โครงสร้างเมื่อพื้นที่ทำงานขยายตัว นอกจากนี้ คุณยังใช้ระดับการเข้าถึงเมื่อเลิกใช้งาน API สาธารณะเพื่ออนุญาตผู้ใช้ปัจจุบันในขณะที่ปฏิเสธผู้ใช้ใหม่ได้ด้วย

ระดับการเข้าถึงเป้าหมาย

ระดับการเข้าถึงเป้าหมายจะควบคุมผู้ที่อาจใช้เป้าหมายของคุณ ซึ่งก็คือผู้ที่อาจใช้ป้ายกำกับของเป้าหมายภายในแอตทริบิวต์ เช่น deps

A เป้าหมายจะมองเห็นได้ต่อ B เป้าหมายหากอยู่ในแพ็กเกจเดียวกัน หรือหาก A ให้ระดับการเข้าถึงแพ็กเกจของ B ดังนั้น แพ็กเกจจึงเป็นหน่วยความละเอียดในการเลือกว่าจะอนุญาตการเข้าถึงหรือไม่ หาก B ขึ้นอยู่กับ A แต่ B มองไม่เห็น A การพยายามสร้าง B จะล้มเหลวในระหว่างการวิเคราะห์

โปรดทราบว่าการให้สิทธิ์เข้าถึงแพ็กเกจไม่ได้เป็นการให้สิทธิ์เข้าถึงแพ็กเกจย่อย ดูรายละเอียดเพิ่มเติมเกี่ยวกับแพ็กเกจและแพ็กเกจย่อยได้ที่แนวคิดและคําศัพท์

สําหรับการสร้างต้นแบบ คุณสามารถปิดใช้การบังคับใช้ระดับการเข้าถึงเป้าหมายได้โดยการตั้งค่า Flag --check_visibility=false ไม่ควรทำเช่นนี้สำหรับการใช้งานเวอร์ชันที่ใช้งานจริงในโค้ดที่ส่ง

วิธีหลักในการควบคุมระดับการเข้าถึงคือการใช้แอตทริบิวต์ visibility ในเป้าหมายของกฎ ส่วนนี้จะอธิบายรูปแบบของแอตทริบิวต์นี้และวิธีกำหนดระดับการแชร์ของเป้าหมาย

ข้อกําหนดเฉพาะของการแสดงผล

เป้าหมายกฎทั้งหมดมีแอตทริบิวต์ visibility ที่ใช้รายการป้ายกำกับ โดยแต่ละป้ายกำกับจะมีรูปแบบใดรูปแบบหนึ่งต่อไปนี้ ยกเว้นรูปแบบสุดท้าย ตัวยึดตําแหน่งเหล่านี้เป็นเพียงตัวยึดตําแหน่งทางไวยากรณ์ที่ไม่ได้สอดคล้องกับเป้าหมายจริง

  • "//visibility:public": ให้สิทธิ์เข้าถึงแพ็กเกจทั้งหมด (ใช้ร่วมกับข้อกำหนดอื่นๆ ไม่ได้)

  • "//visibility:private": ไม่ได้ให้สิทธิ์เข้าถึงเพิ่มเติม เฉพาะเป้าหมายในแพ็กเกจนี้เท่านั้นที่จะใช้เป้าหมายนี้ได้ (ใช้ร่วมกับข้อกำหนดอื่นๆ ไม่ได้)

  • "//foo/bar:__pkg__": ให้สิทธิ์เข้าถึง //foo/bar (แต่ไม่รวมถึงแพ็กเกจย่อย)

  • "//foo/bar:__subpackages__": ให้สิทธิ์เข้าถึง //foo/bar และแพ็กเกจย่อยทั้งหมดทั้งแบบโดยตรงและโดยอ้อม

  • "//some_pkg:my_package_group": ให้สิทธิ์เข้าถึงแพ็กเกจทั้งหมดที่เป็นส่วนหนึ่งของ package_group ที่ระบุ

    • กลุ่มแพ็กเกจใช้ไวยากรณ์ที่แตกต่างกันในการระบุแพ็กเกจ ภายในกลุ่มแพ็กเกจ ระบบจะแทนที่แบบฟอร์ม "//foo/bar:__pkg__" และ "//foo/bar:__subpackages__" ด้วย "//foo/bar" และ "//foo/bar/..." ตามลำดับ ในทำนองเดียวกัน "//visibility:public" และ "//visibility:private" ก็คือ "public" และ "private"

ตัวอย่างเช่น หาก //some/package:mytarget ตั้งค่า visibility เป็น [":__subpackages__", "//tests:__pkg__"] ก็สามารถใช้โดยเป้าหมายซึ่งเป็นส่วนหนึ่งของโครงสร้างแหล่งที่มาของ //some/package/... รวมถึงเป้าหมายที่กำหนดไว้ใน //tests/BUILD แต่ไม่ใช่เป้าหมายที่กำหนดไว้ใน //tests/integration/BUILD

แนวทางปฏิบัติแนะนำ: หากต้องการให้แพ็กเกจชุดเดียวกันมองเห็นเป้าหมายหลายรายการ ให้ใช้ package_group แทนการระบุรายการซ้ำในแอตทริบิวต์ visibility ของเป้าหมายแต่ละรายการ วิธีนี้ช่วยเพิ่มความอ่านง่ายและป้องกันไม่ให้รายการไม่ซิงค์กัน

ระดับการเข้าถึงเป้าหมายของกฎ

ระดับการเข้าถึงของเป้าหมายกฎมีดังนี้

  1. ค่าของแอตทริบิวต์ visibility หากมีการตั้งค่าไว้ หรือ

  2. ค่าของตัวแปร default_visibility ของคำสั่ง package ในไฟล์ BUILD ของเป้าหมาย หากมีการประกาศดังกล่าว หรือ

  3. //visibility:private

แนวทางปฏิบัติแนะนำ: หลีกเลี่ยงการตั้งค่า default_visibility เป็นสาธารณะ วิธีการนี้อาจสะดวกสำหรับการสร้างต้นแบบหรือในโค้ดเบสขนาดเล็ก แต่ความเสี่ยงในการสร้างเป้าหมายสาธารณะโดยไม่ได้ตั้งใจก็จะเพิ่มขึ้นเมื่อโค้ดเบสเติบโตขึ้น คุณควรระบุเป้าหมายที่เป็นส่วนหนึ่งของอินเทอร์เฟซสาธารณะของแพ็กเกจอย่างชัดเจน

ตัวอย่าง

ไฟล์ //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"],
)

ไฟล์ //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",
    ],
)

ระดับการเข้าถึงเป้าหมายไฟล์ที่สร้างขึ้น

เป้าหมายไฟล์ที่สร้างขึ้นจะมีระดับการเข้าถึงเดียวกับเป้าหมายกฎที่สร้างเป้าหมายไฟล์

การเปิดเผยเป้าหมายไฟล์ต้นฉบับ

คุณสามารถตั้งค่าระดับการแชร์ของเป้าหมายไฟล์ต้นฉบับได้อย่างชัดเจนโดยเรียกใช้ exports_files เมื่อไม่มีการส่งvisibility อาร์กิวเมนต์ไปยัง exports_files ระบบจะทําให้ระดับการเข้าถึงเป็นแบบสาธารณะ คุณใช้ exports_files เพื่อลบล้างระดับการแชร์ของไฟล์ที่สร้างขึ้นไม่ได้

สําหรับเป้าหมายไฟล์ต้นฉบับที่ไม่ได้ปรากฏในการเรียกใช้ exports_files ระดับการมองเห็นจะขึ้นอยู่กับค่าของ Flag --incompatible_no_implicit_file_export ดังนี้

  • หากตั้งค่าการแจ้งว่าไม่เหมาะสม ระดับการแชร์จะเป็นแบบส่วนตัว

  • มิเช่นนั้น ระบบจะใช้ลักษณะการทำงานเดิม ซึ่งระดับการแชร์จะเหมือนกับdefault_visibilityของไฟล์BUILD หรือเป็นแบบส่วนตัวหากไม่ได้ระบุระดับการแชร์เริ่มต้น

หลีกเลี่ยงการพึ่งพาลักษณะการทำงานเดิม เขียนประกาศ exports_files ทุกครั้งเมื่อเป้าหมายไฟล์ต้นฉบับต้องมีระดับการเข้าถึงแบบไม่เป็นส่วนตัว

แนวทางปฏิบัติแนะนำ: หากเป็นไปได้ คุณควรแสดงเป้าหมายของกฎแทนไฟล์ต้นฉบับ ตัวอย่างเช่น แทนที่จะเรียก exports_files ในไฟล์ .java ให้รวมไฟล์ไว้ในเป้าหมาย java_library แบบไม่เป็นส่วนตัว โดยทั่วไปแล้ว เป้าหมายของกฎควรอ้างอิงไฟล์ต้นฉบับที่อยู่ในแพ็กเกจเดียวกันโดยตรงเท่านั้น

ตัวอย่าง

ไฟล์ //frobber/data/BUILD:

exports_files(["readme.txt"])

ไฟล์ //frobber/bin/BUILD:

cc_binary(
  name = "my-program",
  data = ["//frobber/data:readme.txt"],
)

ระดับการเข้าถึงการตั้งค่าการกำหนดค่า

ที่ผ่านมา Bazel ไม่ได้บังคับใช้การแสดงผลสำหรับเป้าหมาย config_setting ที่อ้างอิงในคีย์ของ select() การนำลักษณะการทำงานแบบเดิมนี้ออกมี 2 สถานะ ได้แก่

  • --incompatible_enforce_config_setting_visibility เปิดใช้การตรวจสอบระดับการเข้าถึงสําหรับเป้าหมายเหล่านี้ นอกจากนี้ ระบบจะถือว่า config_setting ที่ไม่ได้ระบุ visibility เป็นสาธารณะ (โดยไม่คำนึงถึง default_visibility ระดับแพ็กเกจ) เพื่อช่วยในการย้ายข้อมูล

  • --incompatible_config_setting_private_default_visibility ทำให้ config_setting ที่ไม่ได้ระบุ visibility เป็นไปตาม default_visibility ของแพ็กเกจและแสดงผลแบบส่วนตัวเป็นค่าเริ่มต้น เช่นเดียวกับเป้าหมายกฎอื่นๆ จะไม่มีผลหากไม่ได้ตั้งค่า --incompatible_enforce_config_setting_visibility

หลีกเลี่ยงการพึ่งพาลักษณะการทำงานเดิม config_setting ที่มีไว้เพื่อใช้นอกแพ็กเกจปัจจุบันควรมี visibility ที่ชัดเจน หากแพ็กเกจไม่ได้ระบุ default_visibility ที่เหมาะสมไว้แล้ว

ระดับการเข้าถึงกลุ่มเป้าหมายของกลุ่มแพ็กเกจ

เป้าหมาย package_group ไม่มีแอตทริบิวต์ visibility โฆษณาจะแสดงแบบสาธารณะเสมอ

ระดับการเข้าถึงทรัพยากร Dependency โดยนัย

กฎบางข้อมีข้อกําหนดโดยนัย ซึ่งก็คือข้อกําหนดที่ไม่ได้ระบุไว้ในไฟล์ BUILD แต่มีอยู่ในอินสแตนซ์ของกฎนั้นทุกรายการ ตัวอย่างเช่น กฎ cc_library อาจสร้างการพึ่งพาโดยนัยจากเป้าหมายกฎแต่ละรายการไปยังเป้าหมายที่เรียกใช้งานได้ซึ่งแสดงคอมไพเลอร์ C++

ระดับการเข้าถึงของทรัพยากร Dependency โดยนัยดังกล่าวจะได้รับการตรวจสอบในส่วนที่เกี่ยวข้องกับแพ็กเกจที่มีไฟล์ .bzl ซึ่งมีการกำหนดกฎ (หรือสัดส่วนภาพ) ไว้ ในตัวอย่างของเรา คอมไพเลอร์ C++ อาจเป็นแบบส่วนตัวได้ ตราบใดที่คอมไพเลอร์อยู่ในแพ็กเกจเดียวกับคําจํากัดความของกฎ cc_library ในกรณีที่ไม่มีการแสดงข้อมูลการพึ่งพาโดยนัยจากคําจํากัดความ ระบบจะตรวจสอบการพึ่งพาดังกล่าวโดยพิจารณาจากเป้าหมาย cc_library

คุณเปลี่ยนลักษณะการทำงานนี้ได้โดยปิดใช้ --incompatible_visibility_private_attributes_at_definition เมื่อปิดใช้ ระบบจะถือว่าการอ้างอิงโดยนัยเหมือนกับการอ้างอิงอื่นๆ ซึ่งหมายความว่าเป้าหมายที่ใช้ร่วมกัน (เช่น คอมไพเลอร์ C++) ต้องปรากฏต่ออินสแตนซ์ของกฎทุกรายการ ในทางปฏิบัติ โดยทั่วไปแล้วหมายความว่าเป้าหมายต้องเปิดเผยต่อสาธารณะ

หากต้องการจำกัดการใช้กฎในบางแพ็กเกจ ให้ใช้การแสดงผลของโหลดแทน

ระดับการเข้าถึงการโหลด

ระดับการมองเห็นการโหลดจะควบคุมว่าจะโหลดไฟล์ .bzl จากไฟล์ BUILD หรือ .bzl อื่นๆ นอกแพ็กเกจปัจจุบันได้หรือไม่

การเปิดเผยโหลดจะปกป้องตรรกะของบิลด์ที่ห่อหุ้มด้วยไฟล์ .bzl ในลักษณะเดียวกับที่ระดับการเข้าถึงเป้าหมายปกป้องซอร์สโค้ดที่ห่อหุ้มโดยเป้าหมาย เช่น ผู้เขียนไฟล์ BUILD อาจต้องการรวมคำจำกัดความเป้าหมายที่ซ้ำกันบางส่วนไว้ในมาโครในไฟล์ .bzl หากไม่มีการป้องกันระดับการมองเห็นการโหลด ผู้ใช้อาจพบว่าผู้ทำงานร่วมกันคนอื่นๆ ใช้มาโครของตนซ้ำในเวิร์กスペースเดียวกัน การแก้ไขมาโครจึงทำให้บิลด์ของทีมอื่นๆ ใช้งานไม่ได้

โปรดทราบว่าไฟล์ .bzl อาจมีหรือไม่มีเป้าหมายไฟล์ต้นฉบับที่เกี่ยวข้อง หากเป็นเช่นนั้น ไม่มีการรับประกันว่าระดับการมองเห็นการโหลดและระดับการมองเห็นเป้าหมายจะตรงกัน นั่นคือ ไฟล์ BUILD เดียวกันอาจโหลดไฟล์ .bzl ได้ แต่ไม่ได้แสดงใน srcs ของ filegroup หรือในทางกลับกัน ซึ่งบางครั้งอาจทำให้เกิดปัญหากับกฎที่ต้องการใช้ไฟล์ .bzl เป็นซอร์สโค้ด เช่น การสร้างเอกสารประกอบหรือการทดสอบ

สําหรับการสร้างต้นแบบ คุณปิดใช้การบังคับใช้ระดับการเข้าถึงโหลดได้โดยการตั้งค่า --check_bzl_visibility=false เช่นเดียวกับ --check_visibility=false คุณไม่ควรทำเช่นนี้กับโค้ดที่ส่ง

การแสดงผลของโหลดพร้อมใช้งานใน Bazel 6.0

การประกาศระดับการเข้าถึงการโหลด

หากต้องการตั้งค่าระดับการมองเห็นการโหลดของไฟล์ .bzl ให้เรียกใช้ฟังก์ชัน visibility() จากภายในไฟล์ อาร์กิวเมนต์ของ visibility() คือรายการข้อมูลจำเพาะของแพ็กเกจ เช่นเดียวกับแอตทริบิวต์ packages ของ package_group อย่างไรก็ตาม visibility() ไม่ยอมรับข้อกำหนดของแพ็กเกจเชิงลบ

การเรียกใช้ visibility() ต้องเกิดขึ้นเพียงครั้งเดียวต่อไฟล์ที่ระดับบนสุด (ไม่ใช่ภายในฟังก์ชัน) และควรอยู่ต่อจากคำสั่ง load() ทันที

ระดับการมองเห็นการโหลดเริ่มต้นจะเป็นแบบสาธารณะเสมอ ซึ่งแตกต่างจากระดับการมองเห็นเป้าหมาย ไฟล์ที่ไม่ได้เรียกใช้ visibility() จะโหลดได้จากทุกที่ในพื้นที่ทำงานเสมอ เราขอแนะนำให้เพิ่ม visibility("private") ไว้ที่ด้านบนของไฟล์ .bzl ใหม่ที่ไม่ได้ตั้งใจให้ใช้นอกแพ็กเกจโดยเฉพาะ

ตัวอย่าง

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

...

โหลดแนวทางปฏิบัติด้านระดับการเข้าถึง

ส่วนนี้จะอธิบายเคล็ดลับในการจัดการการประกาศระดับการมองเห็นโหลด

ระดับการเข้าถึงของปัจจัย

เมื่อไฟล์ .bzl หลายไฟล์ควรมีระดับการเข้าถึงเดียวกัน คุณอาจต้องรวมข้อกำหนดเฉพาะของแพ็กเกจไว้ในรายการทั่วไป เช่น

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

...

วิธีนี้จะช่วยป้องกันไม่ให้ระดับการแชร์ของไฟล์ .bzl ต่างๆ บิดเบือนโดยไม่ตั้งใจ นอกจากนี้ ยังอ่านได้ง่ายขึ้นเมื่อรายการ clients มีขนาดใหญ่

การเปิดเผยการเขียน

บางครั้งไฟล์ .bzl อาจต้องแสดงในรายการที่อนุญาตซึ่งประกอบด้วยรายการที่อนุญาตขนาดเล็กหลายรายการ ซึ่งคล้ายกับวิธีที่ package_group รวม package_group อื่นๆ ผ่านแอตทริบิวต์ includes

สมมติว่าคุณกำลังจะเลิกใช้งานมาโครที่ใช้กันอย่างแพร่หลาย คุณต้องการให้ผู้ใช้ปัจจุบันและแพ็กเกจที่ทีมของคุณเป็นเจ้าของเท่านั้นที่มองเห็น โดยคุณอาจเขียนข้อความดังนี้

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

การกรองข้อมูลซ้ำออกด้วยกลุ่มแพ็กเกจ

คุณจะกําหนดระดับการมองเห็นการโหลดในแง่ของ package_group ไม่ได้ ต่างจากระดับการมองเห็นเป้าหมาย หากต้องการใช้รายการที่อนุญาตเดียวกันสำหรับทั้งการแสดงผลเป้าหมายและการแสดงผลที่โหลดซ้ำ ทางที่ดีควรย้ายรายการข้อกำหนดของแพ็กเกจไปยังไฟล์ .bzl ซึ่งประกาศทั้ง 2 ประเภทอาจอ้างอิงถึงรายการดังกล่าว จากตัวอย่างในการแสดงปัจจัยข้างต้น คุณอาจเขียนดังนี้

# //mylib/BUILD

load(":internal_defs", "clients")

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

วิธีนี้จะใช้ได้เฉพาะเมื่อรายการไม่มีข้อมูลจำเพาะของแพ็กเกจเชิงลบ

การปกป้องสัญลักษณ์แต่ละตัว

สัญลักษณ์ Starlark ที่มีชื่อขึ้นต้นด้วยขีดล่างจะโหลดจากไฟล์อื่นไม่ได้ วิธีนี้ช่วยให้สร้างสัญลักษณ์ส่วนตัวได้ง่าย แต่จะไม่อนุญาตให้คุณแชร์สัญลักษณ์เหล่านี้กับไฟล์ที่เชื่อถือได้ชุดหนึ่งๆ ในทางกลับกัน ระดับการมองเห็นการโหลดจะช่วยให้คุณควบคุมสิ่งที่แพ็กเกจอื่นๆ อาจเห็น.bzl fileได้ แต่ไม่อนุญาตให้คุณป้องกันไม่ให้โหลดสัญลักษณ์ที่ไม่มีขีดล่าง

แต่คุณใช้ฟีเจอร์ทั้ง 2 อย่างนี้ร่วมกันเพื่อการควบคุมแบบละเอียดได้

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

โปรแกรมตรวจสอบ Buildifier ของ bzl-visibility

มี Buildifier Lint ที่แสดงคำเตือนหากผู้ใช้โหลดไฟล์จากไดเรกทอรีชื่อ internal หรือ private เมื่อไฟล์ของผู้ใช้ไม่ได้อยู่ภายใต้ไดเรกทอรีหลักของไดเรกทอรีนั้น ไฟล์ Lint นี้สร้างขึ้นก่อนฟีเจอร์ระดับการเข้าถึงการโหลดและไม่จำเป็นในพื้นที่ทำงานที่ไฟล์ .bzl ประกาศระดับการเข้าถึง