blob: 774452d74acc510dd47ca9bc0d8b93f5e756549a [file] [log] [blame]
juliexxiacb9b2af2018-11-30 17:21:22 -08001// Copyright 2018 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
laurentlbc67c5702020-07-30 09:25:45 -070015package com.google.devtools.build.lib.starlark;
juliexxiacb9b2af2018-11-30 17:21:22 -080016
17import static com.google.common.truth.Truth.assertThat;
michajlo660d17f2020-03-27 09:01:57 -070018import static org.junit.Assert.assertThrows;
juliexxiacb9b2af2018-11-30 17:21:22 -080019
Julie Xia69d4ace2020-05-29 11:25:25 -070020import com.google.common.collect.ImmutableList;
gregcebf31feb2020-12-08 08:23:18 -080021import com.google.devtools.build.lib.events.Event;
22import com.google.devtools.build.lib.events.ExtendedEventHandler;
23import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
24import com.google.devtools.build.lib.pkgcache.TargetParsingCompleteEvent;
laurentlbc67c5702020-07-30 09:25:45 -070025import com.google.devtools.build.lib.starlark.util.StarlarkOptionsTestCase;
juliexxiacb9b2af2018-11-30 17:21:22 -080026import com.google.devtools.common.options.OptionsParsingException;
27import com.google.devtools.common.options.OptionsParsingResult;
gregcebf31feb2020-12-08 08:23:18 -080028import java.util.ArrayList;
29import java.util.List;
30import java.util.stream.Collectors;
adonovan3ed7ed52020-09-30 12:03:28 -070031import net.starlark.java.eval.StarlarkInt;
gregcebf31feb2020-12-08 08:23:18 -080032import org.junit.Before;
juliexxiacb9b2af2018-11-30 17:21:22 -080033import org.junit.Test;
34import org.junit.runner.RunWith;
35import org.junit.runners.JUnit4;
36
37/** Unit test for the {@code StarlarkOptionsParser}. */
38@RunWith(JUnit4.class)
jcater741926e2019-12-02 06:50:08 -080039public class StarlarkOptionsParsingTest extends StarlarkOptionsTestCase {
juliexxiacb9b2af2018-11-30 17:21:22 -080040
gregcebf31feb2020-12-08 08:23:18 -080041 private List<Postable> postedEvents;
42
43 @Before
44 public void addPostableEventHandler() {
45 postedEvents = new ArrayList<>();
46 reporter.addHandler(
47 new ExtendedEventHandler() {
48 @Override
49 public void post(Postable obj) {
50 postedEvents.add(obj);
51 }
52
53 @Override
54 public void handle(Event event) {}
55 });
56 }
57
58 /** Returns only the posted events of the given class. */
59 private List<Postable> eventsOfType(Class<? extends Postable> clazz) {
60 return postedEvents.stream()
61 .filter(event -> event.getClass().equals(clazz))
62 .collect(Collectors.toList());
63 }
64
juliexxiacb9b2af2018-11-30 17:21:22 -080065 // test --flag=value
66 @Test
67 public void testFlagEqualsValueForm() throws Exception {
68 writeBasicIntFlag();
69
70 OptionsParsingResult result = parseStarlarkOptions("--//test:my_int_setting=666");
71
72 assertThat(result.getStarlarkOptions()).hasSize(1);
adonovan3ed7ed52020-09-30 12:03:28 -070073 assertThat(result.getStarlarkOptions().get("//test:my_int_setting"))
74 .isEqualTo(StarlarkInt.of(666));
juliexxiacb9b2af2018-11-30 17:21:22 -080075 assertThat(result.getResidue()).isEmpty();
76 }
77
juliexxiac2285582021-01-07 09:13:36 -080078 // test --@main_workspace//flag=value parses out to //flag=value
79 // test --@other_workspace//flag=value parses out to @other_workspace//flag=value
John Millikinfff73092019-10-22 15:12:32 -070080 @Test
Googler5881c382024-08-27 02:28:33 -070081 public void testFlagNameWithExternalRepo() throws Exception {
John Millikinfff73092019-10-22 15:12:32 -070082 writeBasicIntFlag();
Googler5881c382024-08-27 02:28:33 -070083 scratch.file("test/repo2/MODULE.bazel", "module(name = 'repo2')");
juliexxiac2285582021-01-07 09:13:36 -080084 scratch.file(
85 "test/repo2/defs.bzl",
Googler09bf5472024-03-28 10:08:10 -070086 """
87 def _impl(ctx):
88 pass
89
90 my_flag = rule(
91 implementation = _impl,
92 build_setting = config.int(flag = True),
93 )
94 """);
juliexxiac2285582021-01-07 09:13:36 -080095 scratch.file(
96 "test/repo2/BUILD",
Googler09bf5472024-03-28 10:08:10 -070097 """
98 load(":defs.bzl", "my_flag")
99
100 my_flag(
101 name = "flag2",
102 build_setting_default = 2,
103 )
104 """);
juliexxiac2285582021-01-07 09:13:36 -0800105
Googler5881c382024-08-27 02:28:33 -0700106 rewriteModuleDotBazel(
107 "module(name='starlark_options_test')",
108 "bazel_dep(name='repo2')",
109 "local_path_override(",
110 " module_name = 'repo2',",
juliexxiac2285582021-01-07 09:13:36 -0800111 " path = 'test/repo2',",
112 ")");
John Millikinfff73092019-10-22 15:12:32 -0700113
114 OptionsParsingResult result =
juliexxiac2285582021-01-07 09:13:36 -0800115 parseStarlarkOptions(
Googlerf272df32023-02-03 15:07:37 -0800116 "--@starlark_options_test//test:my_int_setting=666 --@repo2//:flag2=222",
117 /* onlyStarlarkParser= */ true);
John Millikinfff73092019-10-22 15:12:32 -0700118
juliexxiac2285582021-01-07 09:13:36 -0800119 assertThat(result.getStarlarkOptions()).hasSize(2);
120 assertThat(result.getStarlarkOptions().get("//test:my_int_setting"))
adonovan3ed7ed52020-09-30 12:03:28 -0700121 .isEqualTo(StarlarkInt.of(666));
Googler5881c382024-08-27 02:28:33 -0700122 assertThat(result.getStarlarkOptions().get("@@repo2+//:flag2")).isEqualTo(StarlarkInt.of(222));
John Millikinfff73092019-10-22 15:12:32 -0700123 assertThat(result.getResidue()).isEmpty();
124 }
125
juliexxiacb9b2af2018-11-30 17:21:22 -0800126 // test --fake_flag=value
127 @Test
128 public void testBadFlag_equalsForm() throws Exception {
129 scratch.file("test/BUILD");
130 reporter.removeHandler(failFastHandler);
131
132 OptionsParsingException e =
133 assertThrows(
134 OptionsParsingException.class,
135 () -> parseStarlarkOptions("--//fake_flag=blahblahblah"));
136
137 assertThat(e).hasMessageThat().contains("Error loading option //fake_flag");
Googlerddb9b9a2020-06-26 12:50:43 -0700138 assertThat(e.getInvalidArgument()).isEqualTo("//fake_flag");
juliexxiacb9b2af2018-11-30 17:21:22 -0800139 }
140
juliexxiacb9b2af2018-11-30 17:21:22 -0800141 // test --fake_flag
142 @Test
143 public void testBadFlag_boolForm() throws Exception {
144 scratch.file("test/BUILD");
145 reporter.removeHandler(failFastHandler);
146
147 OptionsParsingException e =
148 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//fake_flag"));
149
150 assertThat(e).hasMessageThat().contains("Error loading option //fake_flag");
Googlerddb9b9a2020-06-26 12:50:43 -0700151 assertThat(e.getInvalidArgument()).isEqualTo("//fake_flag");
juliexxiacb9b2af2018-11-30 17:21:22 -0800152 }
153
juliexxiacb9b2af2018-11-30 17:21:22 -0800154 @Test
gregceae07b7c2020-12-07 11:00:04 -0800155 public void testBadFlag_keepGoing() throws Exception {
156 optionsParser.parse("--keep_going");
157 scratch.file("test/BUILD");
158 reporter.removeHandler(failFastHandler);
159
160 OptionsParsingException e =
161 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//fake_flag"));
162
163 assertThat(e).hasMessageThat().contains("Error loading option //fake_flag");
164 assertThat(e.getInvalidArgument()).isEqualTo("//fake_flag");
165 }
166
167 @Test
juliexxiacb9b2af2018-11-30 17:21:22 -0800168 public void testSingleDash_notAllowed() throws Exception {
juliexxiacb9b2af2018-11-30 17:21:22 -0800169 writeBasicIntFlag();
170
Fabian Meumertzheim9f2542f2022-08-16 08:29:35 -0700171 OptionsParsingException e =
172 assertThrows(
173 OptionsParsingException.class,
Googlerf272df32023-02-03 15:07:37 -0800174 () ->
175 parseStarlarkOptions("-//test:my_int_setting=666", /* onlyStarlarkParser= */ true));
Fabian Meumertzheim9f2542f2022-08-16 08:29:35 -0700176 assertThat(e).hasMessageThat().isEqualTo("Invalid options syntax: -//test:my_int_setting=666");
juliexxiacb9b2af2018-11-30 17:21:22 -0800177 }
178
179 // test --non_flag_setting=value
180 @Test
181 public void testNonFlagParsing() throws Exception {
juliexxiacb9b2af2018-11-30 17:21:22 -0800182 scratch.file(
183 "test/build_setting.bzl",
Googler09bf5472024-03-28 10:08:10 -0700184 """
185 def _build_setting_impl(ctx):
186 return []
187
188 int_flag = rule(
189 implementation = _build_setting_impl,
190 build_setting = config.int(flag = False),
191 )
192 """);
juliexxiacb9b2af2018-11-30 17:21:22 -0800193 scratch.file(
194 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700195 """
196 load("//test:build_setting.bzl", "int_flag")
197
198 int_flag(
199 name = "my_int_setting",
200 build_setting_default = 42,
201 )
202 """);
juliexxiacb9b2af2018-11-30 17:21:22 -0800203
204 OptionsParsingException e =
205 assertThrows(
206 OptionsParsingException.class,
207 () -> parseStarlarkOptions("--//test:my_int_setting=666"));
208
209 assertThat(e).hasMessageThat().isEqualTo("Unrecognized option: //test:my_int_setting=666");
210 }
211
juliexxiacb9b2af2018-11-30 17:21:22 -0800212 // test --bool_flag
213 @Test
214 public void testBooleanFlag() throws Exception {
215 writeBasicBoolFlag();
216
juliexxiad2860842019-06-05 12:30:35 -0700217 OptionsParsingResult result = parseStarlarkOptions("--//test:my_bool_setting=false");
juliexxiacb9b2af2018-11-30 17:21:22 -0800218
219 assertThat(result.getStarlarkOptions()).hasSize(1);
juliexxiad2860842019-06-05 12:30:35 -0700220 assertThat(result.getStarlarkOptions().get("//test:my_bool_setting")).isEqualTo(false);
juliexxiacb9b2af2018-11-30 17:21:22 -0800221 assertThat(result.getResidue()).isEmpty();
222 }
223
224 // test --nobool_flag
225 @Test
226 public void testNoPrefixedBooleanFlag() throws Exception {
227 writeBasicBoolFlag();
228
229 OptionsParsingResult result = parseStarlarkOptions("--no//test:my_bool_setting");
230
231 assertThat(result.getStarlarkOptions()).hasSize(1);
232 assertThat(result.getStarlarkOptions().get("//test:my_bool_setting")).isEqualTo(false);
233 assertThat(result.getResidue()).isEmpty();
234 }
235
Ryan Beasley32d52682021-05-07 13:35:46 -0700236 // test --no@main_workspace//:bool_flag
237 @Test
238 public void testNoPrefixedBooleanFlag_withWorkspace() throws Exception {
239 writeBasicBoolFlag();
240
241 OptionsParsingResult result = parseStarlarkOptions("--no@//test:my_bool_setting");
242
243 assertThat(result.getStarlarkOptions()).hasSize(1);
244 assertThat(result.getStarlarkOptions().get("//test:my_bool_setting")).isEqualTo(false);
245 assertThat(result.getResidue()).isEmpty();
246 }
247
juliexxiacb9b2af2018-11-30 17:21:22 -0800248 // test --noint_flag
249 @Test
250 public void testNoPrefixedNonBooleanFlag() throws Exception {
251 writeBasicIntFlag();
252
253 OptionsParsingException e =
254 assertThrows(
255 OptionsParsingException.class, () -> parseStarlarkOptions("--no//test:my_int_setting"));
256
257 assertThat(e)
258 .hasMessageThat()
259 .isEqualTo("Illegal use of 'no' prefix on non-boolean option: //test:my_int_setting");
260 }
261
262 // test --int_flag
263 @Test
264 public void testFlagWithoutValue() throws Exception {
265 writeBasicIntFlag();
266
267 OptionsParsingException e =
268 assertThrows(
269 OptionsParsingException.class, () -> parseStarlarkOptions("--//test:my_int_setting"));
270
271 assertThat(e).hasMessageThat().isEqualTo("Expected value after --//test:my_int_setting");
272 }
273
274 // test --flag --flag
275 @Test
276 public void testRepeatFlagLastOneWins() throws Exception {
277 writeBasicIntFlag();
278
279 OptionsParsingResult result =
280 parseStarlarkOptions("--//test:my_int_setting=4 --//test:my_int_setting=7");
281
282 assertThat(result.getStarlarkOptions()).hasSize(1);
adonovan3ed7ed52020-09-30 12:03:28 -0700283 assertThat(result.getStarlarkOptions().get("//test:my_int_setting"))
284 .isEqualTo(StarlarkInt.of(7));
juliexxiacb9b2af2018-11-30 17:21:22 -0800285 assertThat(result.getResidue()).isEmpty();
286 }
287
288 // test --flagA=valueA --flagB=valueB
289 @Test
290 public void testMultipleFlags() throws Exception {
juliexxiacb9b2af2018-11-30 17:21:22 -0800291 scratch.file(
292 "test/build_setting.bzl",
Googler09bf5472024-03-28 10:08:10 -0700293 """
294 def _build_setting_impl(ctx):
295 return []
296
297 int_flag = rule(
298 implementation = _build_setting_impl,
299 build_setting = config.int(flag = True),
300 )
301 """);
juliexxiacb9b2af2018-11-30 17:21:22 -0800302 scratch.file(
303 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700304 """
305 load("//test:build_setting.bzl", "int_flag")
306
307 int_flag(
308 name = "my_int_setting",
309 build_setting_default = 42,
310 )
311
312 int_flag(
313 name = "my_other_int_setting",
314 build_setting_default = 77,
315 )
316 """);
juliexxiacb9b2af2018-11-30 17:21:22 -0800317
318 OptionsParsingResult result =
319 parseStarlarkOptions("--//test:my_int_setting=0 --//test:my_other_int_setting=0");
320
321 assertThat(result.getResidue()).isEmpty();
322 assertThat(result.getStarlarkOptions()).hasSize(2);
adonovan3ed7ed52020-09-30 12:03:28 -0700323 assertThat(result.getStarlarkOptions().get("//test:my_int_setting"))
324 .isEqualTo(StarlarkInt.of(0));
325 assertThat(result.getStarlarkOptions().get("//test:my_other_int_setting"))
326 .isEqualTo(StarlarkInt.of(0));
juliexxiacb9b2af2018-11-30 17:21:22 -0800327 }
328
329 // test --non_build_setting
330 @Test
331 public void testNonBuildSetting() throws Exception {
332 scratch.file(
333 "test/rules.bzl",
Googler09bf5472024-03-28 10:08:10 -0700334 """
335 def _impl(ctx):
336 return []
337
338 my_rule = rule(
339 implementation = _impl,
340 )
341 """);
342 scratch.file(
343 "test/BUILD",
344 """
345 load("//test:rules.bzl", "my_rule")
346
347 my_rule(name = "my_rule")
348 """);
juliexxiacb9b2af2018-11-30 17:21:22 -0800349 OptionsParsingException e =
350 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//test:my_rule"));
351 assertThat(e).hasMessageThat().isEqualTo("Unrecognized option: //test:my_rule");
352 }
353
354 // test --non_rule_configured_target
355 @Test
356 public void testNonRuleConfiguredTarget() throws Exception {
357 scratch.file(
358 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700359 """
360 genrule(
361 name = "my_gen",
362 srcs = ["x.in"],
363 outs = ["x.cc"],
364 cmd = "$(locations :tool) $< >$@",
365 tools = [":tool"],
366 )
367
368 cc_library(name = "tool-dep")
369 """);
juliexxiacb9b2af2018-11-30 17:21:22 -0800370 OptionsParsingException e =
371 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//test:x.in"));
372 assertThat(e).hasMessageThat().isEqualTo("Unrecognized option: //test:x.in");
373 }
374
375 // test --int_flag=non_int_value
376 @Test
377 public void testWrongValueType_int() throws Exception {
378 writeBasicIntFlag();
379
380 OptionsParsingException e =
381 assertThrows(
382 OptionsParsingException.class,
383 () -> parseStarlarkOptions("--//test:my_int_setting=woohoo"));
384
385 assertThat(e)
386 .hasMessageThat()
387 .isEqualTo("While parsing option //test:my_int_setting=woohoo: 'woohoo' is not a int");
388 }
389
390 // test --bool_flag=non_bool_value
391 @Test
392 public void testWrongValueType_bool() throws Exception {
393 writeBasicBoolFlag();
394
395 OptionsParsingException e =
396 assertThrows(
397 OptionsParsingException.class,
398 () -> parseStarlarkOptions("--//test:my_bool_setting=woohoo"));
399
400 assertThat(e)
401 .hasMessageThat()
402 .isEqualTo("While parsing option //test:my_bool_setting=woohoo: 'woohoo' is not a boolean");
403 }
juliexxiad2860842019-06-05 12:30:35 -0700404
405 // test --int-flag=same value as default
406 @Test
407 public void testDontStoreDefaultValue() throws Exception {
408 // build_setting_default = 42
409 writeBasicIntFlag();
410
411 OptionsParsingResult result = parseStarlarkOptions("--//test:my_int_setting=42");
412
413 assertThat(result.getStarlarkOptions()).isEmpty();
414 }
schmitt8fd43cf2019-11-19 15:49:27 -0800415
416 @Test
417 public void testOptionsAreParsedWithBuildTestsOnly() throws Exception {
418 writeBasicIntFlag();
419 optionsParser.parse("--build_tests_only");
420
421 OptionsParsingResult result = parseStarlarkOptions("--//test:my_int_setting=15");
422
adonovan3ed7ed52020-09-30 12:03:28 -0700423 assertThat(result.getStarlarkOptions().get("//test:my_int_setting"))
424 .isEqualTo(StarlarkInt.of(15));
schmitt8fd43cf2019-11-19 15:49:27 -0800425 }
Julie Xia69d4ace2020-05-29 11:25:25 -0700426
gregcebf31feb2020-12-08 08:23:18 -0800427 /**
428 * When Starlark flags are only set as flags, they shouldn't produce {@link
429 * TargetParsingCompleteEvent}s. That's intended to communicate (to the build event protocol)
430 * which of the targets in {@code blaze build //foo:all //bar:all} were built.
431 */
432 @Test
433 public void testExpectedBuildEventOutput_asFlag() throws Exception {
434 writeBasicIntFlag();
435 scratch.file("blah/BUILD", "cc_library(name = 'mylib')");
Googler1ee0f642024-02-09 06:30:35 -0800436 useConfiguration("--//test:my_int_setting=15");
gregcebf31feb2020-12-08 08:23:18 -0800437 update(
438 ImmutableList.of("//blah:mylib"),
439 /*keepGoing=*/ false,
440 /*loadingPhaseThreads=*/ LOADING_PHASE_THREADS,
441 /*doAnalysis*/ true,
442 eventBus);
443 List<Postable> targetParsingCompleteEvents = eventsOfType(TargetParsingCompleteEvent.class);
444 assertThat(targetParsingCompleteEvents).hasSize(1);
445 assertThat(
446 ((TargetParsingCompleteEvent) targetParsingCompleteEvents.get(0))
447 .getOriginalTargetPattern())
448 .containsExactly("//blah:mylib");
449 }
450
451 /**
452 * But Starlark are also targets. When they're requested as normal build targets they should
453 * produce {@link TargetParsingCompleteEvent} just like any other target.
454 */
455 @Test
456 public void testExpectedBuildEventOutput_asTarget() throws Exception {
457 writeBasicIntFlag();
458 scratch.file("blah/BUILD", "cc_library(name = 'mylib')");
Googler1ee0f642024-02-09 06:30:35 -0800459 useConfiguration("--//test:my_int_setting=15");
gregcebf31feb2020-12-08 08:23:18 -0800460 update(
461 ImmutableList.of("//blah:mylib", "//test:my_int_setting"),
462 /*keepGoing=*/ false,
463 /*loadingPhaseThreads=*/ LOADING_PHASE_THREADS,
464 /*doAnalysis*/ true,
465 eventBus);
466 List<Postable> targetParsingCompleteEvents = eventsOfType(TargetParsingCompleteEvent.class);
467 assertThat(targetParsingCompleteEvents).hasSize(1);
468 assertThat(
469 ((TargetParsingCompleteEvent) targetParsingCompleteEvents.get(0))
470 .getOriginalTargetPattern())
471 .containsExactly("//blah:mylib", "//test:my_int_setting");
472 }
juliexxiaa13f5902020-12-10 09:19:01 -0800473
474 @Test
475 @SuppressWarnings("unchecked")
476 public void testAllowMultipleStringFlag() throws Exception {
477 scratch.file(
478 "test/build_setting.bzl",
Googler09bf5472024-03-28 10:08:10 -0700479 """
480 def _build_setting_impl(ctx):
481 return []
482
483 allow_multiple_flag = rule(
484 implementation = _build_setting_impl,
485 build_setting = config.string(flag = True, allow_multiple = True),
486 )
487 """);
juliexxiaa13f5902020-12-10 09:19:01 -0800488 scratch.file(
489 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700490 """
491 load("//test:build_setting.bzl", "allow_multiple_flag")
492
493 allow_multiple_flag(
494 name = "cats",
495 build_setting_default = "tabby",
496 )
497 """);
juliexxiaa13f5902020-12-10 09:19:01 -0800498
499 OptionsParsingResult result = parseStarlarkOptions("--//test:cats=calico --//test:cats=bengal");
500
501 assertThat(result.getStarlarkOptions().keySet()).containsExactly("//test:cats");
502 assertThat((List<String>) result.getStarlarkOptions().get("//test:cats"))
503 .containsExactly("calico", "bengal");
504 }
Fabian Meumertzheimcdf01a32022-07-20 08:22:48 -0700505
506 @Test
507 @SuppressWarnings("unchecked")
508 public void testRepeatedStringListFlag() throws Exception {
509 scratch.file(
510 "test/build_setting.bzl",
Googler09bf5472024-03-28 10:08:10 -0700511 """
512 def _build_setting_impl(ctx):
513 return []
514
515 repeated_flag = rule(
516 implementation = _build_setting_impl,
517 build_setting = config.string_list(flag = True, repeatable = True),
518 )
519 """);
Fabian Meumertzheimcdf01a32022-07-20 08:22:48 -0700520 scratch.file(
521 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700522 """
523 load("//test:build_setting.bzl", "repeated_flag")
524
525 repeated_flag(
526 name = "cats",
527 build_setting_default = ["tabby"],
528 )
529 """);
Fabian Meumertzheimcdf01a32022-07-20 08:22:48 -0700530
531 OptionsParsingResult result = parseStarlarkOptions("--//test:cats=calico --//test:cats=bengal");
532
533 assertThat(result.getStarlarkOptions().keySet()).containsExactly("//test:cats");
534 assertThat((List<String>) result.getStarlarkOptions().get("//test:cats"))
535 .containsExactly("calico", "bengal");
536 }
Googler96864602023-06-12 17:41:24 -0700537
538 @Test
539 public void flagReferencesExactlyOneTarget() throws Exception {
540 scratch.file(
541 "test/build_setting.bzl",
Googler09bf5472024-03-28 10:08:10 -0700542 """
543 string_flag = rule(
544 implementation = lambda ctx, attr: [],
545 build_setting = config.string(flag = True),
546 )
547 """);
Googler96864602023-06-12 17:41:24 -0700548 scratch.file(
549 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700550 """
551 load("//test:build_setting.bzl", "string_flag")
552
553 string_flag(
554 name = "one",
555 build_setting_default = "",
556 )
557
558 string_flag(
559 name = "two",
560 build_setting_default = "",
561 )
562 """);
Googler96864602023-06-12 17:41:24 -0700563
564 OptionsParsingException e =
565 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//test:all"));
566
567 assertThat(e)
568 .hasMessageThat()
569 .contains("//test:all: user-defined flags must reference exactly one target");
570 }
Fabian Meumertzheim43fdcd32024-05-01 16:02:55 -0700571
572 @Test
573 public void flagIsAlias() throws Exception {
574 scratch.file(
575 "test/build_setting.bzl",
576 """
577 string_flag = rule(
578 implementation = lambda ctx: [],
579 build_setting = config.string(flag = True),
580 )
581 """);
582 scratch.file(
583 "test/BUILD",
584 """
585 load("//test:build_setting.bzl", "string_flag")
586
587 alias(
588 name = "one",
589 actual = "//test/pkg:two",
590 )
591
592 string_flag(
593 name = "three",
594 build_setting_default = "",
595 )
596 """);
597 scratch.file(
598 "test/pkg/BUILD",
599 """
600 alias(
601 name = "two",
602 actual = "//test:three",
603 )
604 """);
605
606 OptionsParsingResult result = parseStarlarkOptions("--//test:one=one --//test/pkg:two=two");
607
608 assertThat(result.getStarlarkOptions()).containsExactly("//test:three", "two");
609 }
610
611 @Test
612 public void flagIsAlias_cycle() throws Exception {
613 scratch.file(
614 "test/BUILD",
615 """
616 alias(
617 name = "one",
618 actual = "//test/pkg:two",
619 )
620
621 alias(
622 name = "three",
623 actual = ":one",
624 )
625 """);
626 scratch.file(
627 "test/pkg/BUILD",
628 """
629 alias(
630 name = "two",
631 actual = "//test:three",
632 )
633 """);
634
635 OptionsParsingException e =
636 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//test:one=one"));
637
638 assertThat(e)
639 .hasMessageThat()
640 .isEqualTo(
641 "Failed to load build setting '//test:one' due to a cycle in alias chain: //test:one"
642 + " -> //test/pkg:two -> //test:three -> //test:one");
643 }
644
645 @Test
646 public void flagIsAlias_usesSelect() throws Exception {
647 scratch.file(
648 "test/BUILD",
649 """
650 alias(
651 name = "one",
652 actual = "//test/pkg:two",
653 )
654
655 alias(
656 name = "three",
657 actual = ":one",
658 )
659 """);
660 scratch.file(
661 "test/pkg/BUILD",
662 """
Googler0c100ef2024-09-30 17:31:27 -0700663 # Needed to avoid select() being eliminated as trivial.
664 config_setting(
665 name = "config",
666 values = {"define": "pi=3"},
667 )
668
Fabian Meumertzheim43fdcd32024-05-01 16:02:55 -0700669 alias(
670 name = "two",
Googler0c100ef2024-09-30 17:31:27 -0700671 actual = select({
672 ":config": "//test:three",
673 "//conditions:default": "//test:three",
674 }),
Fabian Meumertzheim43fdcd32024-05-01 16:02:55 -0700675 )
676 """);
677
678 OptionsParsingException e =
679 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//test:one=one"));
680
681 assertThat(e)
682 .hasMessageThat()
683 .isEqualTo(
684 "Failed to load build setting '//test:one' as it resolves to an alias with an actual"
685 + " value that uses select(): //test:one -> //test/pkg:two. This is not supported"
686 + " as build settings are needed to determine the configuration the select is"
687 + " evaluated in.");
688 }
689
690 @Test
691 public void flagIsAlias_resolvesToNonBuildSettingTarget() throws Exception {
692 scratch.file(
693 "test/BUILD",
694 """
695 alias(
696 name = "one",
697 actual = "//test/pkg:two",
698 )
699
700 genrule(
701 name = "three",
702 outs = ["out"],
703 cmd = "echo hello > $@",
704 )
705 """);
706 scratch.file(
707 "test/pkg/BUILD",
708 """
709 alias(
710 name = "two",
711 actual = "//test:three",
712 )
713 """);
714
715 OptionsParsingException e =
716 assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//test:one=one"));
717
718 assertThat(e)
719 .hasMessageThat()
720 .isEqualTo("Unrecognized option: //test:one -> //test/pkg:two -> //test:three");
721 }
juliexxiacb9b2af2018-11-30 17:21:22 -0800722}