หน้านี้อธิบายพื้นฐานและประโยชน์ของการใช้แง่มุม และให้ตัวอย่างแบบง่ายและขั้นสูง
ส่วนต่างๆ ช่วยเสริมการสร้างกราฟการอ้างอิงที่มีข้อมูลและการดำเนินการเพิ่มเติม ตัวอย่างสถานการณ์ทั่วไปที่อาจมีประโยชน์ในด้านต่างๆ มีดังนี้
- IDE ที่ผสานรวมกับ Bazel สามารถใช้ส่วนต่างๆ เพื่อรวบรวมข้อมูลเกี่ยวกับโปรเจ็กต์ได้
- เครื่องมือสร้างโค้ดสามารถใช้ประโยชน์จากแง่มุมต่างๆ เพื่อดำเนินการกับข้อมูลที่ป้อนในลักษณะที่เข้าใจได้ง่าย ตัวอย่างเช่น ไฟล์
BUILD
สามารถระบุลำดับชั้นของคำจำกัดความของไลบรารี protobuf และกฎเฉพาะภาษาจะใช้แง่มุมต่างๆ เพื่อแนบการดำเนินการที่สร้างโค้ดการสนับสนุน Protobuf สำหรับภาษาใดภาษาหนึ่ง
ข้อมูลเบื้องต้นเกี่ยวกับสัดส่วนภาพ
ไฟล์ BUILD
ให้คำอธิบายซอร์สโค้ดของโปรเจ็กต์ เช่น ไฟล์ต้นฉบับใดที่เป็นส่วนหนึ่งของโปรเจ็กต์ อาร์ติแฟกต์ (เป้าหมาย) ควรสร้างจากไฟล์เหล่านี้ ทรัพยากร Dependency ระหว่างไฟล์เหล่านั้นคืออะไร และ Bazel ใช้ข้อมูลนี้เพื่อสร้างบิลด์ กล่าวคือ ระบุชุดการดำเนินการที่จำเป็นในการสร้างอาร์ติแฟกต์ (เช่น การเรียกใช้คอมไพเลอร์หรือ Linker) และดำเนินการกับการดำเนินการเหล่านั้น Bazel ทำเช่นนี้ได้โดยการสร้างกราฟการพึ่งพากันระหว่างเป้าหมายต่างๆ และเข้าชมกราฟนี้เพื่อรวบรวมการดำเนินการเหล่านั้น
ลองใช้ไฟล์ BUILD
ต่อไปนี้
java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)
ไฟล์ BUILD
นี้ระบุกราฟการอ้างอิงที่แสดงในรูปต่อไปนี้
รูปที่ 1 กราฟการอ้างอิงไฟล์ BUILD
Bazel วิเคราะห์กราฟการอ้างอิงนี้โดยการเรียกใช้ฟังก์ชันการใช้งานของกฎที่เกี่ยวข้อง (ในกรณีนี้คือ "java_library") สำหรับทุกเป้าหมายในตัวอย่างข้างต้น ฟังก์ชันการใช้งานกฎจะสร้างการดำเนินการที่สร้างอาร์ติแฟกต์ เช่น ไฟล์ .jar
และส่งข้อมูล เช่น ตำแหน่งและชื่ออาร์ติแฟกต์เหล่านั้นไปยังทรัพยากร Dependency แบบย้อนกลับของเป้าหมายเหล่านั้นในผู้ให้บริการ
ส่วนต่างๆ มีความคล้ายคลึงกับกฎตรงที่มีฟังก์ชันการติดตั้งใช้งานที่สร้างการดำเนินการและแสดงผลผู้ให้บริการ แต่พลังมาจากวิธีสร้างกราฟการอ้างอิง แง่มุมหนึ่งมีการใช้งานและมีรายการแอตทริบิวต์ทั้งหมดที่เผยแพร่ไปพร้อมกัน ลองพิจารณาด้าน A ที่ เผยแพร่พร้อมกับแอตทริบิวต์ที่ชื่อว่า "deps" สามารถใช้มุมมองนี้กับเป้าหมาย X โดยให้โหนด A(X) ของแอปพลิเคชันด้านมุมมอง ระหว่างการใช้งาน ระบบจะใช้มุม A ซ้ำๆ กับเป้าหมายทั้งหมดที่ X อ้างถึงในแอตทริบิวต์ "deps" (แอตทริบิวต์ทั้งหมดในรายการการเผยแพร่ของ A)
ดังนั้นการใช้มุมมอง A กับเป้าหมาย X เพียงครั้งเดียวจะให้ผลลัพธ์เป็น "กราฟเงา" ของกราฟการอ้างอิงเดิมของเป้าหมายที่แสดงในรูปต่อไปนี้
รูปที่ 2 สร้างกราฟที่มีสัดส่วนต่างๆ
ขอบที่ถูกเงาเพียงอย่างเดียวคือขอบของแอตทริบิวต์ในชุดการเผยแพร่ ดังนั้น ขอบ runtime_deps
จึงไม่เงาในตัวอย่างนี้ จากนั้นจะมีการเรียกใช้ฟังก์ชันการใช้งานด้านมุมมองบนโหนดทั้งหมดในกราฟเงา ซึ่งคล้ายกับวิธีเรียกใช้กฎบนโหนดของกราฟเดิม
ตัวอย่างง่ายๆ
ตัวอย่างนี้แสดงวิธีพิมพ์ไฟล์ต้นฉบับซ้ำสำหรับกฎและทรัพยากร Dependency ทั้งหมดของกฎที่มีแอตทริบิวต์ deps
โดยจะแสดงการใช้งานด้านต่างๆ การกำหนดองค์ประกอบ และวิธีเรียกใช้องค์ประกอบดังกล่าวจากบรรทัดคำสั่ง Bazel
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
เราจะแบ่งตัวอย่างออกเป็นส่วนต่างๆ และตรวจสอบทีละรายการ
คำจำกัดความของภาพ
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
คำจำกัดความนั้นคล้ายกับคำจำกัดความของกฎและกำหนดโดยใช้ฟังก์ชัน aspect
เช่นเดียวกับกฎ มุมมองจะมีฟังก์ชันการติดตั้งใช้งาน ซึ่งในกรณีนี้คือ _print_aspect_impl
attr_aspects
คือรายการแอตทริบิวต์ของกฎพร้อมทั้งแสดงองค์ประกอบ
ในกรณีนี้ องค์ประกอบจะกระจายไปตามแอตทริบิวต์ deps
ของกฎที่ฟีเจอร์นั้นใช้ด้วย
อาร์กิวเมนต์อีกตัวหนึ่งที่ใช้บ่อยสำหรับ attr_aspects
คือ ['*']
ซึ่งจะกระจายการแสดงผลไปยังแอตทริบิวต์ทั้งหมดของกฎ
การใช้งานด้าน
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
ฟังก์ชันการใช้งานด้านต่างๆ คล้ายกับฟังก์ชันการใช้งานกฎ โดยจะแสดงผล providers สามารถสร้างactions และใช้อาร์กิวเมนต์ 2 ตัว ได้แก่
target
: กำหนดเป้าหมายที่กำลังใช้มุมมองctx
: ออบเจ็กต์ctx
ที่ใช้เข้าถึงแอตทริบิวต์และสร้างเอาต์พุตและการดำเนินการได้
ฟังก์ชันการใช้งานจะเข้าถึงแอตทริบิวต์ของกฎเป้าหมายได้ผ่าน ctx.rule.attr
โดยสามารถตรวจสอบผู้ให้บริการที่ระบุโดยเป้าหมายที่ใช้ (ผ่านอาร์กิวเมนต์ target
)
ต้องมีส่วนต่างๆ จึงจะแสดงรายชื่อผู้ให้บริการได้ ในตัวอย่างนี้ มุมมองไม่ได้ให้ข้อมูลใดๆ จึงแสดงรายการที่ว่างเปล่า
เรียกใช้ด้านนี้โดยใช้บรรทัดคำสั่ง
วิธีที่ง่ายที่สุดในการนำองค์ประกอบหนึ่งๆ ไปใช้คือการใช้บรรทัดคำสั่งโดยใช้อาร์กิวเมนต์ --aspects
สมมติว่าได้กำหนดลักษณะข้างต้นไว้ในไฟล์ชื่อ print.bzl
ดังนี้
bazel build //MyExample:example --aspects print.bzl%print_aspect
จะใช้ print_aspect
กับเป้าหมาย example
และกฎเป้าหมายทั้งหมดที่เข้าถึงได้ซ้ำผ่านทางแอตทริบิวต์ deps
แฟล็ก --aspects
จะมีอาร์กิวเมนต์ 1 รายการ ซึ่งเป็นข้อมูลจำเพาะของด้านต่างๆ ในรูปแบบ <extension file label>%<aspect top-level name>
ตัวอย่างขั้นสูง
ตัวอย่างต่อไปนี้แสดงการใช้องค์ประกอบจากกฎเป้าหมายที่นับไฟล์ในเป้าหมาย ซึ่งอาจกรองไฟล์ตามส่วนขยาย โดยจะแสดงวิธีใช้ผู้ให้บริการเพื่อแสดงผลค่า วิธีใช้พารามิเตอร์เพื่อส่งอาร์กิวเมนต์เข้าสู่การใช้งานด้านลักษณะ และวิธีเรียกใช้องค์ประกอบจากกฎ
ไฟล์ file_count.bzl
:
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
ไฟล์ BUILD.bazel
:
load('//:file_count.bzl', 'file_count_rule')
cc_library(
name = 'lib',
srcs = [
'lib.h',
'lib.cc',
],
)
cc_binary(
name = 'app',
srcs = [
'app.h',
'app.cc',
'main.cc',
],
deps = ['lib'],
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
คำจำกัดความของภาพ
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
ตัวอย่างนี้แสดงวิธีการเผยแพร่มุมมองผ่านแอตทริบิวต์ deps
attrs
กำหนดชุดแอตทริบิวต์สำหรับลักษณะหนึ่งๆ แอตทริบิวต์มุมมองสาธารณะจะกำหนดพารามิเตอร์และต้องเป็นประเภท bool
, int
หรือ string
เท่านั้น
สำหรับแง่มุมที่ช่วยเพิ่มกฎ พารามิเตอร์ int
และ string
จะต้องระบุ values
ด้วย ตัวอย่างนี้มีพารามิเตอร์ชื่อ extension
ที่ได้รับอนุญาตให้ใช้ "*
", "h
" หรือ "cc
"
สำหรับด้านที่มีการเผยแพร่กฎ ค่าพารามิเตอร์จะมาจากกฎที่ขอมุมมอง โดยใช้แอตทริบิวต์ของกฎที่มีชื่อและประเภทเดียวกัน
(ดูคำจำกัดความของ file_count_rule
)
สำหรับลักษณะบรรทัดคำสั่ง ระบบจะส่งค่าพารามิเตอร์โดยใช้แฟล็ก --aspects_parameters
อาจมีการละเว้นข้อจำกัด values
ของพารามิเตอร์ int
และ string
ส่วนต่างๆ ยังได้รับอนุญาตให้มีแอตทริบิวต์ส่วนตัวประเภท label
หรือ label_list
แอตทริบิวต์ป้ายกำกับส่วนตัวสามารถใช้เพื่อระบุทรัพยากร Dependency ของเครื่องมือหรือไลบรารีที่จำเป็นสำหรับการดำเนินการที่ด้านต่างๆ สร้างขึ้น ไม่มีแอตทริบิวต์ส่วนตัวที่กำหนดไว้ในตัวอย่างนี้ แต่ข้อมูลโค้ดต่อไปนี้จะแสดงวิธีการส่งผ่านเครื่องมือไปยังมุมมองหนึ่งๆ
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
การใช้งานด้าน
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
เช่นเดียวกับฟังก์ชันการใช้งานกฎ ฟังก์ชันการใช้งานด้านการแสดงผลจะแสดงผลโครงสร้างของผู้ให้บริการที่เข้าถึงได้โดยทรัพยากร Dependency
ในตัวอย่างนี้ FileCountInfo
คือผู้ให้บริการที่มี count
ในช่องเดียว แนวทางปฏิบัติแนะนำคือการกำหนดช่องของผู้ให้บริการอย่างชัดแจ้งโดยใช้แอตทริบิวต์ fields
กลุ่มผู้ให้บริการสำหรับแอปพลิเคชัน A(X) คือการรวมผู้ให้บริการที่มาจากการใช้กฎสำหรับเป้าหมาย X และจากการใช้งานด้าน A จะมีการสร้างและตรึงผู้ให้บริการที่มีการใช้กฎและมีการตรึงก่อนที่จะนำไปใช้ และจะแก้ไขจากมุมมองใดไม่ได้ ระบบจะแสดงข้อผิดพลาดหากเป้าหมายและลักษณะที่ใช้กับเป้าหมายนั้นๆ ระบุผู้ให้บริการที่มีประเภทเดียวกัน โดยมีข้อยกเว้นของ OutputGroupInfo
(ซึ่งรวมกันตราบใดที่กฎและลักษณะระบุกลุ่มเอาต์พุตที่แตกต่างกัน) และ InstrumentedFilesInfo
(ซึ่งนำมาจากองค์ประกอบ) ซึ่งหมายความว่าการติดตั้งใช้งานลักษณะการแสดงผลอาจ
ไม่แสดงผล DefaultInfo
ระบบจะส่งพารามิเตอร์และแอตทริบิวต์ส่วนตัวในแอตทริบิวต์ของ ctx
ตัวอย่างนี้อ้างอิงพารามิเตอร์ extension
และกําหนดไฟล์ที่จะนับ
สำหรับผู้ให้บริการที่กลับมา ระบบจะแทนที่ค่าของแอตทริบิวต์พร้อมการแสดงผลด้าน (จากรายการ attr_aspects
) ด้วยผลลัพธ์ของการประยุกต์ใช้ด้านนั้นๆ เช่น หากเป้าหมาย X มี Y และ Z อยู่ใน Dep แล้ว ctx.rule.attr.deps
สำหรับ A(X) จะเป็น [A(Y), A(Z)]
ในตัวอย่างนี้ ctx.rule.attr.deps
เป็นออบเจ็กต์เป้าหมายที่เป็นผลลัพธ์ของการนำสัดส่วนภาพไปใช้กับ "deps" ของเป้าหมายเดิมที่มีการนำแง่มุมดังกล่าวไปใช้
ในตัวอย่างนี้ องค์ประกอบเข้าถึงผู้ให้บริการ FileCountInfo
จากทรัพยากร Dependency ของเป้าหมายเพื่อสะสมจำนวนไฟล์ทางอ้อมทั้งหมด
เรียกใช้องค์ประกอบจากกฎ
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
การใช้กฎแสดงวิธีเข้าถึง FileCountInfo
ผ่าน ctx.attr.deps
คำจำกัดความของกฎแสดงวิธีกำหนดพารามิเตอร์ (extension
)
และกำหนดค่าเริ่มต้น (*
) โปรดทราบว่าการมีค่าเริ่มต้นที่ไม่ใช่หนึ่งใน "cc
", "h
" หรือ "*
" จะเป็นข้อผิดพลาดเนื่องจากมีข้อจำกัดเกี่ยวกับพารามิเตอร์ในคำจำกัดความด้านต่างๆ
เรียกใช้องค์ประกอบผ่านกฎเป้าหมาย
load('//:file_count.bzl', 'file_count_rule')
cc_binary(
name = 'app',
...
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
ตัวอย่างนี้แสดงวิธีส่งพารามิเตอร์ extension
ไปยังแง่มุมต่างๆ ผ่านกฎ เนื่องจากพารามิเตอร์ extension
มีค่าเริ่มต้นในการใช้งานกฎ extension
จึงถือเป็นพารามิเตอร์ที่ไม่บังคับ
เมื่อสร้างเป้าหมาย file_count
แล้ว จะมีการประเมินด้านต่างๆ ของเราด้วยตัวเอง และเป้าหมายทั้งหมดที่เข้าถึงได้ซ้ำๆ ผ่านทาง deps