กฎการเขียนใน 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 ไม่มี รากระบบไฟล์

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

วิธีแก้ไข:

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

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

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

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

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

  • แทนที่ / ด้วย "" ในเส้นทางในการดำเนินการ/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 หาก action ต้องการตัวแปรสภาพแวดล้อมเพิ่มเติม ใส่ตัวแปรทั้งหมดในพจนานุกรมแล้วส่งผ่านไปยังการดำเนินการ ตัวอย่าง

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

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

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

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

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

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