สัดส่วนภาพ

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

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 เดิมของเป้าหมายที่แสดงในรูปต่อไปนี้

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

รูปที่ 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

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