คู่มือแนะนำรูปแบบการสร้าง

รายงานปัญหา ดูซอร์ส รุ่น Nightly · 8.1 · 8.0 · 7.5 · 7.4

โปรดใช้ไฟล์ DAMP BUILD แทน DRY

หลักการ DRY หรือ "Don't Repeat Yourself" (อย่าทำซ้ำ) ส่งเสริมความเป็นเอกลักษณ์ด้วยการนำการแยกความคิด เช่น ตัวแปรและฟังก์ชัน มาใช้เพื่อหลีกเลี่ยงการเขียนโค้ดซ้ำ

ในทางตรงกันข้าม หลักการ DAMP หรือ "วลีที่สื่อความหมายและเข้าใจง่าย" จะเน้นความสามารถในการอ่านมากกว่าความเฉพาะตัว เพื่อให้ไฟล์เข้าใจและดูแลรักษาได้ง่ายขึ้น

ไฟล์ BUILD ไม่ใช่โค้ด แต่เป็นการกำหนดค่า ข้อมูลดังกล่าวไม่ได้รับการทดสอบเหมือนโค้ด แต่ต้องได้รับการดูแลรักษาโดยบุคคลและเครื่องมือ ซึ่งทำให้ DAMP เหมาะกับผู้ใช้รายดังกล่าวมากกว่า DRY

การจัดรูปแบบไฟล์ BUILD.bazel

การจัดรูปแบบไฟล์ BUILD เป็นไปตามแนวทางเดียวกับ Go ซึ่งเครื่องมือมาตรฐานจะจัดการปัญหาการจัดรูปแบบส่วนใหญ่ Buildifier เป็นเครื่องมือที่แยกวิเคราะห์และแสดงซอร์สโค้ดในรูปแบบมาตรฐาน ดังนั้นไฟล์ BUILD ทุกไฟล์จึงได้รับการจัดรูปแบบด้วยวิธีอัตโนมัติเดียวกัน ซึ่งทำให้การจัดรูปแบบไม่ใช่ปัญหาระหว่างการตรวจสอบโค้ด นอกจากนี้ ยังช่วยให้เครื่องมือเข้าใจ แก้ไข และสร้างไฟล์ BUILD ได้ง่ายขึ้นด้วย

การจัดรูปแบบไฟล์ BUILD ต้องตรงกับเอาต์พุตของ buildifier

ตัวอย่างการจัดรูปแบบ

# Test code implementing the Foo controller.
package(default_testonly = True)

py_test(
    name = "foo_test",
    srcs = glob(["*.py"]),
    data = [
        "//data/production/foo:startfoo",
        "//foo",
        "//third_party/java/jdk:jdk-k8",
    ],
    flaky = True,
    deps = [
        ":check_bar_lib",
        ":foo_data_check",
        ":pick_foo_port",
        "//pyglib",
        "//testing/pybase",
    ],
)

โครงสร้างไฟล์

คําแนะนํา: ใช้ลําดับต่อไปนี้ (องค์ประกอบทุกรายการเป็นองค์ประกอบที่ไม่บังคับ)

  • คำอธิบายแพ็กเกจ (ความคิดเห็น)

  • ใบแจ้งยอด load() ทั้งหมด

  • ฟังก์ชัน package()

  • การเรียกใช้กฎและมาโคร

Buildifier จะแยกความแตกต่างระหว่างความคิดเห็นแบบสแตนด์อโลนและความคิดเห็นที่แนบมากับองค์ประกอบ หากความคิดเห็นไม่ได้แนบอยู่กับองค์ประกอบใด ให้ใช้บรรทัดว่างต่อจากความคิดเห็น การแยกความแตกต่างนี้สำคัญเมื่อทำการเปลี่ยนแปลงอัตโนมัติ (เช่น เก็บหรือนำความคิดเห็นออกเมื่อลบกฎ)

# Standalone comment (such as to make a section in a file)

