Search code examples
bazelobjcopy

How to invoke CROSSTOOL tools from Bazel macros / rules?


I'm building ARM Cortex-M firmware from Bazel with a custom CROSSTOOL. I'm successfully building elf files and manually objcopying them to binary files with the usual:

path/to/my/objcopy -o binary hello.elf hello.bin

I want to make a Bazel macro or rule called cc_firmware that:

  • Adds the -Wl,-Map=hello.map flags to generate a mapfile
  • Changes the output elf name from hello to hello.elf
  • Invokes path/to/my/objcopy to convert the elf to a bin.

I don't know how to get the name of a CROSSTOOL tool (objcopy) to invoke it, and it feels wrong to have the rule know the path to the tool executable.

Is there a way to use the objcopy that I've already told Bazel about in my CROSSTOOL file?


Solution

  • You can actually access this from a custom rule. Basically you need to tell Bazel that you want access to the cpp configuration information (fragments = ["cpp"]) and then access its path via ctx.fragments.cpp.objcopy_executable, e.g.,:

    def _impl(ctx):
        print("path: {}".format(ctx.fragments.cpp.objcopy_executable))
        # TODO: actually do something with the path...
    
    cc_firmware = rule(
        implementation = _impl,
        fragments = ["cpp"],
        attrs = {
             "src" : attr.label(allow_single_file = True),
             "map" : attr.label(allow_single_file = True),
        },
        outputs = {"elf" : "%{name}.elf"}
    )
    

    Then create the output you want with something like (untested):

    def _impl(ctx):
      src = ctx.attr.src.files.to_list()[0]
      m = ctx.attr.map.files.to_list()[0]
    
      ctx.action(
        command = "{objcopy} -Wl,-Map={map} -o binary {elf_out} {cc_bin}".format(
            objcopy=ctx.fragments.cpp.objcopy_executable,
            map=m,
            elf_out=ctx.outputs.elf.path,
            cc_bin=src,
        ),
        outputs = [ctx.outputs.elf],
        inputs = [src, m],
      )