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

วันที่ รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 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 จะล้มเหลวระหว่าง การวิเคราะห์

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

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

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

ข้อกำหนดเกี่ยวกับระดับการเข้าถึง

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

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

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

  • "//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 ค่า ระดับการมองเห็นจะขึ้นอยู่กับค่าของการแจ้ง --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 โดยนัย

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

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

คุณเปลี่ยนลักษณะการทำงานนี้ได้โดยปิดใช้ --incompatible_visibility_private_attributes_at_definition เมื่อปิดใช้ ระบบจะดำเนินการกับทรัพยากร Dependency โดยนัยเช่นเดียวกับทรัพยากร Dependency อื่นๆ ซึ่งหมายความว่าเป้าหมายที่ขึ้นอยู่กับ (เช่น คอมไพเลอร์ 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() จะสามารถโหลดได้จากทุกที่ใน Google Workspace ได้อย่างเต็มประสิทธิภาพ ควรเพิ่ม 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 หากต้องการนำรายการที่อนุญาตเดียวกันไปใช้กับเป้าหมายทั้ง 2 รายการ ระดับการมองเห็นและการโหลด จะเป็นการดีที่สุดที่จะย้ายรายการแพ็กเกจ เป็นไฟล์ .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

Lint บิลด์การมองเห็น bzl

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