สัดส่วนภาพ

รายงานปัญหา ดูแหล่งที่มา รุ่น Nightly · 7.4

หน้านี้จะอธิบายพื้นฐานและประโยชน์ของการใช้แง่มุม รวมถึงแสดงตัวอย่างแบบง่ายและแบบขั้นสูง

ส่วนช่วยให้คุณเพิ่มกราฟความเกี่ยวข้องของบิลด์ด้วยข้อมูลเพิ่มเติมและการดําเนินการ สถานการณ์ทั่วไปที่อาจเป็นประโยชน์ในการใช้แง่มุมมีดังนี้

  • IDE ที่ผสานรวม Bazel สามารถใช้ส่วนต่างๆ ในการรวบรวมข้อมูลเกี่ยวกับโปรเจ็กต์
  • เครื่องมือสร้างโค้ดสามารถใช้ประโยชน์จากแง่มุมต่างๆ เพื่อดำเนินการกับอินพุตในลักษณะไม่เจาะจงเป้าหมาย ตัวอย่างเช่น ไฟล์ BUILD สามารถระบุลําดับชั้นของคําจํากัดความของไลบรารี protobuf และกฎเฉพาะภาษาสามารถใช้แง่มุมเพื่อแนบการดำเนินการที่สร้างโค้ดการสนับสนุน protobuf สําหรับภาษาหนึ่งๆ

ข้อมูลเบื้องต้นเกี่ยวกับการแสดงผล

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

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

ดังนั้น การนำมุมมอง A ไปใช้กับเป้าหมาย X เพียงครั้งเดียวก็จะได้ "กราฟเงา" ของกราฟทรัพยากร Dependency เดิมของเป้าหมายที่แสดงในรูปต่อไปนี้

สร้างกราฟด้วย Aspect

รูปที่ 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'],
)

เรามาแยกตัวอย่างออกเป็นส่วนๆ และพิจารณาแต่ละส่วนกัน

คำจำกัดความ

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 []

ฟังก์ชันการติดตั้งใช้งานแง่มุมจะคล้ายกับฟังก์ชันการติดตั้งใช้งานกฎ โดยจะแสดงผลผู้ให้บริการ สร้างการดำเนินการ และรับอาร์กิวเมนต์ 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

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 ได้ด้วย แอตทริบิวต์ป้ายกำกับส่วนตัวสามารถใช้เพื่อระบุทรัพยากร 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 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

ข้อมูลอ้างอิง