Trang này tập trung vào việc viết các quy tắc tương thích với Windows, các vấn đề thường gặp khi viết các quy tắc có thể di chuyển và một số giải pháp.
Đường dẫn
Vấn đề:
Giới hạn độ dài: độ dài đường dẫn tối đa là 259 ký tự.
Mặc dù Windows cũng hỗ trợ các đường dẫn dài hơn (tối đa 32.767 ký tự), nhưng nhiều chương trình được tạo với giới hạn thấp hơn.
Hãy lưu ý điều này về các chương trình mà bạn chạy trong các hành động.
Thư mục đang hoạt động: cũng chỉ được dài tối đa 259 ký tự.
Các quy trình không thể
cd
vào một thư mục dài hơn 259 ký tự.Phân biệt chữ hoa chữ thường: Đường dẫn Windows không phân biệt chữ hoa chữ thường, đường dẫn Unix thì có phân biệt chữ hoa chữ thường.
Hãy lưu ý điều này khi tạo dòng lệnh cho các thao tác.
Dấu phân cách đường dẫn: là dấu gạch chéo ngược (
\`), not forward slash (
/`).Bazel lưu trữ các đường dẫn theo kiểu Unix bằng dấu phân cách
/
. Mặc dù một số chương trình Windows hỗ trợ đường dẫn theo kiểu Unix, nhưng những chương trình khác thì không. Một số lệnh tích hợp trong cmd.exe hỗ trợ các lệnh này, một số thì không.Bạn nên luôn sử dụng
\` separators on Windows: replace
/with
khi tạo dòng lệnh và biến môi trường cho các thao tác.Đường dẫn tuyệt đối: không bắt đầu bằng dấu gạch chéo (
/
).Đường dẫn tuyệt đối trên Windows bắt đầu bằng một chữ cái là tên ổ đĩa, chẳng hạn như
C:\foo\bar.txt
. Không có một thư mục gốc duy nhất trong hệ thống tệp.Hãy lưu ý điều này nếu quy tắc của bạn kiểm tra xem một đường dẫn có phải là đường dẫn tuyệt đối hay không. Bạn nên tránh sử dụng đường dẫn tuyệt đối vì chúng thường không di động.
Giải pháp:
Giữ cho đường dẫn ngắn.
Tránh tên thư mục dài, cấu trúc thư mục lồng nhau sâu, tên tệp dài, tên không gian làm việc dài, tên mục tiêu dài.
Tất cả những thành phần này có thể trở thành thành phần đường dẫn của các tệp đầu vào của thao tác và có thể vượt quá giới hạn độ dài đường dẫn.
Sử dụng một thư mục gốc đầu ra ngắn.
Sử dụng cờ
--output_user_root=<path>
để chỉ định một đường dẫn ngắn cho các đầu ra của Bazel. Bạn nên có một ổ đĩa (hoặc ổ đĩa ảo) chỉ dành cho đầu ra của Bazel (chẳng hạn như tệpD:\`), and adding this line to your
.bazelrc`:build --output_user_root=D:/
hoặc
build --output_user_root=C:/_bzl
Sử dụng giao lộ.
Nói một cách đơn giản[1], các điểm kết nối là symlink thư mục. Bạn có thể dễ dàng tạo các điểm kết nối và có thể trỏ đến các thư mục (trên cùng một máy tính) có đường dẫn dài. Nếu một thao tác tạo bản dựng tạo ra một đường liên kết có đường dẫn ngắn nhưng có mục tiêu dài, thì các công cụ có giới hạn đường dẫn ngắn có thể truy cập vào các tệp trong thư mục được liên kết.
Trong tệp
.bat
hoặc trong cmd.exe, bạn có thể tạo các điểm kết nối như sau:mklink /J c:\path\to\junction c:\path\to\very\long\target\path
[1]: Nói một cách chính xác, Junction không phải là Symbolic Link, nhưng vì mục đích của các thao tác xây dựng, bạn có thể coi Junction là Directory Symlink.
Thay thế
/
bằng "" trong các đường dẫn trong thao tác / biến môi trường.Khi bạn tạo dòng lệnh hoặc biến môi trường cho một thao tác, hãy tạo các đường dẫn theo kiểu Windows. Ví dụ:
def as_path(p, is_windows): if is_windows: return p.replace("/", "\\") else: return p
Biến môi trường
Vấn đề:
Phân biệt chữ hoa chữ thường: Tên biến môi trường Windows không phân biệt chữ hoa chữ thường.
Ví dụ: trong Java,
System.getenv("SystemRoot")
vàSystem.getenv("SYSTEMROOT")
cho ra cùng một kết quả. (Điều này cũng áp dụng cho các ngôn ngữ khác.)Hermeticity: các thao tác nên sử dụng ít biến môi trường tuỳ chỉnh nhất có thể.
Các biến môi trường là một phần của khoá bộ nhớ đệm của thao tác. Nếu một thao tác sử dụng các biến môi trường thường xuyên thay đổi hoặc được tuỳ chỉnh cho người dùng, thì điều đó sẽ khiến quy tắc ít có khả năng lưu vào bộ nhớ đệm hơn.
Giải pháp:
Chỉ sử dụng tên biến môi trường viết hoa.
Tính năng này hoạt động trên Windows, macOS và Linux.
Giảm thiểu các môi trường hành động.
Khi sử dụng
ctx.actions.run
, hãy đặt môi trường thànhctx.configuration.default_shell_env
. Nếu thao tác cần thêm các biến môi trường, hãy đặt tất cả các biến đó vào một từ điển và truyền từ điển đó đến thao tác. Ví dụ: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})
Thao tác
Vấn đề:
Đầu ra có thể thực thi: Mọi tệp thực thi đều phải có đuôi thực thi.
Các đuôi phổ biến nhất là
.exe
(tệp nhị phân) và.bat
(tập lệnh hàng loạt).Xin lưu ý rằng bạn KHÔNG thể thực thi tập lệnh shell (
.sh
) trên Windows; bạn không thể chỉ định các tập lệnh này làmexecutable
củactx.actions.run
. Ngoài ra, không có quyền+x
mà tệp có thể có, vì vậy, bạn không thể thực thi các tệp tuỳ ý như trên Linux.Lệnh Bash: Để đảm bảo tính di động, hãy tránh chạy trực tiếp các lệnh Bash trong các thao tác.
Bash được sử dụng rộng rãi trên các hệ thống tương tự như Unix, nhưng thường không có trên Windows. Bản thân Bazel ngày càng ít phụ thuộc vào Bash (MSYS2), vì vậy trong tương lai, người dùng sẽ ít có khả năng cài đặt MSYS2 cùng với Bazel. Để dễ dàng sử dụng các quy tắc trên Windows, hãy tránh chạy các lệnh Bash trong các thao tác.
Ký tự kết thúc dòng: Windows dùng CRLF (
\r\n
), các hệ thống tương tự như Unix dùng LF (\n
).Hãy lưu ý điều này khi so sánh các tệp văn bản. Hãy lưu ý đến chế độ cài đặt Git, đặc biệt là phần cuối dòng khi kiểm tra hoặc xác nhận. (Xem chế độ cài đặt
core.autocrlf
của Git.)
Giải pháp:
Sử dụng quy tắc chuyên dụng không có Bash.
native.genrule()
là một trình bao bọc cho các lệnh Bash và thường được dùng để giải quyết các vấn đề đơn giản như sao chép tệp hoặc ghi tệp văn bản. Bạn có thể tránh dựa vào Bash (và phát minh lại bánh xe): xem bazel-skylib có quy tắc được tạo riêng cho nhu cầu của bạn hay không. Không có tệp nào trong số đó phụ thuộc vào Bash khi được tạo/kiểm thử trên Windows.Ví dụ về quy tắc xây dựng:
copy_file()
(source, documentation): sao chép một tệp ở nơi khác, có thể thực thi tệp đówrite_file()
(source, documentation): ghi một tệp văn bản, với dấu kết thúc dòng mong muốn (auto
,unix
hoặcwindows
), không bắt buộc phải thực thi (nếu đó là một tập lệnh)run_binary()
(source, documentation): chạy một tệp nhị phân (hoặc quy tắc*_binary
) với các đầu vào và đầu ra dự kiến nhất định dưới dạng một thao tác xây dựng (đây là trình bao bọc quy tắc xây dựng choctx.actions.run
)native_binary()
(source, documentation): gói một tệp nhị phân gốc trong quy tắc*_binary
mà bạn có thểbazel run
hoặc sử dụng trong thuộc tínhtool
củarun_binary()
hoặc thuộc tínhtools
củanative.genrule()
Ví dụ về quy tắc kiểm thử:
diff_test()
(source, documentation): kiểm thử để so sánh nội dung của hai tệpnative_test()
(source, documentation): bao bọc một tệp nhị phân gốc trong quy tắc*_test
mà bạn có thểbazel test
Trên Windows, hãy cân nhắc sử dụng tập lệnh
.bat
cho những việc không quan trọng.Thay vì dùng tập lệnh
.sh
, bạn có thể giải quyết các tác vụ không quan trọng bằng tập lệnh.bat
.Ví dụ: nếu bạn cần một tập lệnh không làm gì cả, hoặc in một thông báo, hoặc thoát với mã lỗi cố định, thì một tệp
.bat
đơn giản sẽ là đủ. Nếu quy tắc của bạn trả về một trình cung cấpDefaultInfo()
, thì trườngexecutable
có thể tham chiếu đến tệp.bat
đó trên Windows.Vì phần mở rộng của tệp không quan trọng trên macOS và Linux, nên bạn luôn có thể sử dụng
.bat
làm phần mở rộng, ngay cả đối với tập lệnh shell.Xin lưu ý rằng bạn không thể thực thi các tệp
.bat
trống. Nếu bạn cần một tập lệnh trống, hãy viết một khoảng trắng trong đó.Sử dụng Bash theo cách có nguyên tắc.
Trong các quy tắc kiểm thử và bản dựng Starlark, hãy dùng
ctx.actions.run_shell
để chạy tập lệnh Bash và các lệnh Bash dưới dạng hành động.Trong các macro Starlark, hãy gói các tập lệnh và lệnh Bash trong
native.sh_binary()
hoặcnative.genrule()
. Bazel sẽ kiểm tra xem Bash có sẵn hay không và chạy tập lệnh hoặc lệnh thông qua Bash.Trong các quy tắc kho lưu trữ Starlark, hãy cố gắng tránh dùng Bash. Bazel hiện không cung cấp cách chạy các lệnh Bash theo cách có nguyên tắc trong các quy tắc kho lưu trữ.
Đang xóa tệp
Vấn đề:
Không thể xoá tệp khi đang mở.
Bạn không thể xoá các tệp đang mở (theo mặc định), nếu cố gắng, bạn sẽ gặp lỗi "Truy cập bị từ chối". Nếu bạn không thể xoá một tệp, thì có thể một quy trình đang chạy vẫn giữ tệp đó ở trạng thái mở.
Bạn không thể xoá thư mục đang hoạt động của một quy trình đang chạy.
Các quy trình có một đối tượng mở đến thư mục làm việc của chúng và bạn không thể xoá thư mục này cho đến khi quy trình kết thúc.
Giải pháp:
Trong mã của bạn, hãy cố gắng đóng các tệp một cách nhanh chóng.
Trong Java, hãy sử dụng
try-with-resources
. Trong Python, hãy sử dụngwith open(...) as f:
. Về nguyên tắc, hãy cố gắng đóng các đối tượng càng sớm càng tốt.