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

7.3 · 7.2 · 7.1 · 7.0 · 6.5

หน้านี้มุ่งเน้นที่การเขียนกฎที่เข้ากันได้กับ 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
    
  • ใช้ทางแยก

    Junction หมายถึงลิงก์สัญลักษณ์ของไดเรกทอรี[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 (สคริปต์แบตช์)

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

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

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

  • ส่วนท้ายบรรทัด: 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() (source, documentation): เรียกใช้ไบนารี (หรือกฎ *_binary) ด้วยอินพุตที่ระบุและเอาต์พุตที่คาดไว้เป็นการดำเนินการบิลด์ (นี่คือตัวแฝงกฎการสร้างสำหรับ 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: โดยหลักการแล้ว ให้ลองปิดแฮนเดิลโดยเร็วที่สุด