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

รายงานปัญหา ดูแหล่งข้อมูล ดูแหล่งข้อมูล 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 ไม่มีรูทไฟล์ระบบเดียว

    โปรดระมัดระวังเรื่องนี้หากกฎตรวจสอบว่าเส้นทางสมบูรณ์หรือไม่ คุณควรหลีกเลี่ยงการใช้ Absolute Path เนื่องจากมักใช้ไม่ได้กับแพลตฟอร์มอื่นๆ

วิธีแก้ไข:

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

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

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

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

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

  • แทนที่ / ด้วย "" ในเส้นทางในการดำเนินการ / 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) ส่วนระบบที่คล้าย Unix ใช้ LF (\n)

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

วิธีแก้ไข:

  • ใช้กฎที่เขียนขึ้นเพื่อวัตถุประสงค์โดยเฉพาะซึ่งไม่มี Bash

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

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

    • copy_file() (source, documentation): คัดลอกไฟล์ไปยังตำแหน่งอื่น โดยเลือกทำให้ไฟล์เป็นแบบเรียกใช้ได้

    • write_file() (source, documentation): เขียนไฟล์ข้อความที่มีการตัดบรรทัดที่ต้องการ (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 ที่ว่างเปล่าจะเรียกใช้ไม่ได้ หากต้องการสคริปต์ที่ว่างเปล่า ให้เขียนช่องว่างหนึ่งบรรทัด

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