Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native.package_relative_label function #17435

Merged
merged 1 commit into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -994,8 +994,11 @@ public boolean isImmutable() {
.build();

@Override
public Label label(String labelString, StarlarkThread thread) throws EvalException {
// The label string is interpeted with respect to the .bzl module containing the call to
public Label label(Object input, StarlarkThread thread) throws EvalException {
if (input instanceof Label) {
return (Label) input;
}
// The label string is interpreted with respect to the .bzl module containing the call to
// `Label()`. An alternative to this approach that avoids stack inspection is to have each .bzl
// module define its own copy of the `Label()` builtin embedding the module's own name. This
// would lead to peculiarities like foo.bzl being able to call bar.bzl's `Label()` symbol to
Expand All @@ -1004,9 +1007,9 @@ public Label label(String labelString, StarlarkThread thread) throws EvalExcepti
BazelModuleContext moduleContext =
BazelModuleContext.of(Module.ofInnermostEnclosingStarlarkFunction(thread));
try {
return Label.parseWithRepoContext(labelString, moduleContext.packageContext());
return Label.parseWithPackageContext((String) input, moduleContext.packageContext());
} catch (LabelSyntaxException e) {
throw Starlark.errorf("Illegal absolute label syntax: %s", e.getMessage());
throw Starlark.errorf("invalid label in Label(): %s", e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,20 @@ public String repositoryName(StarlarkThread thread) throws EvalException {
return packageId.getRepository().getNameWithAt();
}

@Override
public Label packageRelativeLabel(Object input, StarlarkThread thread) throws EvalException {
BazelStarlarkContext.from(thread).checkLoadingPhase("native.package_relative_label");
if (input instanceof Label) {
return (Label) input;
}
try {
String s = (String) input;
return PackageFactory.getContext(thread).getBuilder().getLabelConverter().convert(s);
} catch (LabelSyntaxException e) {
throw Starlark.errorf("invalid label in native.package_relative_label: %s", e.getMessage());
}
}

private static Dict<String, Object> getRuleDict(Rule rule, Mutability mu) throws EvalException {
Dict.Builder<String, Object> values = Dict.builder();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.devtools.build.lib.starlarkbuildapi;

import com.google.devtools.build.docgen.annot.DocCategory;
import com.google.devtools.build.lib.cmdline.Label;
import net.starlark.java.annot.Param;
import net.starlark.java.annot.ParamType;
import net.starlark.java.annot.StarlarkBuiltin;
Expand Down Expand Up @@ -241,6 +242,38 @@ NoneType exportsFiles(Sequence<?> srcs, Object visibility, Object licenses, Star
useStarlarkThread = true)
String repositoryName(StarlarkThread thread) throws EvalException;

@StarlarkMethod(
name = "package_relative_label",
doc =
"Converts the input string into a <a href='Label'>Label</a> object, in the context of the"
+ " package currently being initialized (that is, the <code>BUILD</code> file for"
+ " which the current macro is executing). If the input is already a"
+ " <code>Label</code>, it is returned unchanged.<p>This function may only be called"
+ " while evaluating a BUILD file and the macros it directly or indirectly calls; it"
+ " may not be called in (for instance) a rule implementation function. <p>The result"
+ " of this function is the same <code>Label</code> value as would be produced by"
+ " passing the given string to a label-valued attribute of a target declared in the"
+ " BUILD file. <p><i>Usage note:</i> The difference between this function and <a"
+ " href='Label#Label'>Label()</a></code> is that <code>Label()</code> uses the"
+ " context of the package of the <code>.bzl</code> file that called it, not the"
+ " package of the <code>BUILD</code> file. Use <code>Label()</code> when you need to"
+ " refer to a fixed target that is hardcoded into the macro, such as a compiler. Use"
+ " <code>package_relative_label()</code> when you need to normalize a label string"
+ " supplied by the BUILD file to a <code>Label</code> object. (There is no way to"
+ " convert a string to a <code>Label</code> in the context of a package other than"
+ " the BUILD file or the calling .bzl file. For that reason, outer macros should"
+ " always prefer to pass Label objects to inner macros rather than label strings.)",
parameters = {
@Param(
name = "input",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = Label.class)},
doc =
"The input label string or Label object. If a Label object is passed, it's"
+ " returned as is.")
},
useStarlarkThread = true)
Label packageRelativeLabel(Object input, StarlarkThread thread) throws EvalException;

@StarlarkMethod(
name = "subpackages",
doc =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -693,18 +693,26 @@ StarlarkAspectApi aspect(
@StarlarkMethod(
name = "Label",
doc =
"Creates a Label referring to a BUILD target. Use this function when you want to give a"
+ " default value for the label attributes of a rule or when referring to a target"
+ " via an absolute label from a macro. The argument must refer to an absolute label."
+ " The repo part of the label (or its absence) is interpreted in the context of the"
+ " repo where this Label() call appears. Example: <br><pre"
+ " class=language-python>Label(\"//tools:default\")</pre>",
"Converts a label string into a <code>Label</code> object, in the context of the package"
+ " where the calling <code>.bzl</code> source file lives. If the given value is"
+ " already a <code>Label</code>, it is returned unchanged."
+ "<p>For macros, a related function,"
+ " <code><a"
+ " href='native#package_relative_label'>native.package_relative_label()</a></code>,"
+ " converts the input into a <code>Label</code> in the context of the package"
+ " currently being constructed. Use that function to mimic the string-to-label"
+ " conversion that is automatically done by label-valued rule attributes.",
parameters = {
@Param(name = "label_string", doc = "the label string."),
@Param(
name = "input",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = Label.class)},
doc =
"The input label string or Label object. If a Label object is passed, it's"
+ " returned as is.")
},
useStarlarkThread = true)
@StarlarkConstructor
Label label(String labelString, StarlarkThread thread) throws EvalException;
Label label(Object input, StarlarkThread thread) throws EvalException;

@StarlarkMethod(
name = "exec_group",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.starlarkbuildapi.StarlarkNativeModuleApi;
import javax.annotation.Nullable;
import net.starlark.java.eval.Dict;
Expand Down Expand Up @@ -77,6 +78,11 @@ public String repositoryName(StarlarkThread thread) {
return "";
}

@Override
public Label packageRelativeLabel(Object input, StarlarkThread thread) throws EvalException {
return Label.parseCanonicalUnchecked("//:fake_label");
}

@Override
public Sequence<?> subpackages(
Sequence<?> include, Sequence<?> exclude, boolean allowEmpty, StarlarkThread thread)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,14 @@ public StarlarkCallable rule(
}

@Override
public Label label(String labelString, StarlarkThread thread) throws EvalException {
public Label label(Object input, StarlarkThread thread) throws EvalException {
if (input instanceof Label) {
return (Label) input;
}
try {
return Label.parseCanonical(labelString);
return Label.parseCanonical((String) input);
} catch (LabelSyntaxException e) {
throw Starlark.errorf("Illegal absolute label syntax: %s", labelString);
throw Starlark.errorf("Illegal absolute label syntax: %s", input);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,13 @@ public void testLabel() throws Exception {
assertThat(result.toString()).isEqualTo("//foo/foo:foo");
}

@Test
public void testLabelIdempotence() throws Exception {
Object result = ev.eval("Label(Label('//foo/foo:foo'))");
assertThat(result).isInstanceOf(Label.class);
assertThat(result.toString()).isEqualTo("//foo/foo:foo");
}

@Test
public void testLabelSameInstance() throws Exception {
Object l1 = ev.eval("Label('//foo/foo:foo')");
Expand Down
57 changes: 57 additions & 0 deletions src/test/py/bazel/bzlmod/bazel_module_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,5 +874,62 @@ def testBashRunfilesLibraryRepoMapping(self):
env_add={'RUNFILES_LIB_DEBUG': '1'})
self.AssertExitCode(exit_code, 0, stderr, stdout)

def testNativePackageRelativeLabel(self):
self.ScratchFile(
'MODULE.bazel',
[
'module(name="foo")',
'bazel_dep(name="bar")',
'local_path_override(module_name="bar",path="bar")',
],
)
self.ScratchFile('WORKSPACE')
self.ScratchFile('BUILD')
self.ScratchFile(
'defs.bzl',
[
'def mac(name):',
' native.filegroup(name=name)',
' print("1st: " + str(native.package_relative_label(":bleb")))',
' print("2nd: " + str(native.package_relative_label('
+ '"//bleb:bleb")))',
' print("3rd: " + str(native.package_relative_label('
+ '"@bleb//bleb:bleb")))',
' print("4th: " + str(native.package_relative_label("//bleb")))',
' print("5th: " + str(native.package_relative_label('
+ '"@@bleb//bleb:bleb")))',
' print("6th: " + str(native.package_relative_label(Label('
+ '"//bleb"))))',
],
)

self.ScratchFile(
'bar/MODULE.bazel',
[
'module(name="bar")',
'bazel_dep(name="foo", repo_name="bleb")',
],
)
self.ScratchFile('bar/WORKSPACE')
self.ScratchFile(
'bar/quux/BUILD',
[
'load("@bleb//:defs.bzl", "mac")',
'mac(name="book")',
],
)

_, _, stderr = self.RunBazel(
['build', '@bar//quux:book'], allow_failure=False
)
stderr = '\n'.join(stderr)
self.assertIn('1st: @@bar~override//quux:bleb', stderr)
self.assertIn('2nd: @@bar~override//bleb:bleb', stderr)
self.assertIn('3rd: @@//bleb:bleb', stderr)
self.assertIn('4th: @@bar~override//bleb:bleb', stderr)
self.assertIn('5th: @@bleb//bleb:bleb', stderr)
self.assertIn('6th: @@//bleb:bleb', stderr)


if __name__ == '__main__':
unittest.main()