blob: b70aa8bfd4fe53c0bd961142beb32aaab7963a71 [file] [log] [blame]
brandjon1af65cf2020-06-06 18:17:53 -07001// Copyright 2020 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.skyframe;
16
17import static com.google.common.truth.Truth.assertThat;
brandjon3f0917a2020-10-26 18:01:50 -070018import static com.google.common.truth.Truth.assertWithMessage;
brandjon1af65cf2020-06-06 18:17:53 -070019
20import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
21import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
22import com.google.devtools.build.lib.analysis.util.MockRule;
adonovanc0e86902020-11-19 15:50:29 -080023import com.google.devtools.build.lib.events.Event;
brandjon1af65cf2020-06-06 18:17:53 -070024import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
28import org.junit.Before;
29import org.junit.Test;
30import org.junit.runner.RunWith;
31import org.junit.runners.JUnit4;
32
33/**
34 * Tests for Starlark builtin injection.
35 *
36 * <p>Essentially these are integration tests between {@link StarlarkBuiltinsFunction} and {@link
37 * BzlLoadFunction}.
38 */
39@RunWith(JUnit4.class)
40public class BuiltinsInjectionTest extends BuildViewTestCase {
41
42 private static final MockRule OVERRIDABLE_RULE = () -> MockRule.define("overridable_rule");
43
44 @Override
brandjon232a6b82020-06-08 12:32:49 -070045 protected ConfiguredRuleClassProvider createRuleClassProvider() {
brandjon1af65cf2020-06-06 18:17:53 -070046 // Add a fake rule and top-level symbol to override.
47 ConfiguredRuleClassProvider.Builder builder =
48 new ConfiguredRuleClassProvider.Builder()
49 .addRuleDefinition(OVERRIDABLE_RULE)
50 .addStarlarkAccessibleTopLevels("overridable_symbol", "original_value");
51 TestRuleClassProvider.addStandardRules(builder);
52 return builder.build();
53 }
54
55 @Before
56 public void setUp() throws Exception {
brandjond5d86772020-10-26 16:21:22 -070057 setBuildLanguageOptions("--experimental_builtins_bzl_path=tools/builtins_staging");
brandjon1af65cf2020-06-06 18:17:53 -070058 }
59
60 /**
brandjon3f0917a2020-10-26 18:01:50 -070061 * Writes an exports.bzl file with the given content, in the builtins location.
brandjon1af65cf2020-06-06 18:17:53 -070062 *
63 * <p>See {@link StarlarkBuiltinsFunction#EXPORTS_ENTRYPOINT} for the significance of exports.bzl.
64 */
brandjon1af65cf2020-06-06 18:17:53 -070065 private void writeExportsBzl(String... lines) throws Exception {
brandjon1af65cf2020-06-06 18:17:53 -070066 scratch.file("tools/builtins_staging/exports.bzl", lines);
67 }
68
69 /**
brandjon3f0917a2020-10-26 18:01:50 -070070 * Writes a pkg/dummy.bzl file that prints a marker phrase when it finishes evaluating, and an
71 * accompanying BUILD file that loads it.
brandjon1af65cf2020-06-06 18:17:53 -070072 */
73 private void writePkgBzl(String... lines) throws Exception {
brandjon3f0917a2020-10-26 18:01:50 -070074 scratch.file("pkg/BUILD", "load(':dummy.bzl', 'dummy_symbol')");
brandjon1af65cf2020-06-06 18:17:53 -070075 scratch.file("pkg/dummy");
76 List<String> modifiedLines = new ArrayList<>(Arrays.asList(lines));
brandjon3f0917a2020-10-26 18:01:50 -070077 modifiedLines.add("dummy_symbol = None");
78 // The marker phrase might not be needed, but I don't entirely trust BuildViewTestCase.
79 modifiedLines.add("print('dummy.bzl evaluation completed')");
brandjon1af65cf2020-06-06 18:17:53 -070080 scratch.file("pkg/dummy.bzl", modifiedLines.toArray(lines));
81 }
82
brandjon3f0917a2020-10-26 18:01:50 -070083 /** Builds {@code //pkg} and asserts success, including that the marker print() event occurs. */
84 private void buildAndAssertSuccess() throws Exception {
85 Object result = getConfiguredTarget("//pkg:BUILD");
86 assertContainsEvent("dummy.bzl evaluation completed");
87 // On error, getConfiguredTarget sometimes returns null without emitting events; see b/26382502.
88 // Though in that case it seems unlikely the above assertion would've passed.
brandjon1af65cf2020-06-06 18:17:53 -070089 assertThat(result).isNotNull();
90 }
91
brandjon3f0917a2020-10-26 18:01:50 -070092 /** Builds {@code //pkg:dummy} and asserts on the absence of the marker print() event. */
93 private void buildAndAssertFailure() throws Exception {
94 reporter.removeHandler(failFastHandler);
95 Object result = getConfiguredTarget("//pkg:BUILD");
96 assertDoesNotContainEvent("dummy.bzl evaluation completed");
97 assertWithMessage("Loading of //pkg succeeded unexpectedly").that(result).isNull();
brandjon1af65cf2020-06-06 18:17:53 -070098 }
99
100 @Test
brandjon3f0917a2020-10-26 18:01:50 -0700101 public void basicFunctionality() throws Exception {
brandjon1af65cf2020-06-06 18:17:53 -0700102 writeExportsBzl(
103 "exported_toplevels = {'overridable_symbol': 'new_value'}",
104 "exported_rules = {'overridable_rule': 'new_rule'}",
105 "exported_to_java = {}");
106 writePkgBzl(
107 "print('overridable_symbol :: ' + str(overridable_symbol))",
108 "print('overridable_rule :: ' + str(native.overridable_rule))");
109
brandjon3f0917a2020-10-26 18:01:50 -0700110 buildAndAssertSuccess();
brandjon1af65cf2020-06-06 18:17:53 -0700111 assertContainsEvent("overridable_symbol :: new_value");
112 assertContainsEvent("overridable_rule :: new_rule");
113 }
114
brandjon844fef52020-06-08 10:30:36 -0700115 @Test
brandjon3f0917a2020-10-26 18:01:50 -0700116 public void builtinsCanLoadFromBuiltins() throws Exception {
117 // Define a few files that we can load with different kinds of label syntax. In each case,
118 // access the `_internal` symbol to demonstrate that we're being loaded as a builtins bzl.
119 scratch.file(
120 "tools/builtins_staging/absolute.bzl", //
121 "_internal",
122 "a = 'A'");
123 scratch.file(
124 "tools/builtins_staging/repo_relative.bzl", //
125 "_internal",
126 "b = 'B'");
127 scratch.file(
128 "tools/builtins_staging/subdir/pkg_relative1.bzl", //
129 // Do a relative load within a load, to show it's relative to the (pseudo) package, i.e. the
130 // root, and not relative to the file. That is, we specify 'subdir/pkg_relative2.bzl', not
131 // just 'pkg_relative2.bzl'.
132 "load('subdir/pkg_relative2.bzl', 'c2')",
133 "_internal",
134 "c = c2");
135 scratch.file(
136 "tools/builtins_staging/subdir/pkg_relative2.bzl", //
137 "_internal",
138 "c2 = 'C'");
139
140 // Also create a file in the main repo whose package path coincides with a file in the builtins
141 // pseudo-repo, to show that we get the right one.
142 scratch.file("BUILD");
143 scratch.file("repo_relative.bzl");
144
145 writeExportsBzl(
146 "load('@_builtins//:absolute.bzl', 'a')",
147 "load('//:repo_relative.bzl', 'b')", // default repo is @_builtins, not main repo
148 "load('subdir/pkg_relative1.bzl', 'c')", // relative to (pseudo) package, which is repo root
149 "exported_toplevels = {'overridable_symbol': a + b + c}",
150 "exported_rules = {}",
151 "exported_to_java = {}");
152 writePkgBzl("print('overridable_symbol :: ' + str(overridable_symbol))");
153
154 buildAndAssertSuccess();
155 assertContainsEvent("overridable_symbol :: ABC");
156 }
157
158 @Test
159 public void otherBzlsCannotLoadFromBuiltins() throws Exception {
160 writeExportsBzl(
161 "exported_toplevels = {}", //
162 "exported_rules = {}",
163 "exported_to_java = {}");
164 writePkgBzl("load('@_builtins//:exports.bzl', 'exported_toplevels')");
165
166 buildAndAssertFailure();
167 assertContainsEvent("The repository '@_builtins' could not be resolved");
168 }
169
170 @Test
171 public void builtinsCannotLoadFromNonBuiltins() throws Exception {
172 scratch.file("BUILD");
173 scratch.file(
174 "a_user_written.bzl", //
175 "toplevels = {'overridable_symbol': 'new_value'}");
176 writeExportsBzl(
177 // Use @// syntax to specify the main repo. Otherwise, the load would be relative to the
178 // @_builtins pseudo-repo.
179 "load('@//:a_user_written.bzl', 'toplevels')",
180 "exported_toplevels = toplevels",
181 "exported_rules = {}",
182 "exported_to_java = {}");
183 writePkgBzl();
184
185 buildAndAssertFailure();
186 assertContainsEvent(
187 "in load statement: .bzl files in @_builtins cannot load from outside of @_builtins");
188 }
189
190 @Test
191 public void builtinsCannotLoadWithMisplacedColon() throws Exception {
192 scratch.file(
193 "tools/builtins_staging/subdir/helper.bzl", //
194 "toplevels = {'overridable_symbol': 'new_value'}");
195 writeExportsBzl(
196 "load('//subdir:helper.bzl', 'toplevels')", // Should've been loaded as //:subdir/helper.bzl
197 "exported_toplevels = toplevels",
198 "exported_rules = {}",
199 "exported_to_java = {}");
200 writePkgBzl();
201
202 buildAndAssertFailure();
203 assertContainsEvent("@_builtins cannot have subpackages");
204 }
205
206 @Test
207 public void errorInEvaluatingBuiltinsDependency() throws Exception {
brandjon5a33c632020-10-23 22:09:26 -0700208 // Test case with a Starlark error in the @_builtins pseudo-repo itself.
brandjon844fef52020-06-08 10:30:36 -0700209 scratch.file(
210 "tools/builtins_staging/helper.bzl", //
211 "toplevels = {'overridable_symbol': 1//0} # <-- dynamic error");
212 writeExportsBzl(
brandjond5d86772020-10-26 16:21:22 -0700213 "load('@_builtins//:helper.bzl', 'toplevels')",
brandjon844fef52020-06-08 10:30:36 -0700214 "exported_toplevels = toplevels",
215 "exported_rules = {}",
216 "exported_to_java = {}");
brandjon3f0917a2020-10-26 18:01:50 -0700217 writePkgBzl();
brandjon844fef52020-06-08 10:30:36 -0700218
brandjon3f0917a2020-10-26 18:01:50 -0700219 buildAndAssertFailure();
brandjon844fef52020-06-08 10:30:36 -0700220 assertContainsEvent(
adonovan3391e172020-08-05 14:21:14 -0700221 "File \"/workspace/tools/builtins_staging/helper.bzl\", line 1, column 37, in <toplevel>");
222 assertContainsEvent("Error: integer division by zero");
adonovanc0e86902020-11-19 15:50:29 -0800223
224 // We assert only the parts of the message before and after the module name, since the module
225 // identified by the message depends on whether or not the test environment has a prelude file.
226 Event ev = assertContainsEvent("Internal error while loading Starlark builtins");
227 assertThat(ev.getMessage())
228 .contains(
229 "Failed to load builtins sources: "
230 + "in /workspace/tools/builtins_staging/exports.bzl: "
231 + "Extension file 'helper.bzl' (internal) has errors");
brandjon844fef52020-06-08 10:30:36 -0700232 }
233
234 @Test
brandjon9db98c42020-08-24 08:43:55 -0700235 public void errorInProcessingExports() throws Exception {
brandjon844fef52020-06-08 10:30:36 -0700236 // Test case with an error in the symbols exported by exports.bzl, but no actual Starlark errors
brandjon5a33c632020-10-23 22:09:26 -0700237 // in the builtins files themselves.
brandjon844fef52020-06-08 10:30:36 -0700238 writeExportsBzl(
239 "exported_toplevels = None", // should be dict
240 "exported_rules = {}",
241 "exported_to_java = {}");
brandjon3f0917a2020-10-26 18:01:50 -0700242 writePkgBzl();
brandjon844fef52020-06-08 10:30:36 -0700243
brandjon3f0917a2020-10-26 18:01:50 -0700244 buildAndAssertFailure();
adonovanc0e86902020-11-19 15:50:29 -0800245
246 // We assert only the parts of the message before and after the module name, since the module
247 // identified by the message depends on whether or not the test environment has a prelude file.
248 Event ev = assertContainsEvent("Internal error while loading Starlark builtins");
249 assertThat(ev.getMessage())
250 .contains(
251 "Failed to apply declared builtins: "
252 + "got NoneType for 'exported_toplevels dict', want dict");
brandjon844fef52020-06-08 10:30:36 -0700253 }
254
brandjon1af65cf2020-06-06 18:17:53 -0700255 // TODO(#11437): Remove once disabling is not allowed.
256 @Test
257 public void injectionDisabledByFlag() throws Exception {
258 writeExportsBzl(
259 "exported_toplevels = {'overridable_symbol': 'new_value'}",
260 "exported_rules = {'overridable_rule': 'new_rule'}",
261 "exported_to_java = {}");
262 writePkgBzl(
263 "print('overridable_symbol :: ' + str(overridable_symbol))",
264 "print('overridable_rule :: ' + str(native.overridable_rule))");
adonovan240bdea2020-09-03 15:24:12 -0700265 setBuildLanguageOptions("--experimental_builtins_bzl_path=");
brandjon1af65cf2020-06-06 18:17:53 -0700266
brandjon3f0917a2020-10-26 18:01:50 -0700267 buildAndAssertSuccess();
brandjon1af65cf2020-06-06 18:17:53 -0700268 assertContainsEvent("overridable_symbol :: original_value");
269 assertContainsEvent("overridable_rule :: <built-in rule overridable_rule>");
270 }
271
272 // TODO(#11437): Remove once disabling is not allowed.
273 @Test
274 public void exportsBzlMayBeInErrorWhenInjectionIsDisabled() throws Exception {
275 writeExportsBzl( //
276 "PARSE ERROR");
277 writePkgBzl(
278 "print('overridable_symbol :: ' + str(overridable_symbol))",
279 "print('overridable_rule :: ' + str(native.overridable_rule))");
adonovan240bdea2020-09-03 15:24:12 -0700280 setBuildLanguageOptions("--experimental_builtins_bzl_path=");
brandjon1af65cf2020-06-06 18:17:53 -0700281
brandjon3f0917a2020-10-26 18:01:50 -0700282 buildAndAssertSuccess();
brandjon1af65cf2020-06-06 18:17:53 -0700283 assertContainsEvent("overridable_symbol :: original_value");
284 assertContainsEvent("overridable_rule :: <built-in rule overridable_rule>");
285 }
286
brandjon9db98c42020-08-24 08:43:55 -0700287 // TODO(#11954): Once WORKSPACE- and BUILD-loaded bzls use the exact same environments, we'll want
288 // to apply injection to both. This is for uniformity, not because we actually care about builtins
289 // injection for WORKSPACE bzls. In the meantime, assert the status quo: WORKSPACE bzls do not use
290 // injection.
brandjon1af65cf2020-06-06 18:17:53 -0700291 @Test
brandjon9db98c42020-08-24 08:43:55 -0700292 public void workspaceBzlDoesNotUseInjection() throws Exception {
brandjon1af65cf2020-06-06 18:17:53 -0700293 writeExportsBzl(
294 "exported_toplevels = {'overridable_symbol': 'new_value'}",
295 "exported_rules = {'overridable_rule': 'new_rule'}",
296 "exported_to_java = {}");
297 writePkgBzl();
298 scratch.appendFile(
299 "WORKSPACE", //
300 "load(':foo.bzl', 'dummy_symbol')",
301 "print(dummy_symbol)");
302 scratch.file("BUILD");
303 scratch.file(
304 "foo.bzl",
305 "dummy_symbol = None",
306 "print('overridable_symbol :: ' + str(overridable_symbol))");
307
brandjon3f0917a2020-10-26 18:01:50 -0700308 buildAndAssertSuccess();
brandjon1af65cf2020-06-06 18:17:53 -0700309 // Builtins for WORKSPACE bzls are populated essentially the same as for BUILD bzls, except that
310 // injection doesn't apply.
311 assertContainsEvent("overridable_symbol :: original_value");
312 // We don't assert that the rule isn't injected because the workspace native object doesn't
313 // contain our original mock rule. We can test this for WORKSPACE files at the top-level though.
314 }
315
brandjon1af65cf2020-06-06 18:17:53 -0700316 // TODO(#11437): Add tests of the _internal symbol's usage within builtins bzls.
317
318 // TODO(#11437): Add test cases for BUILD file injection, and WORKSPACE file non-injection, when
319 // we add injection support to PackageFunction.
brandjon28632522020-06-08 13:14:07 -0700320
321 /**
322 * Tests for injection, under inlining of {@link BzlLoadFunction}.
323 *
324 * <p>See {@link BzlLoadFunction#computeInline} for an explanation of inlining.
325 */
326 @RunWith(JUnit4.class)
327 public static class BuiltinsInjectionTestWithInlining extends BuiltinsInjectionTest {
328
329 @Override
330 protected boolean usesInliningBzlLoadFunction() {
331 return true;
332 }
333 }
brandjon1af65cf2020-06-06 18:17:53 -0700334}