หน้านี้ครอบคลุมระบบระดับการเข้าถึง 3 ระบบของ Bazel ได้แก่ ระดับการเข้าถึงเป้าหมาย ระดับการเข้าถึงแบบทรานซิทีฟ และ ระดับการเข้าถึงการโหลด
ระดับการเข้าถึงประเภทนี้ช่วยให้นักพัฒนาซอฟต์แวร์รายอื่นๆ แยกความแตกต่างระหว่าง API สาธารณะของไลบรารีกับรายละเอียดการใช้งาน และช่วยบังคับใช้โครงสร้างเมื่อพื้นที่ทำงานของคุณเติบโตขึ้น นอกจากนี้ คุณยังใช้ระดับการเข้าถึงเมื่อเลิกใช้งาน API สาธารณะเพื่ออนุญาตผู้ใช้ปัจจุบันขณะปฏิเสธผู้ใช้ใหม่ได้ด้วย
ระดับการเข้าถึงเป้าหมาย
ระดับการเข้าถึงเป้าหมายจะควบคุมผู้ที่อาจขึ้นอยู่กับเป้าหมายของคุณ กล่าวคือ ผู้ที่อาจใช้ป้ายกำกับของเป้าหมายภายในแอตทริบิวต์ เช่น deps เป้าหมายจะสร้างไม่สำเร็จในระยะการวิเคราะห์หากละเมิดระดับการเข้าถึงของการขึ้นต่อกันรายการใดรายการหนึ่ง
โดยทั่วไปแล้ว เป้าหมาย 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 ของแต่ละเป้าหมาย ซึ่งจะช่วยเพิ่มความสามารถในการอ่านและป้องกันไม่ให้รายการไม่ซิงค์กัน
แนวทางปฏิบัติแนะนำ: เมื่อให้สิทธิ์เข้าถึงโปรเจ็กต์ของทีมอื่น ให้เลือก __subpackages__ แทน __pkg__ เพื่อหลีกเลี่ยงการเปลี่ยนแปลงสิทธิ์เข้าถึงโดยไม่จำเป็นเมื่อโปรเจ็กต์นั้นพัฒนาขึ้นและเพิ่มแพ็กเกจย่อยใหม่
ระดับการเข้าถึงเป้าหมายของกฎ
ระดับการเข้าถึงของเป้าหมายกฎจะกำหนดโดยใช้แอตทริบิวต์ visibility หรือค่าเริ่มต้นที่เหมาะสมหากไม่ได้ระบุไว้ และต่อท้ายตำแหน่งที่มีการประกาศเป้าหมาย สำหรับเป้าหมายที่ไม่ได้ประกาศในมาโครสัญลักษณ์ หากแพ็กเกจระบุ default_visibility ระบบจะใช้ค่าเริ่มต้นนี้ สำหรับแพ็กเกจอื่นๆ ทั้งหมดและเป้าหมายที่ประกาศในมาโครสัญลักษณ์ ค่าเริ่มต้นจะเป็นเพียง ["//visibility:private"]
# //mypkg/BUILD
package(default_visibility = ["//friend:__pkg__"])
cc_library(
name = "t1",
...
# No visibility explicitly specified.
# Effective visibility is ["//friend:__pkg__", "//mypkg:__pkg__"].
# If no default_visibility were given in package(...), the visibility would
# instead default to ["//visibility:private"], and the effective visibility
# would be ["//mypkg:__pkg__"].
)
cc_library(
name = "t2",
...
visibility = [":clients"],
# Effective visibility is ["//mypkg:clients, "//mypkg:__pkg__"], which will
# expand to ["//another_friend:__subpackages__", "//mypkg:__pkg__"].
)
cc_library(
name = "t3",
...
visibility = ["//visibility:private"],
# Effective visibility is ["//mypkg:__pkg__"]
)
package_group(
name = "clients",
packages = ["//another_friend/..."],
)
แนวทางปฏิบัติแนะนำ: หลีกเลี่ยงการตั้งค่า default_visibility เป็นสาธารณะ ซึ่งอาจสะดวกสำหรับการสร้างต้นแบบหรือในฐานของโค้ดขนาดเล็ก แต่ความเสี่ยงในการสร้างเป้าหมายสาธารณะโดยไม่ตั้งใจจะเพิ่มขึ้นเมื่อฐานของโค้ดมีขนาดใหญ่ขึ้น คุณควรระบุอย่างชัดเจนว่าเป้าหมายใดเป็นส่วนหนึ่งของอินเทอร์เฟซสาธารณะของแพ็กเกจ
ระดับการเข้าถึงเป้าหมายของไฟล์ที่สร้างขึ้น
เป้าหมายไฟล์ที่สร้างขึ้นจะมีระดับการเข้าถึงเหมือนกับเป้าหมายกฎที่สร้างไฟล์นั้น
# //mypkg/BUILD
java_binary(
name = "foo",
...
visibility = ["//friend:__pkg__"],
)
# //friend/BUILD
some_rule(
name = "bar",
deps = [
# Allowed directly by visibility of foo.
"//mypkg:foo",
# Also allowed. The java_binary's "_deploy.jar" implicit output file
# target the same visibility as the rule target itself.
"//mypkg:foo_deploy.jar",
]
...
)
ระดับการเข้าถึงเป้าหมายของไฟล์ต้นฉบับ
เป้าหมายไฟล์ต้นฉบับสามารถประกาศอย่างชัดเจนโดยใช้ exports_files หรือสร้างโดยนัยโดยอ้างอิงชื่อไฟล์ในแอตทริบิวต์ป้ายกำกับของกฎ (นอกมาโครสัญลักษณ์) เช่นเดียวกับเป้าหมายกฎ ระบบจะเพิ่มตำแหน่งของการเรียกไปยัง exports_files หรือไฟล์ BUILD ที่อ้างอิงถึงไฟล์อินพุตต่อท้ายระดับการเข้าถึงของไฟล์โดยอัตโนมัติเสมอ
ไฟล์ที่ประกาศโดย exports_files สามารถตั้งค่าระดับการเข้าถึงได้โดยใช้พารามิเตอร์ visibility สำหรับฟังก์ชันนั้น หากไม่ได้ระบุพารามิเตอร์นี้ ระดับการเข้าถึงจะเป็นสาธารณะ
สำหรับไฟล์ที่ไม่ปรากฏในการเรียก 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++
ระบบจะตรวจสอบระดับการเข้าถึงของการขึ้นต่อกันโดยนัยดังกล่าวโดยอิงตามแพ็กเกจที่มีไฟล์ .bzl ซึ่งมีการกำหนดกฎ (หรือแง่มุม) ไว้ ในตัวอย่างของเรา คอมไพเลอร์ C++ อาจเป็นแบบส่วนตัวได้ตราบใดที่อยู่ในแพ็กเกจเดียวกับคำจำกัดความของกฎ cc_library หากไม่เห็นทรัพยากร Dependency โดยนัยจากคำจำกัดความ ระบบจะตรวจสอบทรัพยากร Dependency โดยนัยโดยอิงตามเป้าหมาย cc_library เป็นทางเลือกสำรอง
หากต้องการจำกัดการใช้กฎกับแพ็กเกจบางรายการ ให้ใช้ load visibility แทน
ระดับการเข้าถึงและมาโครสัญลักษณ์
ส่วนนี้จะอธิบายวิธีที่ระบบระดับการเข้าถึงโต้ตอบกับมาโครสัญลักษณ์
ตำแหน่งภายในมาโครสัญลักษณ์
รายละเอียดสำคัญของระบบระดับการเข้าถึงคือวิธีที่เรากำหนดตำแหน่งของการประกาศ สำหรับเป้าหมายที่ไม่ได้ประกาศในมาโครสัญลักษณ์ ตำแหน่งจะเป็นเพียงแพ็กเกจที่เป้าหมายอยู่ ซึ่งก็คือแพ็กเกจของไฟล์ BUILD
แต่สำหรับเป้าหมายที่สร้างในมาโครสัญลักษณ์ ตำแหน่งจะเป็นแพ็กเกจที่มีไฟล์ .bzl ซึ่งมีคำจำกัดความของมาโคร (คำสั่ง my_macro = macro(...)) ปรากฏอยู่ เมื่อสร้างเป้าหมายภายในเป้าหมายที่ซ้อนกันหลายรายการ ระบบจะใช้คำจำกัดความของมาโครสัญลักษณ์ที่อยู่ด้านในสุดเสมอ
ระบบเดียวกันนี้ใช้เพื่อระบุตำแหน่งที่จะตรวจสอบระดับการเข้าถึงของทรัพยากร Dependency ที่ระบุ หากมีการสร้างเป้าหมายที่ใช้ภายในมาโคร เราจะดูคำจำกัดความของมาโครที่อยู่ด้านในสุดแทนแพ็กเกจที่เป้าหมายที่ใช้อยู่
ซึ่งหมายความว่ามาโครทั้งหมดที่มีการกำหนดโค้ดไว้ในแพ็กเกจเดียวกันจะเป็น "เพื่อน" กันโดยอัตโนมัติ เป้าหมายใดก็ตามที่สร้างขึ้นโดยตรงจากมาโครที่กำหนดไว้ใน //lib:defs.bzl จะมองเห็นได้จากมาโครอื่นๆ ที่กำหนดไว้ใน //lib ไม่ว่ามาโครจะสร้างอินสแตนซ์ในแพ็กเกจใดก็ตาม ในทำนองเดียวกัน เป้าหมายที่ประกาศโดยตรงใน //lib/BUILD และมาโครเดิมจะเห็นและถูกเห็นโดยเป้าหมายอื่นๆ ได้ ในทางกลับกัน เป้าหมายที่อยู่ในแพ็กเกจเดียวกันอาจมองไม่เห็นกันและกันหากมีเป้าหมายอย่างน้อย 1 รายการที่สร้างโดยมาโครสัญลักษณ์
ภายในฟังก์ชันการใช้งานของมาโครสัญลักษณ์ พารามิเตอร์ visibility จะมีค่าที่มีผลของแอตทริบิวต์ visibility ของมาโครหลังจากต่อท้ายตำแหน่งที่มีการเรียกใช้มาโคร วิธีมาตรฐานสำหรับมาโครในการส่งออกเป้าหมายรายการใดรายการหนึ่งไปยังผู้เรียกคือการส่งต่อค่านี้ไปยังการประกาศของเป้าหมาย เช่นใน some_rule(..., visibility = visibility) เป้าหมายที่ละเว้นแอตทริบิวต์นี้จะไม่ปรากฏต่อผู้เรียกใช้มาโคร เว้นแต่ผู้เรียกใช้จะอยู่ในแพ็กเกจเดียวกับคำจำกัดความของมาโคร ลักษณะการทำงานนี้จะประกอบขึ้นในแง่ที่ว่าเชนของการเรียกที่ซ้อนกันไปยังมาโครย่อยอาจส่งผ่าน visibility = visibility แต่ละรายการ โดยส่งออกเป้าหมายที่ส่งออกของมาโครภายในไปยังผู้เรียกในแต่ละระดับอีกครั้ง โดยไม่เปิดเผยรายละเอียดการใช้งานของมาโคร
การมอบสิทธิ์ให้กับมาโครย่อย
โมเดลระดับการเข้าถึงมีฟีเจอร์พิเศษที่อนุญาตให้มาโครมอบสิทธิ์ของตนให้กับมาโครย่อยได้ ซึ่งสำคัญต่อการแยกตัวประกอบและการเขียนมาโคร
สมมติว่าคุณมีมาโคร my_macro ที่สร้างเอดจ์ทรัพยากร Dependency โดยใช้กฎ some_library จากแพ็กเกจอื่น
# //macro/defs.bzl
load("//lib:defs.bzl", "some_library")
def _impl(name, visibility, ...):
...
native.genrule(
name = name + "_dependency"
...
)
some_library(
name = name + "_consumer",
deps = [name + "_dependency"],
...
)
my_macro = macro(implementation = _impl, ...)
# //pkg/BUILD
load("//macro:defs.bzl", "my_macro")
my_macro(name = "foo", ...)
เป้าหมาย //pkg:foo_dependency ไม่ได้ระบุ visibility ไว้ จึงมองเห็นได้เฉพาะภายใน //macro ซึ่งใช้ได้ดีกับเป้าหมายที่ใช้ทรัพยากร ทีนี้จะเกิดอะไรขึ้นหากผู้เขียน //lib ปรับโครงสร้าง some_library ใหม่เพื่อใช้มาโครแทน
# //lib:defs.bzl
def _impl(name, visibility, deps, ...):
some_rule(
# Main target, exported.
name = name,
visibility = visibility,
deps = deps,
...)
some_library = macro(implementation = _impl, ...)
การเปลี่ยนแปลงนี้ทำให้ตำแหน่งของ //pkg:foo_consumer เป็น //lib แทนที่จะเป็น //macro ดังนั้นการใช้ //pkg:foo_dependency จึงละเมิดระดับการเข้าถึงของทรัพยากร Dependency ผู้เขียน my_macro ไม่สามารถคาดหวังให้ส่ง visibility = ["//lib"] ไปยังการประกาศของทรัพยากร Dependency เพียงเพื่อหลีกเลี่ยงรายละเอียดการใช้งานนี้
ด้วยเหตุนี้ เมื่อทรัพยากร Dependency ของเป้าหมายเป็นค่าแอตทริบิวต์ของมาโครที่ประกาศเป้าหมายด้วย เราจะตรวจสอบระดับการเข้าถึงของทรัพยากร Dependency เทียบกับตำแหน่งของมาโครแทนตำแหน่งของเป้าหมายที่ใช้
ในตัวอย่างนี้ หากต้องการตรวจสอบว่า //pkg:foo_consumer สามารถเห็น //pkg:foo_dependency หรือไม่ เราจะเห็นว่า //pkg:foo_dependency ถูกส่งเป็นอินพุตไปยังการเรียก some_library ภายใน my_macro ด้วย และจะตรวจสอบระดับการเข้าถึงของทรัพยากร Dependency เทียบกับตำแหน่งของการเรียกนี้ //macro แทน
กระบวนการนี้สามารถทำซ้ำได้ตราบใดที่การประกาศเป้าหมายหรือมาโครอยู่ภายในมาโครเชิงสัญลักษณ์อื่นที่ใช้ป้ายกำกับของทรัพยากร Dependency ในแอตทริบิวต์ประเภทป้ายกำกับรายการใดรายการหนึ่ง
Finalizer
เป้าหมายที่ประกาศในตัวสรุปกฎ (มาโครสัญลักษณ์ที่มี finalizer = True) นอกเหนือจากการเห็นเป้าหมายตามกฎการแสดงผลมาโครสัญลักษณ์ตามปกติแล้ว ยังเห็นเป้าหมายทั้งหมดที่แสดงต่อแพ็กเกจของเป้าหมายตัวสรุปด้วย
กล่าวคือ หากคุณย้ายข้อมูลมาโครเดิมที่ใช้ native.existing_rules() ไปยัง Finalizer เป้าหมายที่ประกาศโดย Finalizer จะยังคงเห็นทรัพยากร Dependency เดิมได้
คุณสามารถกำหนดเป้าหมายที่ Finalizer สามารถตรวจสอบภายในได้โดยใช้ native.existing_rules() แต่จะใช้เป็นทรัพยากร Dependency ภายใต้ระบบระดับการเข้าถึงไม่ได้ ตัวอย่างเช่น หากเป้าหมายที่กำหนดโดยมาโครไม่ปรากฏต่อแพ็กเกจของตัวเองหรือต่อคำจำกัดความของมาโคร Finalizer และไม่ได้มอบสิทธิ์ให้กับ Finalizer ทาง Finalizer จะไม่เห็นเป้าหมายดังกล่าว อย่างไรก็ตาม โปรดทราบว่ามาโครเดิมที่ใช้ native.existing_rules() จะไม่เห็นเป้าหมายดังกล่าวด้วย
ระดับการเข้าถึงแบบสกรรมกริยา
ระดับการมองเห็นแบบสกรรมกริยาเป็นวิธีจำกัดผู้ที่อาจขึ้นอยู่กับเป้าหมาย ซึ่งรวมถึงเมื่อการขึ้นต่อกันเป็นแบบอ้อมเท่านั้น โดยจะใช้แยกต่างหากจากระบบระดับการเข้าถึงเป้าหมายปกติ ดังนั้นจึงอาจเกิดข้อผิดพลาดระดับการเข้าถึงแบบสกรรมกริยาได้แม้ว่าลิงก์ทุกรายการในเชนทรัพยากร Dependency จะถูกต้องก็ตาม ซึ่งจะช่วยให้ผู้เขียนเป้าหมายแชร์เป้าหมายกับชุดเป้าหมายไคลเอ็นต์ที่เชื่อถือได้ ขณะเดียวกันก็ป้องกันไม่ให้เป้าหมายรั่วไหลไปยังส่วนที่เหลือของบิลด์โดยไม่ตั้งใจ
ระบบจะประกาศระดับการเข้าถึงแบบสกรรมกริยาที่ระดับแพ็กเกจ โดยใช้พารามิเตอร์ transitive_visibility ของฟังก์ชัน [package][package] การประกาศนี้มีผลกับเป้าหมายทั้งหมดในแพ็กเกจ พารามิเตอร์นี้ใช้ป้ายกำกับเดียวของ package_group ซึ่งระบุแพ็กเกจที่เป้าหมายอาจขึ้นอยู่กับเป้าหมายของแพ็กเกจนี้แบบสกรรมกริยา
# //third_party/sensitive_dep/BUILD
package(
# This must be explicitly listed
transitive_visibility = ":sensitive_dep_users",
)
package_group(
name = "sensitive_dep_users",
packages = [
"//third_party/sensitive_dep/...", # has to include itself
"//allowed",
],
)
sh_library(
name = "sensitive_dep"
)
# //not_allowed/BUILD
sh_binary(
name = "bad",
deps = ["//third_party/sensitive_dep:sensitive_dep"]
)
การสร้าง //not_allowed:bad ซึ่งขึ้นอยู่กับ //third_party/sensitive_dep:sensitive_dep แต่ไม่ได้เป็นส่วนหนึ่งของ transitive_visibility จะทำให้เกิดข้อผิดพลาดต่อไปนี้
Transitive visibility error: //third_party/sensitive_dep:sensitive_dep is not transitively visible from ///not_allowed:bad. //"//third_party/sensitive_dep:sensitive_dep inherits a transitive_visibility declaration from its package or one of its dependencies that does not allow //not_allowed:bad.
กลุ่มแพ็กเกจที่ใช้ใน transitive_visibility อาจไม่includeกลุ่มแพ็กเกจอื่นๆ
โปรดทราบว่าหากเป้าหมายมีการขึ้นต่อกันแบบสกรรมกริยาที่มีข้อจำกัดระดับการเข้าถึงแบบสกรรมกริยา เป้าหมายนั้นจะรับช่วงข้อจำกัดและส่งต่อไปยังเป้าหมายที่ขึ้นอยู่กับเป้าหมายนั้น หากเป้าหมายมีการขึ้นต่อกันแบบสกรรมกริยาหลายรายการที่มีข้อจำกัดระดับการเข้าถึงแบบสกรรมกริยา เป้าหมายนั้นต้องเป็นไปตามข้อจำกัดทั้งหมด
ระดับการเข้าถึงแบบทรานซิทีฟยังใช้กับเป้าหมายที่ประกาศในมาโครสัญลักษณ์ด้วย อย่างไรก็ตาม ระดับการเข้าถึงนี้จะพิจารณาเฉพาะแพ็กเกจที่เป้าหมายเหล่านี้อยู่เท่านั้น ไม่ใช่แพ็กเกจที่กำหนดมาโครสัญลักษณ์ ซึ่งแตกต่างจากระดับการเข้าถึงเป้าหมายทั่วไป
การแสดงผลการโหลด
ระดับการเข้าถึงการโหลดจะควบคุมว่าไฟล์ .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
มี Buildifier lint ที่แสดงคำเตือนหากผู้ใช้โหลดไฟล์จากไดเรกทอรีชื่อ internal หรือ private เมื่อไฟล์ของผู้ใช้ไม่ได้อยู่ภายใต้ระดับบนสุดของไดเรกทอรีนั้น Lint นี้มีมาก่อนฟีเจอร์ระดับการเข้าถึงการโหลด และไม่จำเป็นในพื้นที่ทำงานที่ไฟล์ .bzl ประกาศระดับการเข้าถึง