คู่มือสไตล์ .bzl

หน้านี้ครอบคลุมหลักเกณฑ์พื้นฐานด้านสไตล์สำหรับ Starlark และยังมีข้อมูลเกี่ยวกับมาโครและกฎต่างๆ อีกด้วย

Starlark เป็นภาษาที่ระบุวิธีการสร้างซอฟต์แวร์ ดังนั้นจึงเป็นทั้งภาษาในการเขียนโปรแกรมและการกำหนดค่า

คุณจะใช้ Starlark เขียนไฟล์ มาโคร และสร้างกฎ BUILD รายการ โดยหลักแล้วมาโครและกฎเป็นภาษาเมตาซึ่งเป็นตัวกำหนดวิธีเขียนไฟล์ BUILD ไฟล์ BUILD มีจุดประสงค์ให้เรียบง่ายและซ้ำกัน

มีการใช้ซอฟต์แวร์ทั้งหมดบ่อยกว่าการเขียน โดยเฉพาะอย่างยิ่งสำหรับ Starlark เนื่องจากวิศวกรอ่านไฟล์ BUILD เพื่อทำความเข้าใจทรัพยากร Dependency ของเป้าหมายและรายละเอียดในบิวด์ ค่าที่อ่านได้นี้มักเกิดขึ้นเมื่อผ่าน รีบเร่ง หรือทำงานอื่นไปพร้อมๆ กัน ความเรียบง่ายและความอ่านได้ง่ายจึงเป็นสิ่งที่สำคัญอย่างยิ่งเพื่อให้ผู้ใช้แยกวิเคราะห์และทำความเข้าใจไฟล์ BUILD ได้อย่างรวดเร็ว

เมื่อผู้ใช้เปิดไฟล์ BUILD ผู้ใช้ต้องการทราบรายการเป้าหมายในไฟล์อย่างรวดเร็ว หรือดูรายการของแหล่งที่มาของไลบรารี C++ ดังกล่าว หรือนำการอ้างอิงออกจากไบนารีของ Java ทุกครั้งที่เพิ่มระดับชั้น Abstraction จะทำให้ผู้ใช้ทำงานเหล่านี้ได้ยากขึ้น

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

คำแนะนำทั่วไป

สไตล์

รูปแบบ Python

หากไม่แน่ใจ ให้ทำตามคู่มือแนะนำลักษณะของ PEP 8 หากเป็นไปได้ โดยเฉพาะอย่างยิ่ง ใช้การเยื้อง 4 ช่องแทนที่จะเป็น 2 เว้นวรรคเพื่อดำเนินการตามแบบแผนของ Python

เนื่องจาก Starlark ไม่ใช่ Python ลักษณะบางอย่างของรูปแบบ Python จึงไม่มีผล ตัวอย่างเช่น PEP 8 แนะนำให้เปรียบเทียบกับซิงเกิลด้วย is ซึ่งไม่ใช่โอเปอเรเตอร์ใน Starlark

การสร้างเอกสาร

ไฟล์เอกสารและฟังก์ชันโดยใช้สตริงเอกสาร ใช้สตริงเอกสารที่ด้านบนของไฟล์ .bzl แต่ละไฟล์ และสตริงเอกสารสําหรับฟังก์ชันสาธารณะแต่ละรายการ

กฎและแง่มุมของเอกสาร

คุณควรเขียนเอกสารประกอบเกี่ยวกับกฎและลักษณะ แอตทริบิวต์ ผู้ให้บริการ และช่องของผู้ให้บริการเหล่านั้นโดยใช้อาร์กิวเมนต์ doc

รูปแบบการตั้งชื่อ

  • ชื่อตัวแปรและชื่อฟังก์ชันใช้ตัวพิมพ์เล็กที่มีคำซึ่งคั่นด้วยขีดล่าง ([a-z][a-z0-9_]*) เช่น cc_library
  • ค่าส่วนตัวระดับบนสุดจะเริ่มต้นด้วยขีดล่างเพียง 1 ตัว Bazel บังคับให้ใช้ ค่าส่วนตัวจากไฟล์อื่นๆ ไม่ได้ ตัวแปรภายในไม่ควรใช้คำนำหน้าขีดล่าง

