สัดส่วนภาพ

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

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

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

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

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

แฟล็ก --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 กำหนดชุดแอตทริบิวต์สำหรับแง่มุม แอตทริบิวต์แง่มุมสาธารณะมีประเภทเป็น string และเรียกว่าพารามิเตอร์ พารามิเตอร์ต้องมีการระบุvalues แอตทริบิวต์ ตัวอย่างนี้มีพารามิเตอร์ชื่อ extension ซึ่งอนุญาตให้มีค่าเป็น "*", "h" หรือ "cc"

ระบบจะนําค่าพารามิเตอร์สําหรับแง่มุมมาจากแอตทริบิวต์สตริงที่มีชื่อเดียวกับกฎที่ขอแง่มุม (ดูคําจํากัดความของ file_count_rule) ใช้แง่มุมที่มีพารามิเตอร์ผ่านบรรทัดคําสั่งไม่ได้เนื่องจากไม่มีไวยากรณ์ในการกําหนดพารามิเตอร์

ส่วนมิติข้อมูลยังจะมีแอตทริบิวต์ส่วนตัวประเภท 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 จากทรัพยากร 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