Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ bzl_library(
],
)

bzl_library(
name = "packaging_bzl",
srcs = [
"//python:packaging.bzl",
"//python/private:stamp.bzl",
],
)

stardoc(
name = "core-docs",
out = "python.md_",
Expand Down Expand Up @@ -103,6 +111,7 @@ stardoc(
name = "packaging-docs",
out = "packaging.md_",
input = "//python:packaging.bzl",
deps = [":packaging_bzl"],
)

[
Expand Down
24 changes: 22 additions & 2 deletions docs/packaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This rule is intended to be used as data dependency to py_wheel rule
<pre>
py_wheel(<a href="#py_wheel-name">name</a>, <a href="#py_wheel-abi">abi</a>, <a href="#py_wheel-author">author</a>, <a href="#py_wheel-author_email">author_email</a>, <a href="#py_wheel-classifiers">classifiers</a>, <a href="#py_wheel-console_scripts">console_scripts</a>, <a href="#py_wheel-deps">deps</a>, <a href="#py_wheel-description_file">description_file</a>,
<a href="#py_wheel-distribution">distribution</a>, <a href="#py_wheel-entry_points">entry_points</a>, <a href="#py_wheel-extra_requires">extra_requires</a>, <a href="#py_wheel-homepage">homepage</a>, <a href="#py_wheel-license">license</a>, <a href="#py_wheel-platform">platform</a>, <a href="#py_wheel-python_requires">python_requires</a>,
<a href="#py_wheel-python_tag">python_tag</a>, <a href="#py_wheel-requires">requires</a>, <a href="#py_wheel-strip_path_prefixes">strip_path_prefixes</a>, <a href="#py_wheel-version">version</a>)
<a href="#py_wheel-python_tag">python_tag</a>, <a href="#py_wheel-requires">requires</a>, <a href="#py_wheel-stamp">stamp</a>, <a href="#py_wheel-strip_path_prefixes">strip_path_prefixes</a>, <a href="#py_wheel-version">version</a>)
</pre>


Expand Down Expand Up @@ -101,7 +101,27 @@ py_wheel(
| python_requires | A string specifying what other distributions need to be installed when this one is. See the section on [Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) for details and examples of the format of this argument. | String | optional | "" |
| python_tag | Supported Python version(s), eg <code>py3</code>, <code>cp35.cp36</code>, etc | String | optional | "py3" |
| requires | List of requirements for this package | List of strings | optional | [] |
| stamp | Whether to encode build information into the wheel. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the wheel, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it potentially kills remote caching for the target and any downstream actions that depend on it.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 |
| strip_path_prefixes | path prefixes to strip from files added to the generated package | List of strings | optional | [] |
| version | Version number of the package | String | required | |
| version | Version number of the package. Note that this attribute supports stamp format strings. Eg <code>1.2.3-{BUILD_TIMESTAMP}</code> | String | required | |


<a name="#PyWheelInfo"></a>

## PyWheelInfo

<pre>
PyWheelInfo(<a href="#PyWheelInfo-name_file">name_file</a>, <a href="#PyWheelInfo-wheel">wheel</a>)
</pre>

Information about a wheel produced by `py_wheel`

**FIELDS**


| Name | Description |
| :-------------: | :-------------: |
| name_file | File: A file containing the canonical name of the wheel (after stamping, if enabled). |
| wheel | File: The wheel file itself. |


14 changes: 14 additions & 0 deletions examples/wheel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ py_wheel(
],
)

# Package just a specific py_libraries, without their dependencies
py_wheel(
name = "minimal_with_py_library_with_stamp",
# Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl"
distribution = "example_minimal_library",
python_tag = "py3",
stamp = 1,
version = "0.1.{BUILD_TIMESTAMP}",
deps = [
"//examples/wheel/lib:module_with_data",
"//examples/wheel/lib:simple_module",
],
)

# Use py_package to collect all transitive dependencies of a target,
# selecting just the files within a specific python package.
py_package(
Expand Down
68 changes: 61 additions & 7 deletions python/packaging.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@

"""Rules for building wheels."""

load("//python/private:stamp.bzl", "is_stamping_enabled")

PyWheelInfo = provider(
doc = "Information about a wheel produced by `py_wheel`",
fields = {
"name_file": (
"File: A file containing the canonical name of the wheel (after " +
"stamping, if enabled)."
),
"wheel": "File: The wheel file itself.",
},
)

def _path_inside_wheel(input_file):
# input_file.short_path is sometimes relative ("../${repository_root}/foobar")
# which is not a valid path within a zip file. Fix that.
Expand Down Expand Up @@ -110,6 +123,8 @@ def _py_wheel_impl(ctx):
_escape_filename_segment(ctx.attr.platform),
]) + ".whl")

name_file = ctx.actions.declare_file(ctx.label.name + ".name")

inputs_to_package = depset(
direct = ctx.files.deps,
)
Expand All @@ -133,9 +148,16 @@ def _py_wheel_impl(ctx):
args.add("--python_requires", ctx.attr.python_requires)
args.add("--abi", ctx.attr.abi)
args.add("--platform", ctx.attr.platform)
args.add("--out", outfile.path)
args.add("--out", outfile)
args.add("--name_file", name_file)
args.add_all(ctx.attr.strip_path_prefixes, format_each = "--strip_path_prefix=%s")

# Pass workspace status files if stamping is enabled
if is_stamping_enabled(ctx.attr):
args.add("--volatile_status_file", ctx.version_file)
args.add("--stable_status_file", ctx.version_file)
other_inputs.extend([ctx.version_file, ctx.info_file])

