// Copyright 2015 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.skylark; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static org.junit.Assert.fail; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ActionsProvider; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.configuredtargets.FileConfiguredTarget; import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext; import com.google.devtools.build.lib.analysis.util.MockRule; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.Info; import com.google.devtools.build.lib.packages.SkylarkProvider.SkylarkKey; import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; import com.google.devtools.build.lib.rules.java.JavaInfo; import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider; import com.google.devtools.build.lib.rules.python.PyCommon; import com.google.devtools.build.lib.skylark.util.SkylarkTestCase; import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; import com.google.devtools.build.lib.testutil.TestRuleClassProvider; import com.google.devtools.build.lib.util.FileTypeSet; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for SkylarkRuleContext. */ @RunWith(JUnit4.class) public class SkylarkRuleContextTest extends SkylarkTestCase { /** A test rule that exercises the semantics of mandatory providers. */ private static final MockRule TESTING_RULE_FOR_MANDATORY_PROVIDERS = () -> MockRule.define( "testing_rule_for_mandatory_providers", (builder, env) -> builder .setUndocumented() .add(attr("srcs", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .add( attr("deps", LABEL_LIST) .legacyAllowAnyFileType() .mandatoryProvidersList( ImmutableList.of( ImmutableList.of(SkylarkProviderIdentifier.forLegacy("a")), ImmutableList.of( SkylarkProviderIdentifier.forLegacy("b"), SkylarkProviderIdentifier.forLegacy("c")))))); @Override protected ConfiguredRuleClassProvider getRuleClassProvider() { ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder() .addRuleDefinition(TESTING_RULE_FOR_MANDATORY_PROVIDERS); TestRuleClassProvider.addStandardRules(builder); return builder.build(); } @Before public final void generateBuildFile() throws Exception { scratch.file( "foo/BUILD", "package(features = ['-f1', 'f2', 'f3'])", "genrule(name = 'foo',", " cmd = 'dummy_cmd',", " srcs = ['a.txt', 'b.img'],", " tools = ['t.exe'],", " outs = ['c.txt'])", "genrule(name = 'foo2',", " cmd = 'dummy_cmd',", " outs = ['e.txt'])", "genrule(name = 'bar',", " cmd = 'dummy_cmd',", " srcs = [':jl', ':gl'],", " outs = ['d.txt'])", "java_library(name = 'jl',", " srcs = ['a.java'])", "android_library(name = 'androidlib',", " srcs = ['a.java'])", "java_import(name = 'asr',", " jars = [ 'asr.jar' ],", " srcjar = 'asr-src.jar',", ")", "genrule(name = 'gl',", " cmd = 'touch $(OUTS)',", " srcs = ['a.go'],", " outs = [ 'gl.a', 'gl.gcgox', ],", " output_to_bindir = 1,", ")", "cc_library(name = 'cc_with_features',", " srcs = ['dummy.cc'],", " features = ['f1', '-f3'],", ")" ); } private void setUpAttributeErrorTest() throws Exception { scratch.file("test/BUILD", "load('//test:macros.bzl', 'macro_native_rule', 'macro_skylark_rule', 'skylark_rule')", "macro_native_rule(name = 'm_native',", " deps = [':jlib'])", "macro_skylark_rule(name = 'm_skylark',", " deps = [':jlib'])", "java_library(name = 'jlib',", " srcs = ['bla.java'])", "cc_library(name = 'cclib',", " deps = [':jlib'])", "skylark_rule(name = 'skyrule',", " deps = [':jlib'])"); scratch.file("test/macros.bzl", "def _impl(ctx):", " return", "skylark_rule = rule(", " implementation = _impl,", " attrs = {", " 'deps': attr.label_list(providers = ['some_provider'], allow_files=True)", " }", ")", "def macro_native_rule(name, deps): ", " native.cc_library(name = name, deps = deps)", "def macro_skylark_rule(name, deps):", " skylark_rule(name = name, deps = deps)"); reporter.removeHandler(failFastHandler); } @Test public void hasCorrectLocationForRuleAttributeError_NativeRuleWithMacro() throws Exception { setUpAttributeErrorTest(); try { createRuleContext("//test:m_native"); fail("Should have failed because of invalid dependency"); } catch (Exception ex) { // Macro creates native rule -> location points to the rule and the message contains details // about the macro. assertContainsEvent( "ERROR /workspace/test/BUILD:2:1: in deps attribute of cc_library rule //test:m_native: " + "java_library rule '//test:jlib' is misplaced here (expected "); // Skip the part of the error message that has details about the allowed deps since the mocks // for the mac tests might have different values for them. assertContainsEvent(". Since this " + "rule was created by the macro 'macro_native_rule', the error might have been caused " + "by the macro implementation in /workspace/test/macros.bzl:10:41"); } } @Test public void hasCorrectLocationForRuleAttributeError_SkylarkRuleWithMacro() throws Exception { setUpAttributeErrorTest(); try { createRuleContext("//test:m_skylark"); fail("Should have failed because of invalid attribute value"); } catch (Exception ex) { // Macro creates Skylark rule -> location points to the rule and the message contains details // about the macro. assertContainsEvent( "ERROR /workspace/test/BUILD:4:1: in deps attribute of skylark_rule rule " + "//test:m_skylark: '//test:jlib' does not have mandatory providers:" + " 'some_provider'. " + "Since this rule was created by the macro 'macro_skylark_rule', the error might " + "have been caused by the macro implementation in /workspace/test/macros.bzl:12:36"); } } @Test public void hasCorrectLocationForRuleAttributeError_NativeRule() throws Exception { setUpAttributeErrorTest(); try { createRuleContext("//test:cclib"); fail("Should have failed because of invalid dependency"); } catch (Exception ex) { // Native rule WITHOUT macro -> location points to the attribute and there is no mention of // 'macro' at all. assertContainsEvent("ERROR /workspace/test/BUILD:9:10: in deps attribute of " + "cc_library rule //test:cclib: java_library rule '//test:jlib' is misplaced here " + "(expected "); // Skip the part of the error message that has details about the allowed deps since the mocks // for the mac tests might have different values for them. assertDoesNotContainEvent("Since this rule was created by the macro"); } } @Test public void hasCorrectLocationForRuleAttributeError_SkylarkRule() throws Exception { setUpAttributeErrorTest(); try { createRuleContext("//test:skyrule"); fail("Should have failed because of invalid dependency"); } catch (Exception ex) { // Skylark rule WITHOUT macro -> location points to the attribute and there is no mention of // 'macro' at all. assertContainsEvent("ERROR /workspace/test/BUILD:11:10: in deps attribute of " + "skylark_rule rule //test:skyrule: '//test:jlib' does not have mandatory providers: " + "'some_provider'"); } } @Test public void testMandatoryProvidersListWithSkylark() throws Exception { scratch.file("test/BUILD", "load('//test:rules.bzl', 'skylark_rule', 'my_rule', 'my_other_rule')", "my_rule(name = 'mylib',", " srcs = ['a.py'])", "skylark_rule(name = 'skyrule1',", " deps = [':mylib'])", "my_other_rule(name = 'my_other_lib',", " srcs = ['a.py'])", "skylark_rule(name = 'skyrule2',", " deps = [':my_other_lib'])"); scratch.file("test/rules.bzl", "def _impl(ctx):", " return", "skylark_rule = rule(", " implementation = _impl,", " attrs = {", " 'deps': attr.label_list(providers = [['a'], ['b', 'c']],", " allow_files=True)", " }", ")", "def my_rule_impl(ctx):", " return struct(a = [])", "my_rule = rule(implementation = my_rule_impl, ", " attrs = { 'srcs' : attr.label_list(allow_files=True)})", "def my_other_rule_impl(ctx):", " return struct(b = [])", "my_other_rule = rule(implementation = my_other_rule_impl, ", " attrs = { 'srcs' : attr.label_list(allow_files=True)})"); reporter.removeHandler(failFastHandler); assertThat(getConfiguredTarget("//test:skyrule1")).isNotNull(); try { createRuleContext("//test:skyrule2"); fail("Should have failed because of wrong mandatory providers"); } catch (Exception ex) { assertContainsEvent("ERROR /workspace/test/BUILD:9:10: in deps attribute of " + "skylark_rule rule //test:skyrule2: '//test:my_other_lib' does not have " + "mandatory providers: 'a' or 'c'"); } } @Test public void testMandatoryProvidersListWithNative() throws Exception { scratch.file("test/BUILD", "load('//test:rules.bzl', 'my_rule', 'my_other_rule')", "my_rule(name = 'mylib',", " srcs = ['a.py'])", "testing_rule_for_mandatory_providers(name = 'skyrule1',", " deps = [':mylib'])", "my_other_rule(name = 'my_other_lib',", " srcs = ['a.py'])", "testing_rule_for_mandatory_providers(name = 'skyrule2',", " deps = [':my_other_lib'])"); scratch.file("test/rules.bzl", "def my_rule_impl(ctx):", " return struct(a = [])", "my_rule = rule(implementation = my_rule_impl, ", " attrs = { 'srcs' : attr.label_list(allow_files=True)})", "def my_other_rule_impl(ctx):", " return struct(b = [])", "my_other_rule = rule(implementation = my_other_rule_impl, ", " attrs = { 'srcs' : attr.label_list(allow_files=True)})"); reporter.removeHandler(failFastHandler); assertThat(getConfiguredTarget("//test:skyrule1")).isNotNull(); try { createRuleContext("//test:skyrule2"); fail("Should have failed because of wrong mandatory providers"); } catch (Exception ex) { assertContainsEvent("ERROR /workspace/test/BUILD:9:10: in deps attribute of " + "testing_rule_for_mandatory_providers rule //test:skyrule2: '//test:my_other_lib' " + "does not have mandatory providers: 'a' or 'c'"); } } /* Sharing setup code between the testPackageBoundaryError*() methods is not possible since the * errors already happen when loading the file. Consequently, all tests would fail at the same * statement. */ @Test public void testPackageBoundaryError_NativeRule() throws Exception { scratch.file("test/BUILD", "cc_library(name = 'cclib',", " srcs = ['sub/my_sub_lib.h'])"); scratch.file("test/sub/BUILD", "cc_library(name = 'my_sub_lib', srcs = ['my_sub_lib.h'])"); reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:cclib"); assertContainsEvent( "ERROR /workspace/test/BUILD:2:10: Label '//test:sub/my_sub_lib.h' crosses boundary of " + "subpackage 'test/sub' (perhaps you meant to put the colon here: " + "'//test/sub:my_sub_lib.h'?)"); } @Test public void testPackageBoundaryError_SkylarkRule() throws Exception { scratch.file("test/BUILD", "load('//test:macros.bzl', 'skylark_rule')", "skylark_rule(name = 'skyrule',", " srcs = ['sub/my_sub_lib.h'])"); scratch.file("test/sub/BUILD", "cc_library(name = 'my_sub_lib', srcs = ['my_sub_lib.h'])"); scratch.file("test/macros.bzl", "def _impl(ctx):", " return", "skylark_rule = rule(", " implementation = _impl,", " attrs = {", " 'srcs': attr.label_list(allow_files=True)", " }", ")"); reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:skyrule"); assertContainsEvent( "ERROR /workspace/test/BUILD:3:10: Label '//test:sub/my_sub_lib.h' crosses boundary of " + "subpackage 'test/sub' (perhaps you meant to put the colon here: " + "'//test/sub:my_sub_lib.h'?)"); } @Test public void testPackageBoundaryError_SkylarkMacro() throws Exception { scratch.file("test/BUILD", "load('//test:macros.bzl', 'macro_skylark_rule')", "macro_skylark_rule(name = 'm_skylark',", " srcs = ['sub/my_sub_lib.h'])"); scratch.file("test/sub/BUILD", "cc_library(name = 'my_sub_lib', srcs = ['my_sub_lib.h'])"); scratch.file("test/macros.bzl", "def _impl(ctx):", " return", "skylark_rule = rule(", " implementation = _impl,", " attrs = {", " 'srcs': attr.label_list(allow_files=True)", " }", ")", "def macro_skylark_rule(name, srcs=[]):", " skylark_rule(name = name, srcs = srcs)"); reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:m_skylark"); assertContainsEvent("ERROR /workspace/test/BUILD:2:1: Label '//test:sub/my_sub_lib.h' " + "crosses boundary of subpackage 'test/sub' (perhaps you meant to put the colon here: " + "'//test/sub:my_sub_lib.h'?)"); } /* The error message for this case used to be wrong. */ @Test public void testPackageBoundaryError_ExternalRepository_Boundary() throws Exception { scratch.file("r/WORKSPACE"); scratch.file("r/BUILD"); scratch.overwriteFile( "WORKSPACE", new ImmutableList.Builder() .addAll(analysisMock.getWorkspaceContents(mockToolsConfig)) .add("local_repository(name='r', path='r')") .build()); scratch.file("BUILD", "cc_library(name = 'cclib',", " srcs = ['r/my_sub_lib.h'])"); invalidatePackages( /*alsoConfigs=*/ false); // Repository shuffling messes with toolchain labels. reporter.removeHandler(failFastHandler); getConfiguredTarget("//:cclib"); assertContainsEvent( "/workspace/BUILD:2:10: Label '//:r/my_sub_lib.h' crosses boundary of " + "subpackage '@r//'"); } /* The error message for this case used to be wrong. */ @Test public void testPackageBoundaryError_ExternalRepository_EntirelyInside() throws Exception { scratch.file("/r/WORKSPACE"); scratch.file("/r/BUILD", "cc_library(name = 'cclib',", " srcs = ['sub/my_sub_lib.h'])"); scratch.file("/r/sub/BUILD", "cc_library(name = 'my_sub_lib', srcs = ['my_sub_lib.h'])"); scratch.overwriteFile("WORKSPACE", new ImmutableList.Builder() .addAll(analysisMock.getWorkspaceContents(mockToolsConfig)) .add("local_repository(name='r', path='/r')") .build()); invalidatePackages(/*alsoConfigs=*/false); // Repository shuffling messes with toolchain labels. reporter.removeHandler(failFastHandler); getConfiguredTarget("@r//:cclib"); assertContainsEvent( "/external/r/BUILD:2:10: Label '@r//:sub/my_sub_lib.h' crosses boundary of " + "subpackage '@r//sub' (perhaps you meant to put the colon here: " + "'@r//sub:my_sub_lib.h'?)"); } /* * Making the location in BUILD file the default for "crosses boundary of subpackage" errors does * not work in this case since the error actually happens in the bzl file. However, because of * the current design, we can neither show the location in the bzl file nor display both * locations (BUILD + bzl). * * Since this case is less common than having such an error in a BUILD file, we can live * with it. */ @Test public void testPackageBoundaryError_SkylarkMacroWithErrorInBzlFile() throws Exception { scratch.file("test/BUILD", "load('//test:macros.bzl', 'macro_skylark_rule')", "macro_skylark_rule(name = 'm_skylark')"); scratch.file("test/sub/BUILD", "cc_library(name = 'my_sub_lib', srcs = ['my_sub_lib.h'])"); scratch.file("test/macros.bzl", "def _impl(ctx):", " return", "skylark_rule = rule(", " implementation = _impl,", " attrs = {", " 'srcs': attr.label_list(allow_files=True)", " }", ")", "def macro_skylark_rule(name, srcs=[]):", " skylark_rule(name = name, srcs = srcs + ['sub/my_sub_lib.h'])"); reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:m_skylark"); assertContainsEvent("ERROR /workspace/test/BUILD:2:1: Label '//test:sub/my_sub_lib.h' " + "crosses boundary of subpackage 'test/sub' (perhaps you meant to put the colon here: " + "'//test/sub:my_sub_lib.h'?)"); } @Test public void testPackageBoundaryError_NativeMacro() throws Exception { scratch.file("test/BUILD", "load('//test:macros.bzl', 'macro_native_rule')", "macro_native_rule(name = 'm_native',", " srcs = ['sub/my_sub_lib.h'])"); scratch.file("test/sub/BUILD", "cc_library(name = 'my_sub_lib', srcs = ['my_sub_lib.h'])"); scratch.file("test/macros.bzl", "def macro_native_rule(name, deps=[], srcs=[]): ", " native.cc_library(name = name, deps = deps, srcs = srcs)"); reporter.removeHandler(failFastHandler); getConfiguredTarget("//test:m_native"); assertContainsEvent("ERROR /workspace/test/BUILD:2:1: Label '//test:sub/my_sub_lib.h' " + "crosses boundary of subpackage 'test/sub' (perhaps you meant to put the colon here: " + "'//test/sub:my_sub_lib.h'?)"); } @Test public void shouldGetPrerequisiteArtifacts() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.files.srcs"); assertArtifactList(result, ImmutableList.of("a.txt", "b.img")); } private void assertArtifactList(Object result, List artifacts) { assertThat(result).isInstanceOf(SkylarkList.class); SkylarkList resultList = (SkylarkList) result; assertThat(resultList).hasSize(artifacts.size()); int i = 0; for (String artifact : artifacts) { assertThat(((Artifact) resultList.get(i++)).getFilename()).isEqualTo(artifact); } } @Test public void shouldGetPrerequisites() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:bar"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.srcs"); // Check for a known provider TransitiveInfoCollection tic1 = (TransitiveInfoCollection) ((SkylarkList) result).get(0); assertThat(JavaInfo.getProvider(JavaSourceJarsProvider.class, tic1)).isNotNull(); // Check an unimplemented provider too assertThat(tic1.get(PyCommon.PYTHON_SKYLARK_PROVIDER_NAME)).isNull(); } @Test public void shouldGetPrerequisite() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:asr"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.srcjar"); TransitiveInfoCollection tic = (TransitiveInfoCollection) result; assertThat(tic).isInstanceOf(FileConfiguredTarget.class); assertThat(tic.getLabel().getName()).isEqualTo("asr-src.jar"); } @Test public void testGetRuleAttributeListType() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.outs"); assertThat(result).isInstanceOf(SkylarkList.class); } @Test public void testGetRuleSelect() throws Exception { scratch.file("test/skylark/BUILD"); scratch.file( "test/skylark/rulestr.bzl", "def rule_dict(name):", " return native.existing_rule(name)"); scratch.file( "test/getrule/BUILD", "load('//test/skylark:rulestr.bzl', 'rule_dict')", "cc_library(name ='x', ", " srcs = select({'//conditions:default': []})", ")", "rule_dict('x')"); // Parse the BUILD file, to make sure select() makes it out of native.rule(). createRuleContext("//test/getrule:x"); } @Test public void testExistingRuleReturnNone() throws Exception { scratch.file( "test/rulestr.bzl", "def test_rule(name, x):", " print(native.existing_rule(x))", " if native.existing_rule(x) == None:", " native.cc_library(name = name)"); scratch.file( "test/BUILD", "load('//test:rulestr.bzl', 'test_rule')", "test_rule('a', 'does not exist')", "test_rule('b', 'BUILD')"); assertThat(getConfiguredTarget("//test:a")).isNotNull(); assertThat(getConfiguredTarget("//test:b")).isNotNull(); } @Test public void existingRuleWithSelect() throws Exception { scratch.file( "test/existing_rule.bzl", "def macro():", " s = select({'//foo': ['//bar']})", " native.cc_library(name = 'x', srcs = s)", " print(native.existing_rule('x')['srcs'])"); scratch.file( "test/BUILD", "load('//test:existing_rule.bzl', 'macro')", "macro()", "cc_library(name = 'a', srcs = [])"); getConfiguredTarget("//test:a"); assertContainsEvent("select({Label(\"//foo:foo\"): [Label(\"//bar:bar\")]})"); } @Test public void testGetRule() throws Exception { scratch.file("test/skylark/BUILD"); scratch.file( "test/skylark/rulestr.bzl", "def rule_dict(name):", " return native.existing_rule(name)", "def rules_dict():", " return native.existing_rules()", "def nop(ctx):", " pass", "nop_rule = rule(attrs = {'x': attr.label()}, implementation = nop)", "consume_rule = rule(attrs = {'s': attr.string_list()}, implementation = nop)"); scratch.file( "test/getrule/BUILD", "load('//test/skylark:rulestr.bzl', 'rules_dict', 'rule_dict', 'nop_rule', 'consume_rule')", "genrule(name = 'a', outs = ['a.txt'], ", " licenses = ['notice'],", " output_to_bindir = False,", " tools = [ '//test:bla' ], cmd = 'touch $@')", "nop_rule(name = 'c', x = ':a')", "rlist= rules_dict()", "consume_rule(name = 'all_str', s = [rlist['a']['kind'], rlist['a']['name'], ", " rlist['c']['kind'], rlist['c']['name']])", "adict = rule_dict('a')", "cdict = rule_dict('c')", "consume_rule(name = 'a_str', ", " s = [adict['kind'], adict['name'], adict['outs'][0], adict['tools'][0]])", "consume_rule(name = 'genrule_attr', ", " s = adict.keys())", "consume_rule(name = 'c_str', s = [cdict['kind'], cdict['name'], cdict['x']])"); SkylarkRuleContext allContext = createRuleContext("//test/getrule:all_str"); List result = (List) evalRuleContextCode(allContext, "ruleContext.attr.s"); assertThat(result).containsExactly("genrule", "a", "nop_rule", "c"); result = (List) evalRuleContextCode( createRuleContext("//test/getrule:a_str"), "ruleContext.attr.s"); assertThat(result).containsExactly("genrule", "a", ":a.txt", "//test:bla"); result = (List) evalRuleContextCode( createRuleContext("//test/getrule:c_str"), "ruleContext.attr.s"); assertThat(result).containsExactly("nop_rule", "c", ":a"); result = (List) evalRuleContextCode( createRuleContext("//test/getrule:genrule_attr"), "ruleContext.attr.s"); assertThat(result).containsExactly( "name", "visibility", "transitive_configs", "tags", "generator_name", "generator_function", "generator_location", "features", "compatible_with", "restricted_to", "srcs", "tools", "toolchains", "outs", "cmd", "output_to_bindir", "local", "message", "executable", "stamp", "heuristic_label_expansion", "kind", "exec_compatible_with"); } @Test public void testGetRuleAttributeListValue() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.outs"); assertThat(((SkylarkList) result)).hasSize(1); } @Test public void testGetRuleAttributeListValueNoGet() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.outs"); assertThat(((SkylarkList) result)).hasSize(1); } @Test public void testGetRuleAttributeStringTypeValue() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.cmd"); assertThat((String) result).isEqualTo("dummy_cmd"); } @Test public void testGetRuleAttributeStringTypeValueNoGet() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.cmd"); assertThat((String) result).isEqualTo("dummy_cmd"); } @Test public void testGetRuleAttributeBadAttributeName() throws Exception { checkErrorContains( createRuleContext("//foo:foo"), "No attribute 'bad'", "ruleContext.attr.bad"); } @Test public void testGetLabel() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.label"); assertThat(((Label) result).toString()).isEqualTo("//foo:foo"); } @Test public void testRuleError() throws Exception { checkErrorContains(createRuleContext("//foo:foo"), "message", "fail('message')"); } @Test public void testAttributeError() throws Exception { checkErrorContains( createRuleContext("//foo:foo"), "attribute srcs: message", "fail(attr='srcs', msg='message')"); } @Test public void testGetExecutablePrerequisite() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:androidlib"); Object result = evalRuleContextCode(ruleContext, "ruleContext.executable._idlclass"); assertThat(((Artifact) result).getFilename()).matches("^IdlClass(\\.exe){0,1}$"); } @Test public void testCreateSpawnActionArgumentsWithExecutableFilesToRunProvider() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:androidlib"); evalRuleContextCode( ruleContext, "ruleContext.actions.run(\n" + " inputs = ruleContext.files.srcs,\n" + " outputs = ruleContext.files.srcs,\n" + " arguments = ['--a','--b'],\n" + " executable = ruleContext.executable._idlclass)\n"); SpawnAction action = (SpawnAction) Iterables.getOnlyElement( ruleContext.getRuleContext().getAnalysisEnvironment().getRegisteredActions()); assertThat(action.getCommandFilename()).matches("^.*/IdlClass(\\.exe){0,1}$"); } @Test public void testOutputs() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:bar"); Iterable result = (Iterable) evalRuleContextCode(ruleContext, "ruleContext.outputs.outs"); assertThat(((Artifact) Iterables.getOnlyElement(result)).getFilename()).isEqualTo("d.txt"); } @Test public void testSkylarkRuleContextGetDefaultShellEnv() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.configuration.default_shell_env"); assertThat(result).isInstanceOf(SkylarkDict.class); } @Test public void testCheckPlaceholders() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.check_placeholders('%{name}', ['name'])"); assertThat(result).isEqualTo(true); } @Test public void testCheckPlaceholdersBadPlaceholder() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.check_placeholders('%{name}', ['abc'])"); assertThat(result).isEqualTo(false); } @Test public void testExpandMakeVariables() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode( ruleContext, "ruleContext.expand_make_variables('cmd', '$(ABC)', {'ABC': 'DEF'})"); assertThat(result).isEqualTo("DEF"); } @Test public void testExpandMakeVariablesShell() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.expand_make_variables('cmd', '$$ABC', {})"); assertThat(result).isEqualTo("$ABC"); } private void setUpMakeVarToolchain() throws Exception { scratch.file( "vars/vars.bzl", "def _make_var_supplier_impl(ctx):", " val = ctx.attr.value", " return [platform_common.TemplateVariableInfo({'MAKE_VAR_VALUE': val})]", "make_var_supplier = rule(", " implementation = _make_var_supplier_impl,", " attrs = {", " 'value': attr.string(mandatory = True),", " })", "def _make_var_user_impl(ctx):", " return []", "make_var_user = rule(", " implementation = _make_var_user_impl,", ")"); scratch.file( "vars/BUILD", "load(':vars.bzl', 'make_var_supplier', 'make_var_user')", "make_var_supplier(name = 'supplier', value = 'foo')", "make_var_user(", " name = 'vars',", " toolchains = [':supplier'],", ")"); } @Test public void testExpandMakeVariables_cc() throws Exception { setUpMakeVarToolchain(); SkylarkRuleContext ruleContext = createRuleContext("//vars:vars"); String result = (String) evalRuleContextCode( ruleContext, "ruleContext.expand_make_variables('cmd', '$(CC)', {})"); assertThat(result).isNotEmpty(); } @Test public void testExpandMakeVariables_toolchain() throws Exception { setUpMakeVarToolchain(); SkylarkRuleContext ruleContext = createRuleContext("//vars:vars"); Object result = evalRuleContextCode( ruleContext, "ruleContext.expand_make_variables('cmd', '$(MAKE_VAR_VALUE)', {})"); assertThat(result).isEqualTo("foo"); } @Test public void testVar_toolchain() throws Exception { setUpMakeVarToolchain(); SkylarkRuleContext ruleContext = createRuleContext("//vars:vars"); Object result = evalRuleContextCode(ruleContext, "ruleContext.var['MAKE_VAR_VALUE']"); assertThat(result).isEqualTo("foo"); } @Test public void testConfiguration() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.configuration"); assertThat(ruleContext.getRuleContext().getConfiguration()).isSameAs(result); } @Test public void testFeatures() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:cc_with_features"); Object result = evalRuleContextCode(ruleContext, "ruleContext.features"); assertThat((SkylarkList) result).containsExactly("cc_include_scanning", "f1", "f2"); } @Test public void testDisabledFeatures() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:cc_with_features"); Object result = evalRuleContextCode(ruleContext, "ruleContext.disabled_features"); assertThat((SkylarkList) result).containsExactly("f3"); } @Test public void testHostConfiguration() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.host_configuration"); assertThat(ruleContext.getRuleContext().getHostConfiguration()).isSameAs(result); } @Test public void testWorkspaceName() throws Exception { assertThat(ruleClassProvider.getRunfilesPrefix()).isNotNull(); assertThat(ruleClassProvider.getRunfilesPrefix()).isNotEmpty(); SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.workspace_name"); assertThat(ruleClassProvider.getRunfilesPrefix()).isEqualTo(result); } @Test public void testDeriveArtifactLegacy() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode( ruleContext, "ruleContext.new_file(ruleContext.genfiles_dir," + " 'a/b.txt')"); PathFragment fragment = ((Artifact) result).getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/a/b.txt"); } @Test public void testDeriveArtifact() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.new_file('a/b.txt')"); PathFragment fragment = ((Artifact) result).getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/a/b.txt"); } @Test public void testDeriveTreeArtifact() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "ruleContext.actions.declare_directory('a/b')"); Artifact artifact = (Artifact) result; PathFragment fragment = artifact.getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/a/b"); assertThat(artifact.isTreeArtifact()).isTrue(); } @Test public void testDeriveTreeArtifactType() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode(ruleContext, "b = ruleContext.actions.declare_directory('a/b')\n" + "type(b)"); assertThat(result).isInstanceOf(String.class); assertThat(result).isEqualTo("File"); } @Test public void testDeriveTreeArtifactNextToSibling() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode( ruleContext, "b = ruleContext.actions.declare_directory('a/b')\n" + "ruleContext.actions.declare_directory('c', sibling=b)"); Artifact artifact = (Artifact) result; PathFragment fragment = artifact.getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/a/c"); assertThat(artifact.isTreeArtifact()).isTrue(); } @Test public void testParamFileLegacy() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode( ruleContext, "ruleContext.new_file(ruleContext.bin_dir," + "ruleContext.files.tools[0], '.params')"); PathFragment fragment = ((Artifact) result).getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/t.exe.params"); } @Test public void testParamFileSuffixLegacy() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode( ruleContext, "ruleContext.new_file(ruleContext.files.tools[0], " + "ruleContext.files.tools[0].basename + '.params')"); PathFragment fragment = ((Artifact) result).getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/t.exe.params"); } @Test public void testParamFileSuffix() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:foo"); Object result = evalRuleContextCode( ruleContext, "ruleContext.actions.declare_file(ruleContext.files.tools[0].basename + '.params', " + "sibling = ruleContext.files.tools[0])"); PathFragment fragment = ((Artifact) result).getRootRelativePath(); assertThat(fragment.getPathString()).isEqualTo("foo/t.exe.params"); } @Test public void testLabelKeyedStringDictConvertsToTargetToStringMap() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(),", " }", ")"); scratch.file( "BUILD", "filegroup(name='dep')", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={':dep': 'value'})"); invalidatePackages(); SkylarkRuleContext context = createRuleContext("//:r"); Label keyLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.label_dict.keys()[0].label"); assertThat(keyLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); String valueString = (String) evalRuleContextCode(context, "ruleContext.attr.label_dict.values()[0]"); assertThat(valueString).isEqualTo("value"); } @Test public void testLabelKeyedStringDictTranslatesAliases() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(),", " }", ")"); scratch.file( "BUILD", "filegroup(name='dep')", "alias(name='alias', actual='dep')", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={':alias': 'value'})"); invalidatePackages(); SkylarkRuleContext context = createRuleContext("//:r"); Label keyLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.label_dict.keys()[0].label"); assertThat(keyLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); String valueString = (String) evalRuleContextCode(context, "ruleContext.attr.label_dict.values()[0]"); assertThat(valueString).isEqualTo("value"); } @Test public void testLabelKeyedStringDictAcceptsDefaultValues() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(default={Label('//:default'): 'defs'}),", " }", ")"); scratch.file( "BUILD", "filegroup(name='default')", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r')"); invalidatePackages(); SkylarkRuleContext context = createRuleContext("//:r"); Label keyLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.label_dict.keys()[0].label"); assertThat(keyLabel).isEqualTo(Label.parseAbsolute("//:default", ImmutableMap.of())); String valueString = (String) evalRuleContextCode(context, "ruleContext.attr.label_dict.values()[0]"); assertThat(valueString).isEqualTo("defs"); } @Test public void testLabelKeyedStringDictAllowsFilesWhenAllowFilesIsTrue() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(allow_files=True),", " }", ")"); scratch.file("myfile.cc"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={'myfile.cc': 'value'})"); invalidatePackages(); createRuleContext("//:r"); assertNoEvents(); } @Test public void testLabelKeyedStringDictAllowsFilesOfAppropriateTypes() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(allow_files=['.cc']),", " }", ")"); scratch.file("myfile.cc"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={'myfile.cc': 'value'})"); invalidatePackages(); createRuleContext("//:r"); assertNoEvents(); } @Test public void testLabelKeyedStringDictForbidsFilesOfIncorrectTypes() throws Exception { reporter.removeHandler(failFastHandler); scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(allow_files=['.cc']),", " }", ")"); scratch.file("myfile.cpp"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={'myfile.cpp': 'value'})"); invalidatePackages(); getConfiguredTarget("//:r"); assertContainsEvent("file '//:myfile.cpp' is misplaced here (expected .cc)"); } @Test public void testLabelKeyedStringDictForbidsFilesWhenAllowFilesIsFalse() throws Exception { reporter.removeHandler(failFastHandler); scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(allow_files=False),", " }", ")"); scratch.file("myfile.cpp"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={'myfile.cpp': 'value'})"); invalidatePackages(); getConfiguredTarget("//:r"); assertContainsEvent("in label_dict attribute of my_rule rule //:r: " + "source file '//:myfile.cpp' is misplaced here (expected no files)"); } @Test public void testLabelKeyedStringDictAllowsRulesWithRequiredProviders() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(providers=[['my_provider']]),", " }", ")", "def _dep_impl(ctx):", " return struct(my_provider=5)", "my_dep_rule = rule(", " implementation = _dep_impl,", " attrs = {}", ")"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule', 'my_dep_rule')", "my_dep_rule(name='dep')", "my_rule(name='r',", " label_dict={':dep': 'value'})"); invalidatePackages(); createRuleContext("//:r"); assertNoEvents(); } @Test public void testLabelKeyedStringDictForbidsRulesMissingRequiredProviders() throws Exception { reporter.removeHandler(failFastHandler); scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(providers=[['my_provider']]),", " }", ")", "def _dep_impl(ctx):", " return", "my_dep_rule = rule(", " implementation = _dep_impl,", " attrs = {}", ")"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule', 'my_dep_rule')", "my_dep_rule(name='dep')", "my_rule(name='r',", " label_dict={':dep': 'value'})"); invalidatePackages(); getConfiguredTarget("//:r"); assertContainsEvent("in label_dict attribute of my_rule rule //:r: " + "'//:dep' does not have mandatory providers: 'my_provider'"); } @Test public void testLabelKeyedStringDictForbidsEmptyDictWhenAllowEmptyIsFalse() throws Exception { reporter.removeHandler(failFastHandler); scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(allow_empty=False),", " }", ")"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={})"); invalidatePackages(); getConfiguredTarget("//:r"); assertContainsEvent("in label_dict attribute of my_rule rule //:r: " + "attribute must be non empty"); } @Test public void testLabelKeyedStringDictAllowsEmptyDictWhenAllowEmptyIsTrue() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(allow_empty=True),", " }", ")"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r',", " label_dict={})"); invalidatePackages(); createRuleContext("//:r"); assertNoEvents(); } @Test public void testLabelKeyedStringDictForbidsMissingAttributeWhenMandatoryIsTrue() throws Exception { reporter.removeHandler(failFastHandler); scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(mandatory=True),", " }", ")"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r')"); invalidatePackages(); getConfiguredTarget("//:r"); assertContainsEvent("missing value for mandatory attribute 'label_dict' in 'my_rule' rule"); } @Test public void testLabelKeyedStringDictAllowsMissingAttributeWhenMandatoryIsFalse() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'label_dict': attr.label_keyed_string_dict(mandatory=False),", " }", ")"); scratch.file( "BUILD", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r')"); invalidatePackages(); createRuleContext("//:r"); assertNoEvents(); } @Test public void testLabelAttributeDefault() throws Exception { scratch.file( "my_rule.bzl", "def _impl(ctx):", " return", "my_rule = rule(", " implementation = _impl,", " attrs = {", " 'explicit_dep': attr.label(default = Label('//:dep')),", " '_implicit_dep': attr.label(default = Label('//:dep')),", " 'explicit_dep_list': attr.label_list(default = [Label('//:dep')]),", " '_implicit_dep_list': attr.label_list(default = [Label('//:dep')]),", " }", ")"); scratch.file( "BUILD", "filegroup(name='dep')", "load('//:my_rule.bzl', 'my_rule')", "my_rule(name='r')"); invalidatePackages(); SkylarkRuleContext context = createRuleContext("//:r"); Label explicitDepLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.explicit_dep.label"); assertThat(explicitDepLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); Label implicitDepLabel = (Label) evalRuleContextCode(context, "ruleContext.attr._implicit_dep.label"); assertThat(implicitDepLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); Label explicitDepListLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.explicit_dep_list[0].label"); assertThat(explicitDepListLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); Label implicitDepListLabel = (Label) evalRuleContextCode(context, "ruleContext.attr._implicit_dep_list[0].label"); assertThat(implicitDepListLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); } @Test public void testRelativeLabelInExternalRepository() throws Exception { scratch.file("external_rule.bzl", "def _impl(ctx):", " return", "external_rule = rule(", " implementation = _impl,", " attrs = {", " 'internal_dep': attr.label(default = Label('//:dep'))", " }", ")"); scratch.file("BUILD", "filegroup(name='dep')"); scratch.file("/r/WORKSPACE"); scratch.file("/r/a/BUILD", "load('@//:external_rule.bzl', 'external_rule')", "external_rule(name='r')"); scratch.overwriteFile("WORKSPACE", new ImmutableList.Builder() .addAll(analysisMock.getWorkspaceContents(mockToolsConfig)) .add("local_repository(name='r', path='/r')") .build()); invalidatePackages(/*alsoConfigs=*/false); // Repository shuffling messes with toolchain labels. SkylarkRuleContext context = createRuleContext("@r//a:r"); Label depLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.internal_dep.label"); assertThat(depLabel).isEqualTo(Label.parseAbsolute("//:dep", ImmutableMap.of())); } @Test public void testCallerRelativeLabelInExternalRepository() throws Exception { scratch.file("BUILD"); scratch.file("external_rule.bzl", "def _impl(ctx):", " return", "external_rule = rule(", " implementation = _impl,", " attrs = {", " 'internal_dep': attr.label(", " default = Label('//:dep', relative_to_caller_repository = True)", " )", " }", ")"); scratch.file("/r/WORKSPACE"); scratch.file("/r/BUILD", "filegroup(name='dep')"); scratch.file("/r/a/BUILD", "load('@//:external_rule.bzl', 'external_rule')", "external_rule(name='r')"); scratch.overwriteFile("WORKSPACE", new ImmutableList.Builder() .addAll(analysisMock.getWorkspaceContents(mockToolsConfig)) .add("local_repository(name='r', path='/r')") .build()); invalidatePackages(/*alsoConfigs=*/false); // Repository shuffling messes with toolchain labels. SkylarkRuleContext context = createRuleContext("@r//a:r"); Label depLabel = (Label) evalRuleContextCode(context, "ruleContext.attr.internal_dep.label"); assertThat(depLabel).isEqualTo(Label.parseAbsolute("@r//:dep", ImmutableMap.of())); } @Test public void testExternalWorkspaceLoad() throws Exception { scratch.file( "/r1/BUILD", "filegroup(name = 'test',", " srcs = ['test.txt'],", " visibility = ['//visibility:public'],", ")"); scratch.file("/r1/WORKSPACE"); scratch.file("/r2/BUILD", "exports_files(['test.bzl'])"); scratch.file( "/r2/test.bzl", "def macro(name, path):", " native.local_repository(name = name, path = path)" ); scratch.file("/r2/WORKSPACE"); scratch.file( "/r2/other_test.bzl", "def other_macro(name, path):", " print(name + ': ' + path)" ); scratch.file("BUILD"); scratch.overwriteFile("WORKSPACE", new ImmutableList.Builder() .addAll(analysisMock.getWorkspaceContents(mockToolsConfig)) .add("local_repository(name='r2', path='/r2')") .add("load('@r2//:test.bzl', 'macro')") .add("macro('r1', '/r1')") .add("NEXT_NAME = 'r3'") // We can still refer to r2 in other chunks: .add("load('@r2//:other_test.bzl', 'other_macro')") .add("macro(NEXT_NAME, '/r2')") // and we can still use macro outside of its chunk. .build()); invalidatePackages(/*alsoConfigs=*/false); // Repository shuffling messes with toolchain labels. assertThat(getConfiguredTarget("@r1//:test")).isNotNull(); } @Test @SuppressWarnings("unchecked") public void testLoadBlockRepositoryRedefinition() throws Exception { reporter.removeHandler(failFastHandler); scratch.file("/bar/WORKSPACE"); scratch.file("/bar/bar.txt"); scratch.file("/bar/BUILD", "filegroup(name = 'baz', srcs = ['bar.txt'])"); scratch.file("/baz/WORKSPACE"); scratch.file("/baz/baz.txt"); scratch.file("/baz/BUILD", "filegroup(name = 'baz', srcs = ['baz.txt'])"); scratch.overwriteFile("WORKSPACE", new ImmutableList.Builder() .addAll(analysisMock.getWorkspaceContents(mockToolsConfig)) .add("local_repository(name = 'foo', path = '/bar')") .add("local_repository(name = 'foo', path = '/baz')") .build()); invalidatePackages(/*alsoConfigs=*/false); // Repository shuffling messes with toolchain labels. assertThat( (List