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 của 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 xây dựng với giới hạn dưới.
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 hoạt động: cũng giới hạn trong 259 ký tự.
Các quy trình không được
cd
vào 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 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 kiểu Unix bằng dòng phân cách
/
. Mặc dù một số chương trình Windows hỗ trợ đường dẫn kiểu Unix thì các đường dẫn 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ông hỗ trợ.Tốt nhất là bạn nên luôn sử dụng
\` separators on Windows: replace
/with
` khi tạo lệnh biến dòng và môi trường để 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ó ứng dụng nào thư mục gốc của 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ó tuyệt đối hay không. Đường dẫn tuyệt đối nên tránh vì chúng thường không di động được.
Giải pháp:
Đảm bảo đường dẫn ngắn gọn.
Tránh tên thư mục dài, cấu trúc thư mục lồng sâu, tên tệp dài, không gian làm việc dài tên, tên mục tiêu dài.
Tất cả những điều này có thể trở thành thành phần đường dẫn của hành động tệp đầu vào và có thể làm cạn kiệt độ dài đường dẫn tối đa.
Sử dụng gốc đầu ra ngắn.
Sử dụng cờ
--output_user_root=<path>
để chỉ định một đường dẫn ngắn cho đầu ra Bazel. Ý tưởng hay là có một ổ đĩa (hoặc ổ ảo) chỉ dành cho đầ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 đường giao nhau.
Nói một cách đơn giản[1], bạn có thể thấy các nút nối là đường liên kết tượng trưng trong thư mục. Dễ dàng tạo các điểm 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 hành động tạo bản dựng tạo ra một đường giao nhau 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 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 mối liên kết như sau:mklink /J c:\path\to\junction c:\path\to\very\long\target\path
[1]: Nói nghiêm túc Nút giao không phải là Đường liên kết tượng trưng mà là nhằm mục đích xây dựng các thao tác, bạn có thể coi các Giao thức này là Liên kết 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 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: Các tên biến môi trường trong Windows không phân biệt chữ hoa chữ thường.
Ví dụ: trong Java,
System.getenv("SystemRoot")
vàSystem.getenv("SYSTEMROOT")
tạo ra giá trị kết quả tương tự. (Đ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ể.
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 hành động sử dụng biến môi trường thay đổi thường xuyên hoặc được tuỳ chỉnh đối với người dùng, khiến quy tắc trở nên í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 được 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 hành động cần thêm biến môi trường, hãy đưa tất cả các biến đó vào một từ điển và truyền biến đó vào hành động. 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 phải có một đuôi có thể 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 theo lô).Lưu ý rằng các tập lệnh shell (
.sh
) KHÔNG thực thi được trên Windows; bạn không thể chỉ định chúng làexecutable
củactx.actions.run
. Tệp cũng không có quyền+x
nên 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 dễ di chuyển, bạn nên tránh chạy lệnh Bash ngay trong hành động.
Bash phổ biến trên các hệ thống giống như Unix nhưng thường không có trên Windows. Chính Bazel dựa vào Bash (MSYS2) ngày càng ít, vì vậy trong tương lai, người dùng sẽ ít có khả năng gặp phải MSYS2 được cài đặt cùng với Bazel. Để dễ sử dụng các quy tắc trên Windows hơn, hãy tránh chạy các lệnh Bash trong hành động.
Phần cuối cùng của 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. Lưu ý đến chế độ cài đặt Git, đặc biệt là về dòng khi thanh toán hoặc cam kết. (Xem cài đặt
core.autocrlf
của Git.)
Giải pháp:
Sử dụng quy tắc không có mục đích riêng.
native.genrule()
là một trình bao bọc cho các lệnh Bash và thường dùng để giải quyết các bài toán đơn giản chẳng hạn như sao chép một tệp hoặc ghi một tệp văn bản. Bạn có thể tránh dựa vào Bash (và đổi mới bánh xe): xem bazel-skylib có quy tắc riêng cho nhu cầu của bạn không. Không có công cụ nào phụ thuộc vào Bash khi được xây dựng/thử nghiệm trên Windows.Ví dụ về quy tắc tạo:
copy_file()
(nguồn, tài liệu): sao chép tệp ở nơi khác, tuỳ ý đặt tệp đó có thể thực thiwrite_file()
(nguồn, tài liệu): ghi một tệp văn bản, với phần cuối dòng mong muốn (auto
,unix
hoặcwindows
), tuỳ chọn làm cho 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 các dữ liệu đầu vào và đầu ra dự kiến cho trước dưới dạng một hành động của bản dựng (đây là trình bao bọc quy tắc xây dựng choctx.actions.run
)native_binary()
(nguồn, tài liệu): bao bọc 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 lệnh củarun_binary()
thuộc tínhtool
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 vấn đề nhỏ.Thay vì tập lệnh
.sh
, bạn có thể giải quyết các tác vụ đơn giản bằng tập lệnh.bat
.Ví dụ: nếu bạn cần một tập lệnh không có tác dụng gì hoặc in thông báo hay thoát với một tập lệnh cố định thì bạn chỉ cần tệp
.bat
đơn giản. Nếu quy tắc của bạn trả vềDefaultInfo()
nhà cung cấp khác, trườngexecutable
có thể tham chiếu đến tệp.bat
đó trên Windows.Và vì đuôi tệp không quan trọng trên macOS và Linux, bạn luôn có thể sử dụng
.bat
làm tiện ích bổ sung, ngay cả đối với các tập lệnh shell.Lưu ý rằng 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 ghi một khoảng trắng trong đó.Sử dụng Bash theo nguyên tắc.
Trong các quy tắc tạo và kiểm thử của Starlark, hãy sử dụng
ctx.actions.run_shell
để chạy tập lệnh Bash và Bash dưới dạng hành động.Trong macro Starlark, hãy gói tập lệnh Bash và lệnh trong một
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 Ồ.Trong các quy tắc của kho lưu trữ Starlark, hãy cố gắng tránh Bash hoàn toàn. Bazel hiện không có cách nào để chạy Lệnh Bash theo nguyên tắc trong quy tắc kho lưu trữ.
Đang xóa tệp
Vấn đề:
Bạ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 thao tác thử sẽ dẫn đến "Truy cập bị từ chối" . Nếu bạn không thể xoá tệp, thì có thể một quy trình đang chạy vẫn lưu giữ tệp đó mở.
Không thể xoá thư mục làm việc của một quy trình đang chạy.
Các quy trình có một ô điều khiển mở cho thư mục đang hoạt động của chúng và không thể xoá thư mục đó cho đến khi quá trình này chấm dứt.
Giải pháp:
Trong mã của bạn, hãy cố gắng đóng các tệp ngay lập tức.
Trong Java, hãy sử dụng
try-with-resources
. Trong Python, hãy dùngwith open(...) as f:
. Về nguyên tắc, hãy thử đóng các tên người dùng càng sớm càng tốt.