อย่าลืมนัดของเรา: BazelCon 2023 จะจัดขึ้นในวันที่ 24-25 ตุลาคมที่ Google ที่มิวนิก ดูข้อมูลเพิ่มเติม

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

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

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

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

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

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

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

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

สําหรับต้นแบบ คุณสามารถปิดใช้การบังคับใช้ระดับการเข้าถึงเป้าหมายได้โดยการตั้งค่าสถานะ --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 ระดับการมองเห็นจะขึ้นอยู่กับค่าของแฟล็ก --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 ที่ไม่ได้สะกดในไฟล์ BUILD แต่โดยพื้นฐานแล้วกับอินสแตนซ์ทั้งหมดของกฎนั้น เช่น กฎ cc_library อาจสร้างทรัพยากร Dependency จากเป้าหมายกฎแต่ละข้อไปยังเป้าหมายปฏิบัติการซึ่งแสดงถึงคอมไพเลอร์ C++

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

คุณสามารถเปลี่ยนลักษณะการทํางานนี้ได้โดยการตั้งค่า --incompatible_visibility_private_attributes_at_definition เมื่อเปิดใช้ เป้าหมายที่ต้องการจะเห็นเฉพาะกฎที่ประกาศการขึ้นต่อกันแบบโดยนัยเท่านั้น กล่าวคือ แพ็กเกจดังกล่าวต้องปรากฏกับแพ็กเกจที่มีไฟล์ .bzl ซึ่งกําหนดกฎ ในตัวอย่างของเรา คอมไพเลอร์ C++ อาจเป็นแบบส่วนตัวตราบใดที่อยู่ในแพ็กเกจเดียวกันกับคําจํากัดความของกฎ 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 ได้ แต่ไม่อนุญาตให้โหลดสัญลักษณ์ที่ไม่มีขีดล่าง

แต่โชคดีที่คุณสามารถใช้ฟีเจอร์ทั้ง 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

ขุยผ้าของ Bzl-visible

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