Skip to content

Avoid different transition output directories when build settings have the same values #14023

@torgil

Description

@torgil

Description of the problem / feature request:

To avoid duplicated actions on dependencies when using build-settings it's necessary to reset build-settings to it's default value. This is currently not enough as we still can get conflicts on the "transition directory name fragment" for instance when two subsystems using different build-settings depend on a common library.

Bugs: what's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

The below example shows the issue with two build settings (used by B and C) triggering and action conflict in a common library (ExtDep) which appears in three branches from the top target:

  1. A -> B -> ExtDep
  2. A -> B -> C -> ExtDep
  3. A -> ExtDep (issue also provoked with A -> C )

B sets a non-default value for bs2 in the input transition and resets it to default value in the edge transition to ExtDep.

Rule names below indicate which transitions they perform, eg the name "input_bs1_extdeps_bs2" indicates that it sets "bs1" build setting in the input transition and "bs2" build setting in the edge transition on the "extdeps" attribute.

rules.bzl

"Rules with different transitions to showcase analyze phase configuration conflicts"

def _dummy_rule_impl(ctx):
    return []

def _input_transition_impl(settings, attr):
    build_settings = {setting: settings[setting] for setting in attr._input_settings}
    build_settings.update(attr.input_build_settings)
    return build_settings

def _extdeps_transition_impl(settings, attr):
    build_settings = {setting: settings[setting] for setting in attr._extdeps_settings}
    build_settings.update(attr.extdeps_build_settings)
    return build_settings

def _create_custom_rule(input, extdeps):
    return rule(
        implementation = _dummy_rule_impl,
        cfg = transition(inputs = input, outputs = input, implementation = _input_transition_impl),
        attrs = {
            "deps": attr.label_list(),
            "extdeps": attr.label_list(cfg = transition(
                inputs = extdeps, outputs = extdeps, implementation = _extdeps_transition_impl)),
            "extdeps_build_settings": attr.string_dict(),
            "input_build_settings": attr.string_dict(),
            "_extdeps_settings": attr.string_list(default = extdeps),
            "_input_settings": attr.string_list(default = input),
            "_whitelist_function_transition": attr.label(
                default = "@bazel_tools//tools/whitelists/function_transition_whitelist",
            ),
        },
    )

build_setting = rule(
    implementation = _dummy_rule_impl,
    build_setting = config.string(flag = False),
)

input_outdir = _create_custom_rule(
    ["//command_line_option:output directory name"],
    [],
)

input_outdir_bs1_bs2_extdeps_bs1_bs2 = _create_custom_rule(
    ["//:bs1", "//:bs2", "//command_line_option:output directory name"],
    ["//:bs1", "//:bs2"],
)

BUILD.bazel

load(":rules.bzl", "build_setting", "input_outdir", "input_outdir_bs1_bs2_extdeps_bs1_bs2")
load("@rules_cc//cc:defs.bzl", "cc_library")

build_setting(
    name = "bs1",
    build_setting_default = "bs1_default",
)

build_setting(
    name = "bs2",
    build_setting_default = "bs2_default",
)

input_outdir(
    name = "A",
    input_build_settings = {
        "//command_line_option:output directory name": "any",
    },
    deps = [":B", ":ExtDep"],  # C instead of ExtDep triggers the same issue
)

input_outdir_bs1_bs2_extdeps_bs1_bs2(
    name = "B",
    input_build_settings = {
        "//command_line_option:output directory name": "any",
        "//:bs1": "bs1_default",
        "//:bs2": "bs2_custom",  # Input transition sets custom bs2 value
    },
    deps = [":C"],
    extdeps = [":ExtDep"],  # Edge transition sets default bs2 value
    extdeps_build_settings = {"//:bs1": "bs1_default", "//:bs2": "bs2_default"},
)

