I am trying to implement custom c_library()
rule in Bazel which takes a bunch of .c
files as input and produces .a
static library. In my implementation, .c
files generate .o
objects and then archives into .a
library. But it seems that .o
files are not getting generated.
Here is the custom c_library()
rule (rules.bzl
file):
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 $1 %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"
}
)
And here is my BUILD
file:
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
)
When I did bazel build //:foo
, I got the following error:
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
How exactly to persist the intermediate files between two successive ctx.action
s ?
You should create one action per .c file instead of one action for all of them, that will also increase parallelism, and then specified the output:
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] )
)