aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Liam Miller-Cushon <cushon@google.com>2016-02-04 02:38:03 +0000
committerGravatar David Chen <dzc@google.com>2016-02-04 18:11:18 +0000
commit28bb362b52dfa379e10115d766658f68a125deeb (patch)
treec52a7f1c23359947783e9f5b7b95d869b8969aee
parent092dee3fad52c2f9c2b17447c58f31dba5be438a (diff)
Turbine options parser
-- MOS_MIGRATED_REVID=113806383
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java291
-rw-r--r--src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java167
-rw-r--r--src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/TurbineOptionsTest.java203
3 files changed, 661 insertions, 0 deletions
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java
new file mode 100644
index 0000000000..e7434c0e2d
--- /dev/null
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptions.java
@@ -0,0 +1,291 @@
+// Copyright 2016 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.java.turbine;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import javax.annotation.Nullable;
+
+/** Header compilation options. */
+public class TurbineOptions {
+
+ private final String output;
+ private final ImmutableList<String> classPath;
+ private final ImmutableList<String> bootClassPath;
+ private final ImmutableList<String> sources;
+ private final ImmutableList<String> processorPath;
+ private final ImmutableList<String> processors;
+ private final String tempDir;
+ private final ImmutableList<String> sourceJars;
+ private final Optional<String> outputDeps;
+ private final ImmutableMap<String, String> directJarsToTargets;
+ private final ImmutableMap<String, String> indirectJarsToTargets;
+ private final String targetLabel;
+ private final ImmutableList<String> depsArtifacts;
+ private final String strictDepsMode;
+ private final String ruleKind;
+ private final ImmutableList<String> javacOpts;
+
+ private TurbineOptions(
+ String output,
+ ImmutableList<String> classPath,
+ ImmutableList<String> bootClassPath,
+ ImmutableList<String> sources,
+ ImmutableList<String> processorPath,
+ ImmutableList<String> processors,
+ String tempDir,
+ ImmutableList<String> sourceJars,
+ @Nullable String outputDeps,
+ ImmutableMap<String, String> directJarsToTargets,
+ ImmutableMap<String, String> indirectJarsToTargets,
+ String targetLabel,
+ ImmutableList<String> depsArtifacts,
+ String strictDepsMode,
+ String ruleKind,
+ ImmutableList<String> javacOpts) {
+ this.output = checkNotNull(output, "output must not be null");
+ this.classPath = checkNotNull(classPath, "classPath must not be null");
+ this.bootClassPath = checkNotNull(bootClassPath, "bootClassPath must not be null");
+ this.sources = checkNotNull(sources, "sources must not be null");
+ this.processorPath = checkNotNull(processorPath, "processorPath must not be null");
+ this.processors = checkNotNull(processors, "processors must not be null");
+ this.tempDir = checkNotNull(tempDir, "tempDir must not be null");
+ this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null");
+ this.outputDeps = Optional.fromNullable(outputDeps);
+ this.directJarsToTargets =
+ checkNotNull(directJarsToTargets, "directJarsToTargets must not be null");
+ this.indirectJarsToTargets =
+ checkNotNull(indirectJarsToTargets, "indirectJarsToTargets must not be null");
+ this.targetLabel = checkNotNull(targetLabel, "targetLabel must not be null");
+ this.depsArtifacts = checkNotNull(depsArtifacts, "depsArtifacts must not be null");
+ this.strictDepsMode = checkNotNull(strictDepsMode, "strictDepsMode must not be null");
+ this.ruleKind = checkNotNull(ruleKind, "ruleKind must not be null");
+ this.javacOpts = checkNotNull(javacOpts, "javacOpts must not be null");
+ }
+
+ /** Paths to the Java source files to compile. */
+ public ImmutableList<String> sources() {
+ return sources;
+ }
+
+ /** Paths to classpath artifacts. */
+ public ImmutableList<String> classPath() {
+ return classPath;
+ }
+
+ /** Paths to compilation bootclasspath artifacts. */
+ public ImmutableList<String> bootClassPath() {
+ return bootClassPath;
+ }
+
+ /** The output jar. */
+ public String outputFile() {
+ return output;
+ }
+
+ /** A temporary directory, e.g. for extracting sourcejar entries to before compilation. */
+ public String tempDir() {
+ return tempDir;
+ }
+
+ /** Paths to annotation processor artifacts. */
+ public ImmutableList<String> processorPath() {
+ return processorPath;
+ }
+
+ /** Annotation processor class names. */
+ public ImmutableList<String> processors() {
+ return processors;
+ }
+
+ /** Source jars for compilation. */
+ public ImmutableList<String> sourceJars() {
+ return sourceJars;
+ }
+
+ /** Output jdeps file. */
+ public Optional<String> outputDeps() {
+ return outputDeps;
+ }
+
+ /** The mapping from the path to a direct dependency to its build label. */
+ public ImmutableMap<String, String> directJarsToTargets() {
+ return directJarsToTargets;
+ }
+
+ /** The mapping from the path to an indirect dependency to its build label. */
+ public ImmutableMap<String, String> indirectJarsToTargets() {
+ return indirectJarsToTargets;
+ }
+
+ /** The label of the target being compiled. */
+ public String targetLabel() {
+ return targetLabel;
+ }
+
+ /** The .jdeps artifacts for direct dependencies. */
+ public ImmutableList<String> depsArtifacts() {
+ return depsArtifacts;
+ }
+
+ /**
+ * The Strict Java Deps mode.
+ *
+ * <p>See {@link com.google.devtools.build.buildjar.javac.plugins.dependency.DependencyModule.StrictJavaDeps}.
+ */
+ public String strictDepsMode() {
+ return strictDepsMode;
+ }
+
+ /** The kind of the build rule being compiled (e.g. {@code java_library}). */
+ public String ruleKind() {
+ return ruleKind;
+ }
+
+ /** Additional Java compiler flags. */
+ public ImmutableList<String> javacOpts() {
+ return javacOpts;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** A {@link Builder} for {@link TurbineOptions}. */
+ public static class Builder {
+
+ private String output;
+ private final ImmutableList.Builder<String> classPath = ImmutableList.builder();
+ private final ImmutableList.Builder<String> sources = ImmutableList.builder();
+ private final ImmutableList.Builder<String> processorPath = ImmutableList.builder();
+ private final ImmutableList.Builder<String> processors = ImmutableList.builder();
+ private String tempDir;
+ private final ImmutableList.Builder<String> sourceJars = ImmutableList.builder();
+ private final ImmutableList.Builder<String> bootClassPath = ImmutableList.builder();
+ private String outputDeps;
+ private final ImmutableMap.Builder<String, String> directJarsToTargets = ImmutableMap.builder();
+ private final ImmutableMap.Builder<String, String> indirectJarsToTargets =
+ ImmutableMap.builder();
+ private String targetLabel;
+ private final ImmutableList.Builder<String> depsArtifacts = ImmutableList.builder();
+ private String strictDepsMode = "OFF";
+ private String ruleKind;
+ private final ImmutableList.Builder<String> javacOpts = ImmutableList.builder();
+
+ public TurbineOptions build() {
+ return new TurbineOptions(
+ output,
+ classPath.build(),
+ bootClassPath.build(),
+ sources.build(),
+ processorPath.build(),
+ processors.build(),
+ tempDir,
+ sourceJars.build(),
+ outputDeps,
+ directJarsToTargets.build(),
+ indirectJarsToTargets.build(),
+ targetLabel,
+ depsArtifacts.build(),
+ strictDepsMode,
+ ruleKind,
+ javacOpts.build());
+ }
+
+ public Builder setStrictJavaDeps(String strictDepsMode) {
+ this.strictDepsMode = strictDepsMode;
+ return this;
+ }
+
+ public Builder setOutput(String output) {
+ this.output = output;
+ return this;
+ }
+
+ public Builder addClassPathEntries(Iterable<String> classPath) {
+ this.classPath.addAll(classPath);
+ return this;
+ }
+
+ public Builder addBootClassPathEntries(Iterable<String> bootClassPath) {
+ this.bootClassPath.addAll(bootClassPath);
+ return this;
+ }
+
+ public Builder addSources(Iterable<String> sources) {
+ this.sources.addAll(sources);
+ return this;
+ }
+
+ public Builder addProcessorPathEntries(Iterable<String> processorPath) {
+ this.processorPath.addAll(processorPath);
+ return this;
+ }
+
+ public Builder setProcessors(Iterable<String> processors) {
+ this.processors.addAll(processors);
+ return this;
+ }
+
+ public Builder setTempDir(String tempDir) {
+ this.tempDir = tempDir;
+ return this;
+ }
+
+ public Builder setSourceJars(Iterable<String> sourceJars) {
+ this.sourceJars.addAll(sourceJars);
+ return this;
+ }
+
+ public Builder setOutputDeps(String outputDeps) {
+ this.outputDeps = outputDeps;
+ return this;
+ }
+
+ public Builder addDirectJarToTarget(String jar, String target) {
+ directJarsToTargets.put(jar, target);
+ return this;
+ }
+
+ public Builder addIndirectJarToTarget(String jar, String target) {
+ indirectJarsToTargets.put(jar, target);
+ return this;
+ }
+
+ public Builder setTargetLabel(String targetLabel) {
+ this.targetLabel = targetLabel;
+ return this;
+ }
+
+ public Builder addAllDepsArtifacts(Iterable<String> depsArtifacts) {
+ this.depsArtifacts.addAll(depsArtifacts);
+ return this;
+ }
+
+ public Builder setRuleKind(String ruleKind) {
+ this.ruleKind = ruleKind;
+ return this;
+ }
+
+ public Builder addAllJavacOpts(Iterable<String> javacOpts) {
+ this.javacOpts.addAll(javacOpts);
+ return this;
+ }
+ }
+}
diff --git a/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java
new file mode 100644
index 0000000000..a0e5f5a729
--- /dev/null
+++ b/src/java_tools/buildjar/java/com/google/devtools/build/java/turbine/TurbineOptionsParser.java
@@ -0,0 +1,167 @@
+// Copyright 2016 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.java.turbine;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import javax.annotation.Nullable;
+
+/** A command line options parser for {@link TurbineOptions}. */
+public class TurbineOptionsParser {
+
+ /**
+ * Parses a list of command-line options into a {@link TurbineOptions}, expanding any
+ * {@code @params} files.
+ */
+ public static TurbineOptions parse(Iterable<String> args) throws IOException {
+ Deque<String> argumentDeque = new ArrayDeque<>();
+ expandParamsFiles(argumentDeque, args);
+ TurbineOptions.Builder builder = TurbineOptions.builder();
+ parse(builder, argumentDeque);
+ return builder.build();
+ }
+
+ private static final Splitter ARG_SPLITTER =
+ Splitter.on(CharMatcher.BREAKING_WHITESPACE).omitEmptyStrings().trimResults();
+
+ /**
+ * Pre-processes an argument list, expanding arguments of the form {@code @filename}
+ * by reading the content of the file and appending whitespace-delimited options to
+ * {@code argumentDeque}.
+ */
+ private static void expandParamsFiles(Deque<String> argumentDeque, Iterable<String> args)
+ throws IOException {
+ for (String arg : args) {
+ if (arg.isEmpty()) {
+ continue;
+ }
+ if (arg.startsWith("@") && !arg.startsWith("@@")) {
+ Path paramsPath = Paths.get(arg.substring(1));
+ expandParamsFiles(
+ argumentDeque, ARG_SPLITTER.split(new String(Files.readAllBytes(paramsPath), UTF_8)));
+ } else {
+ argumentDeque.addLast(arg);
+ }
+ }
+ }
+
+ private static void parse(TurbineOptions.Builder builder, Deque<String> argumentDeque) {
+ while (!argumentDeque.isEmpty()) {
+ String next = argumentDeque.pollFirst();
+ switch (next) {
+ case "--output":
+ builder.setOutput(readOne(argumentDeque));
+ break;
+ case "--source_jars":
+ builder.setSourceJars(readList(argumentDeque));
+ break;
+ case "--temp_dir":
+ builder.setTempDir(readOne(argumentDeque));
+ break;
+ case "--processors":
+ builder.setProcessors(readList(argumentDeque));
+ break;
+ case "--processorpath":
+ builder.addProcessorPathEntries(splitClasspath(readOne(argumentDeque)));
+ break;
+ case "--classpath":
+ builder.addClassPathEntries(splitClasspath(readOne(argumentDeque)));
+ break;
+ case "--bootclasspath":
+ builder.addBootClassPathEntries(splitClasspath(readOne(argumentDeque)));
+ break;
+ case "--javacopts":
+ builder.addAllJavacOpts(readList(argumentDeque));
+ break;
+ case "--sources":
+ builder.addSources(readList(argumentDeque));
+ break;
+ case "--output_deps":
+ builder.setOutputDeps(readOne(argumentDeque));
+ break;
+ case "--direct_dependency":
+ {
+ String jar = readOne(argumentDeque);
+ String target = readOne(argumentDeque);
+ builder.addDirectJarToTarget(jar, target);
+ break;
+ }
+ case "--indirect_dependency":
+ {
+ String jar = readOne(argumentDeque);
+ String target = readOne(argumentDeque);
+ builder.addIndirectJarToTarget(jar, target);
+ break;
+ }
+ case "--deps_artifacts":
+ builder.addAllDepsArtifacts(readList(argumentDeque));
+ break;
+ case "--target_label":
+ builder.setTargetLabel(readOne(argumentDeque));
+ break;
+ case "--strict_java_deps":
+ builder.setStrictJavaDeps(readOne(argumentDeque));
+ break;
+ case "--rule_kind":
+ builder.setRuleKind(readOne(argumentDeque));
+ break;
+ default:
+ if (next.isEmpty() && !argumentDeque.isEmpty()) {
+ throw new IllegalArgumentException("unknown option: " + next);
+ }
+ }
+ }
+ }
+
+ /** Returns the value of an option, or {@code null}. */
+ @Nullable
+ private static String readOne(Deque<String> argumentDeque) {
+ if (argumentDeque.isEmpty() || argumentDeque.peekFirst().startsWith("-")) {
+ return null;
+ }
+ return argumentDeque.pollFirst();
+ }
+
+ /** Returns a list of option values. */
+ private static ImmutableList<String> readList(Deque<String> argumentDeque) {
+ ImmutableList.Builder<String> result = ImmutableList.builder();
+ while (!argumentDeque.isEmpty() && !argumentDeque.peekFirst().startsWith("--")) {
+ result.add(argumentDeque.pollFirst());
+ }
+ return result.build();
+ }
+
+ private static final Splitter CLASSPATH_SPLITTER =
+ Splitter.on(':').trimResults().omitEmptyStrings();
+
+ private static ImmutableList<String> splitClasspath(String path) {
+ ImmutableList.Builder<String> classpath = ImmutableList.builder();
+ if (path != null) {
+ classpath.addAll(CLASSPATH_SPLITTER.split(path));
+ }
+ return classpath.build();
+ }
+}
diff --git a/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/TurbineOptionsTest.java b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/TurbineOptionsTest.java
new file mode 100644
index 0000000000..6eeda1cd1d
--- /dev/null
+++ b/src/java_tools/buildjar/javatests/com/google/devtools/build/java/turbine/TurbineOptionsTest.java
@@ -0,0 +1,203 @@
+// Copyright 2016 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.java.turbine;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+
+@RunWith(JUnit4.class)
+public class TurbineOptionsTest {
+
+ @Rule public final TemporaryFolder tmpFolder = new TemporaryFolder();
+
+ static final ImmutableList<String> BASE_ARGS =
+ ImmutableList.of(
+ "--output",
+ "out.jar",
+ "--temp_dir",
+ "_tmp",
+ "--target_label",
+ "//java/com/google/test",
+ "--rule_kind",
+ "java_library");
+
+ @Test
+ public void exhaustiveArgs() throws Exception {
+ String[] lines = {
+ "--output",
+ "out.jar",
+ "--source_jars",
+ "sources1.srcjar",
+ "sources2.srcjar",
+ "--temp_dir",
+ "_tmp",
+ "--processors",
+ "com.foo.MyProcessor",
+ "com.foo.OtherProcessor",
+ "--processorpath",
+ "libproc1.jar:libproc2.jar",
+ "--classpath",
+ "lib1.jar:lib2.jar",
+ "--bootclasspath",
+ "rt.jar:zipfs.jar",
+ "--javacopts",
+ "-source",
+ "8",
+ "-target",
+ "8",
+ "--sources",
+ "Source1.java",
+ "Source2.java",
+ "--output_deps",
+ "out.jdeps",
+ "--target_label",
+ "//java/com/google/test",
+ "--rule_kind",
+ "java_library",
+ };
+
+ TurbineOptions options =
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
+
+ assertThat(options.outputFile()).isEqualTo("out.jar");
+ assertThat(options.sourceJars())
+ .containsExactly("sources1.srcjar", "sources2.srcjar")
+ .inOrder();
+ assertThat(options.tempDir()).isEqualTo("_tmp");
+ assertThat(options.processors())
+ .containsExactly("com.foo.MyProcessor", "com.foo.OtherProcessor")
+ .inOrder();
+ assertThat(options.processorPath()).containsExactly("libproc1.jar", "libproc2.jar").inOrder();
+ assertThat(options.classPath()).containsExactly("lib1.jar", "lib2.jar").inOrder();
+ assertThat(options.bootClassPath()).containsExactly("rt.jar", "zipfs.jar").inOrder();
+ assertThat(options.javacOpts()).containsExactly("-source", "8", "-target", "8").inOrder();
+ assertThat(options.sources()).containsExactly("Source1.java", "Source2.java");
+ assertThat(options.outputDeps()).hasValue("out.jdeps");
+ assertThat(options.targetLabel()).isEqualTo("//java/com/google/test");
+ assertThat(options.ruleKind()).isEqualTo("java_library");
+ }
+
+ @Test
+ public void strictJavaDepsArgs() throws Exception {
+ String[] lines = {
+ "--strict_java_deps",
+ "OFF",
+ "--direct_dependency",
+ "blaze-out/foo/libbar.jar",
+ "//foo/bar",
+ "--indirect_dependency",
+ "blaze-out/foo/libbaz.jar",
+ "//foo/baz",
+ "blaze-out/foo/libbaz1.jar",
+ "//foo/baz1",
+ "--deps_artifacts",
+ "foo.jdeps",
+ "bar.jdeps",
+ "",
+ };
+
+ TurbineOptions options =
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
+
+ assertThat(options.strictDepsMode()).isEqualTo("OFF");
+ assertThat(options.targetLabel()).isEqualTo("//java/com/google/test");
+ assertThat(options.directJarsToTargets())
+ .containsExactlyEntriesIn(ImmutableMap.of("blaze-out/foo/libbar.jar", "//foo/bar"));
+ assertThat(options.indirectJarsToTargets())
+ .containsExactlyEntriesIn(
+ ImmutableMap.of(
+ "blaze-out/foo/libbaz1.jar", "//foo/baz1",
+ "blaze-out/foo/libbaz2.jar", "//foo/baz2"));
+ assertThat(options.depsArtifacts()).containsExactly("foo.jdeps", "bar.jdeps");
+ }
+
+ @Test
+ public void classpathArgs() throws Exception {
+ String[] lines = {
+ "--classpath",
+ "liba.jar:libb.jar:libc.jar",
+ "--processorpath",
+ "libpa.jar:libpb.jar:libpc.jar",
+ };
+
+ TurbineOptions options =
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
+
+ assertThat(options.classPath()).containsExactly("liba.jar", "libb.jar", "libc.jar").inOrder();
+ assertThat(options.processorPath())
+ .containsExactly("libpa.jar", "libpb.jar", "libpc.jar")
+ .inOrder();
+ }
+
+ @Test
+ public void paramsFile() throws Exception {
+ Iterable<String> paramsArgs =
+ Iterables.concat(BASE_ARGS, Arrays.asList("--javacopts", "-source", "8", "-target", "8"));
+ Path params = tmpFolder.newFile("params.txt").toPath();
+ Files.write(params, paramsArgs, StandardCharsets.UTF_8);
+
+ // @ is a prefix for external repository targets, and the prefix for params files. Targets
+ // are disambiguated by prepending an extra @.
+ String[] lines = {
+ "@" + params.toAbsolutePath(), "--target_label", "//custom/label",
+ };
+
+ TurbineOptions options = TurbineOptionsParser.parse(Arrays.asList(lines));
+
+ // assert that options were read from params file
+ assertThat(options.javacOpts()).containsExactly("-source", "8", "-target", "8").inOrder();
+ // ... and directly from the command line
+ assertThat(options.targetLabel()).isEqualTo("//custom/label");
+ }
+
+ @Test
+ public void escapedExternalRepositoryLabel() throws Exception {
+ // @ is a prefix for external repository targets, and the prefix for params files. Targets
+ // are disambiguated by prepending an extra @.
+ String[] lines = {
+ "--target_label", "@@other-repo//foo:local-jam",
+ };
+
+ TurbineOptions options =
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
+
+ assertThat(options.targetLabel()).isEqualTo("@@other-repo//foo:local-jam");
+ }
+
+ @Test
+ public void failIfMissingExpectedArgs() throws Exception {
+ try {
+ TurbineOptions.builder().build();
+ fail();
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessage("output must not be null");
+ }
+ }
+}