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

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

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

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

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

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

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

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

รูปแบบ

รูปแบบ Python

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

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

Docstring

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

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

กฎและแง่มุมต่างๆ รวมถึงแอตทริบิวต์ของกฎและแง่มุมเหล่านั้น ตลอดจนผู้ให้บริการและฟิลด์ของผู้ให้บริการ ควรจัดทำเอกสารโดยใช้อาร์กิวเมนต์ doc

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

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

ความยาวบรรทัด

เช่นเดียวกับในไฟล์ BUILD ไม่มีข้อจำกัดความยาวบรรทัดที่เข้มงวดเนื่องจากป้ายกำกับอาจยาวได้ หากเป็นไปได้ ให้ใช้ตัวอักษรไม่เกิน 79 ตัวต่อบรรทัด (ตามคู่มือรูปแบบของ Python's style guide, 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 = '%s_bar' % (name)
    • มีการมองเห็นที่จำกัด (//visibility:private) และ
    • มีแท็ก manual เพื่อหลีกเลี่ยงการขยายในเป้าหมายที่มีไวลด์การ์ด (:all, ..., :* ฯลฯ)
  • ควรใช้ name เพื่ออนุมานชื่อเป้าหมายที่กำหนดโดยมาโครเท่านั้น และไม่ใช้เพื่อวัตถุประสงค์อื่น เช่น อย่าใช้ชื่อเพื่ออนุมานทรัพยากร Dependency หรือไฟล์อินพุตที่ไม่ได้สร้างโดยมาโครเอง
  • เป้าหมายทั้งหมดที่สร้างขึ้นในมาโครควรเชื่อมโยงกับเป้าหมายหลักในลักษณะใดลักษณะหนึ่ง
  • โดยทั่วไปแล้ว name ควรเป็นอาร์กิวเมนต์แรกเมื่อกำหนดมาโคร
  • ตั้งชื่อพารามิเตอร์ในมาโครให้สอดคล้องกัน หากมีการส่งพารามิเตอร์เป็นค่าแอตทริบิวต์ไปยังเป้าหมายหลัก ให้ใช้ชื่อเดิม หากพารามิเตอร์มาโครมีวัตถุประสงค์เดียวกับแอตทริบิวต์กฎทั่วไป เช่น deps ให้ตั้งชื่อตามที่คุณจะตั้งชื่อแอตทริบิวต์ (ดูด้านล่าง)
  • เมื่อเรียกมาโคร ให้ใช้อาร์กิวเมนต์คีย์เวิร์ดเท่านั้น ซึ่งสอดคล้องกับกฎและช่วยเพิ่มความสามารถในการอ่านได้อย่างมาก

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

โดยหลักการแล้ว ยิ่งมาโครคล้ายกับกฎมากเท่าใด ก็ยิ่งดีมากขึ้นเท่านั้น

ดูมาโครด้วย

กติกา

  • กฎ แง่มุมต่างๆ และแอตทริบิวต์ของกฎและแง่มุมเหล่านั้นควรใช้ชื่อตัวพิมพ์เล็ก ("snake case")
  • ชื่อกฎเป็นคำนามที่อธิบายสิ่งประดิษฐ์หลักที่กฎสร้างขึ้นจากมุมมองของทรัพยากร Dependency (หรือสำหรับกฎย่อย ผู้ใช้) ซึ่งไม่จำเป็นต้องเป็นคำต่อท้ายของไฟล์ ตัวอย่างเช่น กฎที่สร้างสิ่งประดิษฐ์ C++ ที่มีไว้เพื่อใช้เป็นส่วนขยาย Python อาจเรียกว่า py_extension สำหรับภาษาส่วนใหญ่ กฎทั่วไป ได้แก่
    • *_library - หน่วยการคอมไพล์หรือ "โมดูล"
    • *_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 ว่า _myrule_impl
  • ส่งข้อมูลระหว่างกฎโดยใช้อินเทอร์เฟซ ผู้ให้บริการที่กำหนดไว้อย่างดี ประกาศและจัดทำเอกสารฟิลด์ผู้ให้บริการ
  • ออกแบบกฎโดยคำนึงถึงความสามารถในการขยาย พิจารณาว่ากฎอื่นๆ อาจต้องการโต้ตอบกับกฎของคุณ เข้าถึงผู้ให้บริการของคุณ และใช้ซ้ำการดำเนินการที่คุณสร้างขึ้น
  • ปฏิบัติตามหลักเกณฑ์ด้านประสิทธิภาพในกฎ