Add 'ctx.actions' and implement 'ctx.action.declare_file'. RELNOTES: None. PiperOrigin-RevId: 160264501
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java index 74c7682..4a956db 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java +++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -99,8 +99,8 @@ + "Files. If you have a Skylark rule that needs to create a new File, you might need to " + "add the label to the attrs (if it's an input) or the outputs (if it's an output). Then " + "you can access the File through the rule's <a href='ctx.html'>context</a>. You can " - + "also use <a href='ctx.html#new_file'>ctx.new_file</a> to create a new file in the rule " - + "implementation.</p>") + + "also use <a href='actions.html#declare_file'>ctx.actions.declare_file</a> to " + + "declare a new file in the rule implementation.</p>") public class Artifact implements FileType.HasFilename, ActionInput, SkylarkValue, Comparable<Object> {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java new file mode 100644 index 0000000..6157392 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java
@@ -0,0 +1,110 @@ +// Copyright 2017 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.rules; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.Root; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; +import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.Printer; +import com.google.devtools.build.lib.syntax.Runtime; +import com.google.devtools.build.lib.vfs.PathFragment; + +/** + * Provides a Skylark interface for all action creation needs. + */ +@SkylarkModule( + name = "actions", + category = SkylarkModuleCategory.BUILTIN, + doc = "Module providing functions to create actions." +) + +public class SkylarkActionFactory implements SkylarkValue { + private final SkylarkRuleContext context; + private RuleContext ruleContext; + + + public SkylarkActionFactory(SkylarkRuleContext context, RuleContext ruleContext) { + this.context = context; + this.ruleContext = ruleContext; + } + + Root newFileRoot() throws EvalException { + return context.isForAspect() + ? ruleContext.getConfiguration().getBinDirectory(ruleContext.getRule().getRepository()) + : ruleContext.getBinOrGenfilesDirectory(); + } + + + @SkylarkCallable( + name = "declare_file", + doc = + "Declares that rule or aspect creates a file with the given filename. " + + "If <code>sibling</code> is not specified, file name is relative to " + + "package directory, otherwise the file is in the same directory as " + + "<code>sibling</code>. " + + "You must create an action that generates the file. <br>" + + "Files that are specified in rule's outputs do not need to be declared and are " + + "available through <a href=\"ctx.html#outputs\">ctx.outputs</a>.", + parameters = { + @Param( + name = "filename", + type = String.class, + doc = + "If no 'sibling' provided, path of the new file, relative " + + "to the current package. Otherwise a base name for a file " + + "('sibling' determines a directory)." + ), + @Param( + name = "sibling", + doc = "A file that lives in the same directory as the newly created file.", + type = Artifact.class, + noneable = true, + positional = false, + named = true, + defaultValue = "None" + ) + } + ) + public Artifact declareFile(String filename, Object sibling) throws EvalException { + context.checkMutable("actions.declareFile"); + if (Runtime.NONE.equals(sibling)) { + return ruleContext.getPackageRelativeArtifact(filename, newFileRoot()); + } else { + PathFragment original = ((Artifact) sibling).getRootRelativePath(); + PathFragment fragment = original.replaceName(filename); + return ruleContext.getDerivedArtifact(fragment, newFileRoot()); + } + } + + @Override + public boolean isImmutable() { + return context.isImmutable(); + } + + @Override + public void write(Appendable buffer, char quotationMark) { + Printer.append(buffer, "actions for"); + context.write(buffer, quotationMark); + } + + void nullify() { + ruleContext = null; + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java index d85a2ec..8fee2dd 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
@@ -169,6 +169,8 @@ // after this object has been nullified. private final String ruleLabelCanonicalName; + private final SkylarkActionFactory actionFactory; + // The fields below intended to be final except that they can be cleared by calling `nullify()` // when the object becomes featureless. private RuleContext ruleContext; @@ -195,6 +197,7 @@ public SkylarkRuleContext(RuleContext ruleContext, @Nullable AspectDescriptor aspectDescriptor) throws EvalException, InterruptedException { + this.actionFactory = new SkylarkActionFactory(this, ruleContext); this.ruleContext = Preconditions.checkNotNull(ruleContext); this.ruleLabelCanonicalName = ruleContext.getLabel().getCanonicalForm(); this.fragments = new FragmentCollection(ruleContext, ConfigurationTransition.NONE); @@ -293,6 +296,7 @@ * rule implementation function has exited). */ public void nullify() { + actionFactory.nullify(); ruleContext = null; fragments = null; hostFragments = null; @@ -611,6 +615,15 @@ return DefaultProvider.SKYLARK_CONSTRUCTOR; } + @SkylarkCallable( + name = "actions", + structField = true, + doc = "Functions to declare files and create actions." + ) + public SkylarkActionFactory actions() { + return actionFactory; + } + @SkylarkCallable(name = "created_actions", doc = "For rules with <a href=\"globals.html#rule._skylark_testable\">_skylark_testable" + "</a> set to <code>True</code>, this returns an " @@ -867,7 +880,7 @@ } } - private boolean isForAspect() { + boolean isForAspect() { return ruleAttributesCollection != null; }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java index 6b36afc68..cd2c949 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
@@ -1304,7 +1304,7 @@ " attrs = { 'deps' : attr.label_list(aspects = [aspect1]) })", "", "def _action_rule_impl(ctx):", - " out = ctx.new_file(ctx.label.name)", + " out = ctx.actions.declare_file(ctx.label.name)", " ctx.action(outputs = [out], command = 'dontcare', mnemonic='Mnemonic')", " return struct()", "action_rule = rule(_action_rule_impl, attrs = { 'deps' : attr.label_list() })");
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java index a39cf85..174e050 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java
@@ -493,7 +493,7 @@ scratch.file( "a/def.bzl", "def _aspect_impl(target, ctx):", - " f = ctx.new_file('foo.txt')", + " f = ctx.actions.declare_file('foo.txt')", " ctx.action(outputs = [f], command = 'echo foo > \"$1\"')", " return struct(output=f)", "def _rule_impl(ctx):",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java index 8609c52..4b3740b 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java
@@ -866,7 +866,7 @@ scratch.file( "test/aspect.bzl", "def _impl(target, ctx):", - " ctx.new_file('missing_in_action.txt')", + " ctx.actions.declare_file('missing_in_action.txt')", " return struct()", "", "MyAspect = aspect(implementation=_impl)"); @@ -911,13 +911,13 @@ scratch.file( "test/aspect.bzl", "def _impl(target, ctx):", - " f = ctx.new_file('f.txt')", + " f = ctx.actions.declare_file('f.txt')", " ctx.file_action(f, 'f')", " return struct(output_groups = { 'duplicate' : depset([f]) })", "", "MyAspect = aspect(implementation=_impl)", "def _rule_impl(ctx):", - " g = ctx.new_file('g.txt')", + " g = ctx.actions.declare_file('g.txt')", " ctx.file_action(g, 'g')", " return struct(output_groups = { 'duplicate' : depset([g]) })", "my_rule = rule(_rule_impl)", @@ -947,7 +947,7 @@ scratch.file( "test/aspect.bzl", "def _a1_impl(target, ctx):", - " f = ctx.new_file(target.label.name + '_a1.txt')", + " f = ctx.actions.declare_file(target.label.name + '_a1.txt')", " ctx.file_action(f, 'f')", " return struct(output_groups = { 'a1_group' : depset([f]) })", "", @@ -979,7 +979,7 @@ scratch.file( "test/aspect.bzl", "def _a1_impl(target, ctx):", - " f = ctx.new_file(target.label.name + '_a1.txt')", + " f = ctx.actions.declare_file(target.label.name + '_a1.txt')", " ctx.file_action(f, 'f')", " return [OutputGroupInfo(a1_group = depset([f]))]", "", @@ -1010,7 +1010,7 @@ scratch.file( "test/aspect.bzl", "def _a1_impl(target, ctx):", - " f = ctx.new_file(target.label.name + '_a1.txt')", + " f = ctx.actions.declare_file(target.label.name + '_a1.txt')", " ctx.file_action(f, 'f')", " return struct(output_groups = { 'a1_group' : depset([f]) })", "", @@ -1022,7 +1022,7 @@ " return struct(output_groups = og)", "my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })", "def _a2_impl(target, ctx):", - " g = ctx.new_file(target.label.name + '_a2.txt')", + " g = ctx.actions.declare_file(target.label.name + '_a2.txt')", " ctx.file_action(g, 'f')", " return struct(output_groups = { 'a2_group' : depset([g]) })", "", @@ -1051,7 +1051,7 @@ scratch.file( "test/aspect.bzl", "def _a1_impl(target, ctx):", - " f = ctx.new_file(target.label.name + '_a1.txt')", + " f = ctx.actions.declare_file(target.label.name + '_a1.txt')", " ctx.file_action(f, 'f')", " return [OutputGroupInfo(a1_group = depset([f]))]", "", @@ -1068,7 +1068,7 @@ " return [OutputGroupInfo(**og)]", "my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })", "def _a2_impl(target, ctx):", - " g = ctx.new_file(target.label.name + '_a2.txt')", + " g = ctx.actions.declare_file(target.label.name + '_a2.txt')", " ctx.file_action(g, 'f')", " return [OutputGroupInfo(a2_group = depset([g]))]", "", @@ -1098,7 +1098,7 @@ scratch.file( "test/aspect.bzl", "def _a1_impl(target, ctx):", - " f = ctx.new_file(target.label.name + '_a1.txt')", + " f = ctx.actions.declare_file(target.label.name + '_a1.txt')", " ctx.file_action(f, 'f')", " return struct(output_groups = { 'a1_group' : depset([f]) })", "", @@ -1110,7 +1110,7 @@ " return struct(output_groups = og)", "my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })", "def _a2_impl(target, ctx):", - " g = ctx.new_file(target.label.name + '_a2.txt')", + " g = ctx.actions.declare_file(target.label.name + '_a2.txt')", " ctx.file_action(g, 'f')", " return struct(output_groups = { 'a1_group' : depset([g]) })", "", @@ -1647,7 +1647,7 @@ scratch.file( "foo/extension.bzl", "def _aspect_impl(target, ctx):", - " file = ctx.new_file('aspect-output-' + target.label.name)", + " file = ctx.actions.declare_file('aspect-output-' + target.label.name)", " ctx.file_action(file, 'data')", " return struct(aspect_file = file)", "my_aspect = aspect(_aspect_impl)", @@ -1768,7 +1768,7 @@ scratch.file( "test/aspect.bzl", "def _aspect_impl(target,ctx):", - " f = ctx.new_file('dummy.txt')", + " f = ctx.actions.declare_file('dummy.txt')", " ctx.action(outputs = [f], command='echo xxx > $(location f)', mnemonic='AspectAction')", " return struct()", "my_aspect = aspect(implementation = _aspect_impl)" @@ -1918,7 +1918,7 @@ "", "def _a3_impl(target,ctx):", " value = []", - " f = ctx.new_file('a3.out')", + " f = ctx.actions.declare_file('a3.out')", " ctx.file_action(f, 'text')", " for dep in ctx.rule.attr.deps:", " if hasattr(dep, 'a3p'):",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java index c2010d5..0e636af 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
@@ -1902,7 +1902,7 @@ "def _aspect_impl(target, ctx):", " if ctx.rule.attr.deps:", " dep = ctx.rule.attr.deps[0]", - " file = ctx.new_file('file.txt')", + " file = ctx.actions.declare_file('file.txt')", " foo = dep." + (attribute.startsWith("rule.") ? "" : "ctx.") + attribute, " return struct(ctx = ctx, rule=ctx.rule)", "MyAspect = aspect(implementation=_aspect_impl)",
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java index 20a7084..c7bec75 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
@@ -471,8 +471,8 @@ assertThat(parseFile("x[1::2]").toString()).isEqualTo("[x[1::2]\n]"); assertThat(parseFile("x[1:]").toString()).isEqualTo("[x[1:]\n]"); assertThat(parseFile("str[42]").toString()).isEqualTo("[str[42]\n]"); - assertThat(parseFile("ctx.new_file('hello')").toString()) - .isEqualTo("[ctx.new_file(\"hello\")\n]"); + assertThat(parseFile("ctx.actions.declare_file('hello')").toString()) + .isEqualTo("[ctx.actions.declare_file(\"hello\")\n]"); assertThat(parseFile("new_file(\"hello\")").toString()).isEqualTo("[new_file(\"hello\")\n]"); }
diff --git a/src/test/shell/bazel/bazel_java_test.sh b/src/test/shell/bazel/bazel_java_test.sh index 509eb70..093bc70 100755 --- a/src/test/shell/bazel/bazel_java_test.sh +++ b/src/test/shell/bazel/bazel_java_test.sh
@@ -173,7 +173,7 @@ deps.append(dep[java_common.provider]) deps_provider = java_common.merge(deps) - output_jar = ctx.new_file("lib" + ctx.label.name + ".jar") + output_jar = ctx.actions.declare_file("lib" + ctx.label.name + ".jar") compilation_provider = java_common.compile( ctx, @@ -290,7 +290,7 @@ cat >g/java_custom_library.bzl <<'EOF' def _impl(ctx): - output_jar = ctx.new_file("lib" + ctx.label.name + ".jar") + output_jar = ctx.actions.declare_file("lib" + ctx.label.name + ".jar") compilation_provider = java_common.compile( ctx, @@ -367,7 +367,7 @@ cat >g/java_custom_library.bzl <<'EOF' def _impl(ctx): - output_jar = ctx.new_file("lib" + ctx.label.name + ".jar") + output_jar = ctx.actions.declare_file("lib" + ctx.label.name + ".jar") compilation_provider = java_common.compile( ctx,
diff --git a/src/test/shell/integration/build_event_stream_test.sh b/src/test/shell/integration/build_event_stream_test.sh index 3d85cda..f895661 100755 --- a/src/test/shell/integration/build_event_stream_test.sh +++ b/src/test/shell/integration/build_event_stream_test.sh
@@ -95,7 +95,7 @@ cat > simpleaspect.bzl <<EOF def _simple_aspect_impl(target, ctx): for orig_out in ctx.rule.attr.outs: - aspect_out = ctx.new_file(orig_out.name + ".aspect") + aspect_out = ctx.actions.declare_file(orig_out.name + ".aspect") ctx.file_action( output=aspect_out, content = "Hello from aspect")
diff --git a/src/test/shell/integration/discard_analysis_cache_test.sh b/src/test/shell/integration/discard_analysis_cache_test.sh index 21fbfb3..eda5823 100755 --- a/src/test/shell/integration/discard_analysis_cache_test.sh +++ b/src/test/shell/integration/discard_analysis_cache_test.sh
@@ -70,7 +70,7 @@ def _simple_aspect_impl(target, ctx): result=depset() for orig_out in target.files: - aspect_out = ctx.new_file(orig_out.basename + ".aspect") + aspect_out = ctx.actions.declare_file(orig_out.basename + ".aspect") ctx.file_action( output=aspect_out, content = "Hello from aspect for %s" % orig_out.basename)
diff --git a/src/test/shell/integration/discard_graph_edges_test.sh b/src/test/shell/integration/discard_graph_edges_test.sh index 5baf41d..2e05b01 100755 --- a/src/test/shell/integration/discard_graph_edges_test.sh +++ b/src/test/shell/integration/discard_graph_edges_test.sh
@@ -69,7 +69,7 @@ def _simple_aspect_impl(target, ctx): result=depset() for orig_out in target.files: - aspect_out = ctx.new_file(orig_out.basename + ".aspect") + aspect_out = ctx.actions.declare_file(orig_out.basename + ".aspect") ctx.file_action( output=aspect_out, content = "Hello from aspect for %s" % orig_out.basename) @@ -253,7 +253,7 @@ cat > conflict/conflict_rule.bzl <<EOF || fail "Couldn't write bzl file" def _create(ctx): files_to_build = set(ctx.outputs.outs) - intemediate_outputs = [ctx.new_file("bar")] + intemediate_outputs = [ctx.actions.declare_file("bar")] intermediate_cmd = "cat %s > %s" % (ctx.attr.name, intemediate_outputs[0].path) action_cmd = "touch " + list(files_to_build)[0].path ctx.action(outputs=list(intemediate_outputs),