หน้านี้อธิบายพื้นฐานและประโยชน์ของการใช้มุมมองและให้ตัวอย่างง่ายๆ และขั้นสูง
Aspects ช่วยให้สามารถเสริมสร้างกราฟทรัพยากร Dependency ด้วยข้อมูลและการดำเนินการเพิ่มเติม ตัวอย่างสถานการณ์ทั่วไปที่อาจมีประโยชน์ มีดังนี้
- IDE ที่ผสานรวม Bazel สามารถใช้ส่วนต่างๆ ในการรวบรวมข้อมูลเกี่ยวกับโปรเจ็กต์
- เครื่องมือสร้างโค้ดสามารถใช้ประโยชน์จากแง่มุมต่างๆ เพื่อดำเนินการกับอินพุตแบบไม่อิงตามเป้าหมาย ตัวอย่างเช่น ไฟล์
BUILD
สามารถระบุลำดับชั้นของคำจำกัดความไลบรารี protobuf และกฎเฉพาะภาษาสามารถใช้ส่วนต่างๆ เพื่อแนบการดำเนินการที่สร้างโค้ดสนับสนุน protobuf สำหรับภาษาใดภาษาหนึ่ง
ข้อมูลเบื้องต้นเกี่ยวกับ Aspect
ไฟล์ 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
นี้ระบุกราฟทรัพยากร Dependency ที่แสดงในรูปต่อไปนี้
รูปที่ 1 กราฟทรัพยากร Dependency ของไฟล์ BUILD
Bazel วิเคราะห์กราฟทรัพยากร Dependency นี้โดยการเรียกใช้ฟังก์ชันการใช้งานของกฎที่เกี่ยวข้อง (ในกรณีนี้คือ "java_library") ของทุกเป้าหมายในตัวอย่างข้างต้น ฟังก์ชันการใช้งานกฎจะสร้างการดำเนินการที่สร้างอาร์ติแฟกต์ เช่น ไฟล์ .jar
และส่งข้อมูล เช่น ตำแหน่งและชื่อของอาร์ติแฟกต์ ไปยังทรัพยากร Dependency แบบย้อนกลับของเป้าหมายเหล่านั้นในผู้ให้บริการ
ส่วนคล้ายกับกฎตรงที่มีฟังก์ชันการใช้งานที่สร้างการดำเนินการและผู้ให้บริการการคืนสินค้า แต่พลังของผู้ใช้จะมาจากวิธีที่สร้างกราฟทรัพยากร Dependency นั้น แง่มุมหนึ่งจะมีการใช้งานและรายการแอตทริบิวต์ทั้งหมดที่เผยแพร่ไปพร้อมๆ กัน ลองใช้ด้าน A ที่เผยแพร่ ตามแอตทริบิวต์ที่ชื่อ "deps" ลักษณะนี้จะใช้กับเป้าหมาย X ได้ ซึ่งทำให้เป็นโหนดของแอปพลิเคชัน A(X) ในระหว่างการนำไปใช้ เป้าหมาย A จะใช้กับเป้าหมายทั้งหมดที่ X อ้างถึงในแอตทริบิวต์ "deps" ซ้ำๆ (แอตทริบิวต์ทั้งหมดในรายการการเผยแพร่ A)
ดังนั้น การนำมุมมอง A ไปใช้กับเป้าหมาย X เพียงครั้งเดียวก็จะได้ "กราฟเงา" ของกราฟทรัพยากร Dependency เดิมของเป้าหมายที่แสดงในรูปต่อไปนี้
รูปที่ 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 จะคล้ายกับคําจํากัดความของกฎและกําหนดโดยใช้ฟังก์ชัน 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 จะคล้ายกับฟังก์ชันการใช้งานกฎ โดยจะแสดงผล providers, สร้างการดำเนินการ และรับอาร์กิวเมนต์ 2 รายการ ดังนี้
target
: เป้าหมายที่ใช้การแสดงผลctx
: ออบเจ็กต์ctx
ที่ใช้เข้าถึงแอตทริบิวต์ รวมถึงสร้างเอาต์พุตและการดำเนินการได้
ฟังก์ชันการใช้งานจะเข้าถึงแอตทริบิวต์ของกฎเป้าหมายได้ผ่าน ctx.rule.attr
ซึ่งจะตรวจสอบผู้ให้บริการที่ได้จากเป้าหมายที่ใช้ (ผ่านอาร์กิวเมนต์ target
)
ต้องระบุ Aspect เพื่อแสดงผลรายชื่อผู้ให้บริการ ในตัวอย่างนี้ ลักษณะไม่ได้ระบุข้อมูลใดเลย ดังนั้นจึงแสดงรายการที่ว่างเปล่า
การเรียกใช้มุมมองโดยใช้บรรทัดคำสั่ง
วิธีที่ง่ายที่สุดในการนำองค์ประกอบไปใช้คือใช้บรรทัดคำสั่งโดยใช้อาร์กิวเมนต์ --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"
)
}
...
การใช้งาน 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)]
เช่นเดียวกับฟังก์ชันการใช้งานกฎ ฟังก์ชันการใช้งานมุมมองจะแสดงผลโครงสร้างของผู้ให้บริการที่เข้าถึงได้ด้วยทรัพยากร Dependency ได้
ในตัวอย่างนี้ FileCountInfo
ได้รับการกำหนดให้เป็นผู้ให้บริการที่มี 1 ช่อง count
แนวทางปฏิบัติแนะนำคือการกำหนดช่องข้อมูลของผู้ให้บริการอย่างชัดแจ้งโดยใช้แอตทริบิวต์ fields
กลุ่มผู้ให้บริการสำหรับแอปพลิเคชันด้าน A(X) คือการรวมผู้ให้บริการที่มาจากการใช้กฎสำหรับเป้าหมาย X และจากการใช้งานด้าน A ระบบจะสร้างผู้ให้บริการที่มีการใช้กฎและตรึงไว้ก่อนที่จะใช้ด้านต่างๆ และไม่สามารถแก้ไขจากด้านใดด้านหนึ่งได้ โดยจะเกิดข้อผิดพลาดหากเป้าหมายและลักษณะที่ใช้กับเป้าหมายแต่ละรายการระบุผู้ให้บริการที่มีประเภทเดียวกัน โดยมีข้อยกเว้นสำหรับ OutputGroupInfo
(ซึ่งรวมกันตราบเท่าที่กฎและมุมมองระบุกลุ่มเอาต์พุตต่างกัน) และ InstrumentedFilesInfo
(ซึ่งได้มาจากสัดส่วนภาพ) ซึ่งหมายความว่าการติดตั้งใช้งานด้านต่างๆ อาจไม่แสดงผล DefaultInfo
ระบบจะส่งพารามิเตอร์และแอตทริบิวต์ส่วนตัวในแอตทริบิวต์ของ ctx
ตัวอย่างนี้อ้างอิงพารามิเตอร์ extension
และกำหนดว่าจะนับไฟล์ใด
สำหรับผู้ให้บริการที่กลับมา ระบบจะแทนที่ค่าของแอตทริบิวต์ที่มีการเผยแพร่ (จากรายการ attr_aspects
) ด้วยผลลัพธ์ของการใช้ลักษณะดังกล่าว เช่น ถ้าเป้าหมาย X มี Y และ Z อยู่ในแนวด้าน 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