โปรดใช้ไฟล์ 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
สั้นลง