คู่มือสไตล์ .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

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

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

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

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

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

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

ดูมาโครด้วย

กฎ

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