หน้านี้จะอธิบายพื้นฐานและประโยชน์ของการใช้แง่มุม รวมถึงแสดงตัวอย่างแบบง่ายและแบบขั้นสูง
Aspects ช่วยให้สามารถเสริมสร้างกราฟทรัพยากร Dependency ด้วยข้อมูลและการดำเนินการเพิ่มเติม สถานการณ์ทั่วไปที่อาจเป็นประโยชน์ในการใช้แง่มุมมีดังนี้
- IDE ที่ผสานรวม Bazel สามารถใช้แง่มุมต่างๆ เพื่อรวบรวมข้อมูลเกี่ยวกับโปรเจ็กต์ได้
- เครื่องมือสร้างโค้ดสามารถใช้ประโยชน์จากแง่มุมต่างๆ เพื่อดำเนินการกับอินพุตในลักษณะไม่เจาะจงเป้าหมาย ตัวอย่างเช่น ไฟล์
BUILD
สามารถระบุลําดับชั้นของคําจํากัดความของไลบรารี protobuf และกฎเฉพาะภาษาสามารถใช้แง่มุมเพื่อแนบการดำเนินการที่สร้างโค้ดการสนับสนุน protobuf สําหรับภาษาหนึ่งๆ
ข้อมูลเบื้องต้นเกี่ยวกับการแสดงผล
ไฟล์ BUILD
ให้คำอธิบายซอร์สโค้ดของโปรเจ็กต์ ซึ่งได้แก่ ไฟล์ต้นฉบับใดเป็นส่วนหนึ่งของโปรเจ็กต์ อาร์ติแฟกต์ (เป้าหมาย) ใดควรสร้างจากไฟล์เหล่านั้น ไฟล์เหล่านั้นมีการพึ่งพากันอย่างไร ฯลฯ Bazel ใช้ข้อมูลนี้เพื่อทำการบิลด์ กล่าวคือ ค้นหาชุดการดำเนินการที่จำเป็นในการผลิตอาร์ติแฟกต์ (เช่น การเรียกใช้คอมไพเลอร์หรือโปรแกรมลิงก์) และดำเนินการเหล่านั้น Bazel ทําเช่นนี้ได้โดยการสร้างกราฟ Dependency ระหว่างเป้าหมายและไปที่กราฟนี้เพื่อรวบรวมการดําเนินการเหล่านั้น
ลองพิจารณาไฟล์ 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 แง่มุมหนึ่งๆ มีการนําไปใช้ และรายการแอตทริบิวต์ทั้งหมดที่เผยแพร่ไปพร้อมกับแง่มุมนั้น ลองใช้ด้าน A ที่เผยแพร่ ตามแอตทริบิวต์ที่ชื่อ "deps" สามารถใช้แง่มุมนี้กับเป้าหมาย X ซึ่งจะให้โหนดการนําแอปพลิเคชันแง่มุม A(X) ในระหว่างการใช้งาน ระบบจะใช้แง่มุม A แบบซ้ำซ้อนกับเป้าหมายทั้งหมดที่ X อ้างอิงในแอตทริบิวต์ "deps" (แอตทริบิวต์ทั้งหมดในรายการการนำไปใช้งานของ A)
ดังนั้น การใช้แง่มุม A กับเป้าหมาย X ครั้งเดียวจะให้ "กราฟเงา" ของกราฟ Dependency เดิมของเป้าหมายดังที่แสดงในรูปภาพต่อไปนี้
รูปที่ 2 สร้างกราฟด้วยแง่มุม
ขอบที่มีเงาเพียงขอบเดียวคือขอบตามแอตทริบิวต์ในชุดการนำไปใช้งาน ดังนั้นขอบ runtime_deps
จึงไม่มีเงาในตัวอย่างนี้ จากนั้นระบบจะเรียกใช้ฟังก์ชันการติดตั้งใช้งานแง่มุมในโหนดทั้งหมดในกราฟเงา ซึ่งคล้ายกับวิธีเรียกใช้การติดตั้งใช้งานกฎในโหนดของกราฟต้นฉบับ
ตัวอย่างง่ายๆ
ตัวอย่างนี้แสดงวิธีพิมพ์ไฟล์ต้นทางของกฎและรายการที่เกี่ยวข้องทั้งหมดที่มีแอตทริบิวต์ 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'],
)
เรามาแยกตัวอย่างออกเป็นส่วนๆ และพิจารณาแต่ละส่วนกัน
คําจํากัดความของ Aspect
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
คําจํากัดความของลักษณะจะคล้ายกับคําจํากัดความของกฎ และกำหนดโดยใช้ฟังก์ชัน aspect
เช่นเดียวกับกฎ แอตทริบิวต์มีฟังก์ชันการใช้งาน ซึ่งในกรณีนี้คือ
_print_aspect_impl
attr_aspects
คือรายการแอตทริบิวต์กฎที่ส่งผ่านแง่มุม
ในกรณีนี้ แง่มุมจะเผยแพร่ไปตามแอตทริบิวต์ deps
ของกฎที่ใช้แง่มุมนั้น
อีกอาร์กิวเมนต์ทั่วไปสําหรับ attr_aspects
คือ ['*']
ซึ่งจะเผยแพร่แง่มุมไปยังแอตทริบิวต์ทั้งหมดของกฎ
การใช้งาน 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 []
ฟังก์ชันการใช้งาน Aspect จะคล้ายกับฟังก์ชันการใช้งานกฎ โดยจะแสดงผลผู้ให้บริการ สร้างการดำเนินการ และรับอาร์กิวเมนต์ 2 รายการ ดังนี้
target
: เป้าหมายที่มีการใช้แง่มุมctx
: ออบเจ็กต์ctx
ที่ใช้เข้าถึงแอตทริบิวต์ ตลอดจนสร้างเอาต์พุตและการดําเนินการได้
ฟังก์ชันการใช้งานเข้าถึงแอตทริบิวต์ของกฎเป้าหมายได้ผ่าน ctx.rule.attr
ซึ่งจะตรวจสอบผู้ให้บริการที่ได้จากเป้าหมายที่ใช้ (ผ่านอาร์กิวเมนต์ target
)
จำเป็นต้องระบุแง่มุมเพื่อแสดงรายการผู้ให้บริการ ในตัวอย่างนี้ ข้อมูลพร็อพเพอร์ตี้ไม่ได้ระบุข้อมูลใดๆ ระบบจึงแสดงผลรายการว่าง
การเรียกใช้แอตทริบิวต์โดยใช้บรรทัดคำสั่ง
วิธีที่ง่ายที่สุดในการใช้แง่มุมคือจากบรรทัดคำสั่งโดยใช้อาร์กิวเมนต์ --aspects
สมมติว่ามีการกําหนดแง่มุมข้างต้นในไฟล์ชื่อ print.bzl
ดังนี้
bazel build //MyExample:example --aspects print.bzl%print_aspect
จะใช้ print_aspect
กับ example
เป้าหมายและกฎเป้าหมายทั้งหมดที่เข้าถึงแบบซ้ำผ่านแอตทริบิวต์ deps
Flag --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',
)
คําจํากัดความของ 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
ได้ด้วย คุณใช้แอตทริบิวต์ป้ายกำกับส่วนตัวเพื่อระบุการพึ่งพาเครื่องมือหรือไลบรารีที่จําเป็นสําหรับการดําเนินการที่เกิดจากแง่มุมได้ ตัวอย่างนี้ไม่ได้กำหนดแอตทริบิวต์ส่วนตัว แต่ข้อมูลโค้ดต่อไปนี้แสดงวิธีส่งเครื่องมือไปยังแง่มุม
...
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)]
ฟังก์ชันการใช้งานแง่มุมจะแสดงผลโครงสร้างของผู้ให้บริการที่เข้าถึงได้สำหรับทรัพยากร เช่นเดียวกับฟังก์ชันการใช้งานกฎ
ในตัวอย่างนี้ FileCountInfo
หมายถึงผู้ให้บริการที่มีช่อง count
1 ช่อง แนวทางปฏิบัติแนะนำคือการกำหนดช่องข้อมูลของผู้ให้บริการอย่างชัดแจ้งโดยใช้แอตทริบิวต์ fields
ชุดผู้ให้บริการสําหรับแอปพลิเคชันด้าน A(X) คือสหพันธ์ของผู้ให้บริการที่มาจากการใช้งานกฎสําหรับเป้าหมาย X และจากการใช้งานด้าน A ระบบจะสร้างและตรึงผู้ให้บริการที่การใช้งานกฎนำไปใช้ก่อนที่จะนำแอตทริบิวต์ไปใช้ และไม่สามารถแก้ไขจากแอตทริบิวต์ได้ ระบบจะแสดงข้อผิดพลาดหากเป้าหมายและองค์ประกอบที่ใช้กับเป้าหมายแต่ละรายการมีผู้ให้บริการประเภทเดียวกัน ยกเว้น OutputGroupInfo
(ซึ่งจะผสานกัน ตราบใดที่กฎและองค์ประกอบระบุกลุ่มเอาต์พุตที่แตกต่างกัน) และ InstrumentedFilesInfo
(ซึ่งนำมาจากองค์ประกอบ) ซึ่งหมายความว่าการติดตั้งใช้งานแอตทริบิวต์อาจไม่แสดง DefaultInfo
เลย
ระบบจะส่งพารามิเตอร์และแอตทริบิวต์ส่วนตัวในแอตทริบิวต์ของ ctx
ตัวอย่างนี้อ้างอิงพารามิเตอร์ extension
และกำหนดไฟล์ที่จะนับ
สําหรับผู้ให้บริการที่แสดงผล ระบบจะแทนที่ค่าของแอตทริบิวต์ที่เผยแพร่แง่มุม (จากรายการ attr_aspects
) ด้วยผลลัพธ์ของการใช้แง่มุมกับผู้ให้บริการ เช่น หากเป้าหมาย
X มี Y และ Z ใน deps ctx.rule.attr.deps
สำหรับ A(X) จะเป็น [A(Y), A(Z)]
ในตัวอย่างนี้ ctx.rule.attr.deps
คือออบเจ็กต์เป้าหมายซึ่งเป็นผลมาจากการใช้แอตทริบิวต์กับ "deps" ของเป้าหมายเดิมที่มีการใช้แอตทริบิวต์
ในตัวอย่างนี้ แอตทริบิวต์เข้าถึงผู้ให้บริการ 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