Bazel:处理 ctx.action 中的中间文件

Bazel : handle intermediate files in ctx.action

我正在尝试在 Bazel 中实现自定义 c_library() 规则,它将一堆 .c 文件作为输入并生成 .a 静态库。在我的实现中,.c 个文件生成 .o 个对象,然后归档到 .a 个库中。但似乎没有生成 .o 文件。 这是自定义 c_library() 规则(rules.bzl 文件):

def _change_ext_to_x( file_list, x = 'o' ):
    # inputs a file list (i.e. string list)
    # and changes their extensions to '.{x}'
    return [ "".join( onefile.split('.')[:-1] ) + '.' + x \
                    for onefile in file_list ]

def _str_to_File( ctx, file_list ):
    # A constructor of File() object
    # pass the context
    return [ ctx.new_file( onefile ) for onefile in file_list ]

def _c_library_impl( ctx ):
    # implementation of 'c_library' rule

    # the source files list ( strings )
    in_file_list = []
    for onefile in ctx.files.srcs:
        in_file_list.append( onefile.basename )

    out_file_list = _str_to_File( ctx, # the current context
                           _change_ext_to_x(in_file_list, x = 'o') )

    ctx.action(
        inputs = ctx.files.srcs,
        outputs = out_file_list,
        progress_message = "COMPILING FROM <.c> TO <.o>",
        use_default_shell_env = True,
        command = "gcc -c -O3 %s" % " ".join( in_file_list )
    )

    ctx.action(
        inputs = out_file_list,
        outputs = [ ctx.outputs._libfile ],
        progress_message = "ARCHIVING <.o>s to <.a>",
        use_default_shell_env = True,
        arguments = [ctx.outputs._libfile.basename],
        command = "ar cr  %s" % " ".join( [onefile.basename 
                                        for onefile in out_file_list] )
    )

    pass

c_library = rule(
    # define rule for 'c' library
    implementation = _c_library_impl,
    attrs = {
        "srcs" : attr.label_list( allow_files = True,
                                  mandatory = True,
                                  allow_empty = False ),
        "out" : attr.output( mandatory = False )
    },
    outputs = {
        "_libfile" : "lib%{name}.a"
    }
)

这是我的 BUILD 文件:

load("//:rules.bzl", "c_library")

c_library(
    name = "foo",
    srcs = glob(include = ["*.c"], exclude = ["main.c"])
    # there are two files 'cmd.c' and 'utils.c' in the package
)

当我执行 bazel build //:foo 时,出现以下错误:

INFO: Found 1 target...
ERROR: /home/spyder/Desktop/project/BUILD:3:1: output 'cmd.o' was not created.
ERROR: /home/spyder/Desktop/project/BUILD:3:1: output 'utils.o' was not created.
ERROR: /home/spyder/Desktop/project/BUILD:3:1: not all outputs were created or valid.
Target //:foo failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 2.249s, Critical Path: 1.94s

如何在两个连续的 ctx.action 之间保留中间文件?

您应该为每个 .c 文件创建一个操作,而不是为所有文件创建一个操作,这也会增加并行度,然后指定输出:

def _c_library_impl( ctx ):
    # implementation of 'c_library' rule
    out_file_list = []
    for f in ctx.files.srcs:
      o = ctx.new_file(f.basename + ".o")
      out_file_list.append(o)
      ctx.action(
        inputs = [f],
        outputs = [o],
        progress_message = "COMPILING FROM <.c> TO <.o>",
        use_default_shell_env = True,
        command = "gcc -c -O3 %s %s" % (f.path, o.path)
      )

    ctx.action(
        inputs = out_file_list,
        outputs = [ ctx.outputs._libfile ],
        progress_message = "ARCHIVING <.o>s to <.a>",
        use_default_shell_env = True,
        command = "ar cr %s %s" % (ctx.outputs._libfile.path, 
                                   "\n".join([onefile.basename 
                                        for onefile in out_file_list] )
    )