Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Starlark rules to observe the --stamp setting #11164

Open
alanfalloon opened this issue Apr 20, 2020 · 12 comments
Open

Allow Starlark rules to observe the --stamp setting #11164

alanfalloon opened this issue Apr 20, 2020 · 12 comments
Assignees
Labels
P2 We'll consider working on this in future. (Assignee optional) team-Rules-API API for writing rules/aspects: providers, runfiles, actions, artifacts type: feature request

Comments

@alanfalloon
Copy link
Contributor

Description of the feature request:

As noted in a comment on #1054, Starlark rules can access volatile-status.txt as ctx.version_file and stable-status.txt as ctx.info_file, but there is no way for a Starlark rule to observe the --stamp flag setting to know if the rule should access the files or not.

What underlying problem are you trying to solve with this feature?

I want to write Starlark rules that do not access the status files unless the user specifies the --stamp setting.

Because of issues like #10075 and #10177 it is important that unstamped builds are shielded from any access to the status files:

I tried using a custom build flag as a work-around to propagate my own version of the stamp setting, but it causes all outputs through transitions to change their output path and destroys all caching -- even on targets that don't depend on the new stamp-like setting.

What operating system are you running Bazel on?

macOS and Linux

What's the output of bazel info release?

release 3.0.0
@aiuto aiuto added team-Configurability platforms, toolchains, cquery, select(), config transitions and removed team-Starlark labels Apr 21, 2020
@aiuto
Copy link
Contributor

aiuto commented Apr 21, 2020

This would be incredibly useful. All the rules in bazelbuild/rules_pkg need the capability to either use epoch based times in distribution archives (for reproduceibilty) OR to use the current time (for the release build). To get that behavior, we need it as a runtime flag, not a rule attribute.

@alanfalloon
Copy link
Contributor Author

So, I found a work-around. You can define a config_setting that examines the stamp flag, and select a boolean attribute.

If, like me, you want a private attr, then you can use a label attr and default to an alias that selects over bool_setting instances. This avoids the issue where the bool_flag invalidates the transitions.

@aiuto aiuto removed the team-Configurability platforms, toolchains, cquery, select(), config transitions label Apr 22, 2020
@aiuto
Copy link
Contributor

aiuto commented Apr 22, 2020

configurabilty team looked at this. It might trigger some other feature changes we can do, but we are not planning to work on this issue right now, since there is a workaround.

@laurentlb laurentlb added P3 We're not considering working on this, but happy to review a PR. (No assignee) type: feature request and removed untriaged labels Apr 27, 2020
@aiuto
Copy link
Contributor

aiuto commented Apr 29, 2020

Also: #10907

@ulfjack
Copy link
Contributor

ulfjack commented Jul 22, 2020

What kind of feature changes? It seems straightforward to add a stamp field or method to the context, and I don't see any immediate issue with that.

