Clean up ExecutionRequirements

- remove BaseSpawn.Local; instead, all callers pass in the full set of
  execution requirements they want to set
- disable caching and sandboxing for the symlink tree action - it does not
  declare outputs, so it can't be cached or sandboxed (fixes #4041)
- centralize the existing execution requirements in the ExecutionRequirements
  class
- centralize checking for execution requirements in the Spawn class
  (it's possible that we may need a more decentralized, extensible design in
  the future, but for now having them in a single place is simple and
  effective)
- update the documentation
- forward the relevant tags to execution requirements in TargetUtils (progress
  on #3960)
- this also contributes to #4153

PiperOrigin-RevId: 177288598
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java b/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java
new file mode 100644
index 0000000..3baee79
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java
@@ -0,0 +1,67 @@
+// 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.exec;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
+import com.google.devtools.build.lib.actions.ActionInput;
+import com.google.devtools.build.lib.actions.ActionInputHelper;
+import com.google.devtools.build.lib.actions.ExecutionRequirements;
+import com.google.devtools.build.lib.actions.Spawn;
+import com.google.devtools.build.lib.analysis.config.BinTools;
+import com.google.devtools.build.lib.exec.util.FakeOwner;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link SymlinkTreeHelper}. */
+@RunWith(JUnit4.class)
+public final class SymlinkTreeHelperTest {
+  private final FileSystem fs = new InMemoryFileSystem();
+
+  @Test
+  public void checkCreatedSpawn() {
+    ActionExecutionMetadata owner = new FakeOwner("SymlinkTree", "Creating it");
+    Path execRoot = fs.getPath("/my/workspace");
+    Path inputManifestPath = execRoot.getRelative("input_manifest");
+    ActionInput inputManifest = ActionInputHelper.fromPath(inputManifestPath.asFragment());
+    Spawn spawn =
+        new SymlinkTreeHelper(
+            inputManifestPath,
+            fs.getPath("/my/workspace/output/MANIFEST"),
+            false)
+        .createSpawn(
+            owner,
+            execRoot,
+            BinTools.forUnitTesting(execRoot, ImmutableList.of(SymlinkTreeHelper.BUILD_RUNFILES)),
+            ImmutableMap.of(),
+            inputManifest);
+    assertThat(spawn.getResourceOwner()).isSameAs(owner);
+    assertThat(spawn.getEnvironment()).isEmpty();
+    assertThat(spawn.getExecutionInfo()).containsExactly(
+        ExecutionRequirements.LOCAL, "",
+        ExecutionRequirements.NO_CACHE, "",
+        ExecutionRequirements.NO_SANDBOX, "");
+    assertThat(spawn.getInputFiles()).containsExactly(inputManifest);
+    // At this time, the spawn does not declare any output files.
+    assertThat(spawn.getOutputFiles()).isEmpty();
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java b/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java
index 6d49de9..eb49366 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/TargetUtilsTest.java
@@ -18,6 +18,7 @@
 import com.google.common.base.Predicate;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import java.util.Map;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -75,4 +76,24 @@
     assertThat(tagFilter.apply(tag2)).isTrue();
     assertThat(tagFilter.apply(tag1b)).isFalse();
   }
+
+  @Test
+  public void testExecutionInfo() throws Exception {
+    scratch.file(
+        "tests/BUILD",
+        "sh_binary(name = 'tag1', srcs=['sh.sh'], tags=['supports-workers', 'no-cache'])",
+        "sh_binary(name = 'tag2', srcs=['sh.sh'], tags=['disable-local-prefetch'])",
+        "sh_binary(name = 'tag1b', srcs=['sh.sh'], tags=['local', 'cpu:4'])");
+
+    Rule tag1 = (Rule) getTarget("//tests:tag1");
+    Rule tag2 = (Rule) getTarget("//tests:tag2");
+    Rule tag1b = (Rule) getTarget("//tests:tag1b");
+
+    Map<String, String> execInfo = TargetUtils.getExecutionInfo(tag1);
+    assertThat(execInfo).containsExactly("supports-workers", "", "no-cache", "");
+    execInfo = TargetUtils.getExecutionInfo(tag2);
+    assertThat(execInfo).containsExactly("disable-local-prefetch", "");
+    execInfo = TargetUtils.getExecutionInfo(tag1b);
+    assertThat(execInfo).containsExactly("local", "", "cpu:4", "");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
index 342680b..d2497ff 100644
--- a/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
@@ -26,10 +26,10 @@
 import com.google.devtools.build.lib.actions.ActionInputPrefetcher;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
-import com.google.devtools.build.lib.actions.BaseSpawn;
 import com.google.devtools.build.lib.actions.ExecException;
 import com.google.devtools.build.lib.actions.ResourceManager;
 import com.google.devtools.build.lib.actions.ResourceSet;
+import com.google.devtools.build.lib.actions.SimpleSpawn;
 import com.google.devtools.build.lib.actions.Spawn;
 import com.google.devtools.build.lib.actions.SpawnActionContext;
 import com.google.devtools.build.lib.actions.SpawnResult;
@@ -58,7 +58,6 @@
 import com.google.devtools.common.options.Options;
 import com.google.devtools.common.options.OptionsParser;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import org.junit.Before;
@@ -148,10 +147,13 @@
   }
 
   private Spawn createSpawn(String... arguments) {
-    return new BaseSpawn.Local(
-        Arrays.asList(arguments),
-        ImmutableMap.<String, String>of(),
+    return new SimpleSpawn(
         new ActionsTestUtil.NullAction(),
+        ImmutableList.copyOf(arguments),
+        /*environment=*/ ImmutableMap.of(),
+        /*executionInfo=*/ ImmutableMap.of(),
+        /*inputs=*/ ImmutableList.of(),
+        /*outputs=*/ ImmutableList.of(),
         ResourceSet.ZERO);
   }
 
@@ -230,10 +232,13 @@
       // down where that env var is coming from.
       return;
     }
-    Spawn spawn = new BaseSpawn.Local(
-        Arrays.asList("/usr/bin/env"),
-        ImmutableMap.of("foo", "bar", "baz", "boo"),
+    Spawn spawn = new SimpleSpawn(
         new ActionsTestUtil.NullAction(),
+        ImmutableList.of("/usr/bin/env"),
+        /*environment=*/ ImmutableMap.of("foo", "bar", "baz", "boo"),
+        /*executionInfo=*/ ImmutableMap.of(),
+        /*inputs=*/ ImmutableList.of(),
+        /*outputs=*/ ImmutableList.of(),
         ResourceSet.ZERO);
     run(spawn);
     assertThat(Sets.newHashSet(out().split("\n"))).isEqualTo(Sets.newHashSet("foo=bar", "baz=boo"));