กฎการเขียนใน Windows

รายงานปัญหา ดูแหล่งที่มา

หน้านี้มุ่งเน้นที่การเขียนกฎที่ใช้กับ Windows ได้ ปัญหาที่พบบ่อยในการเขียนกฎแบบพกพา และวิธีแก้ปัญหาบางอย่าง

เส้นทาง

ปัญหา:

  • ความยาวสูงสุด: ความยาวเส้นทางสูงสุดคือ 259 อักขระ

    แม้ว่า Windows จะรองรับเส้นทางที่ยาวกว่า (ไม่เกิน 32767 อักขระ) แต่ก็มีโปรแกรมจำนวนมากที่สร้างขึ้นด้วยขีดจำกัดต่ำสุด

    โปรดคำนึงถึงเรื่องนี้เกี่ยวกับโปรแกรมที่คุณเรียกใช้ในระหว่างการทำงาน

  • ไดเรกทอรีการทำงาน: จำกัดจำนวนอักขระสูงสุด 259 ตัว

    กระบวนการไม่สามารถcdลงในไดเรกทอรีที่ยาวกว่า 259 อักขระ

  • การคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่: เส้นทาง Windows ไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ ส่วนเส้นทาง Unix คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่

    โปรดระมัดระวังเรื่องนี้เมื่อสร้างบรรทัดคำสั่งสำหรับการดำเนินการ

  • ตัวคั่นเส้นทาง: เป็นแบ็กสแลช (\`), not forward slash (/`)

    Bazel จัดเก็บเส้นทางแบบ Unix ที่มีตัวคั่น / แม้ว่าโปรแกรม Windows บางโปรแกรมจะรองรับ เส้นทางแบบ Unix แต่บางโปรแกรมไม่รองรับ คำสั่งในตัวบางคำสั่งใน cmd.exe รองรับคำสั่งดังกล่าว แต่บางคำสั่งก็ไม่รองรับ

    เราขอแนะนำให้ใช้ \` separators on Windows: replace/with` เสมอเมื่อสร้างบรรทัดคำสั่งและตัวแปรสภาพแวดล้อมสำหรับการดำเนินการ

  • เส้นทางสัมบูรณ์: ห้ามเริ่มต้นด้วยเครื่องหมายทับ (/)

    เส้นทางสัมบูรณ์ใน Windows จะขึ้นต้นด้วยอักษรไดรฟ์ เช่น C:\foo\bar.txt ไม่มีรากระบบไฟล์เดียว

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

