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

รูปที่ 1 BUILD กราฟทรัพยากร Dependency ของไฟล์
Bazel จะวิเคราะห์กราฟการขึ้นต่อกันนี้โดยเรียกใช้ฟังก์ชันการใช้งานของกฎที่เกี่ยวข้อง (ในกรณีนี้คือ "java_library") สำหรับทุกเป้าหมายในตัวอย่างข้างต้น ฟังก์ชันการใช้งานกฎจะสร้างการดำเนินการที่สร้างอาร์ติแฟกต์ เช่น ไฟล์ .jar และส่งข้อมูล เช่น ตำแหน่งและชื่อของอาร์ติแฟกต์เหล่านั้น ไปยังทรัพยากร Dependency แบบย้อนกลับของเป้าหมายเหล่านั้นใน Provider
Aspects คล้ายกับกฎตรงที่มีฟังก์ชันการติดตั้งใช้งานที่ สร้างการดำเนินการและแสดงผลผู้ให้บริการ แต่ความสามารถของเครื่องมือนี้มาจาก วิธีสร้างกราฟทรัพยากร Dependency สำหรับเครื่องมือ Aspect มีการติดตั้งใช้งาน และรายการแอตทริบิวต์ทั้งหมดที่เผยแพร่ พิจารณาลักษณะ A ที่ แพร่กระจายไปตามแอตทริบิวต์ที่ชื่อ "deps" คุณใช้แง่มุมนี้กับ เป้าหมาย X ได้ ซึ่งจะให้โหนดการใช้แง่มุม A(X) ในระหว่างการใช้ แอตทริบิวต์ A จะใช้แบบเรียกซ้ำกับเป้าหมายทั้งหมดที่ X อ้างอิงในแอตทริบิวต์ "deps" (แอตทริบิวต์ทั้งหมดในรายการการแพร่กระจายของ A)
ดังนั้น การกระทำเพียงครั้งเดียวในการใช้ลักษณะ A กับเป้าหมาย X จะทำให้เกิด "กราฟเงา" ของ กราฟทรัพยากร Dependency เดิมของเป้าหมายที่แสดงในรูปต่อไปนี้

