|
16 | 16 | load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") |
17 | 17 | load(":util.bzl", "define_bazel_6_provider") |
18 | 18 |
|
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 | | - |
303 | 19 | def _PyCcLinkParamsInfo_init(cc_info): |
304 | 20 | return { |
305 | 21 | "cc_info": CcInfo(linking_context = cc_info.linking_context), |
|
0 commit comments