Skip to content

Commit 92df82c

Browse files
authored
Merge branch 'main' into bootstrap-fix
2 parents 8430043 + 096a04f commit 92df82c

File tree

7 files changed

+28
-297
lines changed

7 files changed

+28
-297
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ Other changes:
9595
([2169](https://github.com/bazelbuild/rules_python/issues/2169)).
9696
* (workspace) Corrected protobuf's name to com_google_protobuf, the name is
9797
hardcoded in Bazel, WORKSPACE mode.
98+
* (pypi): {bzl:obj}`compile_pip_requirements` no longer fails on Windows when `--enable_runfiles` is not enabled.
99+
* (pypi): {bzl:obj}`compile_pip_requirements` now correctly updates files in the source tree on Windows when `--windows_enable_symlinks` is not enabled.
98100
* (repositories): Add libs/python3.lib and pythonXY.dll to the `libpython` target
99101
defined by a repository template. This enables stable ABI builds of Python extensions
100102
on Windows (by defining Py_LIMITED_API).

docs/toolchains.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,17 @@ toolchain(
444444
],
445445
exec_comaptible_with = ["@platforms/os:linux"]
446446
)
447+
448+
# File: MODULE.bazel or WORKSPACE.bazel
449+
# These toolchains will considered before others
450+
register_toolchains("//toolchains:all")
447451
```
448452

453+
When registering custom toolchains, be aware of the the [toolchain registration
454+
order](https://bazel.build/extending/toolchains#toolchain-resolution). In brief,
455+
toolchain order is the BFS-order of the modules; see the bazel docs for a more
456+
detailed description.
457+
449458
:::{note}
450459
The toolchain() calls should be in a separate BUILD file from everything else.
451460
This avoids Bazel having to perform unnecessary work when it discovers the list

python/private/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ licenses(["notice"])
3030
filegroup(
3131
name = "distribution",
3232
srcs = glob(["**"]) + [
33+
"//python/private/api:distribution",
3334
"//python/private/proto:distribution",
3435
"//python/private/pypi:distribution",
3536
"//python/private/whl_filegroup:distribution",

python/private/api/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ package(
1919
default_visibility = ["//:__subpackages__"],
2020
)
2121

22+
filegroup(
23+
name = "distribution",
24+
srcs = glob(["**"]),
25+
)
26+
2227
py_common_api(
2328
name = "py_common_api",
2429
# NOTE: Not actually public. Implicit dependency of public rules.

python/private/py_cc_link_params_info.bzl

Lines changed: 0 additions & 284 deletions
Original file line numberDiff line numberDiff line change
@@ -16,290 +16,6 @@
1616
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
1717
load(":util.bzl", "define_bazel_6_provider")
1818

19-
DEFAULT_STUB_SHEBANG = "#!/usr/bin/env python3"
20-
21-
DEFAULT_BOOTSTRAP_TEMPLATE = Label("//python/private:bootstrap_template")
22-
23-
_PYTHON_VERSION_VALUES = ["PY2", "PY3"]
24-
25-
def _optional_int(value):
26-
return int(value) if value != None else None
27-
28-
def interpreter_version_info_struct_from_dict(info_dict):
29-
"""Create a struct of interpreter version info from a dict from an attribute.
30-
31-
Args:
32-
info_dict: (dict | None) of version info fields. See interpreter_version_info
33-
provider field docs.
34-
35-
Returns:
36-
struct of version info; see interpreter_version_info provider field docs.
37-
"""
38-
info_dict = dict(info_dict or {}) # Copy in case the original is frozen
39-
if info_dict:
40-
if not ("major" in info_dict and "minor" in info_dict):
41-
fail("interpreter_version_info must have at least two keys, 'major' and 'minor'")
42-
version_info_struct = struct(
43-
major = _optional_int(info_dict.pop("major", None)),
44-
minor = _optional_int(info_dict.pop("minor", None)),
45-
micro = _optional_int(info_dict.pop("micro", None)),
46-
releaselevel = str(info_dict.pop("releaselevel")) if "releaselevel" in info_dict else None,
47-
serial = _optional_int(info_dict.pop("serial", None)),
48-
)
49-
50-
if len(info_dict.keys()) > 0:
51-
fail("unexpected keys {} in interpreter_version_info".format(
52-
str(info_dict.keys()),
53-
))
54-
55-
return version_info_struct
56-
57-
def _PyRuntimeInfo_init(
58-
*,
59-
implementation_name = None,
60-
interpreter_path = None,
61-
interpreter = None,
62-
files = None,
63-
coverage_tool = None,
64-
coverage_files = None,
65-
pyc_tag = None,
66-
python_version,
67-
stub_shebang = None,
68-
bootstrap_template = None,
69-
interpreter_version_info = None,
70-
stage2_bootstrap_template = None,
71-
zip_main_template = None):
72-
if (interpreter_path and interpreter) or (not interpreter_path and not interpreter):
73-
fail("exactly one of interpreter or interpreter_path must be specified")
74-
75-
if interpreter_path and files != None:
76-
fail("cannot specify 'files' if 'interpreter_path' is given")
77-
78-
if (coverage_tool and not coverage_files) or (not coverage_tool and coverage_files):
79-
fail(
80-
"coverage_tool and coverage_files must both be set or neither must be set, " +
81-
"got coverage_tool={}, coverage_files={}".format(
82-
coverage_tool,
83-
coverage_files,
84-
),
85-
)
86-
87-
if python_version not in _PYTHON_VERSION_VALUES:
88-
fail("invalid python_version: '{}'; must be one of {}".format(
89-
python_version,
90-
_PYTHON_VERSION_VALUES,
91-
))
92-
93-
if files != None and type(files) != type(depset()):
94-
fail("invalid files: got value of type {}, want depset".format(type(files)))
95-
96-
if interpreter:
97-
if files == None:
98-
files = depset()
99-
else:
100-
files = None
101-
102-
if coverage_files == None:
103-
coverage_files = depset()
104-
105-
if not stub_shebang:
106-
stub_shebang = DEFAULT_STUB_SHEBANG
107-
108-
return {
109-
"bootstrap_template": bootstrap_template,
110-
"coverage_files": coverage_files,
111-
"coverage_tool": coverage_tool,
112-
"files": files,
113-
"implementation_name": implementation_name,
114-
"interpreter": interpreter,
115-
"interpreter_path": interpreter_path,
116-
"interpreter_version_info": interpreter_version_info_struct_from_dict(interpreter_version_info),
117-
"pyc_tag": pyc_tag,
118-
"python_version": python_version,
119-
"stage2_bootstrap_template": stage2_bootstrap_template,
120-
"stub_shebang": stub_shebang,
121-
"zip_main_template": zip_main_template,
122-
}
123-
124-
PyRuntimeInfo, _unused_raw_py_runtime_info_ctor = define_bazel_6_provider(
125-
doc = """Contains information about a Python runtime, as returned by the `py_runtime`
126-
rule.
127-
128-
A Python runtime describes either a *platform runtime* or an *in-build runtime*.
129-
A platform runtime accesses a system-installed interpreter at a known path,
130-
whereas an in-build runtime points to a `File` that acts as the interpreter. In
131-
both cases, an "interpreter" is really any executable binary or wrapper script
132-
that is capable of running a Python script passed on the command line, following
133-
the same conventions as the standard CPython interpreter.
134-
""",
135-
init = _PyRuntimeInfo_init,
136-
fields = {
137-
"bootstrap_template": """
138-
:type: File
139-
140-
A template of code responsible for the initial startup of a program.
141-
142-
This code is responsible for:
143-
144-
* Locating the target interpreter. Typically it is in runfiles, but not always.
145-
* Setting necessary environment variables, command line flags, or other
146-
configuration that can't be modified after the interpreter starts.
147-
* Invoking the appropriate entry point. This is usually a second-stage bootstrap
148-
that performs additional setup prior to running a program's actual entry point.
149-
150-
The {obj}`--bootstrap_impl` flag affects how this stage 1 bootstrap
151-
is expected to behave and the substutitions performed.
152-
153-
* `--bootstrap_impl=system_python` substitutions: `%is_zipfile%`, `%python_binary%`,
154-
`%target%`, `%workspace_name`, `%coverage_tool%`, `%import_all%`, `%imports%`,
155-
`%main%`, `%shebang%`
156-
* `--bootstrap_impl=script` substititions: `%is_zipfile%`, `%python_binary%`,
157-
`%target%`, `%workspace_name`, `%shebang%, `%stage2_bootstrap%`
158-
159-
Substitution definitions:
160-
161-
* `%shebang%`: The shebang to use with the bootstrap; the bootstrap template
162-
may choose to ignore this.
163-
* `%stage2_bootstrap%`: A runfiles-relative path to the stage 2 bootstrap.
164-
* `%python_binary%`: The path to the target Python interpreter. There are three
165-
types of paths:
166-
* An absolute path to a system interpreter (e.g. begins with `/`).
167-
* A runfiles-relative path to an interpreter (e.g. `somerepo/bin/python3`)
168-
* A program to search for on PATH, i.e. a word without spaces, e.g. `python3`.
169-
* `%workspace_name%`: The name of the workspace the target belongs to.
170-
* `%is_zipfile%`: The string `1` if this template is prepended to a zipfile to
171-
create a self-executable zip file. The string `0` otherwise.
172-
173-
For the other substitution definitions, see the {obj}`stage2_bootstrap_template`
174-
docs.
175-
176-
:::{versionchanged} 0.33.0
177-
The set of substitutions depends on {obj}`--bootstrap_impl`
178-
:::
179-
""",
180-
"coverage_files": """
181-
:type: depset[File] | None
182-
183-
The files required at runtime for using `coverage_tool`. Will be `None` if no
184-
`coverage_tool` was provided.
185-
""",
186-
"coverage_tool": """
187-
:type: File | None
188-
189-
If set, this field is a `File` representing tool used for collecting code
190-
coverage information from python tests. Otherwise, this is `None`.
191-
""",
192-
"files": """
193-
:type: depset[File] | None
194-
195-
If this is an in-build runtime, this field is a `depset` of `File`s that need to
196-
be added to the runfiles of an executable target that uses this runtime (in
197-
particular, files needed by `interpreter`). The value of `interpreter` need not
198-
be included in this field. If this is a platform runtime then this field is
199-
`None`.
200-
""",
201-
"implementation_name": """
202-
:type: str | None
203-
204-
The Python implementation name (`sys.implementation.name`)
205-
""",
206-
"interpreter": """
207-
:type: File | None
208-
209-
If this is an in-build runtime, this field is a `File` representing the
210-
interpreter. Otherwise, this is `None`. Note that an in-build runtime can use
211-
either a prebuilt, checked-in interpreter or an interpreter built from source.
212-
""",
213-
"interpreter_path": """
214-
:type: str | None
215-
216-
If this is a platform runtime, this field is the absolute filesystem path to the
217-
interpreter on the target platform. Otherwise, this is `None`.
218-
""",
219-
"interpreter_version_info": """
220-
:type: struct
221-
222-
Version information about the interpreter this runtime provides.
223-
It should match the format given by `sys.version_info`, however
224-
for simplicity, the micro, releaselevel, and serial values are
225-
optional.
226-
A struct with the following fields:
227-
* `major`: {type}`int`, the major version number
228-
* `minor`: {type}`int`, the minor version number
229-
* `micro`: {type}`int | None`, the micro version number
230-
* `releaselevel`: {type}`str | None`, the release level
231-
* `serial`: {type}`int | None`, the serial number of the release
232-
""",
233-
"pyc_tag": """
234-
:type: str | None
235-
236-
The tag portion of a pyc filename, e.g. the `cpython-39` infix
237-
of `foo.cpython-39.pyc`. See PEP 3147. If not specified, it will be computed
238-
from {obj}`implementation_name` and {obj}`interpreter_version_info`. If no
239-
pyc_tag is available, then only source-less pyc generation will function
240-
correctly.
241-
""",
242-
"python_version": """
243-
:type: str
244-
245-
Indicates whether this runtime uses Python major version 2 or 3. Valid values
246-
are (only) `"PY2"` and `"PY3"`.
247-
""",
248-
"stage2_bootstrap_template": """
249-
:type: File
250-
251-
A template of Python code that runs under the desired interpreter and is
252-
responsible for orchestrating calling the program's actual main code. This
253-
bootstrap is responsible for affecting the current runtime's state, such as
254-
import paths or enabling coverage, so that, when it runs the program's actual
255-
main code, it works properly under Bazel.
256-
257-
The following substitutions are made during template expansion:
258-
* `%main%`: A runfiles-relative path to the program's actual main file. This
259-
can be a `.py` or `.pyc` file, depending on precompile settings.
260-
* `%coverage_tool%`: Runfiles-relative path to the coverage library's entry point.
261-
If coverage is not enabled or available, an empty string.
262-
* `%import_all%`: The string `True` if all repositories in the runfiles should
263-
be added to sys.path. The string `False` otherwise.
264-
* `%imports%`: A colon-delimited string of runfiles-relative paths to add to
265-
sys.path.
266-
* `%target%`: The name of the target this is for.
267-
* `%workspace_name%`: The name of the workspace the target belongs to.
268-
269-
:::{versionadded} 0.33.0
270-
:::
271-
""",
272-
"stub_shebang": """
273-
:type: str
274-
275-
"Shebang" expression prepended to the bootstrapping Python stub
276-
script used when executing {obj}`py_binary` targets. Does not
277-
apply to Windows.
278-
""",
279-
"zip_main_template": """
280-
:type: File
281-
282-
A template of Python code that becomes a zip file's top-level `__main__.py`
283-
file. The top-level `__main__.py` file is used when the zip file is explicitly
284-
passed to a Python interpreter. See PEP 441 for more information about zipapp
285-
support. Note that py_binary-generated zip files are self-executing and
286-
skip calling `__main__.py`.
287-
288-
The following substitutions are made during template expansion:
289-
* `%stage2_bootstrap%`: A runfiles-relative string to the stage 2 bootstrap file.
290-
* `%python_binary%`: The path to the target Python interpreter. There are three
291-
types of paths:
292-
* An absolute path to a system interpreter (e.g. begins with `/`).
293-
* A runfiles-relative path to an interpreter (e.g. `somerepo/bin/python3`)
294-
* A program to search for on PATH, i.e. a word without spaces, e.g. `python3`.
295-
* `%workspace_name%`: The name of the workspace for the built target.
296-
297-
:::{versionadded} 0.33.0
298-
:::
299-
""",
300-
},
301-
)
302-
30319
def _PyCcLinkParamsInfo_init(cc_info):
30420
return {
30521
"cc_info": CcInfo(linking_context = cc_info.linking_context),

python/private/pypi/dependency_resolver/dependency_resolver.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,26 @@ def main(
170170

171171
if UPDATE:
172172
print("Updating " + requirements_file_relative)
173+
174+
# Make sure the output file for pip_compile exists. It won't if we are on Windows and --enable_runfiles is not set.
175+
if not os.path.exists(requirements_file_relative):
176+
os.makedirs(os.path.dirname(requirements_file_relative), exist_ok=True)
177+
shutil.copy(resolved_requirements_file, requirements_file_relative)
178+
173179
if "BUILD_WORKSPACE_DIRECTORY" in os.environ:
174180
workspace = os.environ["BUILD_WORKSPACE_DIRECTORY"]
175181
requirements_file_tree = os.path.join(workspace, requirements_file_relative)
182+
absolute_output_file = Path(requirements_file_relative).absolute()
176183
# In most cases, requirements_file will be a symlink to the real file in the source tree.
177184
# If symlinks are not enabled (e.g. on Windows), then requirements_file will be a copy,
178185
# and we should copy the updated requirements back to the source tree.
179-
if not os.path.samefile(resolved_requirements_file, requirements_file_tree):
186+
if not absolute_output_file.samefile(requirements_file_tree):
180187
atexit.register(
181188
lambda: shutil.copy(
182-
resolved_requirements_file, requirements_file_tree
189+
absolute_output_file, requirements_file_tree
183190
)
184191
)
185-
cli(argv)
192+
cli(argv, standalone_mode = False)
186193
requirements_file_relative_path = Path(requirements_file_relative)
187194
content = requirements_file_relative_path.read_text()
188195
content = content.replace(absolute_path_prefix, "")

tests/cc/current_py_cc_libs/BUILD.bazel

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,6 @@ current_py_cc_libs_test_suite(name = "current_py_cc_libs_tests")
2020
cc_test(
2121
name = "python_libs_linking_test",
2222
srcs = ["python_libs_linking_test.cc"],
23-
# Windows fails with linking errors, but its not clear why; someone
24-
# with more C + Windows experience will have to figure it out.
25-
# - rickeylev@
26-
target_compatible_with = select({
27-
"@platforms//os:linux": [],
28-
"@platforms//os:osx": [],
29-
"//conditions:default": ["@platforms//:incompatible"],
30-
}),
3123
deps = [
3224
"@rules_python//python/cc:current_py_cc_headers",
3325
"@rules_python//python/cc:current_py_cc_libs",
@@ -41,10 +33,9 @@ cc_test(
4133
# for libs/python3.lib.
4234
# buildifier: disable=native-cc
4335
cc_test(
44-
name = "python_libs_linking_windows_test",
36+
name = "python_abi3_libs_linking_windows_test",
4537
srcs = ["python_libs_linking_test.cc"],
4638
defines = ["Py_LIMITED_API=0x030A0000"],
47-
env = {"HELLO": "world"},
4839
target_compatible_with = ["@platforms//os:windows"],
4940
deps = [
5041
"@rules_python//python/cc:current_py_cc_headers",

0 commit comments

Comments
 (0)