本页重点介绍如何编写与 Windows 兼容的规则、编写可移植规则时遇到的常见问题以及一些解决方案。
路径
问题:
- 长度限制:最长路径长度为 259 个字符。 - 虽然 Windows 也支持更长的路径(最多 32,767 个字符),但许多程序都是按照较低的限制构建的。 - 请注意,在操作中运行的程序存在此问题。 
- 工作目录:同样不得超过 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]。联接易于创建,并且可以指向具有长路径的目录(在同一台计算机上)。如果 build 操作创建的连接的路径较短,但目标路径较长,则具有短路径限制的工具可以访问连接目录中的文件。 - 在 - .bat文件或 cmd.exe 中,您可以创建如下所示的连接点:- mklink /J c:\path\to\junction c:\path\to\very\long\target\path- [1]:严格来说,联接不是符号链接,但为了构建操作,您可以将联接视为目录符号链接。 
- 将操作 / 环境变量中的路径中的 - /替换为 ``。- 为操作创建命令行或环境变量时,请使用 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(批处理脚本)。- 请注意,Shell 脚本 ( - .sh) 无法在 Windows 上执行;您无法将其指定为- ctx.actions.run的- executable。也没有文件可以拥有的- +x权限,因此您无法像在 Linux 上那样执行任意文件。
- Bash 命令:为了提高可移植性,请避免在操作中直接运行 Bash 命令。 - Bash 在类 Unix 系统上广泛使用,但在 Windows 上通常不可用。Bazel 本身对 Bash (MSYS2) 的依赖程度越来越低,因此未来用户不太可能在安装 Bazel 的同时安装 MSYS2。为了便于在 Windows 上使用规则,请避免在操作中运行 Bash 命令。 
- 行尾:Windows 使用 CRLF ( - \r\n),类 Unix 系统使用 LF (- \n)。- 在比较文本文件时,请注意这一点。请注意您的 Git 设置,尤其是在签出或提交时要注意行尾。(请参阅 Git 的 - core.autocrlf设置。)
解决方案:
- 使用无 Bash 的专用规则。 - native.genrule()是 Bash 命令的封装容器,通常用于解决简单问题,例如复制文件或写入文本文件。您可以避免依赖 Bash(并重新发明轮子):看看 bazel-skylib 是否有专门针对您需求的规则。在 Windows 上构建/测试时,它们都不依赖于 Bash。- build 规则示例: 
- write_file()(来源、文档):写入具有所需行尾(- auto、- unix或- windows)的文本文件,可以选择使其可执行(如果它是脚本)
- run_binary()(来源、文档):以给定的输入和预期输出运行二进制文件(或- *_binary规则)作为 build 操作(这是- ctx.actions.run的 build 规则封装容器)
- native_binary()(来源、文档):将原生二进制文件封装在- *_binary规则中,您可以在- bazel run中使用该规则,也可以在- run_binary()的- tool属性或- native.genrule()的- tools属性中使用该规则
 - 测试规则示例: 
- 在 Windows 上,请考虑使用 - .bat脚本来处理琐碎的事情。- 您可以使用 - .bat脚本解决琐碎的任务,而无需使用- .sh脚本。- 例如,如果您需要一个什么都不做、打印消息或以固定错误代码退出的脚本,那么简单的 - .bat文件就足够了。如果您的规则返回- DefaultInfo()提供程序,则- executable字段可能引用 Windows 上的该- .bat文件。- 由于文件扩展名在 macOS 和 Linux 上并不重要,因此您始终可以使用 - .bat作为扩展名,即使是对于 shell 脚本也是如此。- 请注意,无法执行空的 - .bat文件。如果您需要空白脚本,请在其中写入一个空格。
- 以有原则的方式使用 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:。原则上,应尽快尝试关闭句柄。