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

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

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

เส้นทาง

ปัญหา:

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

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

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

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

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

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

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

  • แทนที่ / ด้วย "" ในเส้นทางในการดําเนินการ / envvars

    เมื่อสร้างบรรทัดคำสั่งหรือตัวแปรสภาพแวดล้อมสำหรับการดำเนินการ ให้สร้างเส้นทางสไตล์ 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) ระบบคล้ายยูนิกซ์ใช้ LF (\n)

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

วิธีแก้ไข:

  • ใช้กฎที่สร้างขึ้นแบบไม่ใช้ Bash

    native.genrule() คือ Wrapper สำหรับคำสั่ง Bash และมักใช้ในการแก้ปัญหาง่ายๆ เช่น การคัดลอกไฟล์หรือการเขียนไฟล์ข้อความ คุณหลีกเลี่ยงการใช้ Bash (และพลิกโฉมวงล้อ) ได้ โดยดูว่า Bazel-skylib มีกฎที่สร้างขึ้นมาสำหรับความต้องการของคุณโดยเฉพาะหรือไม่ ซึ่งทั้ง 2 อย่างนี้ขึ้นอยู่กับ 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 สำหรับสิ่งเล็กๆ น้อยๆ

    แทนที่จะใช้สคริปต์ .sh คุณสามารถแก้ปัญหาเล็กๆ น้อยๆ ได้ด้วยสคริปต์ .bat รายการ

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

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

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

  • ใช้ 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: โดยหลักการแล้ว ให้ลอง ปิดแฮนเดิลโดยเร็วที่สุด