|
16 | 16 |
|
17 | 17 | import static net.starlark.java.eval.Starlark.NONE;
|
18 | 18 |
|
19 |
| -import com.google.common.base.Preconditions; |
| 19 | +import com.google.common.annotations.VisibleForTesting; |
20 | 20 | import com.google.devtools.build.lib.actions.Artifact;
|
| 21 | +import com.google.devtools.build.lib.cmdline.Label; |
21 | 22 | import com.google.devtools.build.lib.collect.nestedset.Depset;
|
22 | 23 | import com.google.devtools.build.lib.collect.nestedset.NestedSet;
|
23 |
| -import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
24 |
| -import com.google.devtools.build.lib.collect.nestedset.Order; |
25 |
| -import com.google.devtools.build.lib.packages.BuiltinProvider; |
26 | 24 | import com.google.devtools.build.lib.packages.Info;
|
| 25 | +import com.google.devtools.build.lib.packages.StarlarkInfo; |
| 26 | +import com.google.devtools.build.lib.packages.StarlarkProviderWrapper; |
27 | 27 | import com.google.devtools.build.lib.starlarkbuildapi.python.PyRuntimeInfoApi;
|
28 |
| -import com.google.devtools.build.lib.vfs.PathFragment; |
29 |
| -import java.util.Objects; |
30 | 28 | import javax.annotation.Nullable;
|
31 | 29 | import net.starlark.java.eval.EvalException;
|
32 | 30 | import net.starlark.java.eval.Starlark;
|
33 |
| -import net.starlark.java.eval.StarlarkThread; |
34 |
| -import net.starlark.java.syntax.Location; |
35 | 31 |
|
36 | 32 | /**
|
37 | 33 | * Instance of the provider type that describes Python runtimes.
|
|
43 | 39 | * invariants mirror the user-visible API on {@link PyRuntimeInfoApi} except that {@code None} is
|
44 | 40 | * replaced by null.
|
45 | 41 | */
|
46 |
| -public final class PyRuntimeInfo implements Info, PyRuntimeInfoApi<Artifact> { |
47 |
| - |
48 |
| - /** The Starlark-accessible top-level builtin name for this provider type. */ |
49 |
| - public static final String STARLARK_NAME = "PyRuntimeInfo"; |
50 |
| - |
| 42 | +@VisibleForTesting |
| 43 | +public final class PyRuntimeInfo { |
51 | 44 | /** The singular {@code PyRuntimeInfo} provider type object. */
|
52 | 45 | public static final PyRuntimeInfoProvider PROVIDER = new PyRuntimeInfoProvider();
|
53 | 46 |
|
54 |
| - private final Location location; |
55 |
| - @Nullable private final PathFragment interpreterPath; |
56 |
| - @Nullable private final Artifact interpreter; |
57 |
| - // Validated on initialization to contain Artifact |
58 |
| - @Nullable private final Depset files; |
59 |
| - @Nullable private final Artifact coverageTool; |
60 |
| - @Nullable private final Depset coverageFiles; |
61 |
| - /** Invariant: either PY2 or PY3. */ |
62 |
| - private final PythonVersion pythonVersion; |
63 |
| - |
64 |
| - private final String stubShebang; |
65 |
| - @Nullable private final Artifact bootstrapTemplate; |
66 |
| - |
67 |
| - private PyRuntimeInfo( |
68 |
| - @Nullable Location location, |
69 |
| - @Nullable PathFragment interpreterPath, |
70 |
| - @Nullable Artifact interpreter, |
71 |
| - @Nullable Depset files, |
72 |
| - @Nullable Artifact coverageTool, |
73 |
| - @Nullable Depset coverageFiles, |
74 |
| - PythonVersion pythonVersion, |
75 |
| - @Nullable String stubShebang, |
76 |
| - @Nullable Artifact bootstrapTemplate) { |
77 |
| - Preconditions.checkArgument((interpreterPath == null) != (interpreter == null)); |
78 |
| - Preconditions.checkArgument((interpreter == null) == (files == null)); |
79 |
| - Preconditions.checkArgument((coverageTool == null) == (coverageFiles == null)); |
80 |
| - Preconditions.checkArgument(pythonVersion.isTargetValue()); |
81 |
| - this.location = location != null ? location : Location.BUILTIN; |
82 |
| - this.files = files; |
83 |
| - this.interpreterPath = interpreterPath; |
84 |
| - this.interpreter = interpreter; |
85 |
| - this.coverageTool = coverageTool; |
86 |
| - this.coverageFiles = coverageFiles; |
87 |
| - this.pythonVersion = pythonVersion; |
88 |
| - if (stubShebang != null && !stubShebang.isEmpty()) { |
89 |
| - this.stubShebang = stubShebang; |
90 |
| - } else { |
91 |
| - this.stubShebang = PyRuntimeInfoApi.DEFAULT_STUB_SHEBANG; |
92 |
| - } |
93 |
| - this.bootstrapTemplate = bootstrapTemplate; |
94 |
| - } |
95 |
| - |
96 |
| - @Override |
97 |
| - public PyRuntimeInfoProvider getProvider() { |
98 |
| - return PROVIDER; |
99 |
| - } |
100 |
| - |
101 |
| - @Override |
102 |
| - public Location getCreationLocation() { |
103 |
| - return location; |
104 |
| - } |
105 |
| - |
106 |
| - /** Constructs an instance from native rule logic (built-in location) for an in-build runtime. */ |
107 |
| - public static PyRuntimeInfo createForInBuildRuntime( |
108 |
| - Artifact interpreter, |
109 |
| - NestedSet<Artifact> files, |
110 |
| - @Nullable Artifact coverageTool, |
111 |
| - @Nullable NestedSet<Artifact> coverageFiles, |
112 |
| - PythonVersion pythonVersion, |
113 |
| - @Nullable String stubShebang, |
114 |
| - @Nullable Artifact bootstrapTemplate) { |
115 |
| - return new PyRuntimeInfo( |
116 |
| - /* location= */ null, |
117 |
| - /* interpreterPath= */ null, |
118 |
| - interpreter, |
119 |
| - Depset.of(Artifact.class, files), |
120 |
| - coverageTool, |
121 |
| - coverageFiles == null ? null : Depset.of(Artifact.class, coverageFiles), |
122 |
| - pythonVersion, |
123 |
| - stubShebang, |
124 |
| - bootstrapTemplate); |
125 |
| - } |
126 |
| - |
127 |
| - /** Constructs an instance from native rule logic (built-in location) for a platform runtime. */ |
128 |
| - public static PyRuntimeInfo createForPlatformRuntime( |
129 |
| - PathFragment interpreterPath, |
130 |
| - @Nullable Artifact coverageTool, |
131 |
| - @Nullable NestedSet<Artifact> coverageFiles, |
132 |
| - PythonVersion pythonVersion, |
133 |
| - @Nullable String stubShebang, |
134 |
| - @Nullable Artifact bootstrapTemplate) { |
135 |
| - return new PyRuntimeInfo( |
136 |
| - /* location= */ null, |
137 |
| - interpreterPath, |
138 |
| - /* interpreter= */ null, |
139 |
| - /* files= */ null, |
140 |
| - coverageTool, |
141 |
| - coverageFiles == null ? null : Depset.of(Artifact.class, coverageFiles), |
142 |
| - pythonVersion, |
143 |
| - stubShebang, |
144 |
| - bootstrapTemplate); |
145 |
| - } |
146 |
| - |
147 |
| - @Override |
148 |
| - public boolean equals(Object other) { |
149 |
| - // PyRuntimeInfo implements value equality, but note that it contains identity-equality fields |
150 |
| - // (depsets), so you generally shouldn't rely on equality comparisons. |
151 |
| - if (!(other instanceof PyRuntimeInfo)) { |
152 |
| - return false; |
153 |
| - } |
154 |
| - PyRuntimeInfo otherInfo = (PyRuntimeInfo) other; |
155 |
| - return (this.interpreterPath.equals(otherInfo.interpreterPath) |
156 |
| - && this.interpreter.equals(otherInfo.interpreter) |
157 |
| - && this.files.equals(otherInfo.files) |
158 |
| - && this.coverageTool.equals(otherInfo.coverageTool) |
159 |
| - && this.coverageFiles.equals(otherInfo.coverageFiles) |
160 |
| - && this.stubShebang.equals(otherInfo.stubShebang)); |
161 |
| - } |
| 47 | + // Only present so PyRuntimeRule can reference it as a default. |
| 48 | + static final String DEFAULT_STUB_SHEBANG = "#!/usr/bin/env python3"; |
162 | 49 |
|
163 |
| - @Override |
164 |
| - public int hashCode() { |
165 |
| - return Objects.hash( |
166 |
| - PyRuntimeInfo.class, |
167 |
| - interpreterPath, |
168 |
| - interpreter, |
169 |
| - coverageTool, |
170 |
| - coverageFiles, |
171 |
| - files, |
172 |
| - stubShebang); |
173 |
| - } |
| 50 | + // Only present so PyRuntimeRule can reference it as a default. |
| 51 | + // Must call getToolsLabel() when using this. |
| 52 | + static final String DEFAULT_BOOTSTRAP_TEMPLATE = "//tools/python:python_bootstrap_template.txt"; |
174 | 53 |
|
175 |
| - /** |
176 |
| - * Returns true if this is an in-build runtime as opposed to a platform runtime -- that is, if |
177 |
| - * this refers to a target within the build as opposed to a path to a system interpreter. |
178 |
| - * |
179 |
| - * <p>{@link #getInterpreter} and {@link #getFiles} are non-null if and only if this is an |
180 |
| - * in-build runtime, whereas {@link #getInterpreterPath} is non-null if and only if this is a |
181 |
| - * platform runtime. |
182 |
| - * |
183 |
| - * <p>Note: It is still possible for an in-build runtime to reference the system interpreter, as |
184 |
| - * in the case where it is a wrapper script. |
185 |
| - */ |
186 |
| - public boolean isInBuild() { |
187 |
| - return getInterpreter() != null; |
188 |
| - } |
| 54 | + private final StarlarkInfo info; |
189 | 55 |
|
190 |
| - @Nullable |
191 |
| - public PathFragment getInterpreterPath() { |
192 |
| - return interpreterPath; |
| 56 | + private PyRuntimeInfo(StarlarkInfo info) { |
| 57 | + this.info = info; |
193 | 58 | }
|
194 | 59 |
|
195 |
| - @Override |
196 | 60 | @Nullable
|
197 | 61 | public String getInterpreterPathString() {
|
198 |
| - return interpreterPath == null ? null : interpreterPath.getPathString(); |
| 62 | + Object value = info.getValue("interpreter_path"); |
| 63 | + return value == Starlark.NONE ? null : (String) value; |
199 | 64 | }
|
200 | 65 |
|
201 |
| - @Override |
202 | 66 | @Nullable
|
203 | 67 | public Artifact getInterpreter() {
|
204 |
| - return interpreter; |
| 68 | + Object value = info.getValue("interpreter"); |
| 69 | + return value == Starlark.NONE ? null : (Artifact) value; |
205 | 70 | }
|
206 | 71 |
|
207 |
| - @Override |
208 |
| - public String getStubShebang() { |
209 |
| - return stubShebang; |
| 72 | + public String getStubShebang() throws EvalException { |
| 73 | + return info.getValue("stub_shebang", String.class); |
210 | 74 | }
|
211 | 75 |
|
212 |
| - @Override |
213 | 76 | @Nullable
|
214 | 77 | public Artifact getBootstrapTemplate() {
|
215 |
| - return bootstrapTemplate; |
| 78 | + Object value = info.getValue("bootstrap_template"); |
| 79 | + return value == Starlark.NONE ? null : (Artifact) value; |
216 | 80 | }
|
217 | 81 |
|
218 | 82 | @Nullable
|
219 |
| - public NestedSet<Artifact> getFiles() { |
220 |
| - try { |
221 |
| - return files == null ? null : files.getSet(Artifact.class); |
222 |
| - } catch (Depset.TypeException ex) { |
223 |
| - throw new IllegalStateException("for files, " + ex.getMessage()); |
| 83 | + public NestedSet<Artifact> getFiles() throws EvalException { |
| 84 | + Object value = info.getValue("files"); |
| 85 | + if (value == NONE) { |
| 86 | + return null; |
| 87 | + } else { |
| 88 | + return Depset.cast(value, Artifact.class, "files"); |
224 | 89 | }
|
225 | 90 | }
|
226 | 91 |
|
227 |
| - @Override |
228 | 92 | @Nullable
|
229 |
| - public Depset getFilesForStarlark() { |
230 |
| - return files; |
| 93 | + public Artifact getCoverageTool() throws EvalException { |
| 94 | + return info.getValue("coverage_tool", Artifact.class); |
231 | 95 | }
|
232 | 96 |
|
233 |
| - @Override |
234 | 97 | @Nullable
|
235 |
| - public Artifact getCoverageTool() { |
236 |
| - return coverageTool; |
| 98 | + public NestedSet<Artifact> getCoverageToolFiles() throws EvalException { |
| 99 | + Object value = info.getValue("coverage_files"); |
| 100 | + return Depset.cast(value, Artifact.class, "coverage_files"); |
237 | 101 | }
|
238 | 102 |
|
239 |
| - @Nullable |
240 |
| - public NestedSet<Artifact> getCoverageToolFiles() { |
241 |
| - try { |
242 |
| - return coverageFiles == null ? null : coverageFiles.getSet(Artifact.class); |
243 |
| - } catch (Depset.TypeException ex) { |
244 |
| - throw new IllegalStateException("for coverage_runfiles, " + ex.getMessage()); |
245 |
| - } |
246 |
| - } |
247 |
| - |
248 |
| - @Override |
249 |
| - @Nullable |
250 |
| - public Depset getCoverageToolFilesForStarlark() { |
251 |
| - return coverageFiles; |
252 |
| - } |
253 |
| - |
254 |
| - public PythonVersion getPythonVersion() { |
255 |
| - return pythonVersion; |
256 |
| - } |
257 |
| - |
258 |
| - @Override |
259 |
| - public String getPythonVersionForStarlark() { |
260 |
| - return pythonVersion.name(); |
| 103 | + public PythonVersion getPythonVersion() throws EvalException { |
| 104 | + return PythonVersion.parseTargetValue(info.getValue("python_version", String.class)); |
261 | 105 | }
|
262 | 106 |
|
263 | 107 | /** The class of the {@code PyRuntimeInfo} provider type. */
|
264 |
| - public static class PyRuntimeInfoProvider extends BuiltinProvider<PyRuntimeInfo> |
265 |
| - implements PyRuntimeInfoApi.PyRuntimeInfoProviderApi { |
| 108 | + public static class PyRuntimeInfoProvider extends StarlarkProviderWrapper<PyRuntimeInfo> { |
266 | 109 |
|
267 | 110 | private PyRuntimeInfoProvider() {
|
268 |
| - super(STARLARK_NAME, PyRuntimeInfo.class); |
| 111 | + super( |
| 112 | + Label.parseCanonicalUnchecked("@_builtins//:common/python/providers.bzl"), |
| 113 | + "PyRuntimeInfo"); |
269 | 114 | }
|
270 | 115 |
|
271 | 116 | @Override
|
272 |
| - public PyRuntimeInfo constructor( |
273 |
| - Object interpreterPathUncast, |
274 |
| - Object interpreterUncast, |
275 |
| - Object filesUncast, |
276 |
| - Object coverageToolUncast, |
277 |
| - Object coverageFilesUncast, |
278 |
| - String pythonVersion, |
279 |
| - String stubShebang, |
280 |
| - Object bootstrapTemplateUncast, |
281 |
| - StarlarkThread thread) |
282 |
| - throws EvalException { |
283 |
| - String interpreterPath = |
284 |
| - interpreterPathUncast == NONE ? null : (String) interpreterPathUncast; |
285 |
| - Artifact interpreter = interpreterUncast == NONE ? null : (Artifact) interpreterUncast; |
286 |
| - Artifact bootstrapTemplate = null; |
287 |
| - if (bootstrapTemplateUncast != NONE) { |
288 |
| - bootstrapTemplate = (Artifact) bootstrapTemplateUncast; |
289 |
| - } |
290 |
| - Depset filesDepset = null; |
291 |
| - if (filesUncast != NONE) { |
292 |
| - // Validate type of filesDepset. |
293 |
| - Depset.cast(filesUncast, Artifact.class, "files"); |
294 |
| - filesDepset = (Depset) filesUncast; |
295 |
| - } |
296 |
| - Artifact coverageTool = coverageToolUncast == NONE ? null : (Artifact) coverageToolUncast; |
297 |
| - Depset coverageDepset = null; |
298 |
| - if (coverageFilesUncast != NONE) { |
299 |
| - // Validate type of filesDepset. |
300 |
| - Depset.cast(coverageFilesUncast, Artifact.class, "coverage_files"); |
301 |
| - coverageDepset = (Depset) coverageFilesUncast; |
302 |
| - } |
303 |
| - |
304 |
| - if ((interpreter == null) == (interpreterPath == null)) { |
305 |
| - throw Starlark.errorf( |
306 |
| - "exactly one of the 'interpreter' or 'interpreter_path' arguments must be specified"); |
307 |
| - } |
308 |
| - boolean isInBuildRuntime = interpreter != null; |
309 |
| - if (!isInBuildRuntime && filesDepset != null) { |
310 |
| - throw Starlark.errorf("cannot specify 'files' if 'interpreter_path' is given"); |
311 |
| - } |
312 |
| - |
313 |
| - PythonVersion parsedPythonVersion; |
314 |
| - try { |
315 |
| - parsedPythonVersion = PythonVersion.parseTargetValue(pythonVersion); |
316 |
| - } catch (IllegalArgumentException ex) { |
317 |
| - throw Starlark.errorf("illegal value for 'python_version': %s", ex.getMessage()); |
318 |
| - } |
319 |
| - |
320 |
| - Location loc = thread.getCallerLocation(); |
321 |
| - if (isInBuildRuntime) { |
322 |
| - if (filesDepset == null) { |
323 |
| - filesDepset = Depset.of(Artifact.class, NestedSetBuilder.emptySet(Order.STABLE_ORDER)); |
324 |
| - } |
325 |
| - return new PyRuntimeInfo( |
326 |
| - loc, |
327 |
| - /* interpreterPath= */ null, |
328 |
| - interpreter, |
329 |
| - filesDepset, |
330 |
| - coverageTool, |
331 |
| - coverageDepset, |
332 |
| - parsedPythonVersion, |
333 |
| - stubShebang, |
334 |
| - bootstrapTemplate); |
335 |
| - } else { |
336 |
| - return new PyRuntimeInfo( |
337 |
| - loc, |
338 |
| - PathFragment.create(interpreterPath), |
339 |
| - /* interpreter= */ null, |
340 |
| - /* files= */ null, |
341 |
| - coverageTool, |
342 |
| - coverageDepset, |
343 |
| - parsedPythonVersion, |
344 |
| - stubShebang, |
345 |
| - bootstrapTemplate); |
346 |
| - } |
| 117 | + public PyRuntimeInfo wrap(Info value) { |
| 118 | + return new PyRuntimeInfo((StarlarkInfo) value); |
347 | 119 | }
|
348 | 120 | }
|
349 | 121 | }
|
0 commit comments