คู่มือสไตล์ .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, 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 เพื่อสร้างชื่อเป้าหมายที่กำหนดโดยมาโครเท่านั้น และไม่ควรใช้เพื่อวัตถุประสงค์อื่น เช่น อย่าใช้ชื่อเพื่อสร้างทรัพยากร 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
  • ส่งข้อมูลระหว่างกฎโดยใช้อินเทอร์เฟซ ผู้ให้บริการที่กำหนดไว้อย่างดี ประกาศและเขียนเอกสารประกอบสำหรับฟิลด์ผู้ให้บริการ
  • ออกแบบกฎโดยคำนึงถึงความสามารถในการขยาย โปรดพิจารณาว่ากฎอื่นๆ อาจต้องการโต้ตอบกับกฎของคุณ เข้าถึงผู้ให้บริการของคุณ และใช้ซ้ำการดำเนินการที่คุณสร้างขึ้น
  • ปฏิบัติตามหลักเกณฑ์ด้านประสิทธิภาพในกฎ