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 quy tắc di động 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 32767 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 bạn chạy trong hành động.
Thư mục đang hoạt động: cũng chỉ được chứa 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 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 kiểu Unix, nhưng một số 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ố lệnh khác thì không.Tốt nhất là 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 tên ổ đĩa, chẳng hạn như
C:\foo\bar.txt
. Không có thư mục gốc hệ thống tệp nào.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 chuyển được.
Giải pháp:
Giữ cho đường dẫn ngắn gọn.
Tránh dùng 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 tệp đầu vào của hành động và có thể làm cạn kiệt giới hạn chiều dài đường dẫn.
Sử dụng 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 Bazel. Bạn nên tạo một ổ (hoặc ổ ảo) chỉ dành cho các đầu ra 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 nút giao.
Nói một cách đơn giản[1], các nút giao là đường liên kết tượng trưng của thư mục. Bạn có thể dễ dàng tạo các nút giao 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 bản dựng tạo ra một điểm nối có đường dẫn ngắn nhưng 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 nối.
Trong tệp
.bat
hoặc trong cmd.exe, bạn có thể tạo các nút giao 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à Đường liên kết tượng trưng, nhưng vì các thao tác xây dựng, bạn có thể coi Junction là Đường liên kết tượng trưng của thư mục.
Thay thế
/
bằng `` trong các đường dẫn trong hành động / envvars.Khi bạn tạo dòng lệnh hoặc biến môi trường cho một hành động, hãy tạo đườ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")
sẽ trả về 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 hành động 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 hành động. Nếu một hành động sử dụng các biến môi trường thường xuyên thay đổi hoặc dành riêng cho người dùng, thì điều đó sẽ khiến quy tắc khó 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.
Cách này hoạt động trên Windows, macOS và Linux.
Giảm thiểu 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 biến môi trường, hãy đặt tất cả biến đó vào một từ điển và truyền từ điển đó vào 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 các tập lệnh shell (
.sh
) KHÔNG thể thực thi được trên Windows; bạn không thể chỉ định các tập lệnh này làmexecutable
củactx.actions.run
. Tệp cũng không có quyền+x
, vì vậy, bạn không thể thực thi các tệp tuỳ ý như trên Linux.Lệnh Bash: Để dễ di chuyển, hãy tránh chạy lệnh Bash trực tiếp trong các hành động.
Bash được sử dụng rộng rãi trên các hệ thống tương tự Unix, nhưng thường không có trên Windows. Bản thân Bazel ngày càng ít dựa vào Bash (MSYS2), vì vậy, trong tương lai, người dùng ít có khả năng cài đặt MSYS2 cùng với Bazel. Để dễ 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.
Dấu kết thúc dòng: Windows sử dụng CRLF (
\r\n
), các hệ thống giống Unix sử 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 kết thúc 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 được tạo theo mục đích 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 việc phụ thuộc vào Bash (và phát minh lại bánh xe): hãy xem bazel-skylib có quy tắc dành riêng cho nhu cầu của bạn hay không. Không có phần phụ thuộc nào phụ thuộc vào Bash khi được tạo/kiểm thử trên Windows.Ví dụ về quy tắc tạo:
copy_file()
(nguồn, tài liệu): sao chép một tệp sang nơi khác, có thể tuỳ ý đặt tệp đó thành có thể thực thiwrite_file()
(nguồn, tài liệu): ghi tệp văn bản, với dấu kết thúc dòng mong muốn (auto
,unix
hoặcwindows
), tuỳ ý tạo tệp có thể thực thi (nếu đó là tập lệnh)run_binary()
(nguồn, tài liệu): chạy một tệp nhị phân (hoặc quy tắc*_binary
) với dữ liệu đầu vào và đầu ra dự kiến dưới dạng một thao tác bản dựng (đây là trình bao bọc quy tắc bản dựng choctx.actions.run
)native_binary()
(nguồn, tài liệu): gói tệp nhị phân gốc trong quy tắc*_binary
. 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ử:
Trên Windows, hãy cân nhắc sử dụng tập lệnh
.bat
cho những việc nhỏ.Thay vì tập lệnh
.sh
, bạn có thể giải quyết các tác vụ nhỏ 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 bằng một mã lỗi cố định, thì một tệp
.bat
đơn giản 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à vì các đuôi 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 đuôi, 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 dấu cách vào tập lệnh đó.Sử dụng Bash theo nguyên tắc.
Trong quy tắc kiểm thử và bản dựng Starlark, hãy sử dụng
ctx.actions.run_shell
để chạy tập lệnh Bash và lệnh Bash dưới dạng hành động.Trong macro Starlark, hãy gói 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 của kho lưu trữ Starlark, hãy cố gắng tránh sử dụng Bash. Bazel hiện không cung cấp cách nào để chạy các lệnh Bash theo nguyên tắc trong quy tắc kho lưu trữ.
Đang xóa tệp
Vấn đề:
Không thể xoá tệp khi đang mở.
Không thể xoá các tệp đang mở (theo mặc định), các lần thử sẽ dẫn đến lỗi "Access Denied" (Từ chối truy cập). 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 đó mở.
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 tay cầm mở đến thư mục đang hoạt động và không thể xoá thư mục cho đến khi quy trình kết thúc.
Giải pháp:
Trong mã, 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 tay cầm càng sớm càng tốt.