ต้องการให้ใช้ไฟล์ DAMP BUILD มากกว่า DRY
หลักการ DRY หรือ "อย่าทำซ้ำ" สนับสนุนความไม่ซ้ำโดย การนำการแยกส่วน เช่น ตัวแปรและฟังก์ชัน มาใช้เพื่อหลีกเลี่ยงความซ้ำซ้อนใน โค้ด
ในทางตรงกันข้าม หลักการ 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();' > $@",
)
การตั้งชื่อเป้าหมาย
ชื่อเป้าหมายควรสื่อความหมาย หากเป้าหมายมีไฟล์ต้นฉบับ 1 ไฟล์ โดยทั่วไปเป้าหมายควรมีชื่อที่ได้จากแหล่งที่มานั้น (เช่น 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
รายการเดียว หมายความว่าต้องใช้ชื่อที่ไม่เหมือนกับชื่อไฟล์ที่ไม่มีส่วนขยาย - สำหรับกฎ Java
*_binary
และ*_test
ให้ใช้ "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 ให้ทำดังนี้
proto_library
เป้าหมายควรมีชื่อที่ลงท้ายด้วย_proto
- ภาษาที่เฉพาะเจาะจง
*_proto_library
ควรตรงกับโปรโตที่เกี่ยวข้อง แต่ให้แทนที่_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 แบบทรานซิทีฟ
ควรแสดงรายการการอ้างอิงเฉพาะแพ็กเกจก่อนและอ้างอิงในลักษณะที่ เข้ากันได้กับส่วนการอ้างอิงเป้าหมายในแพ็กเกจปัจจุบัน ด้านบน (ไม่ใช่ตามชื่อแพ็กเกจแบบสัมบูรณ์)
ควรแสดงรายการการอ้างอิงโดยตรงเป็นรายการเดียว การใส่การอ้างอิง "ทั่วไป" ของหลายเป้าหมายลงในตัวแปรจะลดความสามารถในการบำรุงรักษา ทำให้ เครื่องมือไม่สามารถเปลี่ยนการอ้างอิงของเป้าหมาย และอาจนำไปสู่ การอ้างอิงที่ไม่ได้ใช้
Globs
ระบุ "ไม่มีเป้าหมาย" ด้วย []
อย่าใช้ Glob ที่ไม่ตรงกับอะไรเลย เพราะจะทำให้เกิดข้อผิดพลาดได้ง่ายกว่าและไม่ชัดเจนเท่ากับรายการที่ว่างเปล่า
แบบเรียกซ้ำ
อย่าใช้ Glob แบบเรียกซ้ำเพื่อจับคู่ไฟล์ต้นฉบับ (เช่น
glob(["**/*.java"])
)
Glob แบบเรียกซ้ำทำให้ไฟล์ BUILD
จัดการได้ยากเนื่องจากจะข้ามไดเรกทอรีย่อยที่มีไฟล์ BUILD
โดยทั่วไปแล้ว Glob แบบเรียกซ้ำจะมีประสิทธิภาพน้อยกว่าการมีไฟล์ BUILD
ต่อไดเรกทอรีที่มีการกำหนดกราฟการขึ้นต่อกันระหว่างไฟล์เหล่านั้น เนื่องจากจะช่วยให้แคชระยะไกลและการทำงานแบบขนานมีประสิทธิภาพมากขึ้น
แนวทางปฏิบัติที่ดีคือการสร้างไฟล์ BUILD
ในแต่ละไดเรกทอรีและกำหนดกราฟการอ้างอิงระหว่างไฟล์เหล่านั้น
แบบไม่เกิดซ้ำ
โดยทั่วไปแล้ว ระบบจะยอมรับ Glob ที่ไม่ใช่แบบเรียกซ้ำ
หลีกเลี่ยงการทำความเข้าใจรายการ
หลีกเลี่ยงการใช้ List Comprehension ที่ระดับบนสุดของไฟล์ BUILD.bazel
ทำให้การเรียกใช้ที่ซ้ำๆ เป็นไปโดยอัตโนมัติด้วยการสร้างเป้าหมายที่มีชื่อแต่ละรายการด้วย
กฎระดับบนสุดหรือการเรียกใช้มาโครแยกกัน ตั้งชื่อพารามิเตอร์ name
สั้นๆ เพื่อความชัดเจน
List Comprehension ช่วยลดสิ่งต่อไปนี้
- ความสามารถในการบำรุงรักษา การอัปเดต List Comprehension ให้ถูกต้องเป็นเรื่องยากหรือเป็นไปไม่ได้สำหรับผู้ดูแลที่เป็นมนุษย์และ การเปลี่ยนแปลงอัตโนมัติขนาดใหญ่
- การค้นพบได้ เนื่องจากรูปแบบไม่มีพารามิเตอร์
name
จึงค้นหากฎตามชื่อได้ยาก
การใช้งานรูปแบบ List Comprehension โดยทั่วไปคือการสร้างการทดสอบ เช่น
[[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 และเครื่องมืออื่นๆ ดูแล แม้จะมีการทำซ้ำ แต่คุณก็ไม่ต้องกังวลว่าจะจัดการการอ้างอิงอย่างไร
ต้องการใช้สตริงตามตัวอักษร
แม้ว่า Starlark จะมีตัวดำเนินการสตริงสำหรับการต่อกัน (+
) และการจัดรูปแบบ (%
) แต่ควรใช้ด้วยความระมัดระวัง การแยกส่วนสตริงที่ใช้ร่วมกันออกมาเพื่อทำให้นิพจน์กระชับขึ้นหรือแบ่งบรรทัดยาวๆ อาจเป็นสิ่งที่น่าสนใจ
อย่างไรก็ตาม
ค่าสตริงที่แยกกันจะอ่านยากกว่าเมื่อมองผ่านๆ
เครื่องมืออัตโนมัติ เช่น buildozer และ Code Search จะพบปัญหาในการค้นหาค่าและอัปเดตค่าอย่างถูกต้องเมื่อค่าถูกแบ่งออก
ในไฟล์
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
สั้นลง