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

รายงานปัญหา ดูแหล่งที่มา

หน้านี้จะครอบคลุมระบบระดับการเข้าถึง 2 ระบบของ Bazel ซึ่งได้แก่ ระดับการเข้าถึงเป้าหมายและระดับการเข้าถึงการโหลด

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

การแสดงผลเป้าหมาย

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

เป้าหมาย B จะเห็น A ได้หากอยู่ในแพ็กเกจเดียวกัน หรือหาก 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 ของแต่ละเป้าหมาย วิธีนี้ทำให้อ่านง่ายขึ้นและป้องกันไม่ให้ รายการซิงค์กัน

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

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

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

  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 ระดับการเข้าถึงจะขึ้นอยู่กับค่าของแฟล็ก --incompatible_no_implicit_file_export ดังนี้

  • หากมีการตั้งค่าแฟล็ก การเปิดเผยจะเป็นแบบส่วนตัว

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

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

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

ตัวอย่าง

ไฟล์ //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 โดยนัย

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

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

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

การเปิดเผยการโหลด

การเปิดเผยการโหลดควบคุมว่าสามารถโหลดไฟล์ .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 ของคุณอย่างไรบ้าง แต่จะไม่อนุญาตให้คุณโหลดสัญลักษณ์ที่ไม่มีเครื่องหมายขีดล่าง

โชคดีที่คุณสามารถรวมคุณลักษณะทั้งสองเข้าด้วยกัน เพื่อให้ได้การควบคุมแบบละเอียด

# //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 ที่ให้คำเตือนหากผู้ใช้โหลดไฟล์จากไดเรกทอรีชื่อ internal หรือ private เมื่อไฟล์ของผู้ใช้ไม่ได้อยู่ภายใต้ระดับบนสุดของไดเรกทอรีนั้น ไลบรารีนี้ใช้ก่อนฟีเจอร์การเปิดเผยการโหลด และไม่จำเป็นในพื้นที่ทำงานที่ไฟล์ .bzl ประกาศระดับการเข้าถึง