Create a base implementation of GenRule that can be used by both Bazel and
internal Google code.

--
PiperOrigin-RevId: 145795255
MOS_MIGRATED_REVID=145795255
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 4838cb9..72ba580 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -32,6 +32,7 @@
         "//src/main/java/com/google/devtools/build/lib/rules/cpp:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/genquery:srcs",
+        "//src/main/java/com/google/devtools/build/lib/rules/genrule:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/objc:srcs",
         "//src/main/java/com/google/devtools/build/lib/sandbox:srcs",
         "//src/main/java/com/google/devtools/build/lib/ssd:srcs",
@@ -570,6 +571,7 @@
         "//src/main/java/com/google/devtools/build/lib/rules/cpp",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:CcProtoLibrary",
         "//src/main/java/com/google/devtools/build/lib/rules/genquery",
+        "//src/main/java/com/google/devtools/build/lib/rules/genrule",
         "//src/main/java/com/google/devtools/build/lib/rules/objc",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/common/options",
@@ -1112,6 +1114,7 @@
         "//src/main/java/com/google/devtools/build/lib/rules/apple/cpp:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/genquery:srcs",
+        "//src/main/java/com/google/devtools/build/lib/rules/genrule:srcs",
         "//src/main/java/com/google/devtools/build/lib/rules/objc:srcs",
     ],
 )
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
index 3ffc18c..c24f659 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
@@ -99,7 +99,7 @@
   public CommandHelper(
       RuleContext ruleContext,
       Iterable<? extends TransitiveInfoCollection> tools,
-      ImmutableMap<Label, Iterable<Artifact>> labelMap) {
+      ImmutableMap<Label, ? extends Iterable<Artifact>> labelMap) {
     this.ruleContext = ruleContext;
 
     ImmutableList.Builder<Artifact> resolvedToolsBuilder = ImmutableList.builder();
@@ -107,7 +107,7 @@
         ImmutableMap.builder();
     Map<Label, Collection<Artifact>> tempLabelMap = new HashMap<>();
 
-    for (Map.Entry<Label, Iterable<Artifact>> entry : labelMap.entrySet()) {
+    for (Map.Entry<Label, ? extends Iterable<Artifact>> entry : labelMap.entrySet()) {
       Iterables.addAll(mapGet(tempLabelMap, entry.getKey()), entry.getValue());
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 31a7e39..a0a4f22 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -113,6 +113,7 @@
 import com.google.devtools.build.lib.rules.extra.ActionListenerRule;
 import com.google.devtools.build.lib.rules.extra.ExtraActionRule;
 import com.google.devtools.build.lib.rules.genquery.GenQueryRule;
+import com.google.devtools.build.lib.rules.genrule.GenRuleBaseRule;
 import com.google.devtools.build.lib.rules.java.JavaConfigurationLoader;
 import com.google.devtools.build.lib.rules.java.JavaImportBaseRule;
 import com.google.devtools.build.lib.rules.java.JavaOptions;
@@ -342,6 +343,7 @@
           builder.addRuleDefinition(new AliasRule());
           builder.addRuleDefinition(new BazelFilegroupRule());
           builder.addRuleDefinition(new TestSuiteRule());
+          builder.addRuleDefinition(new GenRuleBaseRule());
           builder.addRuleDefinition(new BazelGenRuleRule());
           builder.addRuleDefinition(new GenQueryRule());
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java
index a13c022..f588a75 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java
@@ -14,227 +14,20 @@
 
 package com.google.devtools.build.lib.bazel.rules.genrule;
 
-import static com.google.devtools.build.lib.analysis.RunfilesProvider.withData;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.analysis.CommandHelper;
-import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
-import com.google.devtools.build.lib.analysis.ConfiguredTarget;
-import com.google.devtools.build.lib.analysis.FileProvider;
-import com.google.devtools.build.lib.analysis.FilesToRunProvider;
-import com.google.devtools.build.lib.analysis.MakeVariableExpander.ExpansionException;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
 import com.google.devtools.build.lib.analysis.RuleContext;
-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.cmdline.Label;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
-import com.google.devtools.build.lib.collect.nestedset.Order;
-import com.google.devtools.build.lib.packages.TargetUtils;
-import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
-import com.google.devtools.build.lib.rules.ToolchainProvider;
+import com.google.devtools.build.lib.rules.genrule.GenRuleBase;
 import com.google.devtools.build.lib.syntax.Type;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import java.util.List;
-import java.util.Map;
 
 /**
- * An implementation of genrule.
+ * An implementation of genrule for Bazel.
  */
-public class BazelGenRule implements RuleConfiguredTargetFactory {
-
-  private Artifact getExecutable(RuleContext ruleContext, NestedSet<Artifact> filesToBuild) {
-    if (Iterables.size(filesToBuild) == 1) {
-      Artifact out = Iterables.getOnlyElement(filesToBuild);
-      if (ruleContext.attributes().get("executable", Type.BOOLEAN)) {
-        return out;
-      }
-    }
-    return null;
-  }
+public class BazelGenRule extends GenRuleBase {
 
   @Override
-  public ConfiguredTarget create(RuleContext ruleContext)
-      throws RuleErrorException, InterruptedException {
-    final List<Artifact> resolvedSrcs = Lists.newArrayList();
-
-    final NestedSet<Artifact> filesToBuild =
-        NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts());
-    if (filesToBuild.isEmpty()) {
-      ruleContext.attributeError("outs", "Genrules without outputs don't make sense");
+  protected boolean isStampingEnabled(RuleContext ruleContext) {
+    if (!ruleContext.attributes().has("stamp", Type.BOOLEAN)) {
+      return false;
     }
-    if (ruleContext.attributes().get("executable", Type.BOOLEAN)
-        && Iterables.size(filesToBuild) > 1) {
-      ruleContext.attributeError("executable",
-          "if genrules produce executables, they are allowed only one output. "
-          + "If you need the executable=1 argument, then you should split this genrule into "
-          + "genrules producing single outputs");
-    }
-
-    ImmutableMap.Builder<Label, Iterable<Artifact>> labelMap = ImmutableMap.builder();
-    for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("srcs", Mode.TARGET)) {
-      Iterable<Artifact> files = dep.getProvider(FileProvider.class).getFilesToBuild();
-      Iterables.addAll(resolvedSrcs, files);
-      labelMap.put(dep.getLabel(), files);
-    }
-
-    CommandHelper commandHelper =
-        new CommandHelper(
-            ruleContext, ruleContext.getPrerequisites("tools", Mode.HOST), labelMap.build());
-
-    if (ruleContext.hasErrors()) {
-      return null;
-    }
-
-    String baseCommand = commandHelper.resolveCommandAndExpandLabels(
-        ruleContext.attributes().get("heuristic_label_expansion", Type.BOOLEAN), false);
-
-    // Adds the genrule environment setup script before the actual shell command
-    String command = String.format("source %s; %s",
-        ruleContext.getPrerequisiteArtifact("$genrule_setup", Mode.HOST).getExecPath(),
-        baseCommand);
-
-    command = resolveCommand(ruleContext, command, resolvedSrcs, filesToBuild);
-
-    String message = ruleContext.attributes().get("message", Type.STRING);
-    if (message.isEmpty()) {
-      message = "Executing genrule";
-    }
-
-    ImmutableMap<String, String> env = ruleContext.getConfiguration().getLocalShellEnvironment();
-    ImmutableSet<String> clientEnvVars =
-        ruleContext.getConfiguration().getVariableShellEnvironment();
-
-    Map<String, String> executionInfo = Maps.newLinkedHashMap();
-    executionInfo.putAll(TargetUtils.getExecutionInfo(ruleContext.getRule()));
-
-    if (ruleContext.attributes().get("local", Type.BOOLEAN)) {
-      executionInfo.put("local", "");
-    }
-
-    NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder();
-    inputs.addAll(resolvedSrcs);
-    inputs.addAll(commandHelper.getResolvedTools());
-    FilesToRunProvider genruleSetup =
-        ruleContext.getPrerequisite("$genrule_setup", Mode.HOST, FilesToRunProvider.class);
-    inputs.addAll(genruleSetup.getFilesToRun());
-    List<String> argv = commandHelper.buildCommandLine(command, inputs, ".genrule_script.sh",
-        ImmutableMap.copyOf(executionInfo));
-
-    if (ruleContext.attributes().get("stamp", Type.BOOLEAN)) {
-      inputs.add(ruleContext.getAnalysisEnvironment().getStableWorkspaceStatusArtifact());
-      inputs.add(ruleContext.getAnalysisEnvironment().getVolatileWorkspaceStatusArtifact());
-    }
-
-    ruleContext.registerAction(
-        new BazelGenRuleAction(
-            ruleContext.getActionOwner(),
-            ImmutableList.copyOf(commandHelper.getResolvedTools()),
-            inputs.build(),
-            filesToBuild,
-            argv,
-            env,
-            clientEnvVars,
-            ImmutableMap.copyOf(executionInfo),
-            ImmutableMap.copyOf(commandHelper.getRemoteRunfileManifestMap()),
-            message + ' ' + ruleContext.getLabel()));
-
-    RunfilesProvider runfilesProvider = withData(
-        // No runfiles provided if not a data dependency.
-        Runfiles.EMPTY,
-        // We only need to consider the outputs of a genrule
-        // No need to visit the dependencies of a genrule. They cross from the target into the host
-        // configuration, because the dependencies of a genrule are always built for the host
-        // configuration.
-        new Runfiles.Builder(
-            ruleContext.getWorkspaceName(),
-            ruleContext.getConfiguration().legacyExternalRunfiles())
-            .addTransitiveArtifacts(filesToBuild)
-            .build());
-
-    return new RuleConfiguredTargetBuilder(ruleContext)
-        .setFilesToBuild(filesToBuild)
-        .setRunfilesSupport(null, getExecutable(ruleContext, filesToBuild))
-        .addProvider(RunfilesProvider.class, runfilesProvider)
-        .build();
-  }
-
-  private String resolveCommand(final RuleContext ruleContext, final String command,
-      final List<Artifact> resolvedSrcs, final NestedSet<Artifact> filesToBuild) {
-    return ruleContext.expandMakeVariables(
-        "cmd",
-        command,
-        new ConfigurationMakeVariableContext(
-            ruleContext.getRule().getPackage(), ruleContext.getConfiguration(),
-            ToolchainProvider.getToolchainMakeVariables(ruleContext, "toolchains")) {
-          @Override
-          public String lookupMakeVariable(String name) throws ExpansionException {
-            if (name.equals("SRCS")) {
-              return Artifact.joinExecPaths(" ", resolvedSrcs);
-            } else if (name.equals("<")) {
-              return expandSingletonArtifact(resolvedSrcs, "$<", "input file");
-            } else if (name.equals("OUTS")) {
-              return Artifact.joinExecPaths(" ", filesToBuild);
-            } else if (name.equals("@")) {
-              return expandSingletonArtifact(filesToBuild, "$@", "output file");
-            } else if (name.equals("@D")) {
-              // The output directory. If there is only one filename in outs,
-              // this expands to the directory containing that file. If there are
-              // multiple filenames, this variable instead expands to the
-              // package's root directory in the genfiles tree, even if all the
-              // generated files belong to the same subdirectory!
-              if (Iterables.size(filesToBuild) == 1) {
-                Artifact outputFile = Iterables.getOnlyElement(filesToBuild);
-                PathFragment relativeOutputFile = outputFile.getExecPath();
-                if (relativeOutputFile.segmentCount() <= 1) {
-                  // This should never happen, since the path should contain at
-                  // least a package name and a file name.
-                  throw new IllegalStateException(
-                      "$(@D) for genrule " + ruleContext.getLabel() + " has less than one segment");
-                }
-                return relativeOutputFile.getParentDirectory().getPathString();
-              } else {
-                PathFragment dir;
-                if (ruleContext.getRule().hasBinaryOutput()) {
-                  dir = ruleContext.getConfiguration().getBinFragment();
-                } else {
-                  dir = ruleContext.getConfiguration().getGenfilesFragment();
-                }
-                PathFragment relPath =
-                    ruleContext.getRule().getLabel().getPackageIdentifier().getSourceRoot();
-                return dir.getRelative(relPath).getPathString();
-              }
-            } else {
-              return super.lookupMakeVariable(name);
-            }
-          }
-        });
-  }
-
-  // Returns the path of the sole element "artifacts", generating an exception
-  // with an informative error message iff the set is not a singleton.
-  //
-  // Used to expand "$<", "$@"
-  private String expandSingletonArtifact(Iterable<Artifact> artifacts,
-                                         String variable,
-                                         String artifactName)
-      throws ExpansionException {
-    if (Iterables.isEmpty(artifacts)) {
-      throw new ExpansionException("variable '" + variable
-                                   + "' : no " + artifactName);
-    } else if (Iterables.size(artifacts) > 1) {
-      throw new ExpansionException("variable '" + variable
-                                   + "' : more than one " + artifactName);
-    }
-    return Iterables.getOnlyElement(artifacts).getExecPathString();
+    return ruleContext.attributes().get("stamp", Type.BOOLEAN);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java
index 2579c1e..21683f2 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java
@@ -16,33 +16,21 @@
 import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
 import static com.google.devtools.build.lib.packages.Attribute.attr;
 import static com.google.devtools.build.lib.packages.BuildType.LABEL;
-import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
-import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
-import static com.google.devtools.build.lib.packages.BuildType.OUTPUT_LIST;
 import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
-import static com.google.devtools.build.lib.syntax.Type.STRING;
 
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.analysis.BaseRuleClasses;
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
-import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
-import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.AttributeMap;
-import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.RuleClass;
-import com.google.devtools.build.lib.packages.RuleClass.Builder;
-import com.google.devtools.build.lib.rules.ToolchainProvider;
-import com.google.devtools.build.lib.util.FileTypeSet;
+import com.google.devtools.build.lib.rules.genrule.GenRuleBaseRule;
 
 /**
- * Rule definition for the genrule rule.
+ * Rule definition for genrule for Bazel.
  */
 public final class BazelGenRuleRule implements RuleDefinition {
   public static final String GENRULE_SETUP_LABEL = "//tools/genrule:genrule-setup.sh";
 
   @Override
-  public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
+  public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
     /* <!-- #BLAZE_RULE(genrule).NAME -->
     <br/>You may refer to this rule by name in the
     <code>srcs</code> or <code>deps</code> section of other <code>BUILD</code>
@@ -50,196 +38,26 @@
     <code>srcs</code> attribute.
     <!-- #END_BLAZE_RULE.NAME --> */
     return builder
+
         .setOutputToGenfiles()
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(srcs) -->
-        A list of inputs for this rule, such as source files to process.
-        <p>
-          <em>This attributes is not suitable to list tools executed by the <code>cmd</code>; use
-          the <a href="${link genrule.tools}"><code>tools</code></a> attribute for them instead.
-          </em>
-        </p>
-        <p>
-          The build system ensures these prerequisites are built before running the genrule
-          command; they are built using the same configuration as the original build request. The
-          names of the files of these prerequisites are available to the command as a
-          space-separated list in <code>$(SRCS)</code>; alternatively the path of an individual
-          <code>srcs</code> target <code>//x:y</code> can be obtained using <code>$(location
-          //x:y)</code>, or using <code>$&lt;</code> provided it's the only entry in
-          //<code>srcs</code>.
-        </p>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("srcs", LABEL_LIST)
-            .direct_compile_time_input()
-            .legacyAllowAnyFileType())
 
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(tools) -->
-        A list of <i>tool</i> dependencies for this rule. See the definition of
-        <a href="../build-ref.html#deps">dependencies</a> for more information. <br/>
-        <p>
-          The build system ensures these prerequisites are built before running the genrule command;
-          they are built using the <a href='../blaze-user-manual.html#configurations'><i>host</i>
-          configuration</a>, since these tools are executed as part of the build. The path of an
-          individual <code>tools</code> target <code>//x:y</code> can be obtained using
-          <code>$(location //x:y)</code>.
-        </p>
-        <p>
-          Any <code>*_binary</code> or tool to be executed by <code>cmd</code> must appear in this
-          list, not in <code>srcs</code>, to ensure they are built in the correct configuration.
-        </p>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("tools", LABEL_LIST).cfg(HOST).legacyAllowAnyFileType())
-        .add(attr("toolchains", LABEL_LIST)
-            .allowedFileTypes(FileTypeSet.NO_FILE)
-            .mandatoryNativeProviders(ImmutableList.<Class<? extends TransitiveInfoProvider>>of(
-                ToolchainProvider.class)))
         .add(attr("$genrule_setup", LABEL).cfg(HOST).value(env.getToolsLabel(GENRULE_SETUP_LABEL)))
 
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(outs) -->
-        A list of files generated by this rule.
-        <p>
-          Output files must not cross package boundaries.
-          Output filenames are interpreted as relative to the package.
-        </p>
-        <p>
-          If the <code>executable</code> flag is set, <code>outs</code> must contain exactly one
-          label.
-        </p>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("outs", OUTPUT_LIST).mandatory())
-
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(cmd) -->
-        The command to run.
-        Subject to <a href="${link make-variables#location}">$(location)</a> and
-        <a href="${link make-variables}">"Make" variable</a> substitution.
-        <ol>
-          <li>
-            First <a href="${link make-variables#location}">$(location)</a> substitution is
-            applied, replacing all occurrences of <code>$(location <i>label</i>)</code> and of
-            <code>$(locations <i>label</i>)</code>.
-          </li>
-          <li>
-            <p>
-              Note that <code>outs</code> are <i>not</i> included in this substitution. Output files
-              are always generated into a predictable location (available via <code>$(@D)</code>,
-              <code>$@</code>, <code>$(OUTS)</code> or <code>$(location <i>output_name</i>)</code>;
-              see below).
-            </p>
-          </li>
-          <li>
-            Next, <a href="${link make-variables}">"Make" variables</a> are expanded. Note that
-            predefined variables <code>$(JAVA)</code>, <code>$(JAVAC)</code> and
-            <code>$(JAVABASE)</code> expand under the <i>host</i> configuration, so Java invocations
-            that run as part of a build step can correctly load shared libraries and other
-            dependencies.
-          </li>
-          <li>
-            Finally, the resulting command is executed using the Bash shell. If its exit code is
-            non-zero the command is considered to have failed.
-          </li>
-        </ol>
-        <p>
-          The command may refer to <code>*_binary</code>targets; it should use a <a
-          href="../build-ref.html#labels">label</a> for this. The following
-          variables are available within the <code>cmd</code> sublanguage:</p>
-        <ul>
-          <li>
-            <a href="${link make-variables#predefined_variables.genrule.cmd}">"Make" variables</a>
-          </li>
-          <li>
-            "Make" variables that are predefined by the build tools.
-            Please use these variables instead of hardcoded values.
-            See <a href="${link make-variables#predefined_variables}">Predefined "Make" Variables
-            </a> in this document for a list of supported values.
-          </li>
-        </ul>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("cmd", STRING).mandatory())
-
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_to_bindir) -->
-        <p>
-          If set to 1, this option causes output files to be written into the <code>bin</code>
-          directory instead of the <code>genfiles</code> directory.
-        </p>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        // TODO(bazel-team): find a location to document genfiles/binfiles, link to them from here.
-        .add(attr("output_to_bindir", BOOLEAN).value(false)
-            .nonconfigurable("policy decision: no reason for this to depend on the configuration"))
-
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(local) -->
-        <p>
-          If set to 1, this option force this <code>genrule</code> to run with the
-          <code>standalone</code> strategy, without sandboxing.
-        </p>
-        <p>
-          This is equivalent to providing 'local' as a tag (<code>tags=["local"]</code>). The
-          local strategy is applied if either one is specified.
-        </p>
-        <p>
-          The <code>--genrule_strategy</code> option value <code>local</code>
-          overrides this attribute.
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("local", BOOLEAN).value(false))
-
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(message) -->
-        A progress message.
-        <p>
-          A progress message that will be printed as this build step is executed. By default, the
-          message is "Generating <i>output</i>" (or something equally bland) but you may provide a
-          more specific one. Use this attribute instead of <code>echo</code> or other print
-          statements in your <code>cmd</code> command, as this allows the build tool to control
-          whether such progress messages are printed or not.
-        </p>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("message", STRING))
-        /*<!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_licenses) -->
-        See <a href="${link common-definitions#binary.output_licenses}"><code>common attributes
-        </code></a>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
-        .add(attr("output_licenses", LICENSE))
-
-        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(executable) -->
-        Declare output to be executable.
-        <p>
-          Setting this flag to 1 means the output is an executable file and can be run using the
-          <code>run</code> command. The genrule must produce exactly one output in this case.
-          If this attribute is set, <code>run</code> will try executing the file regardless of
-          its content.
-        </p>
-        <p>Declaring data dependencies for the generated executable is not supported.</p>
-        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
-        .add(attr("executable", BOOLEAN).value(false).nonconfigurable(
-            "Used in computed default for $is_executable, which is itself non-configurable (and "
-            + " thus expects its dependencies to be non-configurable), because $is_executable"
-            + " is called from RunCommand.isExecutable, which has no configuration context"))
-
         // TODO(bazel-team): stamping doesn't seem to work. Fix it or remove attribute.
         .add(attr("stamp", BOOLEAN).value(false))
-        // This is a misfeature, so don't document it. We would like to get rid of it, but that
-        // would require a cleanup of existing rules.
-        .add(attr("heuristic_label_expansion", BOOLEAN).value(false))
-        .add(attr("$is_executable", BOOLEAN)
-            .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
-            .value(
-            new Attribute.ComputedDefault() {
-              @Override
-              public Object getDefault(AttributeMap rule) {
-                return (rule.get("outs", BuildType.OUTPUT_LIST).size() == 1)
-                    && rule.get("executable", BOOLEAN);
-              }
-            }))
-        .removeAttribute("data")
-        .removeAttribute("deps")
+
         .build();
   }
 
   @Override
-  public Metadata getMetadata() {
+  public RuleDefinition.Metadata getMetadata() {
     return RuleDefinition.Metadata.builder()
         .name("genrule")
-        .ancestors(BaseRuleClasses.RuleBase.class)
+        .ancestors(GenRuleBaseRule.class)
         .factoryClass(BazelGenRule.class)
         .build();
   }
+
 }
 
 /*<!-- #BLAZE_RULE (NAME = genrule, TYPE = OTHER, FAMILY = General)[GENERIC_RULE] -->
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/BUILD b/src/main/java/com/google/devtools/build/lib/rules/genrule/BUILD
new file mode 100644
index 0000000..92001c6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/BUILD
@@ -0,0 +1,28 @@
+# Description:
+#   Genrule support
+
+package(
+    default_visibility = ["//src:__subpackages__"],
+)
+
+java_library(
+    name = "genrule",
+    srcs = glob(["*.java"]),
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:build-base",
+        "//src/main/java/com/google/devtools/build/lib:events",
+        "//src/main/java/com/google/devtools/build/lib:packages",
+        "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib/actions",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//third_party:guava",
+        "//third_party:jsr305",
+        "//third_party/protobuf",
+    ],
+)
+
+filegroup(
+    name = "srcs",
+    testonly = 0,  # All srcs should be not test only, overwrite package default.
+    srcs = glob(["**"]),
+)
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleAction.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleAction.java
similarity index 92%
rename from src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleAction.java
rename to src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleAction.java
index 209b15d..d77b894 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleAction.java
@@ -1,4 +1,4 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
+// 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.
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.devtools.build.lib.bazel.rules.genrule;
+package com.google.devtools.build.lib.rules.genrule;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -31,13 +31,13 @@
  * A spawn action for genrules. Genrules are handled specially in that inputs and outputs are
  * checked for directories.
  */
-public final class BazelGenRuleAction extends SpawnAction {
+public class GenRuleAction extends SpawnAction {
 
   private static final ResourceSet GENRULE_RESOURCES =
       // Not chosen scientifically/carefully.  300MB memory, 100% CPU, no I/O.
       ResourceSet.createWithRamCpuIo(300, 1.0, 0.0);
 
-  public BazelGenRuleAction(
+  public GenRuleAction(
       ActionOwner owner,
       Iterable<Artifact> tools,
       Iterable<Artifact> inputs,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
new file mode 100644
index 0000000..c990b15
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
@@ -0,0 +1,307 @@
+// 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.genrule;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.CommandHelper;
+import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FileProvider;
+import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.MakeVariableExpander.ExpansionException;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+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.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.rules.AliasProvider;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.ToolchainProvider;
+import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A base implementation of genrule, to be used by specific implementing rules which can change some
+ * of the semantics around when the execution info and inputs are changed.
+ */
+public abstract class GenRuleBase implements RuleConfiguredTargetFactory {
+
+  /**
+   * Returns a {@link Map} of execution info, which will be used in later processing to construct
+   * the actual command line that will be executed.
+   *
+   * <p>GenRule implementations can override this method to include additional specific information
+   * needed.
+   */
+  protected Map<String, String> getExtraExecutionInfo(RuleContext ruleContext, String command) {
+    return ImmutableMap.of();
+  }
+
+  /**
+   * Returns an {@link Iterable} of {@link NestedSet}s, which will be added to the genrule's inputs
+   * using the {@link NestedSetBuilder#addTransitive} method.
+   *
+   * <p>GenRule implementations can override this method to better control what inputs are needed
+   * for specific command inputs.
+   */
+  protected Iterable<NestedSet<Artifact>> getExtraInputArtifacts(
+      RuleContext ruleContext, String command) {
+    return ImmutableList.of();
+  }
+
+  /**
+   * Returns {@code true} if the rule should be stamped.
+   *
+   * <p>Genrule implementations can set this based on the rule context, including by defining their
+   * own attributes over and above what is present in {@link GenRuleBaseRule}.
+   */
+  protected abstract boolean isStampingEnabled(RuleContext ruleContext);
+
+  /**
+   * Updates the {@link RuleConfiguredTargetBuilder} that is used for this rule.
+   *
+   * <p>GenRule implementations can override this method to enhance and update the builder without
+   * needing to entirely override the {@link #create} method.
+   */
+  protected RuleConfiguredTargetBuilder updateBuilder(
+      RuleConfiguredTargetBuilder builder,
+      RuleContext ruleContext,
+      NestedSet<Artifact> filesToBuild) {
+    return builder;
+  }
+
+  @Override
+  public ConfiguredTarget create(RuleContext ruleContext)
+      throws RuleErrorException, InterruptedException {
+    NestedSet<Artifact> filesToBuild =
+        NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts());
+    NestedSetBuilder<Artifact> resolvedSrcsBuilder = NestedSetBuilder.stableOrder();
+
+    if (filesToBuild.isEmpty()) {
+      ruleContext.attributeError("outs", "Genrules without outputs don't make sense");
+    }
+    if (ruleContext.attributes().get("executable", Type.BOOLEAN)
+        && Iterables.size(filesToBuild) > 1) {
+      ruleContext.attributeError(
+          "executable",
+          "if genrules produce executables, they are allowed only one output. "
+              + "If you need the executable=1 argument, then you should split this genrule into "
+              + "genrules producing single outputs");
+    }
+
+    ImmutableMap.Builder<Label, NestedSet<Artifact>> labelMap = ImmutableMap.builder();
+    for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("srcs", Mode.TARGET)) {
+      // This target provides specific types of files for genrules.
+      GenRuleSourcesProvider provider = dep.getProvider(GenRuleSourcesProvider.class);
+      NestedSet<Artifact> files = (provider != null)
+          ? provider.getGenruleFiles()
+          : dep.getProvider(FileProvider.class).getFilesToBuild();
+      resolvedSrcsBuilder.addTransitive(files);
+      labelMap.put(AliasProvider.getDependencyLabel(dep), files);
+    }
+    NestedSet<Artifact> resolvedSrcs = resolvedSrcsBuilder.build();
+
+    CommandHelper commandHelper =
+        new CommandHelper(
+            ruleContext, ruleContext.getPrerequisites("tools", Mode.HOST), labelMap.build());
+
+    if (ruleContext.hasErrors()) {
+      return null;
+    }
+
+    String baseCommand = commandHelper.resolveCommandAndExpandLabels(
+        ruleContext.attributes().get("heuristic_label_expansion", Type.BOOLEAN), false);
+
+    // Adds the genrule environment setup script before the actual shell command
+    String command = String.format("source %s; %s",
+        ruleContext.getPrerequisiteArtifact("$genrule_setup", Mode.HOST).getExecPath(),
+        baseCommand);
+
+    command = resolveCommand(command, ruleContext, resolvedSrcs, filesToBuild);
+
+    String message = ruleContext.attributes().get("message", Type.STRING);
+    if (message.isEmpty()) {
+      message = "Executing genrule";
+    }
+
+    ImmutableMap<String, String> env = ruleContext.getConfiguration().getLocalShellEnvironment();
+    ImmutableSet<String> clientEnvVars =
+        ruleContext.getConfiguration().getVariableShellEnvironment();
+
+    Map<String, String> executionInfo = Maps.newLinkedHashMap();
+    executionInfo.putAll(TargetUtils.getExecutionInfo(ruleContext.getRule()));
+
+    if (ruleContext.attributes().get("local", Type.BOOLEAN)) {
+      executionInfo.put("local", "");
+    }
+
+    executionInfo.putAll(getExtraExecutionInfo(ruleContext, baseCommand));
+
+    NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder();
+    inputs.addAll(resolvedSrcs);
+    inputs.addAll(commandHelper.getResolvedTools());
+    FilesToRunProvider genruleSetup =
+        ruleContext.getPrerequisite("$genrule_setup", Mode.HOST, FilesToRunProvider.class);
+    inputs.addAll(genruleSetup.getFilesToRun());
+    List<String> argv = commandHelper.buildCommandLine(command, inputs, ".genrule_script.sh",
+          ImmutableMap.copyOf(executionInfo));
+
+    for (NestedSet<Artifact> extraInputs : getExtraInputArtifacts(ruleContext, baseCommand)) {
+      inputs.addTransitive(extraInputs);
+    }
+
+    if (isStampingEnabled(ruleContext)) {
+      inputs.add(ruleContext.getAnalysisEnvironment().getStableWorkspaceStatusArtifact());
+      inputs.add(ruleContext.getAnalysisEnvironment().getVolatileWorkspaceStatusArtifact());
+    }
+
+    ruleContext.registerAction(
+        new GenRuleAction(
+            ruleContext.getActionOwner(),
+            ImmutableList.copyOf(commandHelper.getResolvedTools()),
+            inputs.build(),
+            filesToBuild,
+            argv,
+            env,
+            clientEnvVars,
+            ImmutableMap.copyOf(executionInfo),
+            ImmutableMap.copyOf(commandHelper.getRemoteRunfileManifestMap()),
+            message + ' ' + ruleContext.getLabel()));
+
+    RunfilesProvider runfilesProvider = RunfilesProvider.withData(
+        // No runfiles provided if not a data dependency.
+        Runfiles.EMPTY,
+        // We only need to consider the outputs of a genrule
+        // No need to visit the dependencies of a genrule. They cross from the target into the host
+        // configuration, because the dependencies of a genrule are always built for the host
+        // configuration.
+        new Runfiles.Builder(
+            ruleContext.getWorkspaceName(),
+            ruleContext.getConfiguration().legacyExternalRunfiles())
+            .addTransitiveArtifacts(filesToBuild)
+            .build());
+
+    RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext)
+        .setFilesToBuild(filesToBuild)
+        .setRunfilesSupport(null, getExecutable(ruleContext, filesToBuild))
+        .addProvider(RunfilesProvider.class, runfilesProvider);
+
+    builder = updateBuilder(builder, ruleContext, filesToBuild);
+    return builder.build();
+  }
+
+  /**
+   * Returns the executable artifact, if the rule is marked as executable and there is only one
+   * artifact.
+   */
+  private static Artifact getExecutable(RuleContext ruleContext, NestedSet<Artifact> filesToBuild) {
+    if (!ruleContext.attributes().get("executable", Type.BOOLEAN)) {
+      return null;
+    }
+    if (Iterables.size(filesToBuild) == 1) {
+      return Iterables.getOnlyElement(filesToBuild);
+    }
+    return null;
+  }
+
+  /**
+   * Resolves any variables, including make and genrule-specific variables, in the command and
+   * returns the expanded command.
+   *
+   * <p>GenRule implementations may override this method to perform additional expansions.
+   */
+  protected String resolveCommand(String command, final RuleContext ruleContext,
+      final NestedSet<Artifact> resolvedSrcs, final NestedSet<Artifact> filesToBuild) {
+    return ruleContext.expandMakeVariables(
+        "cmd",
+        command,
+        new ConfigurationMakeVariableContext(
+            ruleContext.getRule().getPackage(), ruleContext.getConfiguration(),
+            ToolchainProvider.getToolchainMakeVariables(ruleContext, "toolchains")) {
+          @Override
+          public String lookupMakeVariable(String name) throws ExpansionException {
+            if (name.equals("SRCS")) {
+              return Artifact.joinExecPaths(" ", resolvedSrcs);
+            } else if (name.equals("<")) {
+              return expandSingletonArtifact(resolvedSrcs, "$<", "input file");
+            } else if (name.equals("OUTS")) {
+              return Artifact.joinExecPaths(" ", filesToBuild);
+            } else if (name.equals("@")) {
+              return expandSingletonArtifact(filesToBuild, "$@", "output file");
+            } else if (name.equals("@D")) {
+              // The output directory. If there is only one filename in outs,
+              // this expands to the directory containing that file. If there are
+              // multiple filenames, this variable instead expands to the
+              // package's root directory in the genfiles tree, even if all the
+              // generated files belong to the same subdirectory!
+              if (Iterables.size(filesToBuild) == 1) {
+                Artifact outputFile = Iterables.getOnlyElement(filesToBuild);
+                PathFragment relativeOutputFile = outputFile.getExecPath();
+                if (relativeOutputFile.segmentCount() <= 1) {
+                  // This should never happen, since the path should contain at
+                  // least a package name and a file name.
+                  throw new IllegalStateException(
+                      "$(@D) for genrule " + ruleContext.getLabel() + " has less than one segment");
+                }
+                return relativeOutputFile.getParentDirectory().getPathString();
+              } else {
+                PathFragment dir;
+                if (ruleContext.getRule().hasBinaryOutput()) {
+                  dir = ruleContext.getConfiguration().getBinFragment();
+                } else {
+                  dir = ruleContext.getConfiguration().getGenfilesFragment();
+                }
+                PathFragment relPath =
+                    ruleContext.getRule().getLabel().getPackageIdentifier().getSourceRoot();
+                return dir.getRelative(relPath).getPathString();
+              }
+            } else {
+              return super.lookupMakeVariable(name);
+            }
+          }
+        });
+  }
+
+  /**
+   * Returns the path of the sole element "artifacts", generating an exception with an informative
+   * error message iff the set is not a singleton. Used to expand "$<", "$@".
+   */
+  private String expandSingletonArtifact(Iterable<Artifact> artifacts,
+                                         String variable,
+                                         String artifactName)
+      throws ExpansionException {
+    if (Iterables.isEmpty(artifacts)) {
+      throw new ExpansionException("variable '" + variable
+                                   + "' : no " + artifactName);
+    } else if (Iterables.size(artifacts) > 1) {
+      throw new ExpansionException("variable '" + variable
+                                   + "' : more than one " + artifactName);
+    }
+    return Iterables.getOnlyElement(artifacts).getExecPathString();
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBaseRule.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBaseRule.java
new file mode 100644
index 0000000..3a389ca
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBaseRule.java
@@ -0,0 +1,388 @@
+// 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.genrule;
+
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
+import static com.google.devtools.build.lib.packages.BuildType.OUTPUT_LIST;
+import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.rules.ToolchainProvider;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+/**
+ * Rule definition for the genrule rule, intended to be inherited by specific GenRule
+ * implementations. Implementations will need to include any needed additional dependencies, such
+ * as a setup script target.
+ */
+public class GenRuleBaseRule implements RuleDefinition {
+
+  @Override
+  public RuleClass build(
+      RuleClass.Builder builder, RuleDefinitionEnvironment env) {
+    return builder
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(srcs) -->
+        A list of inputs for this rule, such as source files to process.
+        <p>
+          <em>This attributes is not suitable to list tools executed by the <code>cmd</code>; use
+          the <a href="${link genrule.tools}"><code>tools</code></a> attribute for them instead.
+          </em>
+        </p>
+        <p>
+          The build system ensures these prerequisites are built before running the genrule
+          command; they are built using the same configuration as the original build request. The
+          names of the files of these prerequisites are available to the command as a
+          space-separated list in <code>$(SRCS)</code>; alternatively the path of an individual
+          <code>srcs</code> target <code>//x:y</code> can be obtained using <code>$(location
+          //x:y)</code>, or using <code>$&lt;</code> provided it's the only entry in
+          //<code>srcs</code>.
+        </p>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("srcs", LABEL_LIST)
+            .direct_compile_time_input()
+            .allowedFileTypes(FileTypeSet.ANY_FILE))
+
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(tools) -->
+        A list of <i>tool</i> dependencies for this rule. See the definition of
+        <a href="../build-ref.html#deps">dependencies</a> for more information. <br/>
+        <p>
+          The build system ensures these prerequisites are built before running the genrule command;
+          they are built using the <a href='../blaze-user-manual.html#configurations'><i>host</i>
+          configuration</a>, since these tools are executed as part of the build. The path of an
+          individual <code>tools</code> target <code>//x:y</code> can be obtained using
+          <code>$(location //x:y)</code>.
+        </p>
+        <p>
+          Any <code>*_binary</code> or tool to be executed by <code>cmd</code> must appear in this
+          list, not in <code>srcs</code>, to ensure they are built in the correct configuration.
+        </p>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("tools", LABEL_LIST).cfg(HOST)
+            .allowedFileTypes(FileTypeSet.ANY_FILE))
+        .add(attr("toolchains", LABEL_LIST)
+            .allowedFileTypes(FileTypeSet.NO_FILE)
+            .mandatoryNativeProviders(ImmutableList.<Class<? extends TransitiveInfoProvider>>of(
+                ToolchainProvider.class)))
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(outs) -->
+        A list of files generated by this rule.
+        <p>
+          Output files must not cross package boundaries.
+          Output filenames are interpreted as relative to the package.
+        </p>
+        <p>
+          If the <code>executable</code> flag is set, <code>outs</code> must contain exactly one
+          label.
+        </p>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("outs", OUTPUT_LIST).mandatory())
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(cmd) -->
+        The command to run.
+        Subject to <a href="${link make-variables#location}">$(location)</a> and
+        <a href="${link make-variables}">"Make" variable</a> substitution.
+        <ol>
+          <li>
+            First <a href="${link make-variables#location}">$(location)</a> substitution is
+            applied, replacing all occurrences of <code>$(location <i>label</i>)</code> and of
+            <code>$(locations <i>label</i>)</code>.
+          </li>
+          <li>
+            <p>
+              Note that <code>outs</code> are <i>not</i> included in this substitution. Output files
+              are always generated into a predictable location (available via <code>$(@D)</code>,
+              <code>$@</code>, <code>$(OUTS)</code> or <code>$(location <i>output_name</i>)</code>;
+              see below).
+            </p>
+          </li>
+          <li>
+            Next, <a href="${link make-variables}">"Make" variables</a> are expanded. Note that
+            predefined variables <code>$(JAVA)</code>, <code>$(JAVAC)</code> and
+            <code>$(JAVABASE)</code> expand under the <i>host</i> configuration, so Java invocations
+            that run as part of a build step can correctly load shared libraries and other
+            dependencies.
+          </li>
+          <li>
+            Finally, the resulting command is executed using the Bash shell. If its exit code is
+            non-zero the command is considered to have failed.
+          </li>
+        </ol>
+        <p>
+          The command may refer to <code>*_binary</code>targets; it should use a <a
+          href="../build-ref.html#labels">label</a> for this. The following
+          variables are available within the <code>cmd</code> sublanguage:</p>
+        <ul>
+          <li>
+            <a href="${link make-variables#predefined_variables.genrule.cmd}">"Make" variables</a>
+          </li>
+          <li>
+            "Make" variables that are predefined by the build tools.
+            Please use these variables instead of hardcoded values.
+            See <a href="${link make-variables#predefined_variables}">Predefined "Make" Variables
+            </a> in this document for a list of supported values.
+          </li>
+        </ul>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("cmd", STRING).mandatory())
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_to_bindir) -->
+        <p>
+          If set to 1, this option causes output files to be written into the <code>bin</code>
+          directory instead of the <code>genfiles</code> directory.
+        </p>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        // TODO(bazel-team): find a location to document genfiles/binfiles, link to them from here.
+        .add(attr("output_to_bindir", BOOLEAN).value(false)
+            .nonconfigurable("policy decision: no reason for this to depend on the configuration"))
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(local) -->
+        <p>
+          If set to 1, this option force this <code>genrule</code> to run with the
+          <code>standalone</code> strategy, without sandboxing.
+        </p>
+        <p>
+          This is equivalent to providing 'local' as a tag (<code>tags=["local"]</code>). The
+          local strategy is applied if either one is specified.
+        </p>
+        <p>
+          The <code>--genrule_strategy</code> option value <code>local</code>
+          overrides this attribute.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("local", BOOLEAN).value(false))
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(message) -->
+        A progress message.
+        <p>
+          A progress message that will be printed as this build step is executed. By default, the
+          message is "Generating <i>output</i>" (or something equally bland) but you may provide a
+          more specific one. Use this attribute instead of <code>echo</code> or other print
+          statements in your <code>cmd</code> command, as this allows the build tool to control
+          whether such progress messages are printed or not.
+        </p>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("message", STRING))
+        /*<!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_licenses) -->
+        See <a href="${link common-definitions#binary.output_licenses}"><code>common attributes
+        </code></a>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
+        .add(attr("output_licenses", LICENSE))
+
+        /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(executable) -->
+        Declare output to be executable.
+        <p>
+          Setting this flag to 1 means the output is an executable file and can be run using the
+          <code>run</code> command. The genrule must produce exactly one output in this case.
+          If this attribute is set, <code>run</code> will try executing the file regardless of
+          its content.
+        </p>
+        <p>Declaring data dependencies for the generated executable is not supported.</p>
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("executable", BOOLEAN).value(false).nonconfigurable(
+            "Used in computed default for $is_executable, which is itself non-configurable (and "
+            + " thus expects its dependencies to be non-configurable), because $is_executable"
+            + " is called from RunCommand.isExecutable, which has no configuration context"))
+
+        .add(attr("$is_executable", BOOLEAN)
+            .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
+            .value(
+            new Attribute.ComputedDefault() {
+              @Override
+              public Object getDefault(AttributeMap rule) {
+                return (rule.get("outs", BuildType.OUTPUT_LIST).size() == 1)
+                    && rule.get("executable", BOOLEAN);
+              }
+            }))
+
+        // This is a misfeature, so don't document it. We would like to get rid of it, but that
+        // would require a cleanup of existing rules.
+        .add(attr("heuristic_label_expansion", BOOLEAN).value(false))
+
+        .removeAttribute("data")
+        .removeAttribute("deps")
+
+        .build();
+  }
+
+  @Override
+  public RuleDefinition.Metadata getMetadata() {
+    return RuleDefinition.Metadata.builder()
+        .name("$genrule_base")
+        .type(RuleClassType.ABSTRACT)
+        .ancestors(BaseRuleClasses.RuleBase.class)
+        .build();
+  }
+}
+
+/*<!-- #BLAZE_RULE (NAME = genrule, TYPE = OTHER, FAMILY = General)[GENERIC_RULE] -->
+
+<p>A <code>genrule</code> generates one or more files using a user-defined Bash command.</p>
+
+<p>
+  Genrules are generic build rules that you can use if there's no specific rule for the task. If for
+  example you want to minify JavaScript files then you can use a genrule to do so. If however you
+  need to compile C++ files, stick to the existing <code>cc_*</code> rules, because all the heavy
+  lifting has already been done for you.
+</p>
+<p>
+  Do not use a genrule for running tests. There are special dispensations for tests and test
+  results, including caching policies and environment variables. Tests generally need to be run
+  after the build is complete and on the target architecture, whereas genrules are executed during
+  the build and on the host architecture (the two may be different). If you need a general purpose
+  testing rule, use <a href="${link sh_test}"><code>sh_test</code></a>.
+</p>
+
+<h4>Cross-compilation Considerations</h4>
+
+<p>
+  <em>See <a href="../bazel-user-manual.html#configurations">the user manual</a> for more info about
+  cross-compilation.</em>
+</p>
+<p>
+  While genrules run during a build, their outputs are often used after the build, for deployment or
+  testing. Consider the example of compiling C code for a microcontroller: the compiler accepts C
+  source files and generates code that runs on a microcontroller. The generated code obviously
+  cannot run on the CPU that was used for building it, but the C compiler (if compiled from source)
+  itself has to.
+</p>
+<p>
+  The build system uses the host configuration to describe the machine(s) on which the build runs
+  and the target configuration to describe the machine(s) on which the output of the build is
+  supposed to run. It provides options to configure each of these and it segregates the
+  corresponding files into separate directories to avoid conflicts.
+</p>
+<p>
+  For genrules, the build system ensures that dependencies are built appropriately:
+  <code>srcs</code> are built (if necessary) for the <em>target</em> configuration,
+  <code>tools</code> are built for the <em>host</em> configuration, and the output is considered to
+  be for the <em>target</em> configuration. It also provides <a href="${link make-variables}">
+  "Make" variables</a> that genrule commands can pass to the corresponding tools.
+</p>
+<p>
+  It is intentional that genrule defines no <code>deps</code> attribute: other built-in rules use
+  language-dependent meta information passed between the rules to automatically determine how to
+  handle dependent rules, but this level of automation is not possible for genrules. Genrules work
+  purely at the file and runfiles level.
+</p>
+
+<h4>Special Cases</h4>
+
+<p>
+  <i>Host-host compilation</i>: in some cases, the build system needs to run genrules such that the
+  output can also be executed during the build. If for example a genrule builds some custom compiler
+  which is subsequently used by another genrule, the first one has to produce its output for the
+  host configuration, because that's where the compiler will run in the other genrule. In this case,
+  the build system does the right thing automatically: it builds the <code>srcs</code> and
+  <code>outs</code> of the first genrule for the host configuration instead of the target
+  configuration. See <a href="../bazel-user-manual.html#configurations">the user manual</a> for more
+  info.
+</p>
+<p>
+  <i>JDK & C++ Tooling</i>: to use a tool from the JDK or the C++ compiler suite, the build system
+  provides a set of variables to use. See <a href="${link make-variables}">"Make" variable</a> for
+  details.
+</p>
+
+<h4>Genrule Environment</h4>
+
+<p>
+  The genrule command is executed in a Bash shell, configured to fail when a command or a pipeline
+  fails (<code>set -e -o pipefail</code>). Genrules should not access the network (except to create
+  connections between processes running within the same genrule on the same machine), though this is
+  not currently enforced.
+</p>
+<p>
+  The build system automatically deletes any existing output files, but creates any necessary parent
+  directories before it runs a genrule. It also removes any output files in case of a failure.
+</p>
+
+<h4>General Advice</h4>
+
+<ul>
+  <li>Do ensure that tools run by a genrule are deterministic and hermetic. They should not write
+    timestamps to their output, and they should use stable ordering for sets and maps, as well as
+    write only relative file paths to the output, no absolute paths. Not following this rule will
+    lead to unexpected build behavior (Bazel not rebuilding a genrule you thought it would) and
+    degrade cache performance.</li>
+  <li>Do use <code>$(location)</code> extensively, for outputs, tools and sources. Due to the
+    segregation of output files for different configurations, genrules cannot rely on hard-coded
+    and/or absolute paths.</li>
+  <li>Do write a common Skylark macro in case the same or very similar genrules are used in multiple
+    places. If the genrule is complex, consider implementing it in a script or as a Skylark rule.
+    This improves readability as well as testability.</li>
+  <li>Do make sure that the exit code correctly indicates success or failure of the genrule.</li>
+  <li>Do not write informational messages to stdout or stderr. While useful for debugging, this can
+    easily become noise; a successful genrule should be silent. On the other hand, a failing genrule
+    should emit good error messages.</li>
+  <li><code>$$</code> evaluates to a <code>$</code>, a literal dollar-sign, so in order to invoke a
+    shell command containing dollar-signs such as <code>ls $(dirname $x)</code>, one must escape it
+    thus: <code>ls $$(dirname $$x)</code>.</li>
+  <li>Avoid creating symlinks and directories. Bazel doesn't copy over the directory/symlink
+    structure created by genrules and its dependency checking of directories is unsound.</li>
+  <li>When referencing the genrule in other rules, you can use either the genrule's label or the
+    labels of individual output files. Sometimes the one approach is more readable, sometimes the
+    other: referencing outputs by name in a consuming rule's <code>srcs</code> will avoid
+    unintentionally picking up other outputs of the genrule, but can be tedious if the genrule
+    produces many outputs.</li>
+</ul>
+
+<h4 id="genrule_examples">Examples</h4>
+
+<p>
+  This example generates <code>foo.h</code>. There are no sources, because the command doesn't take
+  any input. The "binary" run by the command is a perl script in the same package as the genrule.
+</p>
+<pre class="code">
+genrule(
+    name = "foo",
+    srcs = [],
+    outs = ["foo.h"],
+    cmd = "./$(location create_foo.pl) &gt; \"$@\"",
+    tools = ["create_foo.pl"],
+)
+</pre>
+
+<p>
+  The following example shows how to use a <a href="${link filegroup}"><code>filegroup</code>
+  </a> and the outputs of another <code>genrule</code>. Note that using <code>$(SRCS)</code> instead
+  of explicit <code>$(location)</code> directives would also work; this example uses the latter for
+  sake of demonstration.
+</p>
+<pre class="code">
+genrule(
+    name = "concat_all_files",
+    srcs = [
+        "//some:files",  # a filegroup with multiple files in it ==> $(location<b>s</b>)
+        "//other:gen",   # a genrule with a single output ==> $(location)
+    ],
+    outs = ["concatenated.txt"],
+    cmd = "cat $(locations //some:files) $(location //other:gen) > $@",
+)
+</pre>
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleSourcesProvider.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleSourcesProvider.java
new file mode 100644
index 0000000..fab2f78
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleSourcesProvider.java
@@ -0,0 +1,37 @@
+// 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.genrule;
+
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+
+/**
+ * A transitive info provider that will give source files to genrule.
+ */
+@Immutable
+public final class GenRuleSourcesProvider implements TransitiveInfoProvider {
+
+  private final NestedSet<Artifact> genruleFiles;
+
+  public GenRuleSourcesProvider(NestedSet<Artifact> genruleFiles) {
+    this.genruleFiles = genruleFiles;
+  }
+
+  public NestedSet<Artifact> getGenruleFiles() {
+    return genruleFiles;
+  }
+}
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 de7949c..063da16 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
@@ -527,48 +527,41 @@
         "consume_rule(name = 'c_str', s = [cdict['kind'], cdict['name'], cdict['x']])");
 
     SkylarkRuleContext allContext = createRuleContext("//test/getrule:all_str");
-    Object result = evalRuleContextCode(allContext, "ruleContext.attr.s");
-    assertEquals(
-        SkylarkList.createImmutable(ImmutableList.<String>of("genrule", "a", "nop_rule", "c")),
-        result);
+    List<String> result = (List<String>) evalRuleContextCode(allContext, "ruleContext.attr.s");
+    assertThat(result).containsExactly("genrule", "a", "nop_rule", "c");
 
-    result = evalRuleContextCode(createRuleContext("//test/getrule:a_str"), "ruleContext.attr.s");
-    assertEquals(
-        SkylarkList.createImmutable(
-            ImmutableList.<String>of("genrule", "a", ":a.txt", "//test:bla")),
-        result);
+    result = (List<String>) evalRuleContextCode(
+        createRuleContext("//test/getrule:a_str"), "ruleContext.attr.s");
+    assertThat(result).containsExactly("genrule", "a", ":a.txt", "//test:bla");
 
-    result = evalRuleContextCode(createRuleContext("//test/getrule:c_str"), "ruleContext.attr.s");
-    assertEquals(
-        SkylarkList.createImmutable(ImmutableList.<String>of("nop_rule", "c", ":a")), result);
+    result = (List<String>) evalRuleContextCode(
+        createRuleContext("//test/getrule:c_str"), "ruleContext.attr.s");
+    assertThat(result).containsExactly("nop_rule", "c", ":a");
 
-    result =
-        evalRuleContextCode(createRuleContext("//test/getrule:genrule_attr"), "ruleContext.attr.s");
-    assertEquals(
-        SkylarkList.createImmutable(
-            ImmutableList.<String>of(
-                "name",
-                "visibility",
-                "tags",
-                "generator_name",
-                "generator_function",
-                "generator_location",
-                "features",
-                "compatible_with",
-                "restricted_to",
-                "srcs",
-                "tools",
-                "toolchains",
-                "outs",
-                "cmd",
-                "output_to_bindir",
-                "local",
-                "message",
-                "executable",
-                "stamp",
-                "heuristic_label_expansion",
-                "kind")),
-        result);
+    result = (List<String>) evalRuleContextCode(
+        createRuleContext("//test/getrule:genrule_attr"), "ruleContext.attr.s");
+    assertThat(result).containsExactly(
+        "name",
+        "visibility",
+        "tags",
+        "generator_name",
+        "generator_function",
+        "generator_location",
+        "features",
+        "compatible_with",
+        "restricted_to",
+        "srcs",
+        "tools",
+        "toolchains",
+        "outs",
+        "cmd",
+        "output_to_bindir",
+        "local",
+        "message",
+        "executable",
+        "stamp",
+        "heuristic_label_expansion",
+        "kind");
   }
 
   @Test