diff --git a/site/docs/skylark/macros.md b/site/docs/skylark/macros.md
index e879106bd44500..85048a5cafe111 100644
--- a/site/docs/skylark/macros.md
+++ b/site/docs/skylark/macros.md
@@ -143,6 +143,38 @@ macro), use the function [native.package_name()](lib/native.html#package_name).
Note that `native` can only be used in `.bzl` files, and not in `WORKSPACE` or
`BUILD` files.
+## Label resolution in macros
+
+Since macros are evaluated in the [loading phase](concepts.md#evaluation-model),
+label strings such as `"//foo:bar"` that occur in a macro are interpreted
+relative to the `BUILD` file in which the macro is used rather than relative to
+the `.bzl` file in which it is defined. This behavior is generally undesirable
+for macros that are meant to be used in other repositories, e.g. because they
+are part of a published Starlark ruleset.
+
+To get the same behavior as for Starlark rules, wrap the label strings with the
+[`Label`](lib/Label.html#Label) constructor:
+
+```python
+# @my_ruleset//rules:defs.bzl
+def my_cc_wrapper(name, deps = [], **kwargs):
+ native.cc_library(
+ name = name,
+ deps = deps + select({
+ # Due to the use of Label, this label is resolved within @my_ruleset,
+ # regardless of its site of use.
+ Label("//config:needs_foo"): [
+ # Due to the use of Label, this label will resolve to the correct target
+ # even if the canonical name of @dep_of_my_ruleset should be different
+ # in the main workspace, e.g. due to repo mappings.
+ Label("@dep_of_my_ruleset//tools:foo"),
+ ],
+ "//conditions:default": [],
+ }),
+ **kwargs,
+ )
+```
+
## Debugging
* `bazel query --output=build //my/path:all` will show you how the `BUILD` file
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java b/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
index 799b05023f8fcb..4a2547dbaf3e6a 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
@@ -16,6 +16,7 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.Depset;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import java.util.Arrays;
@@ -87,9 +88,9 @@ public static Object select(Dict, ?> dict, String noMatchError) throws EvalExc
+ " to match");
}
for (Object key : dict.keySet()) {
- if (!(key instanceof String)) {
+ if (!(key instanceof String || key instanceof Label)) {
throw Starlark.errorf(
- "select: got %s for dict key, want a label string", Starlark.type(key));
+ "select: got %s for dict key, want a Label or label string", Starlark.type(key));
}
}
return SelectorList.of(new SelectorValue(dict, noMatchError));
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java
index 75bf5538a31545..6c9025e81923b9 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java
@@ -330,8 +330,10 @@ public Depset depset(
name = "x",
positional = true,
doc =
- "A dict that maps configuration conditions to values. Each key is a label string"
- + " that identifies a config_setting instance."),
+ "A dict that maps configuration conditions to values. Each key is a "
+ + "Label or a label string"
+ + " that identifies a config_setting, constraint_setting, or constraint_value"
+ + " instance."),
@Param(
name = "no_match_error",
defaultValue = "''",
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java
index 5af218fe924b36..7365caa4ac62a8 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java
@@ -579,10 +579,11 @@ StarlarkAspectApi aspect(
@StarlarkMethod(
name = "Label",
doc =
- "Creates a Label referring to a BUILD target. Use this function only when you want to"
- + " give a default value for the label attributes. 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:
Label(\"//tools:default\")", parameters = { @Param(name = "label_string", doc = "the label string."), diff --git a/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java b/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java index 79fb42bbc258af..fb57ba1e020bbb 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java @@ -427,7 +427,8 @@ public void configKeyTypeChecking_Int() throws Exception { " name = 'int_key',", " srcs = select({123: ['a.java']})", ")"); - assertTargetError("//java/foo:int_key", "select: got int for dict key, want a label string"); + assertTargetError( + "//java/foo:int_key", "select: got int for dict key, want a Label or label string"); } @Test @@ -439,7 +440,8 @@ public void configKeyTypeChecking_Bool() throws Exception { " name = 'bool_key',", " srcs = select({True: ['a.java']})", ")"); - assertTargetError("//java/foo:bool_key", "select: got bool for dict key, want a label string"); + assertTargetError( + "//java/foo:bool_key", "select: got bool for dict key, want a Label or label string"); } @Test @@ -452,7 +454,7 @@ public void configKeyTypeChecking_None() throws Exception { " srcs = select({None: ['a.java']})", ")"); assertTargetError( - "//java/foo:none_key", "select: got NoneType for dict key, want a label string"); + "//java/foo:none_key", "select: got NoneType for dict key, want a Label or label string"); } @Test @@ -1526,4 +1528,49 @@ public void publicVisibilityConfigSetting_defaultIsPrivate() throws Exception { assertThat(getConfiguredTarget("//a:binary")).isNotNull(); assertNoEvents(); } + + @Test + public void selectWithLabelKeysInMacro() throws Exception { + writeConfigRules(); + scratch.file("java/BUILD"); + scratch.file( + "java/macros.bzl", + "def my_java_binary(name, deps = [], **kwargs):", + " native.java_binary(", + " name = name,", + " deps = select({", + " Label('//conditions:a'): [Label('//java/foo:a')],", + " '//conditions:b': [Label('//java/foo:b')],", + " }) + select({", + " '//conditions:a': [Label('//java/foo:a2')],", + " Label('//conditions:b'): [Label('//java/foo:b2')],", + " }),", + " **kwargs,", + " )"); + scratch.file( + "java/foo/BUILD", + "load('//java:macros.bzl', 'my_java_binary')", + "my_java_binary(", + " name = 'binary',", + " srcs = ['binary.java'],", + ")", + "java_library(", + " name = 'a',", + " srcs = ['a.java'])", + "java_library(", + " name = 'b',", + " srcs = ['b.java'])", + "java_library(", + " name = 'a2',", + " srcs = ['a2.java'])", + "java_library(", + " name = 'b2',", + " srcs = ['b2.java'])"); + + checkRule( + "//java/foo:binary", + "--foo=b", + /*expected:*/ ImmutableList.of("bin java/foo/libb.jar", "bin java/foo/libb2.jar"), + /*not expected:*/ ImmutableList.of("bin java/foo/liba.jar", "bin java/foo/liba2.jar")); + } }