blob: efa4a6cdf47ac2d21ba386c9ea7607615a9a5877 [file] [log] [blame]
// 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 com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.FileConfiguredTarget;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.rules.SkylarkRuleContext;
import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
import com.google.devtools.build.lib.rules.python.PythonSourcesProvider;
import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.vfs.PathFragment;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.List;
/**
* Tests for SkylarkRuleContext.
*/
@RunWith(JUnit4.class)
public class SkylarkRuleContextTest extends SkylarkTestCase {
@Before
public void setUp() throws Exception {
super.setUp();
scratch.file(
"foo/BUILD",
"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'])",
"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,",
")");
}
private void setUpAttributeErrorTest() throws Exception {
scratch.file("test/BUILD",
"load('/test/macros', '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 testNativeRuleAttributeErrorWithMacro() 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 testSkylarkRuleAttributeErrorWithMacro() 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 provider '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 testNativeRuleAttributeErrorWithoutMacro() 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 testSkylarkRuleAttributeErrorWithoutMacro() 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 provider "
+ "'some_provider'");
}
}
@Test
public void testGetPrerequisiteArtifacts() 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<String> artifacts) {
assertThat(result).isInstanceOf(SkylarkList.class);
SkylarkList resultList = (SkylarkList) result;
assertEquals(artifacts.size(), resultList.size());
int i = 0;
for (String artifact : artifacts) {
assertEquals(artifact, ((Artifact) resultList.get(i++)).getFilename());
}
}
@Test
public void testGetPrerequisites() 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);
assertNotNull(tic1.getProvider(JavaSourceJarsProvider.class));
// Check an unimplemented provider too
assertNull(tic1.getProvider(PythonSourcesProvider.class));
}
@Test
public void testGetPrerequisite() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:asr");
Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.srcjar");
TransitiveInfoCollection tic = (TransitiveInfoCollection) result;
assertThat(tic).isInstanceOf(FileConfiguredTarget.class);
assertEquals("asr-src.jar", tic.getLabel().getName());
}
@Test
public void testMiddleMan() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:jl");
Object result = evalRuleContextCode(ruleContext, "ruleContext.middle_man(':host_jdk')");
assertThat(
Iterables.getOnlyElement(((SkylarkNestedSet) result).getSet(Artifact.class))
.getExecPathString())
.contains("middlemen");
}
@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 testGetRuleAttributeListValue() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.outs");
assertEquals(1, ((SkylarkList) result).size());
}
@Test
public void testGetRuleAttributeListValueNoGet() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.outs");
assertEquals(1, ((SkylarkList) result).size());
}
@Test
public void testGetRuleAttributeStringTypeValue() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.cmd");
assertEquals("dummy_cmd", (String) result);
}
@Test
public void testGetRuleAttributeStringTypeValueNoGet() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.attr.cmd");
assertEquals("dummy_cmd", (String) result);
}
@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");
assertEquals("//foo:foo", ((Label) result).toString());
}
@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:jl");
Object result = evalRuleContextCode(ruleContext, "ruleContext.executable._ijar");
assertEquals("ijar", ((Artifact) result).getFilename());
}
@Test
public void testCreateSpawnActionArgumentsWithExecutableFilesToRunProvider() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:jl");
evalRuleContextCode(
ruleContext,
"ruleContext.action(\n"
+ " inputs = ruleContext.files.srcs,\n"
+ " outputs = ruleContext.files.srcs,\n"
+ " arguments = ['--a','--b'],\n"
+ " executable = ruleContext.executable._ijar)\n");
SpawnAction action =
(SpawnAction)
Iterables.getOnlyElement(
ruleContext.getRuleContext().getAnalysisEnvironment().getRegisteredActions());
assertThat(action.getCommandFilename()).endsWith("/ijar");
}
@Test
public void testOutputs() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:bar");
Iterable<?> result = (Iterable<?>) evalRuleContextCode(ruleContext, "ruleContext.outputs.outs");
assertEquals("d.txt", ((Artifact) Iterables.getOnlyElement(result)).getFilename());
}
@Test
public void testSkylarkRuleContextStr() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "'%s' % ruleContext");
assertEquals("//foo:foo", result);
}
@Test
public void testSkylarkRuleContextGetDefaultShellEnv() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.configuration.default_shell_env");
assertThat(result).isInstanceOf(ImmutableMap.class);
}
@Test
public void testCheckPlaceholders() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result =
evalRuleContextCode(ruleContext, "ruleContext.check_placeholders('%{name}', ['name'])");
assertEquals(true, result);
}
@Test
public void testCheckPlaceholdersBadPlaceholder() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result =
evalRuleContextCode(ruleContext, "ruleContext.check_placeholders('%{name}', ['abc'])");
assertEquals(false, result);
}
@Test
public void testExpandMakeVariables() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result =
evalRuleContextCode(
ruleContext, "ruleContext.expand_make_variables('cmd', '$(ABC)', {'ABC': 'DEF'})");
assertEquals("DEF", result);
}
@Test
public void testExpandMakeVariablesShell() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result =
evalRuleContextCode(ruleContext, "ruleContext.expand_make_variables('cmd', '$$ABC', {})");
assertEquals("$ABC", result);
}
@Test
public void testConfiguration() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.configuration");
assertSame(result, ruleContext.getRuleContext().getConfiguration());
}
@Test
public void testHostConfiguration() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.host_configuration");
assertSame(result, ruleContext.getRuleContext().getHostConfiguration());
}
@Test
public void testWorkspaceName() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result = evalRuleContextCode(ruleContext, "ruleContext.workspace_name");
assertSame(result, TestConstants.WORKSPACE_NAME);
}
@Test
public void testDeriveArtifactLegacy() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result =
evalRuleContextCode(
ruleContext,
"ruleContext.new_file(ruleContext.configuration.genfiles_dir," + " 'a/b.txt')");
PathFragment fragment = ((Artifact) result).getRootRelativePath();
assertEquals("foo/a/b.txt", fragment.getPathString());
}
@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();
assertEquals("foo/a/b.txt", fragment.getPathString());
}
@Test
public void testParamFileLegacy() throws Exception {
SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
Object result =
evalRuleContextCode(
ruleContext,
"ruleContext.new_file(ruleContext.configuration.bin_dir,"
+ "ruleContext.files.tools[0], '.params')");
PathFragment fragment = ((Artifact) result).getRootRelativePath();
assertEquals("foo/t.exe.params", fragment.getPathString());
}
@Test
public void testParamFileSuffix() 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();
assertEquals("foo/t.exe.params", fragment.getPathString());
}
}