args.add("--input_file_list", packageinputfile)

extra_headers = []
Expand Down Expand Up @@ -193,15 +215,21 @@ def _py_wheel_impl(ctx):

ctx.actions.run(
inputs = depset(direct = other_inputs, transitive = [inputs_to_package]),
outputs = [outfile],
outputs = [outfile, name_file],
arguments = [args],
executable = ctx.executable._wheelmaker,
progress_message = "Building wheel",
)
return [DefaultInfo(
files = depset([outfile]),
data_runfiles = ctx.runfiles(files = [outfile]),
)]
return [
DefaultInfo(
files = depset([outfile]),
runfiles = ctx.runfiles(files = [outfile]),
),
PyWheelInfo(
wheel = outfile,
name_file = name_file,
),
]

def _concat_dicts(*dicts):
result = {}
Expand Down Expand Up @@ -247,9 +275,35 @@ platform = select({
default = "py3",
doc = "Supported Python version(s), eg `py3`, `cp35.cp36`, etc",
),
"stamp": attr.int(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good docstring, and I think it's enough copy-paste that we should consider sharing this attribute in the stamp.bzl file

load("//path/to:stamp.bzl", "STAMP_ATTR")

attrs = dict(STAMP_ATTR, **{
  more attrs
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

especially because stamp and _stamp_flag should always come as a pair

Copy link
Contributor Author

@UebelAndre UebelAndre Oct 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to minimize the use of stamp.bzl, if you read the comment at the top of the file, I think it should ideally be deleted so it should ideally be self-contained and as isolated as possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no one is working on bazelbuild/bazel#11164 and all other rulesets have already gone ahead with some local solution. pretty sure what we're doing here will be as permanent as rules_python.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should be but I can unfortunately see that happening. Regardless, I don't think rules_python should be loading attributes more from stamp.bzl. If the desire is to not have duplicate docs, I'll delete them from stamp.bzl in favor of something like "see py_wheel.stamp"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

py_wheel isn't the only rule that needs stamping, I'm sure there are others or there will be. maybe it can be refactored when we add the second one

doc = """\
Whether to encode build information into the wheel. Possible values:

- `stamp = 1`: Always stamp the build information into the wheel, even in \
[--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
This setting should be avoided, since it potentially kills remote caching for the target and \
any downstream actions that depend on it.

- `stamp = 0`: Always replace build information by constant values. This gives good build result caching.

- `stamp = -1`: Embedding of build information is controlled by the \
[--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.

Stamped targets are not rebuilt unless their dependencies change.
""",
default = -1,
values = [1, 0, -1],
),
"version": attr.string(
mandatory = True,
doc = "Version number of the package",
doc = (
"Version number of the package. Note that this attribute " +
"supports stamp format strings. Eg `1.2.3-{BUILD_TIMESTAMP}`"
),
),
"_stamp_flag": attr.label(
doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
default = Label("//python/private:stamp"),
),
}

Expand Down
6 changes: 6 additions & 0 deletions python/private/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

load(":stamp.bzl", "stamp_build_setting")

licenses(["notice"]) # Apache 2.0

filegroup(
Expand All @@ -36,6 +38,10 @@ filegroup(
exports_files(
[
"reexports.bzl",
"stamp.bzl",
],
visibility = ["//docs:__pkg__"],
)

# Used to determine the use of `--stamp` in Starlark rules
stamp_build_setting(name = "stamp")
73 changes: 73 additions & 0 deletions python/private/stamp.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""A small utility module dedicated to detecting whether or not the `--stamp` flag is enabled

This module can be removed likely after the following PRs ar addressed:
- https://github.com/bazelbuild/bazel/issues/11164
"""

StampSettingInfo = provider(
doc = "Information about the `--stamp` command line flag",
fields = {
"value": "bool: Whether or not the `--stamp` flag was enabled",
},
)

def _stamp_build_setting_impl(ctx):
return StampSettingInfo(value = ctx.attr.value)

_stamp_build_setting = rule(
doc = """\
Whether to encode build information into the binary. Possible values:

- stamp = 1: Always stamp the build information into the binary, even in [--nostamp][stamp] builds. \
This setting should be avoided, since it potentially kills remote caching for the binary and \
any downstream actions that depend on it.
- stamp = 0: Always replace build information by constant values. This gives good build result caching.
- stamp = -1: Embedding of build information is controlled by the [--[no]stamp][stamp] flag.

Stamped binaries are not rebuilt unless their dependencies change.
[stamp]: https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
""",
implementation = _stamp_build_setting_impl,
attrs = {
"value": attr.bool(
doc = "The default value of the stamp build flag",
mandatory = True,
),
},
)

def stamp_build_setting(name, visibility = ["//visibility:public"]):
native.config_setting(
name = "stamp_detect",
values = {"stamp": "1"},
visibility = visibility,
)

_stamp_build_setting(
name = name,
value = select({
":stamp_detect": True,
"//conditions:default": False,
}),
visibility = visibility,
)

def is_stamping_enabled(attr):
"""Determine whether or not build staming is enabled

Args:
attr (struct): A rule's struct of attributes (`ctx.attr`)

Returns:
bool: The stamp value
"""
stamp_num = getattr(attr, "stamp", -1)
if stamp_num == 1:
return True
elif stamp_num == 0:
return False
elif stamp_num == -1:
stamp_flag = getattr(attr, "_stamp_flag", None)
return stamp_flag[StampSettingInfo].value if stamp_flag else False
else:
fail("Unexpected `stamp` value: {}".format(stamp_num))
Loading