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

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.4 ที่ใช้เวลาเพียง 2 นาที 7.3 · 7.2 · 7.1 · 7.0 · 6.5

หน้าเว็บนี้มุ่งเน้นที่การเขียนกฎที่ใช้ได้กับ 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 ไม่มีรูทไฟล์ระบบเดียว

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

วิธีแก้ไข:

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

    หลีกเลี่ยงชื่อไดเรกทอรีที่ยาว โครงสร้างไดเรกทอรีที่ฝังลึก ชื่อไฟล์ที่ยาว ชื่อเวิร์กスペースที่ยาว ชื่อเป้าหมายที่ยาว

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

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

    ใช้ Flag --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 เป็นลิงก์สัญลักษณ์ของไดเรกทอรี

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

    เมื่อคุณสร้างบรรทัดคำสั่งหรือตัวแปรสภาพแวดล้อมสำหรับการดำเนินการ ให้สร้างเส้นทาง แบบ 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 ตัวบาเซลเองก็ อาศัย Bash (MSYS2) น้อยลงเรื่อยๆ ในอนาคต ผู้ใช้ในอนาคตจึงมีแนวโน้มน้อยลงที่จะมี MSYS2 ที่ติดตั้งมาพร้อมกับ Bazel หากต้องการให้กฎใช้งานบน Windows ได้ง่ายขึ้น โปรดหลีกเลี่ยงการเรียกใช้คำสั่ง Bash ใน การดำเนินการ

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

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

วิธีแก้ไข:

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

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

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

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

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

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

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

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

    • diff_test() (source, documentation): ทดสอบที่เปรียบเทียบเนื้อหาของ 2 ไฟล์

    • native_test() (source, documentation): รวมไบนารีแบบเนทีฟไว้ในกฎ *_test ซึ่งคุณ bazel test ได้

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

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

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

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

    โปรดทราบว่าไฟล์ .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: โดยหลักการคือ ให้เร็วที่สุด