Introduce ctx.actions.write in place of ctx.file_action.

RELNOTES: None.
PiperOrigin-RevId: 160630261
diff --git a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
index ad2148a..574fa6a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
@@ -578,7 +578,7 @@
 
   @SkylarkCallable(
       name = "content",
-      doc = "For actions created by <a href=\"ctx.html#file_action\">ctx.file_action()</a> or "
+      doc = "For actions created by <a href=\"actions.html#write\">ctx.actions.write()</a> or "
           + "<a href=\"ctx.html#template_action\">ctx.template_action()</a>, the contents of the "
           + "file to be written.",
       structField = true,
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
index 2bd1b4e..985c5f8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java
@@ -20,6 +20,7 @@
 import com.google.devtools.build.lib.actions.extra.SpawnInfo;
 import com.google.devtools.build.lib.analysis.PseudoAction;
 import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.skylarkinterface.Param;
@@ -192,6 +193,38 @@
   }
 
 
+  @SkylarkCallable(
+      name = "write",
+      doc = "Creates a file write action.",
+      parameters = {
+          @Param(
+              name = "output",
+              type = Artifact.class,
+              doc = "the output file.",
+              named = true
+          ),
+          @Param(
+              name = "content",
+              type = String.class,
+              doc = "the contents of the file.",
+              named = true
+          ),
+          @Param(
+              name = "is_executable",
+              type = Boolean.class,
+              defaultValue = "False",
+              doc = "whether the output file should be executable (default is False).",
+              named = true
+          )
+      }
+  )
+  public void write(Artifact output, String content, Boolean isExecutable)
+            throws EvalException {
+    context.checkMutable("actions.write");
+    FileWriteAction action =
+        FileWriteAction.create(ruleContext, output, content, isExecutable);
+    ruleContext.registerAction(action);
+  }
 
   @Override
   public boolean isImmutable() {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java
index 9488165..6603d91 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java
@@ -26,7 +26,6 @@
 import com.google.devtools.build.lib.analysis.Runfiles;
 import com.google.devtools.build.lib.analysis.RunfilesProvider;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
-import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
 import com.google.devtools.build.lib.analysis.actions.SpawnAction;
 import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
 import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
@@ -403,10 +402,10 @@
     return builder.build();
   }
 