วิธีแก้ไข:

  • ทำให้เส้นทางสั้น

    หลีกเลี่ยงการใช้ชื่อไดเรกทอรีแบบยาว โครงสร้างไดเรกทอรีแบบซ้อนหลายระดับ ชื่อไฟล์ยาว ชื่อพื้นที่ทำงานแบบยาว และชื่อเป้าหมายแบบยาว

    ทั้งหมดนี้อาจกลายเป็นคอมโพเนนต์เส้นทางของไฟล์อินพุตของการดำเนินการ และอาจทำให้ความยาวเส้นทางเกินขีดจำกัด

  • ใช้รูทเอาต์พุตสั้นๆ

    ใช้แฟล็ก --output_user_root=<path> เพื่อระบุเส้นทางสั้นสำหรับเอาต์พุต Bazel ขอแนะนำว่าควรมีไดรฟ์ (หรือไดรฟ์เสมือน) สำหรับเอาต์พุต Bazel โดยเฉพาะ (เช่นไฟล์ D:\`), and adding this line to your.bazelrc")

    build --output_user_root=D:/
    

    หรือ

    build --output_user_root=C:/_bzl
    
  • ใช้ทางแยก

    คำต่อกันคือ[1] ซึ่งเป็นลิงก์สัญลักษณ์ของไดเรกทอรี ตัวคั่นนั้นสร้างง่ายและชี้ไปยังไดเรกทอรี (ในคอมพิวเตอร์เครื่องเดียวกัน) ที่มีเส้นทางยาวได้ หากการดำเนินการของบิลด์สร้างทางแยกที่มีเส้นทางสั้น แต่เป้าหมายยาว เครื่องมือที่มีขีดจำกัดเส้นทางสั้นจะสามารถเข้าถึงไฟล์ในไดเรกทอรีของทางแยกได้

    ในไฟล์ .bat หรือใน cmd.exe คุณสามารถสร้างทางแยกได้ดังนี้

    mklink /J c:\path\to\junction c:\path\to\very\long\target\path
    

    [1]: หากจะกล่าวให้ชัดเจนก็คือ Junction ไม่ใช่ลิงก์สัญลักษณ์ แต่เพื่อให้การทำงานของบิลด์ คุณอาจถือว่า Junction เป็นสัญลักษณ์ Symlink ของไดเรกทอรี

  • แทนที่ / ด้วย "" ในเส้นทางการทำงาน / สภาพแวดล้อม

    เมื่อคุณสร้างบรรทัดคำสั่งหรือตัวแปรสภาพแวดล้อมสำหรับการดำเนินการ ให้สร้างเส้นทางเป็นแบบ Windows เช่น

    def as_path(p, is_windows):
        if is_windows:
            return p.replace("/", "\\")
        else:
            return p
    

ตัวแปรสภาพแวดล้อม

ปัญหา:

  • การคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่: ชื่อตัวแปรสภาพแวดล้อม Windows ไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่

    เช่น ใน Java System.getenv("SystemRoot") และ System.getenv("SYSTEMROOT") จะให้ผลลัพธ์เดียวกัน (กรณีนี้จะใช้กับภาษาอื่นๆ ด้วยเช่นกัน)

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

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

วิธีแก้ไข:

  • ใช้ชื่อตัวแปรสภาพแวดล้อมตัวพิมพ์ใหญ่เท่านั้น

    วิธีนี้ใช้ได้ใน Windows, macOS และ Linux

  • ลดสภาพแวดล้อมการดำเนินการ

    เมื่อใช้ ctx.actions.run ให้ตั้งค่าสภาพแวดล้อมเป็น ctx.configuration.default_shell_env หากการดำเนินการต้องการตัวแปรสภาพแวดล้อมเพิ่มเติม ให้ใส่ตัวแปรทั้งหมดลงในพจนานุกรมแล้วส่งผ่านไปยังการดำเนินการ เช่น

    load("@bazel_skylib//lib:dicts.bzl", "dicts")
    
    def _make_env(ctx, output_file, is_windows):
        out_path = output_file.path
        if is_windows:
            out_path = out_path.replace("/", "\\")
        return dicts.add(ctx.configuration.default_shell_env, {"MY_OUTPUT": out_path})
    

การดำเนินการ

ปัญหา:

  • เอาต์พุตที่สั่งได้: ไฟล์ปฏิบัติการทุกไฟล์ต้องมีส่วนขยายที่เป็นไฟล์ปฏิบัติการ

    ส่วนขยายที่ใช้กันมากที่สุดคือ .exe (ไฟล์ไบนารี) และ .bat (สคริปต์แบบกลุ่ม)

    โปรดทราบว่าสคริปต์ Shell (.sh) เรียกใช้ใน Windows ไม่ได้ คุณจึงระบุให้เป็น executable ของ ctx.actions.run ไม่ได้ นอกจากนี้ ไฟล์ก็ไม่มีสิทธิ์ +x ด้วย คุณจึงเรียกใช้ไฟล์ที่กำหนดเอง เช่น บน Linux ไม่ได้

  • คำสั่ง Bash: โปรดหลีกเลี่ยงการเรียกใช้คำสั่ง Bash โดยตรงในการดำเนินการเพื่อให้ถ่ายโอนได้

    Bash มีให้ใช้อย่างแพร่หลายในระบบที่คล้ายกับ Unix แต่มักจะใช้งานไม่ได้ใน Windows Bazel เองก็พึ่งพา Bash (MSYS2) น้อยลงเรื่อยๆ ดังนั้นในอนาคต ผู้ใช้ในอนาคตจึงมีแนวโน้มที่จะติดตั้ง MSYS2 ไปพร้อมกับ Bazel น้อยลง หากต้องการให้กฎใช้งานบน Windows ได้ง่ายขึ้น ให้หลีกเลี่ยงการเรียกใช้คำสั่ง Bash ในการทำงาน

  • ส่วนท้ายบรรทัด: Windows ใช้ CRLF (\r\n) ส่วนระบบที่คล้ายกับ Unix ใช้ LF (\n)

    โปรดระมัดระวังเรื่องนี้เมื่อเปรียบเทียบไฟล์ข้อความ โปรดคำนึงถึงการตั้งค่า Git โดยเฉพาะการจบบรรทัดเมื่อชำระเงินหรือคอมมิต (ดูการตั้งค่า core.autocrlf ของ Git)

วิธีแก้ไข:

  • ใช้กฎที่อิงตามวัตถุประสงค์แบบไม่เป็น Bash

    native.genrule() คือ Wrapper สำหรับคำสั่ง Bash และมักใช้ในการแก้ปัญหาง่ายๆ เช่น คัดลอกไฟล์หรือเขียนไฟล์ข้อความ คุณหลีกเลี่ยงการพึ่งพา Bash (และคิดค้นวงล้อใหม่) ได้: ดูว่า bazel-skylib มีกฎที่ออกแบบมาโดยเฉพาะสำหรับความต้องการของคุณหรือไม่ ไม่มีข้อใดที่ใช้ Bash เมื่อสร้าง/ทดสอบบน Windows

    ตัวอย่างกฎในการสร้าง

    • copy_file() (แหล่งที่มา, เอกสารประกอบ): คัดลอกไฟล์ที่อื่น (ไม่บังคับ) ให้เป็นไฟล์ปฏิบัติการ

    • write_file() (แหล่งที่มา เอกสารประกอบ): เขียนไฟล์ข้อความที่มีนามสกุลบรรทัดตามที่ต้องการ (auto, unix หรือ windows) หรือจะเลือกทำให้เป็นไฟล์ปฏิบัติการก็ได้ (หากเป็นสคริปต์)

    • run_binary() (แหล่งที่มา เอกสารประกอบ): เรียกใช้ไบนารี (หรือกฎ *_binary) ที่มีอินพุตที่กำหนดและเอาต์พุตที่คาดหวังเป็นการดำเนินการของบิลด์ (นี่คือ Wrapper ของกฎบิลด์สำหรับ ctx.actions.run)

    • native_binary() (แหล่งที่มา, เอกสารประกอบ): รวมไบนารีดั้งเดิมในกฎ *_binary ซึ่งคุณสามารถ bazel run หรือใช้ในแอตทริบิวต์ tool ของ run_binary() หรือแอตทริบิวต์ tools ของ native.genrule()

    ตัวอย่างกฎการทดสอบ

  • ใน Windows ลองใช้ .bat กับสคริปต์สำหรับเรื่องเล็กๆ น้อยๆ

    คุณแก้ปัญหางานเล็กๆ น้อยๆ ด้วยสคริปต์ .bat แทนสคริปต์ .sh ได้

    เช่น หากคุณต้องการสคริปต์ที่ไม่มีอะไรหรือพิมพ์ข้อความ หรือออกไปพร้อมรหัสข้อผิดพลาดที่แก้ไขแล้ว การใช้ไฟล์ .bat ง่ายๆ ก็เพียงพอ หากกฎแสดงผลผู้ให้บริการ DefaultInfo() ช่อง executable อาจอ้างถึงไฟล์ .bat นั้นใน Windows

    และเนื่องจากนามสกุลไฟล์ไม่ใช่ส่วนสำคัญใน macOS และ Linux คุณจึงใช้ .bat เป็นส่วนขยายได้เสมอ แม้จะเป็นสคริปต์ Shell ก็ตาม

    โปรดทราบว่าไฟล์ .bat ที่ว่างเปล่าจะเรียกใช้ไม่ได้ หากคุณต้องการสคริปต์ที่ว่างเปล่า ให้เขียนช่องว่างหนึ่งบรรทัด

  • ใช้ Bash ตามหลักการ

    ในกฎการสร้างและทดสอบของ Starlark ให้ใช้ ctx.actions.run_shell เพื่อเรียกใช้สคริปต์ Bash และคำสั่ง Bash เป็นการดำเนินการ

    ในมาโครของ Starlark ให้รวมสคริปต์และคำสั่ง Bash ไว้ใน native.sh_binary() หรือ native.genrule() Bazel จะตรวจสอบว่า Bash พร้อมใช้งานหรือไม่ และเรียกใช้สคริปต์หรือคำสั่งผ่าน Bash

    ในกฎของที่เก็บ Starlark พยายามหลีกเลี่ยง Bash ทั้งหมด ขณะนี้ Bazel ยังไม่มีวิธีเรียกใช้คำสั่ง Bash ตามหลักการในกฎที่เก็บ

กำลังลบไฟล์

ปัญหา:

  • ลบไฟล์ในขณะที่เปิดไม่ได้

    ไม่สามารถลบไฟล์ที่เปิดอยู่ได้ (โดยค่าเริ่มต้น) การลองเปิดไฟล์จะส่งผลให้เกิดข้อผิดพลาด "การเข้าถึงถูกปฏิเสธ" หากคุณลบไฟล์ไม่ได้ บางทีกระบวนการที่ทำงานอยู่อาจเปิดไฟล์ค้างไว้

  • ลบไดเรกทอรีที่ใช้งานอยู่ของกระบวนการที่ทำงานอยู่ไม่ได้

    กระบวนการจะมีแฮนเดิลเปิดสำหรับไดเรกทอรีที่ใช้งานอยู่ และจะลบไดเรกทอรีไม่ได้จนกว่ากระบวนการจะสิ้นสุด

วิธีแก้ไข:

  • พยายามปิดไฟล์อย่างตั้งใจในโค้ด

    ใน Java ให้ใช้ try-with-resources ใน Python ให้ใช้ with open(...) as f: โดยหลักการแล้ว ให้ลองปิดแฮนเดิลโดยเร็วที่สุด