Skip to content

Commit 78265cc

Browse files
fmeumcopybara-github
authored andcommitted
Add ctx.package_relative_label
This is necessary to permit Starlark implementations of `ctx.expand_location`, which can be more memory efficient, support path mapping and provide better defaults. RELNOTES: The new `package_relative_label` function on the rule context (`ctx`) can be used to turn a user-provided label string into a `Label` relative to the target that is currently being analyzed (where `Label(...)` would return a `Label` relative to the `.bzl` file containing the call). Closes #28102. PiperOrigin-RevId: 853170854 Change-Id: Ibecff3b0b599da0be2fbbba707c15ed540c6c38c
1 parent 361c420 commit 78265cc

7 files changed

Lines changed: 149 additions & 15 deletions

File tree

src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleContext.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
5858
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesInfo;
5959
import com.google.devtools.build.lib.cmdline.Label;
60+
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
6061
import com.google.devtools.build.lib.collect.nestedset.Depset;
6162
import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException;
6263
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -1153,7 +1154,7 @@ public Tuple resolveCommand(
11531154
String attribute = Type.STRING.convertOptional(attributeUnchecked, "attribute");
11541155
if (expandLocations) {
11551156
command =
1156-
helper.resolveCommandAndExpandLabels(command, attribute, /*allowDataInLabel=*/ false);
1157+
helper.resolveCommandAndExpandLabels(command, attribute, /* allowDataInLabel= */ false);
11571158
}
11581159
if (!Starlark.isNullOrNone(makeVariablesUnchecked)) {
11591160
Map<String, String> makeVariables =
@@ -1211,6 +1212,23 @@ private void checkResolveToolsAllowed() throws EvalException {
12111212
}
12121213
}
12131214

1215+
@Override
1216+
public Label packageRelativeLabel(Object input) throws EvalException {
1217+
checkMutable("package_relative_label");
1218+
if (input instanceof Label inputLabel) {
1219+
return inputLabel;
1220+
}
1221+
try {
1222+
return Label.parseWithPackageContext(
1223+
(String) input,
1224+
Label.PackageContext.of(
1225+
ruleContext.getLabel().getPackageIdentifier(),
1226+
ruleContext.getRule().getPackageMetadata().repositoryMapping()));
1227+
} catch (LabelSyntaxException e) {
1228+
throw Starlark.errorf("invalid label in ctx.package_relative_label: %s", e.getMessage());
1229+
}
1230+
}
1231+
12141232
@Override
12151233
public StarlarkSemantics getStarlarkSemantics() {
12161234
return ruleContext.getAnalysisEnvironment().getStarlarkSemantics();

src/main/java/com/google/devtools/build/lib/cmdline/Label.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ public Label getSamePackageLabel(String targetName) throws LabelSyntaxException
566566
+ " containing an apparent repo name. Prefer <a"
567567
+ " href=\"#same_package_label\"><code>Label.same_package_label()</code></a>, <a"
568568
+ " href=\"../toplevel/native.html#package_relative_label\"><code>native.package_relative_label()</code></a>,"
569+
+ " <a href=\"ctx.html#package_relative_label\"><code>ctx.package_relative_label()</code></a>,"
569570
+ " or <a href=\"#Label\"><code>Label()</code></a> instead.<p>Resolves a label that"
570571
+ " is either absolute (starts with <code>//</code>) or relative to the current"
571572
+ " package. If this label is in a remote repository, the argument will be resolved"

src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkNativeModuleApi.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,10 @@ NoneType exportsFiles(Sequence<?> srcs, Object visibility, Object licenses, Star
292292
+ " supplied by the BUILD file to a <code>Label</code> object. (There is no way to"
293293
+ " convert a string to a <code>Label</code> in the context of a package other than"
294294
+ " the BUILD file or the calling .bzl file. For that reason, outer macros should"
295-
+ " always prefer to pass Label objects to inner macros rather than label strings.)",
295+
+ " always prefer to pass Label objects to inner macros rather than label strings.)"
296+
+ "<a href='ctx.html#package_relative_label'><code>ctx.package_relative_label()"
297+
+ "</code></a> provides the same functionality within a rule or aspect implementation"
298+
+ " function.",
296299
parameters = {
297300
@Param(
298301
name = "input",

src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleContextApi.java

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,7 @@ public interface StarlarkRuleContextApi<ConstraintValueT extends ConstraintValue
332332
// TODO(cparsons): Look into flipping this to true.
333333
documented = false,
334334
parameters = {
335-
@Param(
336-
name = "option",
337-
positional = true,
338-
named = false,
339-
doc = "The string to split."),
335+
@Param(name = "option", positional = true, named = false, doc = "The string to split."),
340336
})
341337
Sequence<String> tokenize(String optionString) throws EvalException;
342338

@@ -666,4 +662,32 @@ Tuple resolveCommand(
666662
doc = "List of tools (list of targets)."),
667663
})
668664
Tuple resolveTools(Sequence<?> tools) throws EvalException;
665+
666+
@StarlarkMethod(
667+
name = "package_relative_label",
668+
doc =
669+
"""
670+
Converts the input string into a <a href='../builtins/Label.html'>Label</a> object, in \
671+
the context of the package of the target currently being analyzed. If the input is \
672+
already a <code>Label</code>, it is returned unchanged.<p>The result of this function is \
673+
the same <code>Label</code> value as would be produced by passing the given string to a \
674+
label-valued attribute of the rule and accessing the corresponding \
675+
<a href='../builtins/Target.html#label><code>label</code></a> field.
676+
<p><i>Usage note:</i> The difference between this function and \
677+
<a href='../builtins/Label.html#Label'>Label()</a></code> is \
678+
that <code>Label()</code> uses the context of the package of the <code>.bzl</code> file \
679+
that called it, not the package of the target currently being analyzed. This function \
680+
has the same behavior as <a href='../toplevel/native.html#package_relative_label'>
681+
<code>native.package_relative_label()</code></a>, which cannot be used in a rule or
682+
aspect implementation function.
683+
""",
684+
parameters = {
685+
@Param(
686+
name = "input",
687+
allowedTypes = {@ParamType(type = String.class), @ParamType(type = Label.class)},
688+
doc =
689+
"The input label string or Label object. If a Label object is passed, it's"
690+
+ " returned as is.")
691+
})
692+
Label packageRelativeLabel(Object input) throws EvalException;
669693
}

src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,8 +1036,11 @@ StarlarkAspectApi aspect(
10361036
+ " function, <code><a"
10371037
+ " href='../toplevel/native.html#package_relative_label'>native.package_relative_label()</a></code>,"
10381038
+ " converts the input into a <code>Label</code> in the context of the package"
1039-
+ " currently being constructed. Use that function to mimic the string-to-label"
1040-
+ " conversion that is automatically done by label-valued rule attributes.",
1039+
+ " currently being constructed. For rule and aspect implementation functions, <a"
1040+
+ " href='ctx.html#package_relative_label'><code>ctx.package_relative_label()</code></a>"
1041+
+ " can be used for the same purpose. Use these functions to mimic the"
1042+
+ " string-to-label conversion that is automatically done by label-valued rule"
1043+
+ " attributes.",
10411044
parameters = {
10421045
@Param(
10431046
name = "input",

src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
5353
import com.google.devtools.build.lib.analysis.util.MockRule;
5454
import com.google.devtools.build.lib.cmdline.Label;
55+
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
5556
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
5657
import com.google.devtools.build.lib.collect.nestedset.Depset;
5758
import com.google.devtools.build.lib.packages.Provider;
@@ -2792,8 +2793,7 @@ public void testNoAccessToDependencyActionsWithoutStarlarkTest() throws Exceptio
27922793

27932794
@Test
27942795
public void testAbstractActionInterface() throws Exception {
2795-
setBuildLanguageOptions(
2796-
"--incompatible_no_rule_outputs_param=false");
2796+
setBuildLanguageOptions("--incompatible_no_rule_outputs_param=false");
27972797
scratch.file(
27982798
"test/rules.bzl",
27992799
"load('//test:providers.bzl', 'AInfo')",
@@ -2838,8 +2838,7 @@ public void testAbstractActionInterface() throws Exception {
28382838

28392839
@Test
28402840
public void testCreatedActions() throws Exception {
2841-
setBuildLanguageOptions(
2842-
"--incompatible_no_rule_outputs_param=false");
2841+
setBuildLanguageOptions("--incompatible_no_rule_outputs_param=false");
28432842
// createRuleContext() gives us the context for a rule upon entry into its analysis function.
28442843
// But we need to inspect the result of calling created_actions() after the rule context has
28452844
// been modified by creating actions. So we'll call created_actions() from within the analysis
@@ -2927,8 +2926,7 @@ public void testSpawnActionInterface() throws Exception {
29272926

29282927
@Test
29292928
public void testRunShellUsesHelperScriptForLongCommand() throws Exception {
2930-
setBuildLanguageOptions(
2931-
"--incompatible_no_rule_outputs_param=false");
2929+
setBuildLanguageOptions("--incompatible_no_rule_outputs_param=false");
29322930
// createRuleContext() gives us the context for a rule upon entry into its analysis function.
29332931
// But we need to inspect the result of calling created_actions() after the rule context has
29342932
// been modified by creating actions. So we'll call created_actions() from within the analysis
@@ -4999,4 +4997,39 @@ def _dep_impl(ctx):
49994997
// Dependencies output path should have `-opt` after the transition.
50004998
((Iterable<?>) result).forEach(s -> assertThat(s.toString()).contains("-opt/"));
50014999
}
5000+
5001+
@Test
5002+
public void testPackageRelativeLabel() throws Exception {
5003+
scratch.file("rules/BUILD");
5004+
scratch.file(
5005+
"rules/rules.bzl",
5006+
"""
5007+
MyProvider = provider()
5008+
5009+
def _impl(ctx):
5010+
return MyProvider(result = ctx.package_relative_label(":some_target"))
5011+
5012+
my_rule = rule(
5013+
implementation = _impl,
5014+
)
5015+
""");
5016+
5017+
scratch.file(
5018+
"test/BUILD",
5019+
"""
5020+
load("//rules:rules.bzl", "my_rule")
5021+
5022+
my_rule(
5023+
name = "my_target",
5024+
)
5025+
""");
5026+
5027+
ConfiguredTarget myTarget = getConfiguredTarget("//test:my_target");
5028+
Provider.Key myProviderKey =
5029+
new StarlarkProvider.Key(
5030+
keyForBuild(Label.create(PackageIdentifier.createInMainRepo("rules"), "rules.bzl")),
5031+
"MyProvider");
5032+
var result = (Label) ((StarlarkInfo) myTarget.get(myProviderKey)).getValue("result");
5033+
assertThat(result).isEqualTo(Label.parseCanonicalUnchecked("//test:some_target"));
5034+
}
50025035
}

src/test/py/bazel/bzlmod/bazel_module_test.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,58 @@ def testNativePackageRelativeLabel(self):
622622
self.assertIn('5th: @@bleb//bleb:bleb', stderr)
623623
self.assertIn('6th: @@//bleb:bleb', stderr)
624624

625+
def testCtxPackageRelativeLabel(self):
626+
self.ScratchFile(
627+
'MODULE.bazel',
628+
[
629+
'module(name="foo")',
630+
'bazel_dep(name="bar")',
631+
'local_path_override(module_name="bar",path="bar")',
632+
],
633+
)
634+
self.ScratchFile('BUILD')
635+
self.ScratchFile(
636+
'defs.bzl',
637+
[
638+
'def _my_rule_impl(ctx):',
639+
' print("1st: " + str(ctx.package_relative_label(":bleb")))',
640+
' print("2nd: " + str(ctx.package_relative_label('
641+
+ '"//bleb:bleb")))',
642+
' print("3rd: " + str(ctx.package_relative_label('
643+
+ '"@bleb//bleb:bleb")))',
644+
' print("4th: " + str(ctx.package_relative_label("//bleb")))',
645+
' print("5th: " + str(ctx.package_relative_label('
646+
+ '"@@bleb//bleb:bleb")))',
647+
' print("6th: " + str(ctx.package_relative_label(Label('
648+
+ '"//bleb"))))',
649+
'my_rule = rule(_my_rule_impl)',
650+
],
651+
)
652+
653+
self.ScratchFile(
654+
'bar/MODULE.bazel',
655+
[
656+
'module(name="bar")',
657+
'bazel_dep(name="foo", repo_name="bleb")',
658+
],
659+
)
660+
self.ScratchFile(
661+
'bar/quux/BUILD',
662+
[
663+
'load("@bleb//:defs.bzl", "my_rule")',
664+
'my_rule(name="book")',
665+
],
666+
)
667+
668+
_, _, stderr = self.RunBazel(['build', '@bar//quux:book'])
669+
stderr = '\n'.join(stderr)
670+
self.assertIn('1st: @@bar+//quux:bleb', stderr)
671+
self.assertIn('2nd: @@bar+//bleb:bleb', stderr)
672+
self.assertIn('3rd: @@//bleb:bleb', stderr)
673+
self.assertIn('4th: @@bar+//bleb:bleb', stderr)
674+
self.assertIn('5th: @@bleb//bleb:bleb', stderr)
675+
self.assertIn('6th: @@//bleb:bleb', stderr)
676+
625677
def testArchiveWithArchiveType(self):
626678
# make the archive without the .zip extension
627679
self.main_registry.createShModule(

0 commit comments

Comments
 (0)