在 Windows 上编写规则

报告问题 查看源代码 每夜 build · 7.4 . 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]:严格意义 交叉路口不是符号链接, 为了构建操作,您可以将连接视为目录符号链接。

  • 在 actions / 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。如果 操作需要更多的环境变量,请将它们全部放在字典中,然后传递给操作。 示例:

    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.runexecutable。文件也无法拥有 +x 权限,因此您无法像在 Linux 上一样执行任意文件。

  • Bash 命令:为了实现可移植性,请避免直接在 Action 中运行 Bash 命令。

    Bash 在类似 Unix 的系统上非常普遍,但在 Windows 上通常不可用。Bazel 本身越来越少依赖 Bash (MSYS2),因此未来用户不太可能将 MSYS2 与 Bazel 一起安装。为了让规则在 Windows 上更易于使用,请避免在以下位置运行 Bash 命令: 操作。

  • 行结尾:Windows 使用 CRLF (\r\n),类 Unix 系统使用 LF (\n)。

    比较文本文件时请注意这一点。请注意您的 Git 设置,尤其是在检出或提交时行结尾。(请参阅 Git 的 core.autocrlf 设置。)

解决方案:

  • 使用无 Bash 的专用规则。

    native.genrule() 是 Bash 命令的封装容器,通常用于解决复制文件或写入文本文件等简单问题。您可以避免依赖 Bash(和 wheel):查看 bazel-skylib 是否有专门制定的规则来满足您的需求。它们都依赖于 Bash 。

    build 规则示例:

    • copy_file()源代码文档):将文件复制到其他位置,并可选择将其设为可执行

    • write_file()来源文档): 用于写入一个文本文件,以所需的行结尾(autounixwindows),可选 使其可执行(如果是脚本)

    • run_binary()来源文档): 将具有指定输入和预期输出的二进制文件(或 *_binary 规则)作为构建操作运行 (这是 ctx.actions.run 的构建规则封装容器)

    • native_binary()文档):将原生二进制文件封装在 *_binary 规则中,您可以bazel run该规则,也可以在 run_binary()tool 属性或 native.genrule()tools 属性中使用该规则

    测试规则示例:

    • diff_test()源代码文档):用于比较两个文件内容的测试

    • native_test()来源文档): 将原生二进制文件封装在 *_test 规则中,您可以bazel test

  • 在 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:。原则上,请尝试 关闭标识名