@brandjon brandjon added P4 This is either out of scope or we don't have bandwidth to review a PR. (No assignee) team-Build-Language and removed P3 We're not considering working on this, but happy to review a PR. (No assignee) team-Starlark labels Feb 19, 2021
aiuto added a commit to bazelbuild/rules_pkg that referenced this issue May 6, 2021
…1) (#288)

Add stamp attribute to pkg_tar (in the style of cc_binary(stamp=-1,0,1)

Part 1 of #287 This is not pretty, because Bazel does not make --stamp available directly

- add a config setting to mimic --stamp: private_stamp_detect
  - This is a bit of a hack, until the value of stamp is available directly from starlark ctx See: bazelbuild/bazel#11164
  - use the config setting to pass a bool down to the packaging rule to see that --stamp was set
- add the stamp attribute to pkg_tar
- use combination of stamp & private_stamp_detect to invoke stamping
- this uses an undocumented feature to get to bazel-out/volatile-status.txt
  - that is bad in theory
  - In practice Bazel will eventually have admit the cat is out of the bag and document the files.

Future work:
- implement for all 4 package types.
- contain a test where a stamped package is used from a workspace that includes rules_pkg.

RELNOTES: Add stamp to pkg_tar.
@moroten
Copy link
Contributor

moroten commented Dec 9, 2021

It is possible to use a transition to read the command line option and let a provider pass the information back. See the stamp_selected target in the example below. Note that with_stamp.txt is preferably generated using genrule(stamp=True) or ctx.info_file whereas no_stamp.txt is often a static file.

# BUILD.bazel
load("stamp.bzl", "select_stamp_file", "stamp_info_rule")
load("@bazel_skylib//rules:common_settings.bzl", "bool_setting")

bool_setting(
    name = "stamp_setting",
    build_setting_default = False,
    visibility = ["//visibility:private"],
)

stamp_info_rule(
    name = "stamp_info",
    visibility = ["//visibility:private"],
)

select_stamp_file(
    name = "stamp_selected",
    no_stamp = "no_stamp.txt",
    with_stamp = "with_stamp.txt",
)
# stamp.bzl
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")

StampInfo = provider("Holds the value of --stamp", fields = ["enabled"])

def _stamp_transition_impl(settings, attr):
    _ignore = attr
    return {
        "@//:stamp_setting": settings["//command_line_option:stamp"],
    }

stamp_transition = transition(
    implementation = _stamp_transition_impl,
    inputs = ["//command_line_option:stamp"],
    outputs = ["//:stamp_setting"],
)

def _stamp_info_rule_impl(ctx):
    return StampInfo(enabled = ctx.attr._enabled[BuildSettingInfo].value)

stamp_info_rule = rule(
    implementation = _stamp_info_rule_impl,
    cfg = stamp_transition,
    attrs = {
        "_enabled": attr.label(default = "//:stamp_setting"),
        "_allowlist_function_transition": attr.label(
            default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
        ),
    },
)

def _select_stamp_file_impl(ctx):
    if ctx.attr._stamp_info[StampInfo].enabled:
        ret = ctx.file.with_stamp
    else:
        ret = ctx.file.no_stamp
    return DefaultInfo(
        files = depset([ret]),
    )

select_stamp_file = rule(
    implementation = _select_stamp_file_impl,
    attrs = {
        "_stamp_info": attr.label(default = "@//:stamp_info"),
        "no_stamp": attr.label(mandatory = True, allow_single_file=True),
        "with_stamp": attr.label(mandatory = True, allow_single_file=True),
    },
)

@aiuto
Copy link
Contributor

aiuto commented Dec 9, 2021

You can do this without a transition. Something like

//my/BUILD

config_setting(
    name = "private_stamp_detect",
    values = {"stamp": "1"},
)

//some/rule.bzl

foo_inner = rule(
    implementation = _foo_impl,
    attrs = {
        "stamp": attr.int(
            default = 0,  # Mimic the *_binary stamp behavior.
        ),
        # Is --stamp set on the command line?
        "private_stamp_detect": attr.bool(default = False),
   ...
)

def foo(name, **kwargs):
    foo_inner(
        name = name,
        private_stamp_detect = select({
            //my:private_stamp_detect: True,
            "//conditions:default": False,
        }),
        **kwargs,
    )

def _foo_impl(ctx):
 ...
  if ctx.attr.stamp == 1 or (ctx.attr.stamp == -1 and ctx.attr.private_stamp_detect):
      # stamped path
      args.append("--stamp_from=%s" % ctx.version_file.path)
      files.append(ctx.version_file)
  else:
      # unstamped path.

But this has the same limitation as your example - that we need an external helper to extract the time text from a file. What is missing for me is having the time stamp directly accessible from ctx, so we could pass it directly to a helper tool.

@moroten
Copy link
Contributor

moroten commented Dec 9, 2021

@aiuto Your solution with a single config_setting is much simpler. I also prefer filtering stable-status.txt before using the data to improve cache hit rate, so I never tend to be dependent on using the stamp flag in rule implementations.

@aiuto
Copy link
Contributor

aiuto commented Dec 9, 2021 via email

@rickeylev
Copy link
Contributor

I just went through the same as aiuto for the Python rules; having to run another executable is w/e, but having to figure out the necessary shell to transform things was a pain. Plus, since I'm now relying on bash, that inevitably means e.g. Windows or other platforms without bash need more work. This would have been trivially easy and easily cross-platform if they were directly available to Starlark on ctx somewhere.

That said, a couple tips for other who need to process these files:

  1. declare -A and read can be used to easily parse the files into an associate array in bash:
declare -A info
while IFS= read -r line; do
  read key value <<< "$line"
  info["$key"]="$value"
done < <(cat "$VERSION_FILE" "$INFO_FILE")s
echo ${info[BUILD_ID}

Will basically give you an array with all the keys/values from the files in it.

  1. When running this, create an implicit attribute on an executable and use ctx.actions.run instead of run_shell (remember to +x for a shell script file). Now you can easily use an alias with select() to switch to different parser programs depending on the platform.

@brandjon brandjon added untriaged team-Rules-API API for writing rules/aspects: providers, runfiles, actions, artifacts and removed team-Build-Language labels Nov 4, 2022
@alexeagle
Copy link
Contributor

alexeagle commented Jul 27, 2023

We added a stamping feature to Aspect's bazel-lib which solves this problem in a nice, tidy way:
https://docs.aspect.build/rules/aspect_bazel_lib/docs/stamping
We use that in all our rules.

@comius comius added P2 We'll consider working on this in future. (Assignee optional) and removed P4 This is either out of scope or we don't have bandwidth to review a PR. (No assignee) untriaged labels Aug 22, 2023
@comius
Copy link
Contributor

comius commented Aug 22, 2023

It seems the issue is resolved with improved api @buildbreaker2021 is working on

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P2 We'll consider working on this in future. (Assignee optional) team-Rules-API API for writing rules/aspects: providers, runfiles, actions, artifacts type: feature request
Projects
None yet
Development

No branches or pull requests