หน้านี้อธิบายพื้นฐานและประโยชน์ของการใช้แง่มุม พร้อมให้ตัวอย่างแบบง่ายและขั้นสูง
ลักษณะช่วยให้การเสริมสร้างกราฟทรัพยากร Dependency พร้อมข้อมูลและการดำเนินการเพิ่มเติม สถานการณ์ทั่วไปบางกรณีที่มีประโยชน์ในด้านต่างๆ มีดังนี้
- 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 กราฟทรัพยากร Dependency ของไฟล์ BUILD
Bazel วิเคราะห์กราฟการอ้างอิงนี้โดยการเรียกใช้ฟังก์ชันการใช้งานของกฎที่เกี่ยวข้อง (ในกรณีนี้คือ "java_library") สำหรับทุกเป้าหมายในตัวอย่างข้างต้น ฟังก์ชันการใช้กฎจะสร้างการดำเนินการที่สร้างอาร์ติแฟกต์ เช่น ไฟล์ .jar
และส่งข้อมูล เช่น ตำแหน่งและชื่ออาร์ติแฟกต์เหล่านั้นไปยังทรัพยากร Dependency แบบย้อนกลับของเป้าหมายเหล่านั้นในผู้ให้บริการ
แง่มุมต่างๆ คล้ายกับกฎตรงที่มีฟังก์ชันการใช้งานที่สร้างการดำเนินการและแสดงผลผู้ให้บริการ อย่างไรก็ตาม พลังมาจากวิธีสร้างกราฟ 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
คือออบเจ็กต์เป้าหมายที่เป็นผลลัพธ์ของการนำลักษณะไปใช้กับ "ระดับลง" ของเป้าหมายเดิมที่มีการนำไปใช้
ในตัวอย่างนี้ มุมมองเข้าถึงผู้ให้บริการ 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