สัดส่วนภาพ

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

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

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

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

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

ไฟล์ 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 นี้ระบุกราฟทรัพยากร Dependency ที่แสดงในรูปต่อไปนี้

สร้างกราฟ

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

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

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

การใช้งาน 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 []

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

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

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