blob: db45c510d001f275e9a19d90c7c38dbf2d1af60c [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.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.baseArtifactNames;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
import com.google.devtools.build.lib.analysis.RunfilesProvider;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.AttributeContainer;
import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
import com.google.devtools.build.lib.packages.InputFile;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.PackageGroup;
import com.google.devtools.build.lib.skyframe.PackageFunction;
import com.google.devtools.build.lib.skyframe.SkyFunctions;
import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction;
import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.testutil.MoreAsserts;
import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionName;
import junit.framework.AssertionFailedError;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nullable;
/**
* Integration tests for Skylark.
*/
public class SkylarkIntegrationTest extends BuildViewTestCase {
private static final Joiner LINE_JOINER = Joiner.on("\n");
@Override
public void setUp() throws Exception {
super.setUp();
}
public void testSameMethodNames() throws Exception {
// The alias feature of load() may hide the fact that two methods in the stack trace have the
// same name. This is perfectly legal as long as these two methods are actually distinct.
// Consequently, no "Recursion was detected" error must be thrown.
scratch.file(
"test/skylark/extension.bzl",
"load('/test/skylark/other', other_impl = 'impl')",
"def impl(ctx):",
" other_impl(ctx)",
"empty = rule(implementation = impl)");
scratch.file("test/skylark/other.bzl", "def impl(ctx):", " print('This rule does nothing')");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'empty')",
"empty(name = 'test_target')");
getConfiguredTarget("//test/skylark:test_target");
}
public void testRecursionDetection() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file(
"test/skylark/extension.bzl",
"def _impl(ctx):",
" _impl(ctx)",
"empty = rule(implementation = _impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'empty')",
"empty(name = 'test_target')");
getConfiguredTarget("//test/skylark:test_target");
assertContainsEvent("Recursion was detected when calling '_impl' from '_impl'");
}
public void testMacroHasGeneratorAttributes() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def _impl(ctx):",
" print('This rule does nothing')",
"",
"empty = rule(implementation = _impl)",
"no_macro = rule(implementation = _impl)",
"",
"def macro(name, visibility=None):",
" empty(name = name, visibility=visibility)",
"def native_macro(name):",
" native.cc_library(name = name + '_suffix')");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', macro_rule = 'macro', no_macro_rule = 'no_macro',",
" native_macro_rule = 'native_macro')",
"macro_rule(name = 'macro_target')",
"no_macro_rule(name = 'no_macro_target')",
"native_macro_rule(name = 'native_macro_target')",
"cc_binary(name = 'cc_target', deps = ['cc_dep'])",
"cc_library(name = 'cc_dep')");
AttributeContainer withMacro = getContainerForTarget("macro_target");
assertThat(withMacro.getAttr("generator_name")).isEqualTo("macro_target");
assertThat(withMacro.getAttr("generator_function")).isEqualTo("macro");
assertThat(withMacro.getAttr("generator_location")).isEqualTo("test/skylark/BUILD:3");
// Attributes are only set when the rule was created by a macro
AttributeContainer noMacro = getContainerForTarget("no_macro_target");
assertThat(noMacro.getAttr("generator_name")).isEqualTo("");
assertThat(noMacro.getAttr("generator_function")).isEqualTo("");
assertThat(noMacro.getAttr("generator_location")).isEqualTo("");
AttributeContainer nativeMacro = getContainerForTarget("native_macro_target_suffix");
assertThat(nativeMacro.getAttr("generator_name")).isEqualTo("native_macro_target");
assertThat(nativeMacro.getAttr("generator_function")).isEqualTo("native_macro");
assertThat(nativeMacro.getAttr("generator_location")).isEqualTo("test/skylark/BUILD:5");
AttributeContainer ccTarget = getContainerForTarget("cc_target");
assertThat(ccTarget.getAttr("generator_name")).isEqualTo("");
assertThat(ccTarget.getAttr("generator_function")).isEqualTo("");
assertThat(ccTarget.getAttr("generator_location")).isEqualTo("");
}
private AttributeContainer getContainerForTarget(String targetName) throws Exception {
ConfiguredTarget target = getConfiguredTarget("//test/skylark:" + targetName);
return target.getTarget().getAssociatedRule().getAttributeContainer();
}
public void testStackTraceErrorInFunction() throws Exception {
runStackTraceTest(
"str",
"\t\tstr.index(1)\n"
+ "Method string.index(sub: string, start: int, end: int or NoneType) is not "
+ "applicable for arguments (int, int, NoneType): 'sub' is int, "
+ "but should be string");
}
public void testStackTraceMissingMethod() throws Exception {
runStackTraceTest("None", "\t\tNone.index(1)\n" + "Type NoneType has no function index(int)");
}
protected void runStackTraceTest(String object, String errorMessage) throws Exception {
reporter.removeHandler(failFastHandler);
String expectedTrace =
Joiner.on("\n")
.join(
"Traceback (most recent call last):",
"\tFile \"/workspace/test/skylark/BUILD\", line 3",
"\t\tcustom_rule(name = 'cr')",
"\tFile \"/workspace/test/skylark/extension.bzl\", line 5, in custom_rule_impl",
"\t\tfoo()",
"\tFile \"/workspace/test/skylark/extension.bzl\", line 8, in foo",
"\t\tbar(2, 4)",
"\tFile \"/workspace/test/skylark/extension.bzl\", line 10, in bar",
"\t\tfirst(x, y, z)",
"\tFile \"/workspace/test/skylark/functions.bzl\", line 2, in first",
"\t\tsecond(a, b)",
"\tFile \"/workspace/test/skylark/functions.bzl\", line 5, in second",
"\t\tthird('legal')",
"\tFile \"/workspace/test/skylark/functions.bzl\", line 7, in third",
errorMessage);
scratch.file(
"test/skylark/extension.bzl",
"load('/test/skylark/functions', 'first')",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" ftb = set(attr1)",
" foo()",
" return struct(provider_key = ftb)",
"def foo():",
" bar(2,4)",
"def bar(x,y,z=1):",
" first(x,y, z)",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory=True, allow_files=True)})");
scratch.file(
"test/skylark/functions.bzl",
"def first(a, b, c):",
" second(a, b)",
" third(b)",
"def second(a, b):",
" third('legal')",
"def third(str):",
" " + object + ".index(1)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
getConfiguredTarget("//test/skylark:cr");
assertContainsEvent(expectedTrace);
}
public void testFilesToBuild() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" ftb = set(attr1)",
" return struct(runfiles = ctx.runfiles(), files = ftb)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory=True, allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("//test/skylark:cr", target.getLabel().toString());
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("a.txt");
}
public void testRunfiles() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" rf = ctx.runfiles(files = attr1)",
" return struct(runfiles = rf)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory=True, allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("//test/skylark:cr", target.getLabel().toString());
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(RunfilesProvider.class).getDefaultRunfiles().getAllArtifacts()))
.containsExactly("a.txt");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(RunfilesProvider.class).getDataRunfiles().getAllArtifacts()))
.containsExactly("a.txt");
}
public void testAccessRunfiles() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" runfiles = ctx.attr.x.default_runfiles.files",
" return struct(files = runfiles)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'x': attr.label(allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"cc_library(name = 'lib', data = ['a.txt'])",
"custom_rule(name = 'cr1', x = ':lib')",
"custom_rule(name = 'cr2', x = 'b.txt')");
scratch.file("test/skylark/a.txt");
scratch.file("test/skylark/b.txt");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr1");
List<String> baseArtifactNames =
ActionsTestUtil.baseArtifactNames(target.getProvider(FileProvider.class).getFilesToBuild());
assertThat(baseArtifactNames).containsExactly("a.txt");
target = getConfiguredTarget("//test/skylark:cr2");
baseArtifactNames =
ActionsTestUtil.baseArtifactNames(target.getProvider(FileProvider.class).getFilesToBuild());
assertThat(baseArtifactNames).isEmpty();
}
public void testStatefulRunfiles() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" rf1 = ctx.runfiles(files = attr1)",
" rf2 = ctx.runfiles()",
" return struct(data_runfiles = rf1, default_runfiles = rf2)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory = True, allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("//test/skylark:cr", target.getLabel().toString());
assertTrue(target.getProvider(RunfilesProvider.class).getDefaultRunfiles().isEmpty());
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(RunfilesProvider.class).getDataRunfiles().getAllArtifacts()))
.containsExactly("a.txt");
}
public void testExecutableGetsInRunfilesAndFilesToBuild() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" ctx.file_action(output = ctx.outputs.executable, content = 'echo hello')",
" rf = ctx.runfiles(ctx.files.data)",
" return struct(runfiles = rf)",
"",
"custom_rule = rule(implementation = custom_rule_impl, executable = True,",
" attrs = {'data': attr.label_list(cfg=DATA_CFG, allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', data = [':a.txt'])");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("//test/skylark:cr", target.getLabel().toString());
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(RunfilesProvider.class).getDefaultRunfiles().getAllArtifacts()))
.containsExactly("a.txt", "cr")
.inOrder();
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("cr");
}
public void testCannotSpecifyRunfilesWithDataOrDefaultRunfiles() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" rf = ctx.runfiles()",
" return struct(runfiles = rf, default_runfiles = rf)",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
"Cannot specify the provider 'runfiles' together with "
+ "'data_runfiles' or 'default_runfiles'",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
}
public void testTransitiveInfoProviders() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" ftb = set(attr1)",
" return struct(provider_key = ftb)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory=True, allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
RuleConfiguredTarget target = (RuleConfiguredTarget) getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
((SkylarkNestedSet) target.get("provider_key")).getSet(Artifact.class)))
.containsExactly("a.txt");
}
public void testMandatoryProviderMissing() throws Exception {
scratch.file("test/skylark/BUILD");
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" return struct()",
"",
"dependent_rule = rule(implementation = rule_impl)",
"",
"main_rule = rule(implementation = rule_impl,",
" attrs = {'dependencies': attr.label_list(providers = ['some_provider'],",
" allow_files=True)})");
checkError(
"test",
"b",
"in dependencies attribute of main_rule rule //test:b: "
+ "'//test:a' does not have mandatory provider 'some_provider'",
"load('/test/skylark/extension', 'dependent_rule')",
"load('/test/skylark/extension', 'main_rule')",
"",
"dependent_rule(name = 'a')",
"main_rule(name = 'b', dependencies = [':a'])");
}
public void testActions() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" output = ctx.outputs.o",
" ctx.action(",
" inputs = attr1,",
" outputs = [output],",
" command = 'echo')",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory=True, allow_files=True)},",
" outputs = {'o': 'o.txt'})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
getConfiguredTarget("//test/skylark:cr");
FileConfiguredTarget target = getFileConfiguredTarget("//test/skylark:o.txt");
assertThat(
ActionsTestUtil.baseArtifactNames(
getGeneratingAction(target.getArtifact()).getInputs()))
.containsExactly("a.txt");
}
public void testRuleClassImplicitOutputFunction() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" files = [ctx.outputs.o]",
" ctx.action(",
" outputs = files,",
" command = 'echo')",
" ftb = set(files)",
" return struct(runfiles = ctx.runfiles(), files = ftb)",
"",
"def output_func(attr_map):",
" if attr_map.attr2 != None: return {}",
" return {'o': attr_map.attr1 + '.txt'}",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.string(),",
" 'attr2': attr.label()},",
" outputs = output_func)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = 'bar')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("bar.txt");
}
public void testRuleClassImplicitOutputs() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" files = [ctx.outputs.lbl, ctx.outputs.list, ctx.outputs.str]",
" print('==!=!=!=')",
" print(files)",
" ctx.action(",
" outputs = files,",
" command = 'echo')",
" return struct(files = set(files))",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {",
" 'attr1': attr.label(allow_files=True),",
" 'attr2': attr.label_list(allow_files=True),",
" 'attr3': attr.string(),",
" },",
" outputs = {",
" 'lbl': '%{attr1}.a',",
" 'list': '%{attr2}.b',",
" 'str': '%{attr3}.c',",
"})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(",
" name='cr',",
" attr1='f1.txt',",
" attr2=['f2.txt'],",
" attr3='f3.txt',",
")");
scratch.file("test/skylark/f1.txt");
scratch.file("test/skylark/f2.txt");
scratch.file("test/skylark/f3.txt");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("f1.a", "f2.b", "f3.txt.c");
}
public void testRuleClassImplicitOutputFunctionAndDefaultValue() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" ctx.action(",
" outputs = [ctx.outputs.o],",
" command = 'echo')",
" return struct(runfiles = ctx.runfiles())",
"",
"def output_func(attr_map):",
" return {'o': attr_map.attr1 + '.txt'}",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.string(default='bar')},",
" outputs = output_func)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = None)");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("bar.txt");
}
public void testRuleClassNonMandatoryEmptyOutputs() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return struct(",
" o1=ctx.outputs.o1,",
" o2=ctx.outputs.o2)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'o1': attr.output(), 'o2': attr.output_list()})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals(Runtime.NONE, target.get("o1"));
assertEquals(MutableList.EMPTY, target.get("o2"));
}
public void testRuleClassImplicitAndExplicitOutputNamesCollide() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return struct()",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'o': attr.output_list()},",
" outputs = {'o': '%{name}.txt'})");
checkError(
"test/skylark",
"cr",
"Multiple outputs with the same key: o",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', o = [':bar.txt'])");
}
public void testRuleClassDefaultFilesToBuild() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" files = [ctx.outputs.o]",
" ctx.action(",
" outputs = files,",
" command = 'echo')",
" ftb = set(files)",
" for i in ctx.outputs.out:",
" ctx.file_action(output=i, content='hi there')",
"",
"def output_func(attr_map):",
" return {'o': attr_map.attr1 + '.txt'}",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {",
" 'attr1': attr.string(),",
" 'out': attr.output_list()",
" },",
" outputs = output_func)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = 'bar', out=['other'])");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("bar.txt", "other")
.inOrder();
}
public void testBadCallbackFunction() throws Exception {
scratch.file(
"test/skylark/extension.bzl", "def impl(): return 0", "", "custom_rule = rule(impl)");
checkError(
"test/skylark",
"cr",
"impl() does not accept positional arguments",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
}
public void testRuleClassImplicitOutputFunctionBadAttr() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return None",
"",
"def output_func(attr_map):",
" return {'a': attr_map.bad_attr}",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.string()},",
" outputs = output_func)");
checkError(
"test/skylark",
"cr",
"Attribute 'bad_attr' either doesn't exist or uses a select()",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = 'bar')");
}
public void testHelperFunctionInRuleImplementation() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def helper_func(attr1):",
" return set(attr1)",
"",
"def custom_rule_impl(ctx):",
" attr1 = ctx.files.attr1",
" ftb = helper_func(attr1)",
" return struct(runfiles = ctx.runfiles(), files = ftb)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label_list(mandatory=True, allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = [':a.txt'])");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("//test/skylark:cr", target.getLabel().toString());
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("a.txt");
}
public void testMultipleImportsOfSameRule() throws Exception {
scratch.file("test/skylark/BUILD");
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return None",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'dep': attr.label_list(allow_files=True)})");
scratch.file(
"test/skylark1/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name = 'cr1')");
scratch.file(
"test/skylark2/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name = 'cr2', dep = ['//test/skylark1:cr1'])");
getConfiguredTarget("//test/skylark2:cr2");
}
public void testFunctionGeneratingRules() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def impl(ctx): return None",
"def gen(): return rule(impl)",
"r = gen()",
"s = gen()");
scratch.file(
"test/skylark/BUILD", "load('extension', 'r', 's')", "r(name = 'r')", "s(name = 's')");
getConfiguredTarget("//test/skylark:r");
getConfiguredTarget("//test/skylark:s");
}
public void testImportInSkylark() throws Exception {
scratch.file("test/skylark/implementation.bzl", "def custom_rule_impl(ctx):", " return None");
scratch.file(
"test/skylark/extension.bzl",
"load('/test/skylark/implementation', 'custom_rule_impl')",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'dep': attr.label_list(allow_files=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name = 'cr')");
getConfiguredTarget("//test/skylark:cr");
}
public void testRuleAliasing() throws Exception {
scratch.file(
"test/skylark/implementation.bzl",
"def impl(ctx): return struct()",
"custom_rule = rule(implementation = impl)");
scratch.file(
"test/skylark/ext.bzl",
"load('/test/skylark/implementation', 'custom_rule')",
"def impl(ctx): return struct()",
"custom_rule1 = rule(implementation = impl)",
"custom_rule2 = custom_rule1",
"custom_rule3 = custom_rule");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/ext', 'custom_rule1', 'custom_rule2', 'custom_rule3')",
"custom_rule4 = custom_rule3",
"custom_rule1(name = 'cr1')",
"custom_rule2(name = 'cr2')",
"custom_rule3(name = 'cr3')",
"custom_rule4(name = 'cr4')");
getConfiguredTarget("//test/skylark:cr1");
getConfiguredTarget("//test/skylark:cr2");
getConfiguredTarget("//test/skylark:cr3");
getConfiguredTarget("//test/skylark:cr4");
}
public void testRecursiveImport() throws Exception {
scratch.file("test/skylark/ext2.bzl", "load('/test/skylark/ext1', 'symbol2')");
scratch.file("test/skylark/ext1.bzl", "load('/test/skylark/ext2', 'symbol1')");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/ext1', 'custom_rule')",
"genrule(name = 'rule')");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test/skylark:rule");
fail();
} catch (BuildFileContainsErrorsException e) {
// This is expected
}
assertContainsEvent(
"test/skylark/BUILD: cycle in referenced extension files: \n"
+ " * test/skylark/ext1.bzl\n"
+ " test/skylark/ext2.bzl\n"
+ " * test/skylark/ext1.bzl");
}
public void testSymbolPropagateThroughImports() throws Exception {
scratch.file("test/skylark/implementation.bzl", "def custom_rule_impl(ctx):", " return None");
scratch.file(
"test/skylark/extension2.bzl", "load('/test/skylark/implementation', 'custom_rule_impl')");
scratch.file(
"test/skylark/extension1.bzl",
"load('/test/skylark/extension2', 'custom_rule_impl')",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'dep': attr.label_list()})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension1', 'custom_rule')",
"custom_rule(name = 'cr')");
getConfiguredTarget("//test/skylark:cr");
}
public void testAccessingJvmFragment() throws Exception {
checkFieldAccess(
"ctx.fragments.jvm.java_executable", "third_party/java/jdk/jdk-mock-64/bin/java");
}
public void testAccessingJvmHostFragment() throws Exception {
checkFieldAccess(
"ctx.host_fragments.jvm.java_executable", "third_party/java/jdk/jdk-mock-64/bin/java");
}
public void testAccessingCppFragment() throws Exception {
checkFieldAccess("ctx.fragments.cpp.compiler", "gcc-4.4.0");
}
public void testAccessingCppHostFragment() throws Exception {
checkFieldAccess("ctx.host_fragments.cpp.compiler", "gcc-4.4.0");
}
public void testAccessingJavaFragment() throws Exception {
checkFieldAccess("ctx.fragments.java.default_javac_flags", "[]");
}
public void testAccessingJavaHostFragment() throws Exception {
checkFieldAccess("ctx.host_fragments.java.default_javac_flags", "[]");
}
public void testAccessingNonExistingFragment() throws Exception {
expectFragmentError(
"ctx.fragments.nothing.compiler",
"There is no configuration fragment named 'nothing' in target configuration. "
+ "Available fragments: 'cpp', 'java', 'jvm'");
}
public void testAccessingNonExistingHostFragment() throws Exception {
expectFragmentError(
"ctx.host_fragments.nothing.compiler",
"There is no configuration fragment named 'nothing' in host configuration. "
+ "Available fragments: 'cpp', 'java', 'jvm'");
}
// Ensures that the legacy way of accessing fragments via configuration no longer works.
public void testLegacyFragmentAccess() throws Exception {
expectFragmentError("ctx.configuration.fragment(cpp).compiler", "name 'cpp' is not defined");
}
private void expectFragmentError(String fieldName, String expectedError) throws Exception {
reporter.removeHandler(failFastHandler); // expect errors
try {
checkFieldAccess(fieldName, "");
fail("There should have been an exception in expectFragmentError()");
} catch (Exception ex) {
assertContainsEvent(expectedError);
}
}
public void testFragmentAccessError() throws Exception {
reporter.removeHandler(failFastHandler);
getConfiguredTargetForFragment("ctx.fragments.cpp.compiler", "'java'", "'cpp'");
assertContainsEvent(
"custom_rule has to declare 'cpp' as a required fragment in target "
+ "configuration in order to access it. Please update the 'fragments' argument "
+ "of the rule definition (for example: fragments = [\"cpp\"])");
}
public void testHostFragmentAccessError() throws Exception {
reporter.removeHandler(failFastHandler);
getConfiguredTargetForFragment("ctx.host_fragments.cpp.compiler", "'cpp'", "'java'");
assertContainsEvent(
"custom_rule has to declare 'cpp' as a required fragment in host "
+ "configuration in order to access it. Please update the 'host_fragments' argument "
+ "of the rule definition (for example: host_fragments = [\"cpp\"])");
}
private void checkFieldAccess(String fullFieldName, String expectedResult) throws Exception {
checkFieldAccess(fullFieldName, "'cpp', 'java', 'jvm'", expectedResult);
}
private void checkFieldAccess(String fullFieldName, String fragments, String expectedResult)
throws Exception {
assertThat(getConfiguredTargetForFragment(fullFieldName, fragments, fragments).get("result"))
.isEqualTo(expectedResult);
}
private ConfiguredTarget getConfiguredTargetForFragment(
String fullFieldName, String fragments, String hostFragments) throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return struct(result = str(" + fullFieldName + ")",
")",
"custom_rule = rule(implementation = custom_rule_impl,",
String.format(" fragments = [%s],", fragments),
String.format(" host_fragments = [%s],", hostFragments),
")");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension','custom_rule')",
"custom_rule(name = 'cr')");
return getConfiguredTarget("//test/skylark:cr");
}
public void testLateBoundAttribute() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" ftb = set([ctx.file._attr2])",
" return struct(runfiles = ctx.runfiles(), files = ftb)",
"",
"def attr_value(attr_map, cfg):",
" return attr_map.attr1",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label(allow_files=True),",
" '_attr2': attr.label(default=attr_value, allow_files=True, single_file=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = '//test/skylark:file')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("file");
}
public void testLateBoundAttributesNone() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" if ctx.attr._attr1 != None: fail('should be None')",
" f = set(ctx.attr._attr2)", // label_list defaults to []
" return struct(runfiles = ctx.runfiles(), files = f)",
"",
"def attr_value(attr_map, cfg):",
" return None",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'_attr1': attr.label(default=attr_value),",
" '_attr2': attr.label_list(default=attr_value)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(
ActionsTestUtil.baseArtifactNames(
target.getProvider(FileProvider.class).getFilesToBuild()))
.isEmpty();
}
public void testLateBoundAttributeBadType() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx): pass",
"",
"def attr_value(attr_map, cfg): return 5",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'_attr': attr.label(default=attr_value)})");
checkError(
"test/skylark",
"cr",
"When computing the default value of :attr, expected 'label', got 'int'",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
}
public void testLateBoundAttributeBadAttr() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return None",
"",
"def attr_value(attr_map, cfg):",
" return attr_map.bad_attr",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label(allow_files=True),",
" '_attr2': attr.label(default=attr_value, allow_files=True)})");
checkError(
"test/skylark",
"cr",
"No such regular (non late-bound) attribute 'bad_attr'",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = '//test/skylark:file')");
}
public void testLateBoundAttributeBadName() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return None",
"",
"def attr_value(attr_map, cfg):",
" return attr_map.bad_attr",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'attr1': attr.label(default=attr_value)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = '//test/skylark:file')");
reporter.removeHandler(failFastHandler);
try {
// The error happens during the loading of the Skylark file so checkError don't work here
getTarget("//test/skylark:cr");
fail();
} catch (Exception e) {
assertContainsEvent(
"When an attribute value is a function, the attribute must be private (start with '_')");
}
}
public void testEmptyName() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return None",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'': attr.label()})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', attr1 = '//test/skylark:file')");
reporter.removeHandler(failFastHandler);
try {
// The error happens during the loading of the Skylark file so checkError don't work here
getTarget("//test/skylark:cr");
fail();
} catch (Exception e) {
assertContainsEvent("Attribute name cannot be empty");
}
}
public void testValidationEnvironmentDoesNotCollide() throws Exception {
scratch.file("test/skylark/extension1.bzl", "some_variable = 'a'");
scratch.file("test/skylark/extension2.bzl", "some_variable = 1");
scratch.file(
"test/skylark/rule1.bzl",
"load('/test/skylark/extension1', 'some_variable')",
"def custom_rule_impl(ctx):",
" return None",
"",
"def attr_value(attr_map, cfg):",
" return attr_map.bad_attr",
"",
"custom_rule1 = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/rule2.bzl",
"load('/test/skylark/extension2', 'some_variable')",
"def custom_rule_impl(ctx):",
" return None",
"",
"def attr_value(attr_map, cfg):",
" return attr_map.bad_attr",
"",
"custom_rule2 = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/rule1', 'custom_rule1')",
"load('/test/skylark/rule2', 'custom_rule2')",
"",
"custom_rule1(name = 'cr1')",
"custom_rule2(name = 'cr2')");
getConfiguredTarget("//test/skylark:cr1");
getConfiguredTarget("//test/skylark:cr2");
}
public void testImportedFunctionExecutesInItsDefinitionEnvironment() throws Exception {
scratch.file(
"test/skylark/helper.bzl",
"some_constant = set(['a'])",
"def some_func():",
" return set(['b'])",
"def helper_func():",
" return some_constant + some_func()");
scratch.file(
"test/skylark/implementation.bzl",
"load('/test/skylark/helper', 'helper_func')",
"def custom_rule_impl(ctx):",
" return struct(",
" p = helper_func())",
"custom_rule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/implementation', 'custom_rule')",
"custom_rule(name = 'cr')");
assertEquals(
ImmutableList.of("b", "a"),
((SkylarkNestedSet) getConfiguredTarget("//test/skylark:cr").get("p")).toCollection());
}
public void testImportedFunctionValidation() throws Exception {
scratch.file("test/skylark/helper.bzl", "def helper_func():", " return set(['a'])");
scratch.file(
"test/skylark/implementation.bzl",
"load('/test/skylark/helper', 'helper_func')",
"def custom_rule_impl(ctx):",
" a = helper_func()",
" return struct(",
" p = a)",
"custom_rule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/implementation', 'custom_rule')",
"custom_rule(name = 'cr')");
assertEquals(
ImmutableList.of("a"),
((SkylarkNestedSet) getConfiguredTarget("//test/skylark:cr").get("p")).toCollection());
}
public void testExpectFailureWrongError() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx): fail('kaputt')",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
"kaputt",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name='cr', expect_failure='other')");
}
public void testExpectFailureNoError() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx): return struct()",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
"Expected failure not found: other",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name='cr', expect_failure='other')");
}
public void testExpectFailureSuccess() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" fail('kaputt')",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr1', expect_failure = 'kaputt*')",
"custom_rule(name = 'cr2', expect_failure = 'k.*t*')");
getConfiguredTarget("//test/skylark:cr1");
getConfiguredTarget("//test/skylark:cr2");
}
public void testCallFunctionFromBuildFile() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def add_suffix(s): return s + '_suf'",
"def custom_rule_impl(ctx): return struct()",
"custom_rule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule', 'add_suffix')",
"",
"custom_rule(name = add_suffix('foo'))");
getConfiguredTarget("//test/skylark:foo_suf");
}
public void testTransitiveInfoProviderValueIsNotValid() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx): return struct(a = ctx.configuration)",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
"Value of provider 'a' is of an illegal type: configuration",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name='cr')");
}
public void testTransitiveInfoProviderCompositeValueIsNotValid() throws Exception {
reporter.addHandler(printHandler);
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx): return struct(a = struct(a=[{'key': ctx.configuration}]))",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
"Value of provider 'a' is of an illegal type: configuration",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name='cr')");
}
private void createSimpleExtension(String path, String provides, boolean overwrite)
throws Exception {
String content =
LINE_JOINER.join(
"def custom_rule_impl(ctx):",
" return struct(provider=" + provides + ")",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
if (overwrite) {
scratch.overwriteFile(path, content);
} else {
scratch.file(path, content);
}
}
private void createSimpleExtension(String provides, boolean overwrite) throws Exception {
createSimpleExtension("test/skylark/extension.bzl", provides, overwrite);
}
public void testSkylarkExtensionsAreReloaded() throws Exception {
createSimpleExtension("'a'", false);
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
assertEquals("a", getConfiguredTarget("//test/skylark:cr").get("provider"));
createSimpleExtension("'b'", true);
invalidatePackages();
assertEquals("b", getConfiguredTarget("//test/skylark:cr").get("provider"));
}
public void testTransitiveSkylarkExtensionsAreReloaded() throws Exception {
createSimpleExtension("'a'", false);
scratch.file(
"test/skylark/extension2.bzl",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule_2 = custom_rule");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension2', 'custom_rule_2')",
"",
"custom_rule_2(name = 'cr')");
assertEquals("a", getConfiguredTarget("//test/skylark:cr").get("provider"));
createSimpleExtension("'b'", true);
invalidatePackages();
assertEquals("b", getConfiguredTarget("//test/skylark:cr").get("provider"));
}
public void testSkylarkImportOverridesNativeRules() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return struct(custom_provider = 'a')",
"",
"genrule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'genrule')",
"",
"genrule(name = 'cr')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("a", target.get("custom_provider"));
}
public void testSkylarkImportOverridesNativeRulesWithAliasing() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return struct(custom_provider = 'a')",
"",
"custom_rule = rule(implementation = custom_rule_impl)",
"genrule = custom_rule");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'genrule')",
"",
"genrule(name = 'cr')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("a", target.get("custom_provider"));
}
public void testSkylarkImportOverridesNativeRulesTwoImports() throws Exception {
scratch.file(
"test/skylark/extension1.bzl",
"def custom_rule_impl(ctx):",
" return struct(custom_provider = 'a')",
"",
"genrule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/extension2.bzl",
"def custom_rule_impl(ctx):",
" return struct(custom_provider = 'b')",
"",
"genrule = rule(implementation = custom_rule_impl)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension1', 'genrule')",
"first_genrule = genrule",
"load('/test/skylark/extension2', 'genrule')",
"",
"first_genrule(name = 'cr1')",
"genrule(name = 'cr2')");
ConfiguredTarget target1 = getConfiguredTarget("//test/skylark:cr1");
assertEquals("a", target1.get("custom_provider"));
ConfiguredTarget target2 = getConfiguredTarget("//test/skylark:cr2");
assertEquals("b", target2.get("custom_provider"));
}
public void testNativeRulesWorkFromSkylarkBuildExtensions() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def extension_func(name):",
" native.genrule(name = name,",
" outs = [name + '.txt'],",
" tags = None,", // None values should be ignored
" cmd = 'echo hello >@')");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func(name = 'rule1')");
getConfiguredTarget("//test/skylark:rule1");
// Check output file
getFileConfiguredTarget("//test/skylark:rule1.txt");
}
public void testNativeRulesWithGlobInSkylark() throws Exception {
// The glob is evaluated using the BUILD file's package.
scratch.file("test/project/data1.dat", "");
scratch.file("test/skylark/BUILD");
scratch.file(
"test/skylark/extension.bzl",
"def extension_func(name):",
" native.genrule(name = name,",
" srcs = native.glob(['*.dat']),",
" outs = [name + '.txt'],",
" cmd = 'echo $(SRCS) hello >@')");
scratch.file(
"test/project/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func(name = 'rule1')");
ConfiguredTarget target = getConfiguredTarget("//test/project:rule1");
Action action = getGeneratingAction(Iterables.getOnlyElement(getFilesToBuild(target)));
assertThat(baseArtifactNames(action.getInputs())).contains("data1.dat");
}
public void testNativeRulesWithGlobFromBuildFile() throws Exception {
scratch.file("test/BUILD");
scratch.file("test/project/data1.dat", "");
scratch.file(
"test/skylark/extension.bzl",
"def extension_func(name, srcs):",
" native.genrule(name = name,",
" srcs = srcs,",
" outs = [name + '.txt'],",
" cmd = 'echo $(SRCS) hello >@')");
scratch.file(
"test/project/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func(name = 'rule1', srcs = glob(['*.dat']))");
ConfiguredTarget target = getConfiguredTarget("//test/project:rule1");
Action action = getGeneratingAction(Iterables.getOnlyElement(getFilesToBuild(target)));
assertThat(baseArtifactNames(action.getInputs())).contains("data1.dat");
}
public void testGlobWithExcludes() throws Exception {
scratch.file("test/project/a.dat", "");
scratch.file("test/project/b.dat", "");
scratch.file("test/skylark/BUILD");
scratch.file(
"test/skylark/extension.bzl",
"def extension_func(name, includes, excludes):",
" native.genrule(name = name,",
" srcs = native.glob(includes, excludes),",
" outs = [name + '.txt'],",
" cmd = 'echo $(SRCS) hello >@')");
scratch.file(
"test/project/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func(name = 'rule1', includes = ['*.dat'], excludes = ['b.dat'])");
ConfiguredTarget target = getConfiguredTarget("//test/project:rule1");
Action action = getGeneratingAction(Iterables.getOnlyElement(getFilesToBuild(target)));
assertThat(baseArtifactNames(action.getInputs())).contains("a.dat");
assertThat(baseArtifactNames(action.getInputs())).containsNoneIn(ImmutableList.of("b.dat"));
}
public void testFailInSkylarkExtensions() throws Exception {
scratch.file("test/skylark/extension.bzl", "def func(name):", " fail('not implemented')");
checkError(
"test/skylark",
"cr2",
"not implemented",
"load('/test/skylark/extension', 'func')",
"func(name = 'cr')",
"genrule(name = 'cr2')");
}
public void testUseNativeFromTopLevel() throws Exception {
scratch.file("test/skylark/extension.bzl", "native.cc_library(name = 'cr2')", "a = 0");
scratch.file(
"test/skylark/BUILD", "cc_library(name = 'cr')", "load('/test/skylark/extension', 'a')");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test/skylark:cr");
fail();
} catch (BuildFileContainsErrorsException e) {
assertContainsEvent("The native module cannot be accessed from here");
}
}
private void checkSymbolIsNotAccessibleInRuleImplementationPhase(
String symbolName, String ruleImplementationLines) throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
ruleImplementationLines,
" return struct()",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
symbolName + "() can only be called during the loading phase",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name = 'cr')");
}
public void testNativeModuleIsNotAccessibleInRuleImplementationPhase() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" native.genrule(name = 'some_genrule',",
" outs = ['out.txt'],",
" cmd = 'echo hello >@')",
" return struct()",
"",
"custom_rule = rule(implementation = custom_rule_impl)");
checkError(
"test/skylark",
"cr",
"genrule() can only be called during the loading phase",
"load('/test/skylark/extension', 'custom_rule')",
"custom_rule(name = 'cr')");
}
public void testNativeModulePackageFunction() throws Exception {
scratch.file("test/skylark/BUILD");
scratch.file(
"test/skylark/extension.bzl",
"def extension_func():",
" native.package(features = ['foo'])");
scratch.file(
"test/project/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func()",
"genrule(name = 'a', cmd = '', outs = ['b'])");
Package pkg = getConfiguredTarget("//test/project:a").getTarget().getPackage();
assertEquals(Iterables.getOnlyElement(pkg.getFeatures()), "foo");
}
public void testAttrModuleIsNotAccessibleInRuleImplementationPhase() throws Exception {
checkSymbolIsNotAccessibleInRuleImplementationPhase("attr.label", " attr.label()");
}
public void testRuleFunctionIsNotAccessibleInRuleImplementationPhase() throws Exception {
checkSymbolIsNotAccessibleInRuleImplementationPhase(
"rule", " rule(implementation = custom_rule_impl)");
}
public void testPreludeFile() throws Exception {
createSimpleExtension("test/skylark/extension.bzl", "'a'", false);
scratch.overwriteFile(
"tools/build_rules/prelude_blaze", "load('/test/skylark/extension', 'custom_rule')");
scratch.file("test/skylark/BUILD", "custom_rule(name = 'cr')");
invalidatePackages();
simulateLoadingPhase();
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertEquals("a", target.get("provider"));
}
public void testPreludeFileCaching() throws Exception {
createSimpleExtension("test/skylark/extension1.bzl", "'a'", false);
scratch.overwriteFile(
"tools/build_rules/prelude_blaze", "load('/test/skylark/extension1', 'custom_rule')");
scratch.file("test/skylark/BUILD", "custom_rule(name = 'cr')");
invalidatePackages();
simulateLoadingPhase();
ConfiguredTarget target1 = getConfiguredTarget("//test/skylark:cr");
assertEquals("a", target1.get("provider"));
createSimpleExtension("test/skylark/extension2.bzl", "'b'", false);
scratch.overwriteFile(
"tools/build_rules/prelude_blaze", "load('/test/skylark/extension2', 'custom_rule')");
invalidatePackages();
simulateLoadingPhase();
ConfiguredTarget target2 = getConfiguredTarget("//test/skylark:cr");
assertEquals("b", target2.get("provider"));
}
public void testPreludeFileParsedAsBuildFile() throws Exception {
createSimpleExtension("test/skylark/extension.bzl", "'a'", false);
scratch.overwriteFile(
"tools/build_rules/prelude_blaze",
"load('/test/skylark/extension', 'custom_rule')",
"def func(): return None");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule_test')",
"",
"custom_rule_test(name = 'cr')");
invalidatePackages();
reporter.removeHandler(failFastHandler);
try {
getTarget("//test/skylark:cr");
fail();
} catch (NoSuchTargetException e) {
assertContainsEvent("syntax error at 'def': This is not supported in BUILD files");
}
}
public void testTestRule() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" executable = ctx.outputs.executable",
" ctx.file_action(",
" output = executable,",
" executable = True,",
" content = 'echo Hello')",
"",
"custom_rule_test = rule(implementation = rule_impl, test = True)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule_test')",
"",
"custom_rule_test(name = 'cr')");
getConfiguredTarget("//test/skylark:cr");
}
public void testTestRuleWithoutRunfiles() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx): pass",
"",
"custom_rule_test = rule(implementation = rule_impl, test = True)");
checkError(
"test/skylark",
"cr",
"The following files have no generating action",
"load('/test/skylark/extension', 'custom_rule_test')",
"",
"custom_rule_test(name = 'cr')");
}
public void testTestRuleBadRuleClassName() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" return None",
"",
"custom_rule = rule(implementation = rule_impl, test = True)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
reporter.removeHandler(failFastHandler);
try {
// The error happens during the loading of the Skylark file so checkError don't work here
getTarget("//test/skylark:cr");
fail();
} catch (Exception e) {
assertContainsEvent(
"Invalid rule class name 'custom_rule', test rule class names must "
+ "end with '_test' and other rule classes must not");
}
}
public void testPrivateRule() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" return struct(p = 5)",
"",
"_custom_rule = rule(implementation = rule_impl)",
"def macro(name):",
" _custom_rule(name = name)");
scratch.file(
"test/skylark/BUILD", "load('/test/skylark/extension', 'macro')", "", "macro(name = 'cr')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
assertThat(target.get("p")).isEqualTo(5);
}
public void testExecutableRule() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" executable = ctx.outputs.executable",
" ctx.file_action(",
" output = executable,",
" executable = True,",
" content = 'echo Hello')",
" default_runfiles = ctx.runfiles([executable])",
" return struct(runfiles = default_runfiles)",
"",
"custom_rule = rule(implementation = rule_impl, executable = True)");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:cr");
FilesToRunProvider provider = target.getProvider(FilesToRunProvider.class);
assertNotNull(provider.getRunfilesSupport());
assertNotNull(provider.getExecutable());
assertSame(provider.getExecutable(), provider.getRunfilesSupport().getExecutable());
}
public void testLabelMustBeExecutable() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" return None",
"",
"custom_rule = rule(implementation = rule_impl,",
" attrs = {'exe': attr.label(executable=True)})");
checkError(
"test/skylark",
"cr",
"does not refer to a valid executable target",
"load('/test/skylark/extension', 'custom_rule')",
"",
"genrule(name = 'g',",
" cmd = '',",
" outs = ['out'])",
"custom_rule(name = 'cr', exe='g')");
}
public void testExecutableTool() throws Exception {
scratch.file(
"test/skylark/tool.bzl",
"def rule_impl(ctx):",
" executable = ctx.outputs.executable",
" ctx.file_action(",
" output = executable,",
" executable = True,",
" content = 'echo Hello')",
"",
"tool_rule = rule(implementation = rule_impl, executable = True)");
scratch.file(
"test/skylark/main.bzl",
"def rule_impl(ctx):",
" ctx.action(",
" outputs = [ctx.outputs.out],",
" executable = ctx.executable.tool,",
" arguments = ['--flag'])",
"",
"main_rule = rule(implementation = rule_impl,",
" outputs = {'out': '%{name}.sh'},",
" attrs = {'tool': attr.label(executable = True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/main', 'main_rule')",
"load('/test/skylark/tool', 'tool_rule')",
"",
"tool_rule(name = 'mytool')",
"main_rule(name = 'myrule', tool = ':mytool')");
ConfiguredTarget tool = getConfiguredTarget("//test/skylark:mytool");
// Check the tool has runfiles
assertThat(baseArtifactNames(getRunfilesSupport(tool).getRunfiles().getAllArtifacts()))
.contains("mytool");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:myrule");
SpawnAction action =
(SpawnAction) getGeneratingAction(Iterables.getOnlyElement(getFilesToBuild(target)));
// the first argument i.e. the executable is mytool
MoreAsserts.assertEndsWith("test/skylark/mytool", action.getArguments().get(0));
// the runfiles of the tool are among the action inputs
assertThat(baseArtifactNames(action.getInputs())).contains("test_Sskylark_Smytool-runfiles");
assertTrue(
ActionsTestUtil.getFirstArtifactEndingWith(
action.getInputs(), "test_Sskylark_Smytool-runfiles")
.isMiddlemanArtifact());
}
public void testTryingToImportNonexistingExtension() throws Exception {
scratch.file(
"test/BUILD",
"load('/test/skylark/non_existing_extension', 'custom_rule_test')",
"",
"custom_rule_test(name = 'cr')");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test:cr");
fail();
} catch (BuildFileContainsErrorsException e) {
assertContainsEvent("Extension file not found: 'test/skylark/non_existing_extension.bzl'");
}
}
public void testTryingToImportBrokenExtension() throws Exception {
scratch.file("test/ext.bzl", "var = 2", "var = 3");
scratch.file("test/BUILD", "load('/test/ext', 'var')", "", "custom_rule_test(name = 'cr')");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test:cr");
fail();
} catch (BuildFileContainsErrorsException e) {
assertContainsEvent("Variable var is read only");
assertContainsEvent("Extension 'test/ext.bzl' has errors");
}
}
private void writeConfigFile() throws Exception {
scratch.file(
"conditions/BUILD",
"config_setting(",
" name = 'a',",
" values = {'test_arg': 'a'})",
"config_setting(",
" name = 'b',",
" values = {'test_arg': 'b'})");
}
public void testUseConfigurableAttributeValueInSkylarkRule() throws Exception {
writeConfigFile();
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" return struct(provider = ctx.attr.a)",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'a': attr.string()})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', a = select({'//conditions:a' : 'x', '//conditions:b' : 'y'}))");
useConfiguration("--test_arg=a");
assertEquals("x", getConfiguredTarget("//test/skylark:cr").get("provider"));
useConfiguration("--test_arg=b");
assertEquals("y", getConfiguredTarget("//test/skylark:cr").get("provider"));
}
private String helloCcDepTargets() {
return LINE_JOINER.join(
" native.cc_library(",
" name = 'adep',",
" srcs = ['adep.cc'])",
" native.cc_library(",
" name = 'bdep',",
" srcs = ['bdep.cc'])");
}
public void testDefineConfigurableAttributeInSkylarkExtension() throws Exception {
writeConfigFile();
scratch.file(
"test/skylark/extension.bzl",
"def build_extension(name):",
" native.cc_binary(",
" name = name,",
" srcs = ['hello.cc'],",
" deps = select({",
" '//conditions:a': [':adep'],",
" '//conditions:b': [':bdep'],",
" }))",
"",
helloCcDepTargets());
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'build_extension')",
"",
"build_extension('hello')");
checkConfigurableHelloTargets();
}
public void testPassConfigurableAttributeToSkylarkExtension() throws Exception {
writeConfigFile();
scratch.file(
"test/skylark/extension.bzl",
"def build_extension(name, deps):",
" native.cc_binary(",
" name = name,",
" srcs = ['hello.cc'],",
" deps = deps)",
"",
helloCcDepTargets());
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'build_extension')",
"",
"build_extension('hello',",
" select({",
" '//conditions:a': [':adep'],",
" '//conditions:b': [':bdep'],",
" }))");
checkConfigurableHelloTargets();
}
public void testConfigurableAttributesConcatenateSkylarkAndNativeLists() throws Exception {
writeConfigFile();
scratch.file(
"test/skylark/extension.bzl",
"def return_select():",
" return select({'//conditions:a': ['foo.in']}) + ['skylark.list']");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'return_select')",
"genrule(",
" name = 'gen',",
" srcs = return_select() + ['native.list'],",
" outs = ['gen.out'],",
" cmd = 'echo hi > $@')");
useConfiguration("--test_arg=a");
getConfiguredTarget("//test/skylark:gen");
}
public void testConfigurableAttributesNotAllowedInImplicitOutputs() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def _impl(ctx): pass",
"def _get_outputs(attr_map):",
" return {'default': attr_map.x + '.out'}",
"simple = rule(",
" implementation = _impl,",
" attrs = {'x': attr.string()},",
" outputs = _get_outputs)");
checkError(
"test/skylark",
"simple",
"Attribute 'x' either doesn't exist or uses a select()",
"load('/test/skylark/extension', 'simple')",
"simple(",
" name = 'simple',",
" x = select({'//conditions:default': 'foo'}))");
}
public void testArtifactsWithoutGeneratingActions() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" ctx.new_file(ctx.configuration.bin_dir, ctx.outputs.o2, 'param')",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'o1': attr.output_list()},",
" outputs = {'o2': '%{name}.txt'})");
checkError(
"test/skylark",
"cr",
"The following files have no generating action:\n"
+ "test/skylark/cr.txt\n"
+ "test/skylark/cr.txtparam\n"
+ "test/skylark/output.txt",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', o1 = [':output.txt'])");
}
public void testSkylarkListWorksInBuildFile_LoadReturnsCorrectListType() throws Exception {
scratch.file("test/skylark/extension.bzl", "l1 = ['a']");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'l1')",
"",
"l2 = l1 + ['b']",
"genrule(name = l2[0], cmd = 'echo hi >@', outs = ['a.txt'])",
"genrule(name = l2[1], cmd = 'echo hi >@', outs = ['b.txt'])");
getConfiguredTarget("//test/skylark:a");
getConfiguredTarget("//test/skylark:b");
}
public void testSkylarkListWorksInBuildFile_FunctionReturnsCorrectListType() throws Exception {
scratch.file("test/skylark/extension.bzl", "def gen_list():", " return ['a']");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'gen_list')",
"",
"l2 = gen_list() + ['b']",
"genrule(name = l2[0], cmd = 'echo hi >@', outs = ['a.txt'])",
"genrule(name = l2[1], cmd = 'echo hi >@', outs = ['b.txt'])");
getConfiguredTarget("//test/skylark:a");
getConfiguredTarget("//test/skylark:b");
}
public void testSkylarkListWorksInBuildFile_LoadReturnsMutableCopy() throws Exception {
scratch.file("test/skylark/extension.bzl", "l = ['a']");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'l')",
"",
"l.append('b')",
"genrule(name = ''.join(l), cmd = 'echo hi >@', outs = ['a.txt'])");
getConfiguredTarget("//test/skylark:ab");
// Check that the Skylark value isn't modified (i.e. there is no 'b' value).
scratch.file(
"test/skylark2/BUILD",
"load('/test/skylark/extension', 'l')",
"",
"l.append('c')",
"genrule(name = ''.join(l), cmd = 'echo hi >@', outs = ['a.txt'])");
getConfiguredTarget("//test/skylark2:ac");
}
public void testBuildFileListWorksInSkylark() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def extension_func(l):",
" l = l + ['c']",
" for i in l:",
" native.genrule(name = i, cmd = 'echo hi >@', outs = [i + '.txt'])");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func(['a', 'b'])");
getConfiguredTarget("//test/skylark:a");
getConfiguredTarget("//test/skylark:b");
getConfiguredTarget("//test/skylark:c");
}
public void testSkylarkLoadedListCanBeConcatenated() throws Exception {
scratch.file("test/skylark/extension.bzl", "l1 = ('a',)", "l2 = ['a']");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'l1')",
"",
"m = l1 + ('b',) # tuple",
"genrule(name = ''.join(m), cmd = 'echo hi >@', outs = ['a.txt'])");
getConfiguredTarget("//test/skylark:ab");
scratch.file(
"test/skylark2/BUILD",
"load('/test/skylark/extension', 'l2')",
"",
"m = l2 + ['b']",
"genrule(name = ''.join(m), cmd = 'echo hi >@', outs = ['b.txt'])");
getConfiguredTarget("//test/skylark2:ab");
}
public void testSkylarkLoadedListBadType() throws Exception {
scratch.file("test/skylark/extension.bzl", "l1 = ['a']");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'l1')",
"",
"m = l1 + ('b',)",
"genrule(name = ''.join(m), cmd = 'echo hi >@', outs = ['a.txt'])");
try {
getConfiguredTarget("//test/skylark:ab");
fail();
} catch (AssertionFailedError ex) {
assertThat(ex.getMessage()).contains("can only concatenate List (not \"Tuple\") to List");
}
}
public void testPackageNameIsPresentInBuildExtensions() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def extension_func(name):",
" native.genrule(",
" name = name, cmd = 'echo ' + PACKAGE_NAME + ' >@', outs = [name + '.txt'])");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'extension_func')",
"",
"extension_func('a')");
SpawnAction action =
(SpawnAction)
getGeneratingAction(
Iterables.getOnlyElement(getFilesToBuild(getConfiguredTarget("//test/skylark:a"))));
String cmd = action.getArguments().get(2);
assertThat(cmd).contains("echo test/skylark >@");
}
public void testStringDictAttribute() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def impl(ctx):",
" return struct(p = ctx.attr.stringdict['a'])",
"r = rule(implementation=impl, attrs={'stringdict': attr.string_dict()})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'r')",
"",
"r(name='r1', stringdict={'a': 'b'})");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:r1");
assertEquals("b", target.get("p"));
}
public void testPackageNameIsNotPresentWhenSkylarkFileExecutes() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def extension_func():",
// TODO(bazel-team): clean up the behavior of PACKAGE_NAME.
" if not PACKAGE_NAME:",
" fail(\"name 'PACKAGE_NAME' is not defined\")",
" return PACKAGE_NAME",
"v = extension_func()");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'v')",
"",
"genrule(name='a', cmd='echo hi >$@', outs=['a.txt'])");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test/skylark:a");
fail();
} catch (BuildFileContainsErrorsException e) {
assertContainsEvent("name 'PACKAGE_NAME' is not defined");
}
}
public void testNonMandatoryAttributesWithSingleFile() throws Exception {
scratch.file("test/skylark/file.txt", "");
scratch.file(
"test/skylark/extension.bzl",
"def impl(ctx):",
" lbl = ctx.attr.a.label if ctx.attr.a else None",
" if hasattr(ctx.file, 'a'):",
" return struct(f=ctx.file.a, l=lbl)",
" else:",
" return struct(l=lbl)",
"",
"r = rule(implementation=impl,",
" attrs={'a': attr.label(allow_files=True, single_file=True)})");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'r')",
"",
"r(name='r1', a='file.txt')",
"r(name='r2')");
ConfiguredTarget target1 = getConfiguredTarget("//test/skylark:r1");
assertEquals("file.txt", ((Label) target1.get("l")).getName());
assertEquals("file.txt", ((Artifact) target1.get("f")).getFilename());
ConfiguredTarget target2 = getConfiguredTarget("//test/skylark:r2");
assertEquals(Runtime.NONE, target2.get("l"));
assertEquals(Runtime.NONE, target2.get("f"));
}
public void testWorkspaceStatusArtifacts() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def impl(ctx):",
" ctx.file_action(ctx.outputs.o, '')",
// info_file is SpecialArtifact, check if Skylark is unaware of this
" f = ctx.info_file",
" f = ctx.outputs.o",
" l1 = [ctx.info_file, ctx.outputs.o]",
" l2 = [ctx.outputs.o, ctx.info_file]",
" s1 = set([ctx.info_file]) + [ctx.outputs.o]",
" s2 = set([ctx.outputs.o]) + [ctx.info_file]",
" return struct(",
" info=ctx.info_file, version=ctx.version_file)",
"",
"r = rule(implementation=impl, outputs={'o': '%{name}.txt'})");
scratch.file("test/skylark/BUILD", "load('/test/skylark/extension', 'r')", "", "r(name='r')");
ConfiguredTarget target = getConfiguredTarget("//test/skylark:r");
assertEquals("build-info.txt", ((Artifact) target.get("info")).getFilename());
assertEquals("build-changelist.txt", ((Artifact) target.get("version")).getFilename());
}
public void testPrintFunction() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def impl(ctx):",
" print('analysis phase hello')",
"",
"print('loading phase hello')",
"r = rule(implementation = impl)");
scratch.file("test/skylark/BUILD", "load('/test/skylark/extension', 'r')", "", "r(name='r')");
getConfiguredTarget("//test/skylark:r");
assertContainsEventsInOrder("loading phase hello", "analysis phase hello");
}
public void testPrintFunctionInMacro() throws Exception {
scratch.file("test/skylark/extension.bzl", "def macro():", " print('macro hello')");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'macro')",
"",
"macro()",
"genrule(name = 'r', outs = ['r.txt'], cmd = 'echo hi >@')");
getConfiguredTarget("//test/skylark:r");
assertContainsEventsInOrder("macro hello");
}
public void testInvalidLabel() throws Exception {
scratch.file("test/skylark/iproute2_src_pkg", "");
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" Label(ctx.attr.locations[0])",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'locations': attr.string_list()})");
checkError(
"test/skylark",
"cr",
"Illegal absolute label syntax: invalid_label",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr', locations = ['invalid_label'])");
}
public void testAddingToNestedSetComingFromNativeRuleDoesNotWork() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def custom_rule_impl(ctx):",
" ctx.attr.deps[0].files + ctx.attr.deps[1].files",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'deps': attr.label_list()})",
"",
"def macro(name):",
" native.genrule(name = name + '1', cmd = 'echo hi >$@', outs = [name + '1.txt'])",
" native.genrule(name = name + '2', cmd = 'echo hi >$@', outs = [name + '2.txt'])",
" custom_rule(name = name, deps = [':' + name + '1', ':' + name + '2'])");
checkError(
"test/skylark",
"cr",
"Cannot add more elements to this set. Sets created in "
+ "native rules cannot be left side operands of the + operator.",
"load('/test/skylark/extension', 'macro')",
"",
"macro(name = 'cr')");
}
public void testFilesIsNotSetOfFiles() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def rule_impl(ctx):",
" return struct(files = set(['a']))",
"",
"custom_rule = rule(implementation = rule_impl)");
checkError(
"test/skylark",
"cr",
"expected set of Files for 'files' but got set of strings instead: set([\"a\"])",
"load('/test/skylark/extension', 'custom_rule')",
"",
"custom_rule(name = 'cr')");
}
public void testPackageGroup() throws Exception {
scratch.file("test/pkg1/BUILD");
scratch.file("test/pkg2/BUILD");
scratch.file(
"test/skylark/extension.bzl",
"def macro(name):",
" native.package_group(name = 'default_pg', packages = ['//test/pkg2'])",
" native.package_group(name = name,",
" packages = ['//test/pkg1'], includes = [':default_pg'])");
scratch.file("test/skylark/BUILD", "load('extension', 'macro')", "macro('my_pg')");
PackageGroup target = (PackageGroup) getTarget("//test/skylark:my_pg");
assertThat(target.getContainedPackages()).containsExactly("test/pkg1");
assertThat(target.getIncludes())
.containsExactly(Label.parseAbsolute("//test/skylark:default_pg"));
}
public void testExportsFiles() throws Exception {
scratch.file("test/skylark/a.txt");
scratch.file(
"test/skylark/extension.bzl",
"def macro():",
" native.exports_files(['a.txt'], visibility = ['//visibility:private'])");
scratch.file("test/skylark/BUILD", "load('extension', 'macro')", "macro()");
// Without the exports_files in the extension this would throws a NoSuchTargetException
InputFile target = (InputFile) getTarget("//test/skylark:a.txt");
assertThat(target.getVisibility().getDeclaredLabels())
.containsExactly(Label.parseAbsolute("//visibility:private"));
}
public void testErrorPropagation() throws Exception {
scratch.file("test/skylark/extension.bzl", "fail('my error')", "a = 'my_target'");
scratch.file("test/skylark/BUILD", "load('extension', 'a')", "cc_library(name = a)");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test/skylark:my_target");
fail();
} catch (BuildFileContainsErrorsException e) {
assertThat(e.getMessage()).contains("Extension file 'test/skylark/extension.bzl' has errors");
}
assertContainsEvent("my error");
}
public void testErrorPropagation2() throws Exception {
scratch.file("test/skylark/extension.bzl", "a = 'my_target'", "fail('my error')");
scratch.file("test/skylark/BUILD", "load('extension', 'a')", "cc_library(name = a)");
reporter.removeHandler(failFastHandler);
try {
getTarget("//test/skylark:my_target");
fail();
} catch (BuildFileContainsErrorsException e) {
assertThat(e.getMessage()).contains("Extension file 'test/skylark/extension.bzl' has errors");
}
assertContainsEvent("my error");
}
private void checkConfigurableHelloTargets() throws Exception {
useConfiguration("--test_arg=a");
Iterable<String> inputsA = getInputsForFilesToBuildAction("//test/skylark:hello");
assertThat(inputsA).contains("adep.pic.o");
assertThat(inputsA).doesNotContain("bdep.pic.o");
useConfiguration("--test_arg=b");
Iterable<String> inputsB = getInputsForFilesToBuildAction("//test/skylark:hello");
assertThat(inputsB).contains("bdep.pic.o");
assertThat(inputsB).doesNotContain("adep.pic.o");
}
private Iterable<String> getInputsForFilesToBuildAction(String label) throws Exception {
return ActionsTestUtil.baseArtifactNames(
getGeneratingAction(getOnlyElement(getFilesToBuild(getConfiguredTarget(label))))
.getInputs());
}
// Regression test for https://github.com/google/bazel/issues/121
public void testAddingListAndSkylarkList() throws Exception {
scratch.file(
"test/skylark/extension.bzl",
"def macro(n, input_dict):",
" d = ' '.join(input_dict['list'] + ['2'])",
" native.genrule(name = n, cmd = 'echo %s >@' % d, outs = [n + '.txt'])");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'macro')",
"",
"macro(n = 'a', input_dict = {'list': ['1']})");
SpawnAction action =
(SpawnAction)
getGeneratingAction(
Iterables.getOnlyElement(getFilesToBuild(getConfiguredTarget("//test/skylark:a"))));
String cmd = action.getArguments().get(2);
assertThat(cmd).contains("echo 1 2 >@");
}
public void testLoadAlias() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/alias', number = 'one')",
"number(name = 'target')");
checkNumber("target", 1);
}
public void testLoadAliasIdentity() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD", "load('/test/skylark/alias', one = 'one')", "one(name = 'target')");
checkNumber("target", 1);
}
public void testLoadAliasUseExistingName() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD", "load('/test/skylark/alias', two = 'one')", "two(name = 'target')");
checkNumber("target", 1);
}
public void testLoadAliasSwapDefinitions() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/alias', one = 'two', two = 'one')",
"one(name = 'target1')",
"two(name = 'target2')");
checkNumber("target1", 2);
checkNumber("target2", 1);
}
public void testLoadAliasMultipleImports() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/alias', 'one', two = 'one', three = 'one')",
"one(name = 'target1')",
"two(name = 'target2')",
"three(name = 'target3')");
checkNumber("target1", 1);
checkNumber("target2", 1);
checkNumber("target3", 1);
}
public void testLoadAliasConflict() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/alias', one = 'two', 'one')",
"one(name = 'target')");
reporter.removeHandler(failFastHandler);
checkLoadAliasError("Symbol 'one' has already been loaded");
}
public void testLoadAliasNotFound() throws Exception {
createBzlFileForAlias();
scratch.file(
"test/skylark/BUILD", "load('/test/skylark/alias', one = 'four')", "one(name = 'target')");
reporter.removeHandler(failFastHandler);
checkLoadAliasError("no such variable: four");
}
private void checkLoadAliasError(String errorMessage) throws Exception {
try {
getTarget("//test/skylark:target");
fail("Expected NoSuchTargetException");
} catch (NoSuchTargetException nste) {
assertContainsEvent(errorMessage);
}
}
private void createBzlFileForAlias() throws Exception {
scratch.file(
"test/skylark/alias.bzl",
"def one_impl(ctx):",
" return struct(num = 1)",
"",
"def two_impl(ctx):",
" return struct(num = 2)",
"",
"def three_impl(ctx):",
" return struct(num = 3)",
"",
"one = rule(implementation = one_impl)",
"two = rule(implementation = two_impl)",
"three = rule(implementation = three_impl)");
}
private void checkNumber(String targetName, int expectedValue) throws Exception {
ConfiguredTarget target = getConfiguredTarget("//test/skylark:" + targetName);
assertThat(target.get("num")).isEqualTo(expectedValue);
}
public void testInclusionOfRunfilesOfInputs() throws Exception {
scratch.file("run/file/test/secret_script.sh", "#!/bin/bash");
scratch.file("run/file/test/important_data", "secret");
scratch.file("run/file/test/useless_data", "useless!");
scratch.file(
"run/file/test/myrule.bzl",
"def _impl(ctx):",
" ctx.action(command = '%s > %s' % (ctx.executable.script.path, ctx.outputs.out.path), ",
" inputs = [ctx.executable.script], ",
" outputs = [ctx.outputs.out])",
"my_rule = rule(implementation = _impl, ",
" attrs = {",
" 'script' : attr.label(allow_files=True,executable=True,cfg=HOST_CFG),",
" 'junk' : attr.label(allow_files=True,executable=True),",
" }",
" ,",
" outputs = {'out': '%{name}_out'} ",
")");
scratch.file(
"run/file/test/BUILD",
"load('/run/file/test/myrule', 'my_rule')",
"",
"sh_binary(",
" name = 'important_binary', ",
" srcs = ['secret_script.sh'],",
" data = ['important_data'],",
")",
"sh_binary(",
" name = 'useless_binary', ",
" srcs = ['secret_script.sh'],",
" data = ['useless_data'],",
")",
"my_rule(",
" name = 'main',",
" script = ':important_binary',",
" junk = ':useless_binary',",
")");
Iterable<String> runfiles = extractDataRunfiles(getConfiguredTarget("//run/file/test:main"));
assertThat(runfiles).contains("run/file/test/secret_script.sh");
assertThat(runfiles).contains("run/file/test/important_data");
// Attribute 'junk' did not have HOST_CFG, which means that useless_data must
// not be included here
assertThat(runfiles).doesNotContain("run/file/test/useless_data");
}
/**
* Returns the data runfiles via some arcane magic. This includes:
* a) Getting a middleman artifact from the inputs of the generating action
* b) Expanding this artifact, as seen in ActionInputHelper#actionGraphMiddlemanExpander, by
* collecting the inputs of the generating action
*
* <p>RunfilesProvider, FilesToRunProvider and RunfilesSupplier did not return any valid results.
*/
private Iterable<String> extractDataRunfiles(ConfiguredTarget target) {
List<String> result = new LinkedList<>();
Artifact fileToBuild = Iterables.getOnlyElement(getFilesToBuild(target));
Iterable<Artifact> inputs = getGeneratingAction(fileToBuild).getInputs();
for (Artifact current : inputs) {
if (current.isMiddlemanArtifact()) {
Iterables.addAll(result, Artifact.toExecPaths(getGeneratingAction(current).getInputs()));
}
}
return result;
}
public void testLoadList() throws Exception {
scratch.file("test/skylark/extension.bzl", "li = [('abc', '11'), ('def', '22')]");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/extension', 'li')",
"[cc_library(name = lm[0]) for lm in li]");
// Check that the targets can be loaded.
ConfiguredTarget target = getConfiguredTarget("//test/skylark:abc");
assertThat(target.getLabel().toString()).isEqualTo("//test/skylark:abc");
target = getConfiguredTarget("//test/skylark:def");
assertThat(target.getLabel().toString()).isEqualTo("//test/skylark:def");
}
public void testListIsFrozenAfterLoad() throws Exception {
scratch.file(
"test/deflist.bzl",
"l1 = [1, 2]",
"l2 = l1",
"l1.extend([3, 4])",
"if l2 != [1, 2, 3, 4]: fail('l2=%r' % (l2,))");
scratch.file(
"test/uselist.bzl",
"load('deflist', 'l1')",
"print('loading uselist, l1=%r' % (l1,))",
"l3 = l1 + [5, 6]",
"if l3 != [1, 2, 3, 4, 5, 6]: fail('l3=%r' % (l3,))");
scratch.file(
"test/abuselist.bzl",
"load('deflist', 'l1')",
"l4 = []",
"print('loading abuselist, l1=%r' % (l1,))",
"l1.append(5)",
"fail('l1=%r' % (l1,))");
scratch.file(
"test/BUILD",
"load('uselist', 'l3')",
"load('abuselist', 'l4')",
"genrule(name='foo', outs=['one'], cmd='echo 1 > $@')");
try {
getConfiguredTarget("//test:foo");
fail("An Exception was expected, but nothing was thrown.");
} catch (AssertionFailedError ex) {
assertThat(ex)
.hasMessage("ERROR /workspace/test/abuselist.bzl:4:1: trying to mutate a frozen object");
}
}
public void testLoadListIsFrozenBeforeAnalysis() throws Exception {
scratch.file(
"test/library.bzl",
"l = [1, 2]",
"def _impl(ctx): l.append(3) ; return struct()",
"foo = rule(implementation=_impl)");
scratch.file("test/BUILD", "load('library', 'foo')", "foo(name='foo')");
try {
getConfiguredTarget("//test:foo");
fail("An Exception was expected, but nothing was thrown.");
} catch (AssertionFailedError ex) {
assertThat(ex)
.hasMessage(
LINE_JOINER.join(
"ERROR /workspace/test/BUILD:2:1: in foo rule //test:foo: ",
"Traceback (most recent call last):",
"\tFile \"/workspace/test/BUILD\", line 2",
"\t\tfoo(name = 'foo')",
"\tFile \"/workspace/test/library.bzl\", line 2, in _impl",
"\t\tl.append(3)",
"trying to mutate a frozen object"));
}
}
public void testAspect() throws Exception {
scratch.file(
"test/aspect.bzl",
"def _impl(target, ctx):",
" print('This aspect does nothing')",
"MyAspect = aspect(implementation=_impl)");
scratch.file("test/BUILD", "java_library(name = 'xxx',)");
AnalysisResult analysisResult =
update(
ImmutableList.of("//test:xxx"),
ImmutableList.<String>of("test/aspect.bzl%MyAspect"),
false,
LOADING_PHASE_THREADS,
true,
new EventBus());
assertThat(
transform(
analysisResult.getTargetsToBuild(),
new Function<ConfiguredTarget, String>() {
@Nullable
@Override
public String apply(ConfiguredTarget configuredTarget) {
return configuredTarget.getLabel().toString();
}
}))
.containsExactly("//test:xxx");
}
/**
* Skylark integration test that forces inlining.
*/
public static class SkylarkIntegrationTestsWithInlineCalls extends SkylarkIntegrationTest {
@Override
public void setUp() throws Exception {
super.setUp();
ImmutableMap<? extends SkyFunctionName, ? extends SkyFunction> skyFunctions =
((InMemoryMemoizingEvaluator) getSkyframeExecutor().getEvaluatorForTesting())
.getSkyFunctionsForTesting();
SkylarkImportLookupFunction skylarkImportLookupFunction =
new SkylarkImportLookupFunction(this.getRuleClassProvider(), this.getPackageFactory());
((PackageFunction) skyFunctions.get(SkyFunctions.PACKAGE))
.setSkylarkImportLookupFunctionForInliningForTesting(skylarkImportLookupFunction);
}
@Override
public void testRecursiveImport() throws Exception {
scratch.file("test/skylark/ext2.bzl", "load('/test/skylark/ext1', 'symbol2')");
scratch.file("test/skylark/ext1.bzl", "load('/test/skylark/ext2', 'symbol1')");
scratch.file(
"test/skylark/BUILD",
"load('/test/skylark/ext1', 'custom_rule')",
"genrule(name = 'rule')");
reporter.removeHandler(failFastHandler);
try {
// ensureTargetsVisited() produces a different event than getTarget, and it doesn't fail
// even though there is an error in the rule. What's going on here?
ensureTargetsVisited("//test/skylark:rule");
getTarget("//test/skylark:rule");
fail();
} catch (BuildFileContainsErrorsException e) {
// This is expected
}
assertContainsEvent("cycle in referenced extension files");
assertContainsEvent("test/skylark/ext1.bzl");
assertContainsEvent("test/skylark/ext2.bzl");
assertContainsEvent("Skylark import cycle");
assertContainsEvent("Loading of target '//test/skylark:rule' failed; build aborted");
assertThat(eventCollector).hasSize(3);
}
}
}