รูปที่ 2 สร้างกราฟด้วยแง่มุม
ขอบที่ถูกเงาบดบังคือขอบที่อยู่ตามแอตทริบิวต์ในชุดการแพร่กระจายเท่านั้น ดังนั้นขอบ runtime_deps จึงไม่ถูกเงาบดบังในตัวอย่างนี้ จากนั้นฟังก์ชันการติดตั้งใช้งาน Aspect จะเรียกใช้ในโหนดทั้งหมดในกราฟเงา
ในลักษณะเดียวกับที่การติดตั้งใช้งานกฎเรียกใช้ในโหนด
ของกราฟต้นฉบับ
ตัวอย่างง่ายๆ
ตัวอย่างนี้แสดงวิธีพิมพ์ไฟล์ต้นฉบับแบบเรียกซ้ำสำหรับกฎและทรัพยากร Dependency ทั้งหมดที่มีแอตทริบิวต์ deps โดยจะแสดงการติดตั้งใช้งาน Aspect, คำจำกัดความของ Aspect และวิธีเรียกใช้ Aspect
จากบรรทัดคำสั่งของ 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'],
required_providers = [CcInfo],
)
มาแบ่งตัวอย่างออกเป็นส่วนๆ แล้วพิจารณาแต่ละส่วนกัน
คำจำกัดความของ Aspect
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
required_providers = [CcInfo],
)
คำจำกัดความของแง่มุมจะคล้ายกับคำจำกัดความของกฎ และกำหนดโดยใช้ฟังก์ชัน aspect
เช่นเดียวกับกฎ แอสแพ็กต์มีฟังก์ชันการใช้งานซึ่งในกรณีนี้คือ
_print_aspect_impl
attr_aspects คือรายการแอตทริบิวต์ของกฎที่แง่มุมจะแพร่กระจายไปตาม
ในกรณีนี้ Aspect จะแพร่กระจายไปตามแอตทริบิวต์ deps ของ
กฎที่ใช้
อีกอาร์กิวเมนต์ที่ใช้กันทั่วไปสำหรับ attr_aspects คือ ['*'] ซึ่งจะเผยแพร่แง่มุมไปยังแอตทริบิวต์ทั้งหมดของกฎ
required_providers คือรายชื่อผู้ให้บริการที่อนุญาตให้แอตทริบิวต์จำกัด
การเผยแพร่เฉพาะเป้าหมายที่มีกฎโฆษณาผู้ให้บริการที่จำเป็น
โปรดดูรายละเอียดเพิ่มเติมในเอกสารประกอบของฟังก์ชัน Aspect
ในกรณีนี้ ลักษณะจะใช้กับเป้าหมายที่ประกาศ CcInfo
ผู้ให้บริการเท่านั้น
การใช้งาน Aspect
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 []
ฟังก์ชันการใช้งานแง่มุมจะคล้ายกับฟังก์ชันการใช้งานกฎ โดยจะแสดงผล Provider สร้างการดำเนินการ และรับอาร์กิวเมนต์ 2 รายการ
target: เป้าหมายที่ใช้แอตทริบิวต์ctx: ออบเจ็กต์ctxที่ใช้เข้าถึงแอตทริบิวต์ และสร้างเอาต์พุตและการดำเนินการได้
ฟังก์ชันการติดตั้งใช้งานจะเข้าถึงแอตทริบิวต์ของกฎเป้าหมายได้ผ่าน
ctx.rule.attr โดยจะตรวจสอบผู้ให้บริการที่ระบุโดยเป้าหมายที่ใช้ (ผ่านอาร์กิวเมนต์ target)
ต้องระบุแง่มุมเพื่อแสดงรายการผู้ให้บริการ ในตัวอย่างนี้ Aspect ไม่ได้ระบุอะไรเลย จึงแสดงผลเป็นรายการว่าง
เรียกใช้ Aspect โดยใช้บรรทัดคำสั่ง
วิธีที่ง่ายที่สุดในการใช้ Aspect คือการใช้บรรทัดคำสั่งโดยใช้อาร์กิวเมนต์
--aspects
สมมติว่ามีการกำหนดลักษณะข้างต้นในไฟล์ชื่อ print.bzl
this:
bazel build //MyExample:example --aspects print.bzl%print_aspect
จะใช้ print_aspect กับexampleเป้าหมายและกฎเป้าหมายทั้งหมดที่เข้าถึงได้แบบเรียกซ้ำผ่านแอตทริบิวต์ deps
แฟล็ก --aspects รับอาร์กิวเมนต์ 1 รายการ ซึ่งเป็นข้อกำหนดของสัดส่วน
ในรูปแบบ <extension file label>%<aspect top-level name>
ตัวอย่างขั้นสูง
ตัวอย่างต่อไปนี้แสดงการใช้แง่มุมจากกฎเป้าหมาย ที่นับไฟล์ในเป้าหมาย ซึ่งอาจกรองตามนามสกุล โดยจะแสดงวิธีใช้ผู้ให้บริการเพื่อแสดงค่า วิธีใช้พารามิเตอร์เพื่อส่งอาร์กิวเมนต์ไปยังการใช้งาน Aspect และวิธีเรียกใช้ Aspect จากกฎ
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',
)
คำจำกัดความของ Aspect
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 ได้ด้วย คุณสามารถใช้แอตทริบิวต์ป้ายกำกับส่วนตัวเพื่อระบุการขึ้นต่อกันของเครื่องมือหรือไลบรารีที่จำเป็นสำหรับการดำเนินการที่สร้างขึ้นโดย Aspect ตัวอย่างนี้ไม่ได้กำหนดแอตทริบิวต์ส่วนตัว แต่ข้อมูลโค้ดต่อไปนี้แสดงวิธีส่งเครื่องมือไปยัง Aspect
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
การใช้งาน Aspect
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)]
เช่นเดียวกับฟังก์ชันการใช้งานกฎ ฟังก์ชันการใช้งาน Aspect จะแสดงผลโครงสร้างของผู้ให้บริการที่การอ้างอิงเข้าถึงได้
ในตัวอย่างนี้ FileCountInfo จะได้รับการกำหนดให้เป็นผู้ให้บริการที่มีฟิลด์ count 1 รายการ แนวทางปฏิบัติแนะนำคือการกำหนดฟิลด์ของ
ผู้ให้บริการอย่างชัดเจนโดยใช้แอตทริบิวต์ fields
ชุดของผู้ให้บริการสำหรับแอปพลิเคชัน Aspect A(X) คือการรวมของผู้ให้บริการ
ที่มาจากการติดตั้งใช้งานกฎสำหรับเป้าหมาย X และจากการ
ติดตั้งใช้งาน Aspect A ระบบจะสร้างและตรึงผู้ให้บริการที่การใช้งานกฎเผยแพร่
ก่อนที่จะใช้แง่มุม และจะแก้ไขจากแง่มุม
ไม่ได้ การกำหนดเป้าหมายและ Aspect ที่ใช้กับเป้าหมายนั้นแต่ละรายการ
ระบุผู้ให้บริการที่มีประเภทเดียวกันถือเป็นข้อผิดพลาด ยกเว้น
OutputGroupInfo
(ซึ่งจะผสานรวมกัน ตราบใดที่
กฎและ Aspect ระบุกลุ่มเอาต์พุตที่แตกต่างกัน) และ
InstrumentedFilesInfo
(ซึ่งนำมาจาก Aspect) ซึ่งหมายความว่าการใช้งาน Aspect อาจไม่แสดงผล DefaultInfo เลย
ระบบจะส่งพารามิเตอร์และแอตทริบิวต์ส่วนตัวในแอตทริบิวต์ของ
ctx ตัวอย่างนี้อ้างอิงพารามิเตอร์ extension และกำหนด
ไฟล์ที่จะนับ
สำหรับผู้ให้บริการที่ส่งคืน ค่าของแอตทริบิวต์ที่
มีการเผยแพร่องค์ประกอบ (จากรายการ attr_aspects) จะถูกแทนที่ด้วย
ผลลัพธ์ของการใช้องค์ประกอบกับแอตทริบิวต์เหล่านั้น เช่น หากเป้าหมาย
X มี Y และ Z ใน deps ctx.rule.attr.deps สำหรับ A(X) จะเป็น [A(Y), A(Z)]
ในตัวอย่างนี้ ctx.rule.attr.deps คือออบเจ็กต์เป้าหมายซึ่งเป็นผลลัพธ์ของการใช้แง่มุมกับ "deps" ของเป้าหมายเดิมที่ใช้แง่มุม
ในตัวอย่างนี้ Aspect จะเข้าถึง FileCountInfo ผู้ให้บริการจาก
การอ้างอิงของเป้าหมายเพื่อสะสมจำนวนไฟล์ทั้งหมดแบบทรานซิทีฟ
การเรียกใช้แง่มุมจากกฎ
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