ความยาวของบรรทัด

ไฟล์ BUILD ไม่มีขีดจำกัดความยาวบรรทัดที่เข้มงวดเนื่องจากป้ายกำกับอาจยาว หากเป็นไปได้ ให้ใช้อักขระไม่เกิน 79 ตัวต่อบรรทัด (ตามคู่มือแบบ Python ที่ชื่อ PEP 8) คุณไม่ควรบังคับใช้หลักเกณฑ์นี้อย่างเข้มงวด นั่นคือ เครื่องมือแก้ไขควรแสดงคอลัมน์มากกว่า 80 คอลัมน์ การเปลี่ยนแบบอัตโนมัติมักทำให้มีบรรทัดยาวขึ้น และมนุษย์ไม่ควรเสียเวลาแยกบรรทัดที่อ่านได้อยู่แล้ว

อาร์กิวเมนต์คีย์เวิร์ด

ในอาร์กิวเมนต์คีย์เวิร์ด ควรใช้เว้นวรรครอบๆ เครื่องหมายเท่ากับ:

def fct(name, srcs):
    filtered_srcs = my_filter(source = srcs)
    native.cc_library(
        name = name,
        srcs = filtered_srcs,
        testonly = True,
    )

ค่าบูลีน

ใช้ค่า True และ False (แทน 1 และ 0) สำหรับค่าบูลีน (เช่น เมื่อใช้แอตทริบิวต์บูลีนในกฎ)

อย่าใช้ฟังก์ชัน print() ในโค้ดเวอร์ชันที่ใช้งานจริง ฟังก์ชันดังกล่าวมีไว้เพื่อแก้ไขข้อบกพร่องเท่านั้น และจะสแปมผู้ใช้ทั้งทางตรงและทางอ้อมทั้งหมดของไฟล์ .bzl ข้อยกเว้นเพียงอย่างเดียวคือคุณส่งรหัสที่ใช้ print() ได้หากปิดใช้โดยค่าเริ่มต้น และเปิดใช้ได้ด้วยการแก้ไขต้นทางเท่านั้น เช่น ในกรณีที่การใช้ print() ทั้งหมดได้รับการปกป้องโดย if DEBUG: โดยที่ DEBUG ฮาร์ดโค้ดไปยัง False อย่าลืมพิจารณาว่าข้อความเหล่านี้มีประโยชน์พอที่จะอธิบายผลกระทบที่มีต่อความอ่านได้ง่ายหรือไม่

มาโคร

มาโครคือฟังก์ชันที่จะเริ่มต้นกฎอย่างน้อย 1 ข้อระหว่างการโหลด โดยทั่วไป ใช้กฎทุกครั้งที่เป็นไปได้แทนมาโคร กราฟบิลด์ที่ผู้ใช้เห็นไม่เหมือนกับกราฟที่ Bazel ใช้ระหว่างการสร้าง โดยมาโครจะขยายก่อนที่ Bazel จะทำการวิเคราะห์กราฟบิลด์

ด้วยเหตุนี้ เมื่อเกิดข้อผิดพลาด ผู้ใช้จะต้องเข้าใจการใช้งานมาโครเพื่อแก้ปัญหาของบิลด์ นอกจากนี้ ผลลัพธ์ของ bazel query อาจตีความได้ยากเนื่องจากเป้าหมายที่แสดงในผลลัพธ์มาจากการขยายมาโคร สุดท้าย หลายๆ องค์ประกอบไม่ทราบเกี่ยวกับมาโคร ดังนั้นการใช้เครื่องมือต่างๆ ขึ้นอยู่กับด้านต่างๆ (IDE และอื่นๆ) อาจล้มเหลว

