Bazel Rules: Create a File

2021-12-26 • edited 2021-12-30

Our previous examples lack a fundamental attribute of a build solution — they did not produce any artifacts. We need to understand how to create a file in Bazel to correct that.

How to Create a File #

The process typically involves four steps:

  • Declare a file with declare_file function.
  • Execute a build action, most frequently, run or run_shell, to produce the file.
  • Include the file into a depset.
  • Return a provider referring to the depset.

A full explanation of these steps involves a deeper dive into Bazel’s more advanced concepts, so instead, I’ll try to keep it close to the code.

Example #

Let’s copy and modify our hello example to create a hello.txt text file. Apart from a couple of names, the only place we need to change is the implementation function:

def _demo_rule_impl(ctx):
    out = ctx.actions.declare_file("hello.txt")
    ctx.actions.write(
        output = out,
        content = "Hello, World!\n",
    )
    return [DefaultInfo(files = depset([out]))]

In this example, declare_file action creates a file object that we can pass to other actions. We generate the file with the write action. Then we create an instance of a depset, a data structure that can hold and efficiently merge large sets of direct and transitive dependencies. Finally, the function returns a list of providers. A provider is yet another specialized data structure that passes information between rules. We use the DefaultInfo provider here as it is sufficient for our purposes.

Our build is now more meaningful:

$ bazel build //file
INFO: Analyzed target //file:file (4 packages loaded, 7 targets configured).
INFO: Found 1 target...
Target //file:file up-to-date:
  bazel-bin/file/hello.txt
...

And we can check that it did produce the file:

$ cat bazel-bin/file/hello.txt
Hello, World!

The complete example is here.

bazel

Bazel Rules: Hello, World Bazel Rules: Create an Executable