Bazel能否将需要重建的N个M文件批处理为单个命令?

Bazel能否将需要重建的N个M文件批处理为单个命令?,bazel,Bazel,Google Bazel构建工具可以很容易地解释特定目录树中的每个CoffeeScript文件都需要编译为相应的输出JavaScript文件: # Runs "coffee" 100 times if there are 100 files: # will run slowly if most of them need rebuilding. [genrule( name = 'compile-' + f, srcs = [f], outs = [f.replace('src/',

Google Bazel构建工具可以很容易地解释特定目录树中的每个CoffeeScript文件都需要编译为相应的输出JavaScript文件:

# Runs "coffee" 100 times if there are 100 files:
# will run slowly if most of them need rebuilding.

[genrule(
  name = 'compile-' + f,
  srcs = [f],
  outs = [f.replace('src/', 'static/').replace('.coffee', '.js')],
  cmd = 'coffee --compile --map --output $$(dirname $@) $<',
) for f in glob(['src/**/*.coffee'])]

有没有办法向Bazel解释,coffee可以一次调用多个文件,如果N个目标已过期,则只应将N个源文件提供给
coffee
命令,不是所有目标的完整列表,而是它们是否需要重建?

coffeescript文件是否相互独立?如果第一种方法有效,每个文件都分别通过
coffee
运行,那么看起来就是这样。在这种情况下,第一个将实际为您提供最多的并行性和增量

即使运行100次coffee比运行一次包含100个文件的coffee要慢,您也只需在第一次编译所有文件时支付该成本。当您更改1个文件时,其他99个文件将不会重新编译。但是,如果
coffee
的启动时间非常长,以至于100个文件实际上可以忽略不计,那么您最好还是坚持在一个大规则中编译它们

在两个极端之间折衷的一种方法是创建宏:

然后,您可以在构建文件中使用
compile\u coffee
宏,将构建组织到适当大小的目标中:

load("//pkg/path/to:coffee.bzl", "compile_coffee")

compile_coffee(
  name = "lib",
  srcs = glob(["*.coffee"]))
还有完整的skylark规则:但如果咖啡脚本文件并不真正相互依赖,那么这可能是不必要的


还有持久性工作者:它允许您保留一个运行中的咖啡实例,这样您就不必支付启动成本,但二进制必须表现良好,这是一项更大的投资,因为您通常必须编写包装器才能将所有内容连接起来。

这将一次向CoffeeScript编译器传递20个文件:

构建

load(":myrules.bzl", "coffeescript")

coffee_files = glob(["src/*.coffee"])

# 'coffeescript' is a macro, but it will make a target named 'main'
coffeescript(
    name = "main",
    srcs = coffee_files
)
myrules.bzl

def _chunks(l, n):
    n = max(1, n)
    return [l[i:i+n] for i in range(0, len(l), n)]

def coffeescript(name, srcs):
    i = 0
    all_outs = []
    for chunk in _chunks(srcs, 20):
        chunk_name = "{}-{}".format(name, i)
        outs = [f.replace('src/', 'static/').replace('.coffee', '.js') for f in chunk] + \
               [f.replace('src/', 'static/').replace('.coffee', '.js.map') for f in chunk]
        all_outs += outs
        native.genrule(
            name = chunk_name,
            srcs = chunk,
            outs = outs,
            cmd = "coffee --compile --map --output $(@D)/static $(SRCS)"
        )
        i += 1

    # make a filegroup with the original name that groups together all
    # of the output files
    native.filegroup(
        name = name,
        srcs = all_outs,
    )
然后,
bazel build:main
将构建所有的CoffeeScript文件,每次20个

但这确实有一些弱点:

  • 如果修改了一个CoffeeScript文件,那么将重新编译20个。不止一个

  • 如果一个文件被添加或删除,那么很多文件——基本上,从这一点到文件列表的末尾——都会被重新编译

我发现最好的方法是按照@ahumesky的建议:将事情分解成大小合理的Bazel“包”,让每个包进行一次编译

load(":myrules.bzl", "coffeescript")

coffee_files = glob(["src/*.coffee"])

# 'coffeescript' is a macro, but it will make a target named 'main'
coffeescript(
    name = "main",
    srcs = coffee_files
)
def _chunks(l, n):
    n = max(1, n)
    return [l[i:i+n] for i in range(0, len(l), n)]

def coffeescript(name, srcs):
    i = 0
    all_outs = []
    for chunk in _chunks(srcs, 20):
        chunk_name = "{}-{}".format(name, i)
        outs = [f.replace('src/', 'static/').replace('.coffee', '.js') for f in chunk] + \
               [f.replace('src/', 'static/').replace('.coffee', '.js.map') for f in chunk]
        all_outs += outs
        native.genrule(
            name = chunk_name,
            srcs = chunk,
            outs = outs,
            cmd = "coffee --compile --map --output $(@D)/static $(SRCS)"
        )
        i += 1

    # make a filegroup with the original name that groups together all
    # of the output files
    native.filegroup(
        name = name,
        srcs = all_outs,
    )