การใช้มาโครอย่างปลอดภัยคือการกำหนดเป้าหมายเพิ่มเติมที่จะใช้อ้างอิงโดยตรงใน Bazel CLI หรือในไฟล์ BUILD ในกรณีนี้ เฉพาะผู้ใช้ปลายทางของเป้าหมายเหล่านั้นเท่านั้นที่ต้องทราบเกี่ยวกับตน และปัญหาของบิลด์ใดๆ ที่เกิดขึ้นจากมาโครก็ไม่ได้มีไว้ไกลจากการใช้งาน

สำหรับมาโครที่กำหนดเป้าหมายที่สร้างขึ้น (รายละเอียดการใช้งานของมาโครซึ่งไม่ควรอ้างถึงที่ CLI หรือขึ้นอยู่กับเป้าหมายที่ไม่ได้สร้างโดยมาโครนั้น) ให้ทำตามแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้

  • มาโครควรใช้อาร์กิวเมนต์ name และกำหนดเป้าหมายด้วยชื่อนั้น เป้าหมายดังกล่าวจะกลายเป็นเป้าหมายหลักของมาโครดังกล่าว
  • เป้าหมายที่สร้างขึ้นซึ่งก็คือเป้าหมายอื่นๆ ทั้งหมดที่มาโครกำหนด ควรมีลักษณะต่อไปนี้
    • ขึ้นต้นด้วย <name> หรือ _<name> เช่น ใช้ name = '%s_bar' % (name)
    • มีระดับการเข้าถึงที่จำกัด (//visibility:private) และ
    • มีแท็ก manual เพื่อหลีกเลี่ยงการขยายในเป้าหมายไวลด์การ์ด (:all, ..., :* ฯลฯ)
  • ควรใช้ name เพื่อดึงชื่อของเป้าหมายที่มาโครกำหนดเท่านั้น ไม่ใช่สำหรับชื่ออื่น ตัวอย่างเช่น อย่าใช้ชื่อเพื่อรับการอ้างอิงหรือไฟล์อินพุตที่ไม่ได้สร้างขึ้นจากมาโคร
  • เป้าหมายทั้งหมดที่สร้างในมาโครควรมีความเกี่ยวข้องกันในทางใดทางหนึ่งกับเป้าหมายหลัก
  • ทำให้ชื่อพารามิเตอร์ในมาโครสอดคล้องกันอยู่เสมอ หากมีการส่งพารามิเตอร์เป็นค่าแอตทริบิวต์ไปยังเป้าหมายหลัก ให้ใช้ชื่อเดิม หากพารามิเตอร์มาโครมีจุดประสงค์เดียวกับแอตทริบิวต์ของกฎทั่วไป เช่น deps ให้ตั้งชื่อเหมือนกับแอตทริบิวต์ชื่อ (ดูด้านล่าง)
  • เมื่อเรียกมาโคร ให้ใช้เฉพาะอาร์กิวเมนต์คำหลัก วิธีนี้สอดคล้องกับกฎและทำให้อ่านง่ายขึ้นเป็นอย่างมาก

วิศวกรมักเขียนมาโครเมื่อ Starlark API ของกฎที่เกี่ยวข้องไม่เพียงพอสำหรับกรณีการใช้งานเฉพาะ ไม่ว่าจะมีการกำหนดกฎดังกล่าวภายใน Bazel ในโค้ดแบบเนทีฟหรือใน Starlark ก็ตาม หากพบปัญหานี้ โปรดสอบถามผู้เขียนกฎว่าสามารถขยาย API เพื่อบรรลุเป้าหมายของคุณหรือไม่

จากหลักการทั่วไป ยิ่งมาโครมีลักษณะคล้ายกับกฎมากเท่าใด ก็ยิ่งดีเท่านั้น

ดูข้อมูลเพิ่มเติมในมาโคร

กฎ

  • กฎ ลักษณะ และแอตทริบิวต์ควรใช้ชื่อตัวพิมพ์เล็ก ("Snake Case")
  • ชื่อกฎคือคำนามที่อธิบายอาร์ติแฟกต์ประเภทหลักที่กฎสร้างขึ้นจากมุมมองของทรัพยากร Dependency (หรือผู้ใช้สำหรับกฎ Leaf ) ไม่จำเป็นต้องเป็นคำต่อท้ายไฟล์ เช่น อาจมีการเรียกกฎที่สร้างอาร์ติแฟกต์ C++ ที่ใช้เป็นส่วนขยาย Python ว่า py_extension สำหรับภาษาส่วนใหญ่ กฎทั่วไปมีดังนี้
    • *_library - หน่วยคอมไพล์หรือ "module"
    • *_binary - เป้าหมายที่สร้างหน่วยปฏิบัติการหรือหน่วยการทำให้ใช้งานได้
    • *_test - เป้าหมายการทดสอบ ซึ่งอาจรวมถึงการทดสอบหลายรายการ คาดว่าการทดสอบทั้งหมดในเป้าหมาย *_test จะเป็นตัวแปรที่มีธีมเดียวกัน เช่น การทดสอบไลบรารีหนึ่ง
    • *_import: เป้าหมายที่ห่อหุ้มอาร์ติแฟกต์ที่คอมไพล์ไว้ล่วงหน้า เช่น .jar หรือ .dll ที่ใช้ระหว่างการคอมไพล์
  • ใช้ชื่อและประเภทที่สอดคล้องกันสำหรับแอตทริบิวต์ ตัวอย่างแอตทริบิวต์ที่ใช้ได้โดยทั่วไปมีดังนี้
    • srcs: label_list อนุญาตไฟล์: ไฟล์ต้นฉบับ ซึ่งโดยทั่วไปแล้วจะเป็นผู้สร้างขึ้น
    • deps: label_list ซึ่งโดยทั่วไปจะไม่อนุญาตไฟล์: ทรัพยากร Dependency ของการคอมไพล์
    • data: label_list การอนุญาตไฟล์: ไฟล์ข้อมูล เช่น ข้อมูลทดสอบ ฯลฯ
    • runtime_deps: label_list: ทรัพยากร Dependency ของรันไทม์ที่ไม่จำเป็นสำหรับการคอมไพล์
  • สำหรับแอตทริบิวต์ที่มีลักษณะการทำงานที่ไม่ชัดเจน (เช่น เทมเพลตสตริงที่มีการแทนที่พิเศษ หรือเครื่องมือที่ถูกเรียกใช้ตามข้อกำหนดเฉพาะ) ให้ส่งเอกสารประกอบโดยใช้อาร์กิวเมนต์คีย์เวิร์ด doc ในการประกาศของแอตทริบิวต์ (attr.label_list() หรือที่คล้ายกัน)
  • ฟังก์ชันการใช้งานกฎควรเป็นฟังก์ชันส่วนตัวเกือบทุกครั้ง (ตั้งชื่อด้วยขีดล่าง) สไตล์ทั่วไปคือ ตั้งชื่อ _myrule_impl ให้ฟังก์ชันการใช้งานสำหรับ myrule
  • ส่งข้อมูลระหว่างกฎโดยใช้อินเทอร์เฟซ provider ที่กำหนดไว้เป็นอย่างดี ประกาศช่องผู้ให้บริการเอกสาร
  • ออกแบบกฎโดยคำนึงถึงการขยายเป็นหลัก ให้พิจารณาว่ากฎอื่นอาจ ต้องโต้ตอบกับกฎ เข้าถึงผู้ให้บริการของคุณ และใช้การดำเนินการที่คุณสร้างขึ้นซ้ำ
  • ทำตามหลักเกณฑ์ด้านประสิทธิภาพในกฎ