-  // TODO(bazel-team): improve this method to be more memory friendly
   @SkylarkSignature(
     name = "file_action",
-    doc = "Creates a file write action.",
+    doc = "DEPRECATED. Use <a href =\"actions.html#write\">ctx.actions.write</a> instead. <br>"
+        + "Creates a file write action.",
     objectType = SkylarkRuleContext.class,
     returnType = Runtime.NoneType.class,
     parameters = {
@@ -425,11 +424,9 @@
       new BuiltinFunction("file_action") {
         public Runtime.NoneType invoke(
             SkylarkRuleContext ctx, Artifact output, String content, Boolean executable)
-            throws EvalException, ConversionException {
+            throws EvalException {
           ctx.checkMutable("file_action");
-          FileWriteAction action =
-              FileWriteAction.create(ctx.getRuleContext(), output, content, executable);
-          ctx.getRuleContext().registerAction(action);
+          ctx.actions().write(output, content, executable);
           return Runtime.NONE;
         }
       };
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index 3ed9a39..b433443 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -2796,7 +2796,7 @@
     scratch.file(
         "java/com/foo/reader.bzl",
         "def _impl(ctx):",
-        "  ctx.file_action(",
+        "  ctx.actions.write(",
         "      ctx.outputs.java,",
         "      '\\n'.join([",
         "          str(target.label) + ': ' + target[config_common.FeatureFlagInfo].value",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/test/SkylarkTestingModuleTest.java b/src/test/java/com/google/devtools/build/lib/rules/test/SkylarkTestingModuleTest.java
index 4cde8ce..7aecef0 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/test/SkylarkTestingModuleTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/test/SkylarkTestingModuleTest.java
@@ -86,7 +86,7 @@
         "examples/rule/apple_rules.bzl",
         "def my_rule_test_impl(ctx):",
         "  exec_info = testing.ExecutionInfo({'local': ''})",
-        "  ctx.file_action(ctx.outputs.executable, '', True)",
+        "  ctx.actions.write(ctx.outputs.executable, '', True)",
         "  return [exec_info]",
         "my_rule_test = rule(implementation = my_rule_test_impl,",
         "    test = True,",
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 4b3740b..6b86b88 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
@@ -918,7 +918,7 @@
         "MyAspect = aspect(implementation=_impl)",
         "def _rule_impl(ctx):",
         "  g = ctx.actions.declare_file('g.txt')",
-        "  ctx.file_action(g, 'g')",
+        "  ctx.actions.write(g, 'g')",
         "  return struct(output_groups = { 'duplicate' : depset([g]) })",
         "my_rule = rule(_rule_impl)",
         "def _noop(ctx):",
@@ -948,7 +948,7 @@
         "test/aspect.bzl",
         "def _a1_impl(target, ctx):",
         "  f = ctx.actions.declare_file(target.label.name + '_a1.txt')",
-        "  ctx.file_action(f, 'f')",
+        "  ctx.actions.write(f, 'f')",
         "  return struct(output_groups = { 'a1_group' : depset([f]) })",
         "",
         "a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])",
@@ -980,7 +980,7 @@
         "test/aspect.bzl",
         "def _a1_impl(target, ctx):",
         "  f = ctx.actions.declare_file(target.label.name + '_a1.txt')",
-        "  ctx.file_action(f, 'f')",
+        "  ctx.actions.write(f, 'f')",
         "  return [OutputGroupInfo(a1_group = depset([f]))]",
         "",
         "a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])",
@@ -1011,7 +1011,7 @@
         "test/aspect.bzl",
         "def _a1_impl(target, ctx):",
         "  f = ctx.actions.declare_file(target.label.name + '_a1.txt')",
-        "  ctx.file_action(f, 'f')",
+        "  ctx.actions.write(f, 'f')",
         "  return struct(output_groups = { 'a1_group' : depset([f]) })",
         "",
         "a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])",
@@ -1023,7 +1023,7 @@
         "my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })",
         "def _a2_impl(target, ctx):",
         "  g = ctx.actions.declare_file(target.label.name + '_a2.txt')",
-        "  ctx.file_action(g, 'f')",
+        "  ctx.actions.write(g, 'f')",
         "  return struct(output_groups = { 'a2_group' : depset([g]) })",
         "",
         "a2 = aspect(implementation=_a2_impl, attr_aspects = ['dep'])",
@@ -1052,7 +1052,7 @@
         "test/aspect.bzl",
         "def _a1_impl(target, ctx):",
         "  f = ctx.actions.declare_file(target.label.name + '_a1.txt')",
-        "  ctx.file_action(f, 'f')",
+        "  ctx.actions.write(f, 'f')",
         "  return [OutputGroupInfo(a1_group = depset([f]))]",
         "",
         "a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])",
@@ -1069,7 +1069,7 @@
         "my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })",
         "def _a2_impl(target, ctx):",
         "  g = ctx.actions.declare_file(target.label.name + '_a2.txt')",
-        "  ctx.file_action(g, 'f')",
+        "  ctx.actions.write(g, 'f')",
         "  return [OutputGroupInfo(a2_group = depset([g]))]",
         "",
         "a2 = aspect(implementation=_a2_impl, attr_aspects = ['dep'])",
@@ -1099,7 +1099,7 @@
         "test/aspect.bzl",
         "def _a1_impl(target, ctx):",
         "  f = ctx.actions.declare_file(target.label.name + '_a1.txt')",
-        "  ctx.file_action(f, 'f')",
+        "  ctx.actions.write(f, 'f')",
         "  return struct(output_groups = { 'a1_group' : depset([f]) })",
         "",
         "a1 = aspect(implementation=_a1_impl, attr_aspects = ['dep'])",
@@ -1111,7 +1111,7 @@
         "my_rule1 = rule(_rule_impl, attrs = { 'dep' : attr.label(aspects = [a1]) })",
         "def _a2_impl(target, ctx):",
         "  g = ctx.actions.declare_file(target.label.name + '_a2.txt')",
-        "  ctx.file_action(g, 'f')",
+        "  ctx.actions.write(g, 'f')",
         "  return struct(output_groups = { 'a1_group' : depset([g]) })",
         "",
         "a2 = aspect(implementation=_a2_impl, attr_aspects = ['dep'])",
@@ -1648,7 +1648,7 @@
         "foo/extension.bzl",
         "def _aspect_impl(target, ctx):",
         "   file = ctx.actions.declare_file('aspect-output-' + target.label.name)",
-        "   ctx.file_action(file, 'data')",
+        "   ctx.actions.write(file, 'data')",
         "   return struct(aspect_file = file)",
         "my_aspect = aspect(_aspect_impl)",
         "def _rule_impl(ctx):",
@@ -1919,7 +1919,7 @@
         "def _a3_impl(target,ctx):",
         "  value = []",
         "  f = ctx.actions.declare_file('a3.out')",
-        "  ctx.file_action(f, 'text')",
+        "  ctx.actions.write(f, 'text')",
         "  for dep in ctx.rule.attr.deps:",
         "     if hasattr(dep, 'a3p'):",
         "         value += dep.a3p",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
index eeb3900..14842fc 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -527,7 +527,7 @@
     scratch.file(
         "test/skylark/extension.bzl",
         "def custom_rule_impl(ctx):",
-        "  ctx.file_action(output = ctx.outputs.executable, content = 'echo hello')",
+        "  ctx.actions.write(output = ctx.outputs.executable, content = 'echo hello')",
         "  rf = ctx.runfiles(ctx.files.data)",
         "  return struct(runfiles = rf)",
         "",
@@ -967,7 +967,7 @@
         "    command = 'echo')",
         "  ftb = depset(files)",
         "  for i in ctx.outputs.out:",
-        "    ctx.file_action(output=i, content='hi there')",
+        "    ctx.actions.write(output=i, content='hi there')",
         "",
         "def output_func(attr1):",
         "  return {'o': attr1 + '.txt'}",
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 202bc66..f93a7e3 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
@@ -1561,7 +1561,7 @@
         "def _undertest_impl(ctx):",
         "  out1 = ctx.outputs.out1",
         "  out2 = ctx.outputs.out2",
-        "  ctx.file_action(output=out1, content='foo123')",
+        "  ctx.actions.write(output=out1, content='foo123')",
         "  ctx.action(outputs=[out2], inputs=[out1], command='cp ' + out1.path + ' ' + out2.path)",
         "  return struct(out1=out1, out2=out2)",
         "undertest_rule = rule(",
@@ -1681,7 +1681,7 @@
   public void testFileWriteActionInterface() throws Exception {
     scratch.file("test/rules.bzl",
         getSimpleUnderTestDefinition(
-            "ctx.file_action(output=out, content='foo123')"),
+            "ctx.actions.write(output=out, content='foo123')"),
         testingRuleDefinition);
     scratch.file("test/BUILD",
         simpleBuildDefinition);
@@ -1862,6 +1862,7 @@
       "actions.declare_directory('foo.txt')",
       "actions.declare_directory('foo.txt', sibling = file)",
       "actions.do_nothing(mnemonic = 'foo', inputs = [file])",
+      "actions.write(file, 'foo')",
       "check_placeholders('foo', [])",
       "action(command = 'foo', outputs = [file])",
       "file_action(file, 'foo')",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
index 9677078..d908085 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
@@ -409,10 +409,10 @@
     SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
     evalRuleContextCode(
         ruleContext,
-        "ruleContext.file_action(",
+        "ruleContext.actions.write(",
         "  output = ruleContext.files.srcs[0],",
         "  content = 'hello world',",
-        "  executable = False)");
+        "  is_executable = False)");
     FileWriteAction action =
         (FileWriteAction)
             Iterables.getOnlyElement(
@@ -621,12 +621,12 @@
     SkylarkRuleContext ruleContext = createRuleContext("//foo:foo");
     checkErrorContains(
         ruleContext,
-        "method ctx.file_action(output: File, content: string, executable: bool) is not applicable "
-            + "for arguments (File, int, bool): 'content' is 'int', but should be 'string'",
-        "ruleContext.file_action(",
+        "Cannot convert parameter 'content' to type string, in method "
+            + "write(File output, int content, bool is_executable) of 'actions'",
+        "ruleContext.actions.write(",
         "  output = ruleContext.files.srcs[0],",
         "  content = 1,",
-        "  executable = False)");
+        "  is_executable = False)");
   }
 
   @Test
@@ -1533,7 +1533,7 @@
         "  outs = ctx.outputs",
         "  for i in ctx.attr.srcs:",
         "    o = getattr(outs, 'foo_' + i.label.name)",
-        "    ctx.file_action(",
+        "    ctx.actions.write(",
         "      output = o,",
         "      content = 'hoho')",
         "",
diff --git a/src/test/shell/integration/bazel_worker_test.sh b/src/test/shell/integration/bazel_worker_test.sh
index 561336f..bde845e 100755
--- a/src/test/shell/integration/bazel_worker_test.sh
+++ b/src/test/shell/integration/bazel_worker_test.sh
@@ -110,6 +110,7 @@
     for arg in ["--output_file=" + output.path] + ctx.attr.args:
       argfile = ctx.new_file(ctx.bin_dir, "%s_worker_input_%s" % (ctx.label.name, idx))
       ctx.file_action(output=argfile, content=arg)
+      ctx.actions.write(output=argfile, content=arg)
       argfile_inputs.append(argfile)
       flagfile_prefix = "@" if (idx % 2 == 0) else "--flagfile="
       argfile_arguments.append(flagfile_prefix + argfile.path)
@@ -118,7 +119,7 @@
     # Generate the "@"-file containing the command-line args for the unit of work.
     argfile = ctx.new_file(ctx.bin_dir, "%s_worker_input" % ctx.label.name)
     argfile_contents = "\n".join(["--output_file=" + output.path] + ctx.attr.args)
-    ctx.file_action(output=argfile, content=argfile_contents)
+    ctx.actions.write(output=argfile, content=argfile_contents)
     argfile_inputs.append(argfile)
     argfile_arguments.append("@" + argfile.path)
 
diff --git a/src/test/shell/integration/build_event_stream_test.sh b/src/test/shell/integration/build_event_stream_test.sh
index ffe1b4c..8dbea70 100755
--- a/src/test/shell/integration/build_event_stream_test.sh
+++ b/src/test/shell/integration/build_event_stream_test.sh
@@ -99,7 +99,7 @@
 def _simple_aspect_impl(target, ctx):
     for orig_out in ctx.rule.attr.outs:
         aspect_out = ctx.actions.declare_file(orig_out.name + ".aspect")
-        ctx.file_action(
+        ctx.actions.write(
             output=aspect_out,
             content = "Hello from aspect")
     return struct(output_groups={
diff --git a/src/test/shell/integration/discard_analysis_cache_test.sh b/src/test/shell/integration/discard_analysis_cache_test.sh
index eda5823..90386f1 100755
--- a/src/test/shell/integration/discard_analysis_cache_test.sh
+++ b/src/test/shell/integration/discard_analysis_cache_test.sh
@@ -71,7 +71,7 @@
   result=depset()
   for orig_out in target.files:
     aspect_out = ctx.actions.declare_file(orig_out.basename + ".aspect")
-    ctx.file_action(
+    ctx.actions.write(
         output=aspect_out,
         content = "Hello from aspect for %s" % orig_out.basename)
     result += [aspect_out]
diff --git a/src/test/shell/integration/discard_graph_edges_test.sh b/src/test/shell/integration/discard_graph_edges_test.sh
index 2e05b01..2a6eb68 100755
--- a/src/test/shell/integration/discard_graph_edges_test.sh
+++ b/src/test/shell/integration/discard_graph_edges_test.sh
@@ -70,7 +70,7 @@
   result=depset()
   for orig_out in target.files:
     aspect_out = ctx.actions.declare_file(orig_out.basename + ".aspect")
-    ctx.file_action(
+    ctx.actions.write(
         output=aspect_out,
         content = "Hello from aspect for %s" % orig_out.basename)
     result += [aspect_out]