สัดส่วนภาพ

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.4 ที่ใช้เวลาเพียง 2 นาที 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

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

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

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

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

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

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',
)

คำจำกัดความ

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

...
    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 จากข้อกําหนดของเป้าหมายเพื่อรวบรวมจํานวนไฟล์ทั้งหมดที่ส่งผ่าน

การเรียกใช้ลักษณะจากกฎ

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

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