สัดส่วนภาพ

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

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

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

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

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

สร้างกราฟ

รูปที่ 1 กราฟการอ้างอิงไฟล์ BUILD

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

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

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

สร้างกราฟด้วยอัตราส่วน

รูปที่ 2 สร้างกราฟที่มีสัดส่วนต่างๆ

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

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

ตัวอย่างนี้แสดงวิธีพิมพ์ไฟล์ต้นฉบับซ้ำสำหรับกฎและทรัพยากร Dependency ทั้งหมดของกฎที่มีแอตทริบิวต์ 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 คือ ['*'] ซึ่งจะกระจายการแสดงผลไปยังแอตทริบิวต์ทั้งหมดของกฎ

การใช้งานด้าน

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

ฟังก์ชันการใช้งานด้านต่างๆ คล้ายกับฟังก์ชันการใช้งานกฎ โดยจะแสดงผล providers สามารถสร้างactions และใช้อาร์กิวเมนต์ 2 ตัว ได้แก่

  • target: กำหนดเป้าหมายที่กำลังใช้มุมมอง
  • ctx: ออบเจ็กต์ ctx ที่ใช้เข้าถึงแอตทริบิวต์และสร้างเอาต์พุตและการดำเนินการได้

ฟังก์ชันการใช้งานจะเข้าถึงแอตทริบิวต์ของกฎเป้าหมายได้ผ่าน ctx.rule.attr โดยสามารถตรวจสอบผู้ให้บริการที่ระบุโดยเป้าหมายที่ใช้ (ผ่านอาร์กิวเมนต์ target)

ต้องมีส่วนต่างๆ จึงจะแสดงรายชื่อผู้ให้บริการได้ ในตัวอย่างนี้ มุมมองไม่ได้ให้ข้อมูลใดๆ จึงแสดงรายการที่ว่างเปล่า

เรียกใช้ด้านนี้โดยใช้บรรทัดคำสั่ง

วิธีที่ง่ายที่สุดในการนำองค์ประกอบหนึ่งๆ ไปใช้คือการใช้บรรทัดคำสั่งโดยใช้อาร์กิวเมนต์ --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"
        )
    }
...

การใช้งานด้าน

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

เช่นเดียวกับฟังก์ชันการใช้งานกฎ ฟังก์ชันการใช้งานด้านการแสดงผลจะแสดงผลโครงสร้างของผู้ให้บริการที่เข้าถึงได้โดยทรัพยากร Dependency

ในตัวอย่างนี้ FileCountInfo คือผู้ให้บริการที่มี count ในช่องเดียว แนวทางปฏิบัติแนะนำคือการกำหนดช่องของผู้ให้บริการอย่างชัดแจ้งโดยใช้แอตทริบิวต์ fields

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

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

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