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

รายงานปัญหา ดูซอร์สโค้ด รุ่น Nightly · 8.0 7.4 . 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
    
  • ใช้ทางแยก

    Junction หมายถึงลิงก์สัญลักษณ์ของไดเรกทอรี[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") จะให้ผลลัพธ์เดียวกัน (ข้อกำหนดนี้มีผลกับภาษาอื่นๆ ด้วย)

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

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

วิธีแก้ไข:

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

    ซึ่งใช้งานได้ใน 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() เป็นเครื่องมือสำหรับห่อคำสั่ง 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 ที่ว่างเปล่า หากต้องการสคริปต์ว่าง ให้เว้นวรรค 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: โดยหลักการแล้ว ให้ลองปิดแฮนเดิลโดยเร็วที่สุด