# Comment for the cc_library below
cc_library(name = "cc")

การอ้างอิงเป้าหมายในแพ็กเกจปัจจุบัน

ไฟล์ควรอ้างอิงตามเส้นทางที่สัมพันธ์กับไดเรกทอรีแพ็กเกจ (โดยไม่ใช้การอ้างอิงระดับบน เช่น ..) ไฟล์ที่สร้างขึ้นควรมี ":" นำหน้าเพื่อระบุว่าไม่ใช่แหล่งที่มา ไฟล์ต้นฉบับไม่ควรมี:นำหน้า กฎควรขึ้นต้นด้วย : ตัวอย่างเช่น สมมติว่า x.cc เป็นไฟล์ต้นทาง

cc_library(
    name = "lib",
    srcs = ["x.cc"],
    hdrs = [":gen_header"],
)

genrule(
    name = "gen_header",
    srcs = [],
    outs = ["x.h"],
    cmd = "echo 'int x();' > $@",
)

การตั้งชื่อเป้าหมาย

ชื่อเป้าหมายควรสื่อความหมาย หากเป้าหมายมีไฟล์ต้นทางไฟล์เดียว โดยทั่วไปเป้าหมายควรมีชื่อที่มาจากแหล่งที่มานั้น (เช่น cc_library สำหรับ chat.cc อาจตั้งชื่อว่า chat หรือ java_library สำหรับ DirectMessage.java อาจตั้งชื่อว่า direct_message)

เป้าหมายที่มีชื่อเดียวกันสำหรับแพ็กเกจ (เป้าหมายที่มีชื่อเดียวกับไดเรกทอรีที่บรรจุ) ควรให้ฟังก์ชันการทำงานตามที่อธิบายโดยชื่อไดเรกทอรี หากไม่มีเป้าหมายดังกล่าว อย่าสร้างเป้าหมายที่มีชื่อเดียวกัน

