Why doesn't this rule create a file every time it is built?

369 views Asked by At

I write the below bazel rule for incremental test.

This rule(foo_library) has a dependency property. When there are no dependencies, it creates a file with its own name, filling the contents of the file with its own name. When there is a dependency, it creates a file with its own name, and at this time, the contents of the file are filled by adding its own name to the contents of the file created by the dependency target.

In this test, foo3, foo2, and foo1 are linked as dependencies, so I expected 3 files to be created, but only foo3 was created.

What could be the problem? Hera are my test bazel code, build command and log message.

# ~/rules/dev.bzl
def _foo_binary_impl(ctx):

    this_name = ctx.attr.name
    file_name = ctx.actions.declare_file(ctx.label.name)

    print("##-000 this_name: {}".format(this_name))
    print("##-001 file_name: {}".format(file_name))

    file_content = ""

    if ctx.attr.deps:
        for dep in ctx.attr.deps:
            t1 = dep[DefaultInfo].files.to_list()[0].basename
            t1 = t1 + this_name
            print("##-100 t1: {}".format(t1))

            file_content = t1

    else:
        t2 = this_name
        print("##-101 t2: {}".format(t2))

        file_content = t2

    ctx.actions.write(
        output = file_name,
        content = file_content,
    )

    return [
        DefaultInfo(
            files = depset([file_name])
        ),
    ]

foo_binary = rule(
    implementation = _foo_binary_impl,
    attrs = {
        "deps": attr.label_list(),
    },
)
# build command and log
$> bazel build -s //rules/example:foo3                                                                                                                                                                           1 х  11:38:08 
DEBUG: ~/rules/dev.bzl:10:10: ##-000 this_name: foo1
DEBUG: ~/rules/dev.bzl:11:10: ##-001 file_name: <generated file rules/example/foo1>
DEBUG: ~/rules/dev.bzl:25:14: ##-101 t2: foo1
DEBUG: ~/rules/dev.bzl:10:10: ##-000 this_name: foo2
DEBUG: ~/rules/dev.bzl:11:10: ##-001 file_name: <generated file rules/example/foo2>
DEBUG: ~/rules/dev.bzl:19:18: ##-100 t1: foo1foo2
DEBUG: ~/rules/dev.bzl:10:10: ##-000 this_name: foo3
DEBUG: ~/rules/dev.bzl:11:10: ##-001 file_name: <generated file rules/example/foo3>
DEBUG: ~/rules/dev.bzl:19:18: ##-100 t1: foo2foo3
INFO: Analyzed target //rules/example:foo3 (3 packages loaded, 8 targets configured).
INFO: Found 1 target...
Target //rules/example:foo3 up-to-date:
  bazel-bin/rules/example/foo3
INFO: Elapsed time: 0.107s, Critical Path: 0.00s
INFO: 2 processes: 2 internal.
INFO: Build completed successfully, 2 total actions

Build foo3 and expected 3 files(foo1, foo2, foo3) are generated. howver, foo3 is generated only.

1

There are 1 answers

1
Benjamin Peterson On BEST ANSWER

If ctx.attr.deps is nonempty, foo_binary_impl writes a file with content that depends on the file name of the last file in ctx.attr.deps rather than the content of the last file in ctx.attr.deps. No dependency on any file in ctx.attr is ever introduced. Therefore, Bazel does not build anything in deps when producing the output of foo_binary_impl.

To make the output of foo_binary_impl depend on the content of dependencies, make an action to produce the final output that takes the dependency files as inputs. For example, to concatenate all the files in deps into the output:

ctx.actions.run_shell(
    inputs = ctx.files.deps,
    outputs = [file_name],
    command = "cat " + " ".join([f.path for f in ctx.files.deps]) + " >" + file_name.path,
)