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