สัดส่วนภาพ

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

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

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

ข้อมูลเบื้องต้นเกี่ยวกับ Aspect

ไฟล์ 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 BUILD กราฟทรัพยากร Dependency ของไฟล์

Bazel จะวิเคราะห์กราฟการขึ้นต่อกันนี้โดยเรียกใช้ฟังก์ชันการใช้งานของกฎที่เกี่ยวข้อง (ในกรณีนี้คือ "java_library") สำหรับทุกเป้าหมายในตัวอย่างข้างต้น ฟังก์ชันการใช้งานกฎจะสร้างการดำเนินการที่ สร้างอาร์ติแฟกต์ เช่น ไฟล์ .jar และส่งข้อมูล เช่น ตำแหน่ง และชื่อของอาร์ติแฟกต์เหล่านั้น ไปยังการขึ้นต่อกันแบบย้อนกลับของเป้าหมายเหล่านั้นในผู้ให้บริการ

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

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

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

รูปที่ 2 สร้างกราฟด้วยแง่มุม

ขอบที่ซ่อนอยู่คือขอบตามแอตทริบิวต์ในชุดการแพร่กระจายเท่านั้น ดังนั้นขอบ runtime_deps จึงไม่ซ่อนอยู่ในตัวอย่างนี้ จากนั้นฟังก์ชันการติดตั้งใช้งาน Aspect จะเรียกใช้ในโหนดทั้งหมดใน กราฟ Shadow ในลักษณะเดียวกับที่การติดตั้งใช้งานกฎเรียกใช้ในโหนด ของกราฟต้นฉบับ

ตัวอย่างง่ายๆ

ตัวอย่างนี้แสดงวิธีพิมพ์ไฟล์ต้นฉบับแบบเรียกซ้ำสำหรับกฎและทรัพยากร 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'],
)

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

คำจำกัดความของ 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 []

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

...
    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 ระบบจะสร้างและตรึงผู้ให้บริการที่การติดตั้งใช้งานกฎเผยแพร่ ก่อนที่จะใช้แง่มุมต่างๆ และจะแก้ไขจากแง่มุม ไม่ได้ การกำหนดเป้าหมายและลักษณะที่ใช้กับเป้าหมายนั้นแต่ละรายการต้องมีประเภทเดียวกัน ยกเว้น OutputGroupInfo (ซึ่งจะผสานรวมกัน ตราบใดที่ กฎและลักษณะระบุกลุ่มเอาต์พุตที่แตกต่างกัน) และ InstrumentedFilesInfo (ซึ่งนำมาจากลักษณะ) ซึ่งหมายความว่าการติดตั้งใช้งาน Aspect อาจไม่แสดงผล DefaultInfo เลย

ระบบจะส่งพารามิเตอร์และแอตทริบิวต์ส่วนตัวในแอตทริบิวต์ของ ctx ตัวอย่างนี้อ้างอิงพารามิเตอร์ extension และกำหนด ไฟล์ที่จะนับ

สำหรับผู้ให้บริการที่ส่งคืน ค่าของแอตทริบิวต์ที่ มีการเผยแพร่องค์ประกอบ (จากรายการ attr_aspects) จะถูกแทนที่ด้วย ผลลัพธ์ของการใช้องค์ประกอบกับแอตทริบิวต์เหล่านั้น เช่น หาก target 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

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