โปรดใช้ชื่อย่อเมื่ออ้างอิงถึงเป้าหมายที่มีชื่อเดียวกัน (//xแทน //x:x) หากคุณอยู่ในแพ็กเกจเดียวกัน ให้ใช้การอ้างอิงภายใน (:x แทน //x)

หลีกเลี่ยงการใช้ชื่อเป้าหมายที่ "สงวนไว้" ซึ่งมีความหมายพิเศษ ซึ่งรวมถึง all, __pkg__ และ __subpackages__ ชื่อเหล่านี้มีความหมายเชิงอรรถศาสตร์พิเศษและอาจทำให้เกิดความสับสนและลักษณะการทำงานที่ไม่คาดคิดเมื่อนำมาใช้

หากไม่มีแบบแผนของทีมที่แพร่หลาย ต่อไปนี้คือคําแนะนําที่ไม่ผูกมัดซึ่งใช้กันอย่างกว้างขวางใน Google

  • โดยทั่วไป ให้ใช้ "snake_case"
    • สําหรับ java_library ที่มี src 1 ตัว หมายความว่าใช้ชื่อที่ไม่ใช่ชื่อไฟล์ที่ไม่มีนามสกุล
    • สำหรับกฎ *_binary และ *_test ของ Java ให้ใช้"Upper CamelCase" ซึ่งช่วยให้ชื่อเป้าหมายตรงกับ src รายการใดรายการหนึ่งได้ สําหรับ java_test การดำเนินการนี้ช่วยให้ระบบอนุมานแอตทริบิวต์ test_class จากชื่อของเป้าหมายได้
  • หากเป้าหมายหนึ่งๆ มีตัวแปรหลายรายการ ให้เพิ่มส่วนต่อท้ายเพื่อคํานวณให้ชัดเจน (เช่น :foo_dev, :foo_prod หรือ :bar_x86, :bar_x64)
  • เป้าหมายคำต่อท้าย _test ที่มี _test, _unittest, Test หรือ Tests
  • หลีกเลี่ยงคำต่อท้ายที่ไม่มีความหมาย เช่น _lib หรือ _library (เว้นแต่จำเป็นเพื่อหลีกเลี่ยงความขัดแย้งระหว่างเป้าหมาย _library กับ _binary ที่เกี่ยวข้อง)
  • สําหรับเป้าหมายที่เกี่ยวข้องกับโปรโต ให้ทําดังนี้
    • เป้าหมาย proto_library ควรมีชื่อที่ลงท้ายด้วย _proto
    • กฎ *_proto_library สำหรับภาษาที่เฉพาะเจาะจงควรตรงกับ proto พื้นฐาน แต่แทนที่ _proto ด้วยส่วนต่อท้ายที่เฉพาะเจาะจงตามภาษา เช่น
      • cc_proto_library: _cc_proto
      • java_proto_library: _java_proto
      • java_lite_proto_library: _java_proto_lite

ระดับการแชร์

คุณควรกำหนดขอบเขตระดับการเข้าถึงให้แคบที่สุดเท่าที่จะเป็นไปได้ ในขณะที่ยังคงอนุญาตให้การทดสอบและความสัมพันธ์ย้อนกลับเข้าถึงได้ ใช้ __pkg__ และ __subpackages__ ตามเหมาะสม

หลีกเลี่ยงการตั้งค่าแพ็กเกจ default_visibility เป็น //visibility:public //visibility:public ควรตั้งค่าแยกต่างหากสําหรับเป้าหมายใน API สาธารณะของโปรเจ็กต์เท่านั้น รายการเหล่านี้อาจเป็นไลบรารีที่ออกแบบมาเพื่อให้โปรเจ็กต์ภายนอกใช้ หรือไบนารีที่กระบวนการบิลด์ของโปรเจ็กต์ภายนอกอาจใช้

การอ้างอิง

Dependency ควรจํากัดไว้เฉพาะ Dependency โดยตรง (Dependency ที่แหล่งที่มาที่ระบุไว้ในกฎจําเป็นต้องใช้) อย่าแสดงรายการ Dependency แบบทรานซิทีฟ

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

โปรดระบุรายการพึ่งพาโดยตรงเป็นรายการเดียว การวางข้อกำหนด "ทั่วไป" ของเป้าหมายหลายรายการไว้ในตัวแปรจะลดความสามารถในการบำรุงรักษา ทำให้เครื่องมือเปลี่ยนข้อกำหนดของเป้าหมายไม่ได้ และอาจนำไปสู่ข้อกำหนดที่ไม่ได้ใช้

ก้อน

ระบุ "ไม่มีเป้าหมาย" ด้วย [] อย่าใช้นิพจน์ทั่วไปที่ตรงกับรายการใดๆ เลย เนื่องจากมีแนวโน้มที่จะเกิดข้อผิดพลาดและไม่ค่อยชัดเจนเท่ากับรายการว่าง

เกิดซ้ำ

อย่าใช้นิพจน์ทั่วไปแบบย้อนกลับเพื่อจับคู่ไฟล์ต้นฉบับ (เช่น glob(["**/*.java"]))

รูปแบบทั่วไปแบบเรียกซ้ำทำให้การหาเหตุผลของไฟล์ BUILD ทำได้ยาก เนื่องจากจะข้ามไดเรกทอรีย่อยที่มีไฟล์ BUILD

โดยทั่วไปแล้ว รูปแบบทั่วไปแบบเรียกซ้ำจะมีประสิทธิภาพน้อยกว่าการมีไฟล์ BUILD 1 ไฟล์ต่อไดเรกทอรีที่มีการกำหนดกราฟความเกี่ยวข้องไว้ระหว่างกัน เนื่องจากวิธีนี้ช่วยให้แคชระยะไกลและการทำงานแบบขนานมีประสิทธิภาพมากขึ้น

แนวทางปฏิบัติแนะนำคือให้เขียนไฟล์ BUILD ในแต่ละไดเรกทอรีและกำหนดกราฟความเกี่ยวข้องระหว่างไฟล์

ไม่ใช้การเรียกซ้ำ

โดยทั่วไประบบจะยอมรับรูปแบบทั่วไปแบบไม่ซ้ำ

หลีกเลี่ยงนิพจน์ทั่วไปของลิสต์

หลีกเลี่ยงการใช้การสรุปรายการที่ระดับบนสุดของไฟล์ BUILD.bazel เรียกใช้ซ้ำๆ โดยอัตโนมัติโดยสร้างเป้าหมายที่มีชื่อแต่ละรายการด้วยกฎระดับบนสุดหรือการเรียกใช้มาโครแยกต่างหาก ตั้งชื่อพารามิเตอร์ name สั้นๆ เพื่อความชัดเจน

การเขียนนิพจน์ลิสต์จะช่วยลดสิ่งต่อไปนี้

  • ความสามารถในการบำรุงรักษา ผู้ดูแลระบบและการเปลี่ยนแปลงอัตโนมัติขนาดใหญ่จะอัปเดตการสรุปข้อมูลรายการได้อย่างถูกต้องได้ยากหรือเป็นไปไม่ได้
  • การค้นพบได้ เนื่องจากรูปแบบไม่มีพารามิเตอร์ name จึงค้นหากฎตามชื่อได้ยาก

การใช้งานทั่วไปของรูปแบบการทําความเข้าใจรายการคือการสร้างการทดสอบ เช่น

[[java_test(
    name = "test_%s_%s" % (backend, count),
    srcs = [ ... ],
    deps = [ ... ],
    ...
) for backend in [
    "fake",
    "mock",
]] for count in [
    1,
    10,
]]

เราขอแนะนำให้ใช้วิธีอื่นที่ง่ายกว่า เช่น กำหนดมาโครที่สร้างขึ้น 1 รายการและเรียกใช้สำหรับ name ระดับบนสุดแต่ละรายการ

my_java_test(name = "test_fake_1",
    ...)
my_java_test(name = "test_fake_10",
    ...)
...

อย่าใช้ตัวแปร deps

อย่าใช้ตัวแปรรายการเพื่อรวมข้อมูลต่อไปนี้

COMMON_DEPS = [
  "//d:e",
  "//x/y:z",
]

cc_library(name = "a",
    srcs = ["a.cc"],
    deps = COMMON_DEPS + [ ... ],
)

cc_library(name = "b",
    srcs = ["b.cc"],
    deps = COMMON_DEPS + [ ... ],
)

ในทํานองเดียวกัน อย่าใช้เป้าหมายไลบรารีที่มี exports เพื่อจัดกลุ่มทรัพยากร Dependency

แต่ให้ระบุรายการพึ่งพาแยกกันสำหรับแต่ละเป้าหมาย ดังนี้

cc_library(name = "a",
    srcs = ["a.cc"],
    deps = [
      "//a:b",
      "//x/y:z",
      ...
    ],
)

cc_library(name = "b",
    srcs = ["b.cc"],
    deps = [
      "//a:b",
      "//x/y:z",
      ...
    ],
)

ปล่อยให้ Gazelle และเครื่องมืออื่นๆ ดูแลรักษา การดำเนินการจะซ้ำกัน แต่คุณไม่ต้องกังวลว่าจะจัดการกับ Dependency อย่างไร

โปรดใช้สตริงตามตัวอักษร

แม้ว่า Starlark จะมีโอเปอเรเตอร์สตริงสำหรับการต่อสตริง (+) และการฟอร์แมต (%) แต่โปรดใช้ด้วยความระมัดระวัง คุณอาจต้องการแยกส่วนสตริงที่ซ้ำกันออกเพื่อให้นิพจน์กระชับขึ้นหรือแบ่งบรรทัดยาว อย่างไรก็ตาม

  • ค่าสตริงที่แบ่งออกเป็นส่วนๆ จะอ่านได้ยากกว่า

  • เครื่องมืออัตโนมัติ เช่น buildozer และการค้นหาโค้ดจะพบปัญหาในการค้นหาค่าและอัปเดตค่าอย่างถูกต้องเมื่อค่าถูกแยก

  • ในไฟล์ BUILD ความสะดวกในการอ่านสำคัญกว่าการหลีกเลี่ยงการซ้ำ (ดูDAMP เทียบกับ DRY)

  • คู่มือสไตล์นี้เตือนไม่ให้แยกสตริงค่าป้ายกำกับ และอนุญาตบรรทัดยาวอย่างชัดเจน

  • Buildifier จะรวมสตริงที่ต่อเชื่อมกันโดยอัตโนมัติเมื่อตรวจพบว่าสตริงเหล่านั้นเป็นป้ายกำกับ

ดังนั้น โปรดใช้สตริงที่ตรงตามตัวอักษรอย่างชัดแจ้งแทนสตริงที่ต่อเชื่อมหรือมีการจัดรูปแบบ โดยเฉพาะในแอตทริบิวต์ประเภทป้ายกำกับ เช่น name และ deps เช่น ตัวอย่างข้อความ BUILD นี้

NAME = "foo"
PACKAGE = "//a/b"

proto_library(
  name = "%s_proto" % NAME,
  deps = [PACKAGE + ":other_proto"],
  alt_dep = "//surprisingly/long/chain/of/package/names:" +
            "extravagantly_long_target_name",
)

ควรเขียนใหม่เป็น

proto_library(
  name = "foo_proto",
  deps = ["//a/b:other_proto"],
  alt_dep = "//surprisingly/long/chain/of/package/names:extravagantly_long_target_name",
)

จำกัดสัญลักษณ์ที่ส่งออกโดยไฟล์ .bzl แต่ละไฟล์

ลดจำนวนสัญลักษณ์ (กฎ มาโคร ค่าคงที่ ฟังก์ชัน) ที่ส่งออกโดยไฟล์ .bzl (Starlark) สาธารณะแต่ละไฟล์ เราขอแนะนำให้ส่งออกสัญลักษณ์หลายรายการในไฟล์ในกรณีที่มีการใช้สัญลักษณ์เหล่านั้นร่วมกันเท่านั้น หรือแยกออกเป็นไฟล์ .bzl หลายไฟล์ โดยแต่ละไฟล์จะมี bzl_library เป็นของตัวเอง

สัญลักษณ์ที่มากเกินไปอาจทําให้ไฟล์ .bzl กลายเป็น "คลัง" สัญลักษณ์ขนาดใหญ่ ซึ่งทําให้การเปลี่ยนแปลงไฟล์เดียวทําให้ Bazel ต้องสร้างเป้าหมายหลายรายการอีกครั้ง

การประชุมอื่นๆ

  • ใช้อักษรตัวพิมพ์ใหญ่และขีดล่างเพื่อประกาศค่าคงที่ (เช่น GLOBAL_CONSTANT) ใช้อักษรตัวพิมพ์เล็กและขีดล่างเพื่อประกาศตัวแปร (เช่น my_variable)

  • ไม่ควรแบ่งป้ายกำกับ แม้ว่าป้ายกำกับจะยาวกว่า 79 อักขระก็ตาม ป้ายกำกับควรเป็นสตริงตัวอักษรล้วนทุกครั้งที่เป็นไปได้ เหตุผล: ช่วยให้ค้นหาและแทนที่ได้ง่าย และยังช่วยปรับปรุงความง่ายในการอ่านด้วย

  • ค่าของแอตทริบิวต์ชื่อควรเป็นสตริงค่าคงที่ตามตัวอักษร (ยกเว้นในมาโคร) เหตุผล: เครื่องมือภายนอกใช้แอตทริบิวต์ชื่อเพื่ออ้างอิงกฎ ผู้ใช้ต้องค้นหากฎได้โดยไม่ต้องตีความโค้ด

  • เมื่อตั้งค่าแอตทริบิวต์ประเภทบูลีน ให้ใช้ค่าบูลีน ไม่ใช่ค่าจำนวนเต็ม กฎยังคงแปลงจำนวนเต็มเป็นบูลีนตามที่จำเป็นเนื่องจากเหตุผลเดิม แต่เราไม่แนะนําให้ทำเช่นนั้น เหตุผล: flaky = 1 อาจทำให้เข้าใจผิดว่าหมายความว่า "แยกกลุ่มเป้าหมายนี้ออกโดยเรียกใช้อีกครั้ง 1 ครั้ง" flaky = True พูดอย่างตรงไปตรงมาว่า "การทดสอบนี้ทำงานไม่เสถียร"

ความแตกต่างกับคู่มือสไตล์ Python

แม้ว่าความเข้ากันได้กับคู่มือสไตล์ Python จะเป็นเป้าหมาย แต่ก็มีความแตกต่างอยู่บ้าง ดังนี้

  • ไม่มีขีดจำกัดความยาวบรรทัดอย่างเข้มงวด ความคิดเห็นและสตริงที่ยาวมักจะมีการแยกออกเป็น 79 คอลัมน์ แต่ก็ไม่บังคับ ไม่ควรบังคับใช้ในการตรวจสอบโค้ดหรือสคริปต์ก่อนส่ง เหตุผล: ป้ายกำกับมีความยาวเกินขีดจำกัดนี้ได้ การสร้างหรือแก้ไขไฟล์ BUILD ด้วยเครื่องมือเป็นที่นิยมกันโดยทั่วไป ซึ่งไม่เหมาะกับขีดจำกัดความยาวบรรทัด

  • ไม่รองรับการต่อสตริงโดยนัย ใช้โอเปอเรเตอร์ + เหตุผล: ไฟล์ BUILD มีรายการสตริงจำนวนมาก คุณอาจลืมใส่คอมมา ซึ่งจะทำให้ผลลัพธ์แตกต่างออกไปโดยสิ้นเชิง ซึ่งทำให้เกิดข้อบกพร่องมากมายในอดีต ดูการสนทนานี้ด้วย

  • ใช้เว้นวรรครอบเครื่องหมาย = สำหรับอาร์กิวเมนต์คีย์เวิร์ดในกฎ เหตุผล: อาร์กิวเมนต์ที่มีชื่อมีการใช้งานบ่อยกว่าใน Python มากและจะอยู่บรรทัดแยกต่างหากเสมอ การเว้นวรรคช่วยให้อ่านง่ายขึ้น รูปแบบนี้มีมานานแล้ว และการปรับแต่งไฟล์ BUILD ที่มีอยู่ทั้งหมดนั้นไม่คุ้มค่า

  • โดยค่าเริ่มต้น ให้ใช้เครื่องหมายคำพูดคู่สำหรับสตริง เหตุผล: ไม่ได้ระบุไว้ในคู่มือสไตล์ Python แต่แนะนำให้ใช้รูปแบบที่สอดคล้องกัน เราจึงตัดสินใจใช้เฉพาะสตริงที่มีเครื่องหมายคำพูดคู่ หลายภาษาใช้เครื่องหมายคำพูดแบบคู่สำหรับสตริงตัวอักษร

  • ใช้บรรทัดว่าง 1 บรรทัดระหว่างคําจํากัดความระดับบนสุด 2 รายการ เหตุผล: โครงสร้างของไฟล์ BUILD ไม่เหมือนไฟล์ Python ทั่วไป มีเฉพาะคำสั่งระดับบนสุด การใช้บรรทัดว่างเดี่ยวจะทำให้ไฟล์ BUILD สั้นลง