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

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

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

Globs

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

แบบเรียกซ้ำ

อย่าใช้ Globs แบบเรียกซ้ำเพื่อจับคู่ไฟล์ต้นฉบับ (เช่น glob(["**/*.java"]))

Globs แบบเรียกซ้ำทำให้ไฟล์ BUILD เข้าใจได้ยากเนื่องจากข้ามไดเรกทอรีย่อยที่มีไฟล์ BUILD

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

การสร้างไฟล์ BUILD ในแต่ละไดเรกทอรีและกำหนดกราฟทรัพยากร Dependency ระหว่างไฟล์เป็นแนวทางปฏิบัติที่ดี

แบบไม่เรียกซ้ำ

โดยทั่วไป Globs แบบไม่เรียกซ้ำเป็นที่ยอมรับ

ข้อกำหนดอื่นๆ

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

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

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

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

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

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

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

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

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

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

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