input_outdir_bs1_bs2_extdeps_bs1_bs2(
    name = "C",
    input_build_settings = {
        "//command_line_option:output directory name": "any",
        "//:bs1": "bs1_default",
        "//:bs2": "bs2_default",
    },
    extdeps = [":ExtDep"],
    extdeps_build_settings = {"//:bs1": "bs1_default", "//:bs2": "bs2_default"},
)

# Dependency with action to trigger the action conflict (any rule generating actions may apply here)
cc_library(
    name = "ExtDep",
    srcs = ["extdep.c"],
)

Example run:

$ bazel build //:A
ERROR: file '_objs/ExtDep/extdep.pic.o' is generated by these conflicting actions:
Label: //:ExtDep
RuleClass: cc_library rule
Configuration: 0ef19c895a80334be3739ffbe96bbd7f6766cbb2944f2a649b67acb81d261d43, 643272508e08a8348ad8387f39b923f0972d5a26bb916ec22e0cc03e63c97511
<...>
$ bazel config 0ef19c895a80334be3739ffbe96bbd7f6766cbb2944f2a649b67acb81d261d43 643272508e08a8348ad8387f39b923f0972d5a26bb916ec22e0cc03e63c97511
INFO: Displaying diff between configs 0ef19c895a80334be3739ffbe96bbd7f6766cbb2944f2a649b67acb81d261d43 and 643272508e08a8348ad8387f39b923f0972d5a26bb916ec22e0cc03e63c97511
Displaying diff between configs 0ef19c895a80334be3739ffbe96bbd7f6766cbb2944f2a649b67acb81d261d43 and 643272508e08a8348ad8387f39b923f0972d5a26bb916ec22e0cc03e63c97511
FragmentOptions com.google.devtools.build.lib.analysis.config.CoreOptions {
  affected by starlark transition: [//:bs2, //command_line_option:output directory name], [//command_line_option:output directory name]
  transition directory name fragment: ST-1e8dc79add0a, ST-a28aa8ec4aa8

Opts passed to transitionDirectoryNameFragment function:

ST-a28aa8ec4aa8: [//command_line_option:output directory name=any]
ST-1e8dc79add0a: [//:bs2=bs2_default, //command_line_option:output directory name=any]

What operating system are you running Bazel on?

Linux

What's the output of bazel info release?

development version

If bazel info release returns "development version" or "(@Non-Git)", tell us how you built Bazel.

1. git checkout f0da3ecd186a33439b6255521e4d13a0e65ce88f  # current master
2. git revert commit af0e20f9d9808d8fba03b712491ad8851b3d5c26  # revert "Remove outdated "output directory name" option."
3. bazel-4.1.0-linux-x86_64 build //src:bazel-dev

What's the output of git remote get-url origin ; git rev-parse master ; git rev-parse HEAD ?

not applicable

$ git remote get-url bazelbuild; git rev-parse bazelbuild/master ; git rev-parse HEAD ; git rev-parse HEAD~1
https://github.com/bazelbuild/bazel.git
f0da3ecd186a33439b6255521e4d13a0e65ce88f
b7308ccc7d763088c2efe997ac111bd9e09cec6a
f0da3ecd186a33439b6255521e4d13a0e65ce88f

Issue isolated on a custom version of v5.0.0-pre.20210604.6 but originally found in 4.x versions

Have you found anything relevant by searching the web?

Related issues: #12171 #12731

Any other information, logs, or outputs that you want to share?

Prior to #13580 it's possible to trigger the issue with only B -> ExtDeps and B -> C -> ExtDeps given that bs1 is not included in the extdeps edge transition (found with git bisect building //:B above but using input_outdir_bs1_bs2_extdeps_bs2 instead of input_outdir_bs1_bs2_extdeps_bs1_bs2)

#13587 in combination with a custom output directory scheme using transition on "//command_line_option:output directory name" is designed to address all issues leading to conflicting "transition directory name fragment". This transition turns these types of issues into build failures due to action conflicts rather than silently duplicating actions in different output folders.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2We'll consider working on this in future. (Assignee optional)team-Configurabilityplatforms, toolchains, cquery, select(), config transitionstype: feature request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions