<commit_before>// 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.Maps;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CommandLines;
import com.google.devtools.build.lib.actions.CompositeRunfilesSupplier;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.AliasProvider;
import com.google.devtools.build.lib.analysis.CommandConstructor;
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.MakeVariableSupplier;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory;
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.ShToolchain;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.stringtemplate.ExpansionException;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector.InstrumentationSpec;
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.AttributeMap;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.OnDemandString;
import com.google.devtools.build.lib.util.Pair;
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 {@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);

  enum CommandType {
    BASH,
    WINDOWS_BATCH,
    WINDOWS_POWERSHELL,
  }

  private static Pair<CommandType, String> determineCommandTypeAndAttribute(
      RuleContext ruleContext) {
    AttributeMap attributeMap = ruleContext.attributes();
    // TODO(pcloudy): This should match the execution platform instead of using OS.getCurrent()
    if (OS.getCurrent() == OS.WINDOWS) {
      if (attributeMap.isAttributeValueExplicitlySpecified("cmd_ps")) {
        return Pair.of(CommandType.WINDOWS_POWERSHELL, "cmd_ps");
      }
      if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bat")) {
        return Pair.of(CommandType.WINDOWS_BATCH, "cmd_bat");
      }
    }
    if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bash")) {
      return Pair.of(CommandType.BASH, "cmd_bash");
    }
    if (attributeMap.isAttributeValueExplicitlySpecified("cmd")) {
      return Pair.of(CommandType.BASH, "cmd");
    }
    ruleContext.attributeError(
        "cmd",
        "missing value for `cmd` attribute, you can also set `cmd_ps` or `cmd_bat` on"
            + " Windows and `cmd_bash` on other platforms.");
    return null;
  }

  @Override
  public ConfiguredTarget create(RuleContext ruleContext)
      throws InterruptedException, RuleErrorException, ActionConflictException {
    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)
        && !filesToBuild.isEmpty()
        && !filesToBuild.isSingleton()) {
      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");
    }

    Pair<CommandType, String> cmdTypeAndAttr = determineCommandTypeAndAttribute(ruleContext);

    ImmutableMap.Builder<Label, Iterable<Artifact>> labelMap = ImmutableMap.builder();
    for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("srcs")) {
      // 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);
      // The CommandHelper class makes an explicit copy of this in the constructor, so flattening
      // here should be benign.
      labelMap.put(AliasProvider.getDependencyLabel(dep), files.toList());
    }
    NestedSet<Artifact> resolvedSrcs = resolvedSrcsBuilder.build();

    CommandHelper commandHelper =
        commandHelperBuilder(ruleContext).addLabelMap(labelMap.build()).build();

    if (ruleContext.hasErrors()) {
      return null;
    }

    CommandType cmdType = cmdTypeAndAttr.first;
    String cmdAttr = cmdTypeAndAttr.second;
    boolean expandToWindowsPath = cmdType == CommandType.WINDOWS_BATCH;

    String baseCommand = ruleContext.attributes().get(cmdAttr, Type.STRING);

    // Expand template variables and functions.
    ImmutableList.Builder<MakeVariableSupplier> makeVariableSuppliers =
        new ImmutableList.Builder<>();
    CommandResolverContext commandResolverContext =
        new CommandResolverContext(<commit_msg>Factor out source collection in genrule.<commit_after>// 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.common.collect.ImmutableMap.toImmutableMap;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CommandLines;
import com.google.devtools.build.lib.actions.CompositeRunfilesSupplier;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.AliasProvider;
import com.google.devtools.build.lib.analysis.CommandConstructor;
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.MakeVariableSupplier;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory;
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.ShToolchain;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.stringtemplate.ExpansionException;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector.InstrumentationSpec;
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.AttributeMap;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.OnDemandString;
import com.google.devtools.build.lib.util.Pair;
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 {@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);

  private ImmutableMap<Label, NestedSet<Artifact>> collectSources(
      List<? extends TransitiveInfoCollection> srcs) {
    ImmutableMap.Builder<Label, NestedSet<Artifact>> labelMap = ImmutableMap.builder();

    for (TransitiveInfoCollection dep : srcs) {
      // 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();
      labelMap.put(AliasProvider.getDependencyLabel(dep), files);
    }

    return labelMap.build();
  }

  enum CommandType {
    BASH,
    WINDOWS_BATCH,
    WINDOWS_POWERSHELL,
  }

  private static Pair<CommandType, String> determineCommandTypeAndAttribute(
      RuleContext ruleContext) {
    AttributeMap attributeMap = ruleContext.attributes();
    // TODO(pcloudy): This should match the execution platform instead of using OS.getCurrent()
    if (OS.getCurrent() == OS.WINDOWS) {
      if (attributeMap.isAttributeValueExplicitlySpecified("cmd_ps")) {
        return Pair.of(CommandType.WINDOWS_POWERSHELL, "cmd_ps");
      }
      if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bat")) {
        return Pair.of(CommandType.WINDOWS_BATCH, "cmd_bat");
      }
    }
    if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bash")) {
      return Pair.of(CommandType.BASH, "cmd_bash");
    }
    if (attributeMap.isAttributeValueExplicitlySpecified("cmd")) {
      return Pair.of(CommandType.BASH, "cmd");
    }
    ruleContext.attributeError(
        "cmd",
        "missing value for `cmd` attribute, you can also set `cmd_ps` or `cmd_bat` on"
            + " Windows and `cmd_bash` on other platforms.");
    return null;
  }

  @Override
  public ConfiguredTarget create(RuleContext ruleContext)
      throws InterruptedException, RuleErrorException, ActionConflictException {
    NestedSet<Artifact> filesToBuild =
        NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts());

    if (filesToBuild.isEmpty()) {
      ruleContext.attributeError("outs", "Genrules without outputs don't make sense");
    }
    if (ruleContext.attributes().get("executable", Type.BOOLEAN)
        && !filesToBuild.isEmpty()
        && !filesToBuild.isSingleton()) {
      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");
    }

    Pair<CommandType, String> cmdTypeAndAttr = determineCommandTypeAndAttribute(ruleContext);

    ImmutableMap<Label, NestedSet<Artifact>> labelMap =
        collectSources(ruleContext.getPrerequisites("srcs"));
    NestedSet<Artifact> resolvedSrcs = NestedSetBuilder.fromNestedSets(labelMap.values()).build();

    // The CommandHelper class makes an explicit copy of this in the constructor, so flattening
    // here should be benign.
    CommandHelper commandHelper =
        commandHelperBuilder(ruleContext)
            .addLabelMap(
                labelMap.entrySet().stream()
                    .collect(toImmutableMap(Map.Entry::getKey, e -> e.getValue().toList())))
            .build();

    if (ruleContext.hasErrors()) {
      return null;
    }

    CommandType cmdType = cmdTypeAndAttr.first;
    String cmdAttr = cmdTypeAndAttr.second;
    boolean expandToWindowsPath = cmdType == CommandType.WINDOWS_BATCH;

    String baseCommand = ruleContext.attributes().get(cmdAttr, Type.STRING);

    // Expand template variables and functions.
    ImmutableList.Builder<MakeVariableSupplier> makeVariableSuppliers =
        new ImmutableList.Builder<>();
    CommandResolverContext commandResolverContext =
        new CommandResolverContext(
{'fraction_non_alphanumeric': 0.09840213049267643, 'fraction_numerical': 0.0011984021304926764, 'mean_word_length': 4.319050991501417, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2499433', 'n_tokens_mistral': 4228, 'n_tokens_neox': 4142, 'n_words': 941}