หน้านี้จะอธิบายพื้นฐานและประโยชน์ของการใช้ aspects และแสดงแบบง่ายและแบบขั้นสูง ตัวอย่าง
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
เช่นเดียวกับกฎ องค์ประกอบหนึ่งจะมีฟังก์ชันการใช้งาน ซึ่งในกรณีนี้คือ
_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 จะคล้ายกับการใช้กฎ แสดงผู้ให้บริการซึ่งสามารถสร้าง actions และใช้อาร์กิวเมนต์ 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)]
เช่นเดียวกับฟังก์ชันการใช้งานกฎ ฟังก์ชันการใช้งานด้านต่างๆ จะแสดงผลโครงสร้างของผู้ให้บริการที่การอ้างอิงของผู้ให้บริการดังกล่าวเข้าถึงได้
ในตัวอย่างนี้ FileCountInfo
ได้รับการกำหนดให้เป็นผู้ให้บริการที่มี
ฟิลด์ 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
คือออบเจ็กต์เป้าหมายที่
ผลลัพธ์ของการนำแง่มุมดังกล่าวไปใช้กับ "ความลึก" ของเป้าหมายเดิมซึ่ง
ที่ได้รับการปรับปรุงแล้ว
ในตัวอย่างนี้ สัดส่วนภาพเข้าถึงผู้ให้บริการ 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