Fix terminal output expansion flag spacing & add implicit requirements.
Add unit tests for individual flag usage output. Also move around some of the sample expansion function test options, which could be used more widely, to minimize recreating the boilerplate samples.
RELNOTES: None.
PiperOrigin-RevId: 165478776
diff --git a/src/main/java/com/google/devtools/common/options/OptionsUsage.java b/src/main/java/com/google/devtools/common/options/OptionsUsage.java
index 633b6f6..61829e2 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsUsage.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsUsage.java
@@ -56,6 +56,8 @@
StringBuilder out = new StringBuilder();
String sep = "";
for (String paragraph : NEWLINE_SPLITTER.split(in)) {
+ // TODO(ccalvarin) break iterators expect hyphenated words to be line-breakable, which looks
+ // funny for --flag
BreakIterator boundary = BreakIterator.getLineInstance(); // (factory)
boundary.setText(paragraph);
out.append(sep).append(indentString);
@@ -132,18 +134,27 @@
return;
}
if (!annotation.help().equals("")) {
- usage.append(paragraphFill(annotation.help(), 4, 80)); // (indent, width)
+ usage.append(paragraphFill(annotation.help(), /*indent=*/ 4, /*width=*/ 80));
usage.append('\n');
}
ImmutableList<String> expansion = getExpansionIfKnown(optionField, optionsData);
if (expansion == null) {
- usage.append(" Expands to unknown options.\n");
+ usage.append(paragraphFill("Expands to unknown options.", /*indent=*/ 6, /*width=*/ 80));
+ usage.append('\n');
} else if (!expansion.isEmpty()) {
StringBuilder expandsMsg = new StringBuilder("Expands to: ");
for (String exp : expansion) {
expandsMsg.append(exp).append(" ");
}
- usage.append(paragraphFill(expandsMsg.toString(), 4, 80)); // (indent, width)
+ usage.append(paragraphFill(expandsMsg.toString(), /*indent=*/ 6, /*width=*/ 80));
+ usage.append('\n');
+ }
+ if (annotation.implicitRequirements().length > 0) {
+ StringBuilder requiredMsg = new StringBuilder("Using this option will also add: ");
+ for (String req : annotation.implicitRequirements()) {
+ requiredMsg.append(req).append(" ");
+ }
+ usage.append(paragraphFill(requiredMsg.toString(), /*indent=*/ 6, /*width=*/ 80));
usage.append('\n');
}
}
@@ -187,7 +198,7 @@
usage.append("</dt>\n");
usage.append("<dd>\n");
if (!annotation.help().isEmpty()) {
- usage.append(paragraphFill(escaper.escape(annotation.help()), 0, 80)); // (indent, width)
+ usage.append(paragraphFill(escaper.escape(annotation.help()), /*indent=*/ 0, /*width=*/ 80));
usage.append('\n');
}
ImmutableList<String> expansion = getExpansionIfKnown(optionField, optionsData);
@@ -203,7 +214,7 @@
.append(escaper.escape(exp))
.append("</code><br/>\n");
}
- usage.append(expandsMsg.toString()); // (indent, width)
+ usage.append(expandsMsg.toString());
usage.append('\n');
}
usage.append("</dd>\n");
diff --git a/src/test/java/com/google/devtools/common/options/OptionsParserTest.java b/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
index e1aa563..01084ab 100644
--- a/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
+++ b/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
@@ -1121,8 +1121,8 @@
OptionsParser parser = OptionsParser.newOptionsParser(ExpansionOptions.class);
String usage =
parser.describeOptions(ImmutableMap.<String, String>of(), OptionsParser.HelpVerbosity.LONG);
- assertThat(usage).contains(" --expands\n Expands to: --underlying=from_expansion");
- assertThat(usage).contains(" --expands_by_function\n Expands to: --expands");
+ assertThat(usage).contains(" --expands\n Expands to: --underlying=from_expansion");
+ assertThat(usage).contains(" --expands_by_function\n Expands to: --expands");
}
@Test
diff --git a/src/test/java/com/google/devtools/common/options/OptionsTest.java b/src/test/java/com/google/devtools/common/options/OptionsTest.java
index e8d7237..32f257b 100644
--- a/src/test/java/com/google/devtools/common/options/OptionsTest.java
+++ b/src/test/java/com/google/devtools/common/options/OptionsTest.java
@@ -18,12 +18,9 @@
import static java.util.Arrays.asList;
import static org.junit.Assert.fail;
-import com.google.common.collect.ImmutableList;
import com.google.common.testing.EqualsTester;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.Map;
-import java.util.TreeSet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -85,79 +82,6 @@
expansion = {"--host=special.google.com", "--port=8080"}
)
public Void special;
-
- // Interestingly, the class needs to be public, or else the default constructor ends up not
- // being public and the expander can't be instantiated.
- /**
- * Defines an expansion function that looks at other options defined with it and expands to
- * options that match a pattern.
- */
- public static class ExpansionDependsOnOtherOptionDefinitions implements ExpansionFunction {
- @Override
- public ImmutableList<String> getExpansion(ExpansionContext context) {
- TreeSet<String> flags = new TreeSet<>();
- for (Map.Entry<String, ?> entry : context.getOptionsData().getAllNamedFields()) {
- if (entry.getKey().startsWith("specialexp_")) {
- flags.add("--" + entry.getKey());
- }
- }
- return ImmutableList.copyOf(flags);
- }
- }
-
- /**
- * Defines an expansion function that adapts its expansion to the value assigned to the original
- * expansion option.
- */
- public static class ExpansionDependsOnFlagValue implements ExpansionFunction {
- @Override
- public ImmutableList<String> getExpansion(ExpansionContext context)
- throws OptionsParsingException {
- String value = context.getUnparsedValue();
- if (value == null) {
- throw new ExpansionNeedsValueException("Expansion value not set.");
- }
- if (value.equals("foo_bar")) {
- return ImmutableList.<String>of("--specialexp_foo", "--specialexp_bar");
- }
-
- throw new OptionsParsingException("Unexpected expansion argument: " + value);
- }
- }
-
- @Option(
- name = "specialexp_foo",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "false"
- )
- public boolean specialExpFoo;
-
- @Option(
- name = "specialexp_bar",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "false"
- )
- public boolean specialExpBar;
-
- @Option(
- name = "specialexp",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "null",
- expansionFunction = ExpansionDependsOnOtherOptionDefinitions.class
- )
- public Void specialExp;
-
- @Option(
- name = "dynamicexp",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "null",
- expansionFunction = ExpansionDependsOnFlagValue.class
- )
- public Void variableExpansion;
}
@Test
@@ -423,13 +347,10 @@
}
@Test
- public void usageForExpansion() {
+ public void usageForStaticExpansion() {
String usage = Options.getUsage(HttpOptions.class);
assertThat(usage)
- .contains(" --special\n Expands to: --host=special.google.com --port=8080");
- // Expect that the usage text contains the expansion appropriate to the options bases that were
- // loaded into the options parser.
- assertThat(usage).contains(" --specialexp\n Expands to: --specialexp_bar --specialexp_foo");
+ .contains(" --special\n Expands to: --host=special.google.com --port=8080");
}
public static class NullTestOptions extends OptionsBase {
@@ -462,7 +383,7 @@
+ " The URL at which the server will be running.");
assertThat(usage)
.contains(
- " --none\n" + " An expanded option.\n" + " Expands to: --host=www.google.com");
+ " --none\n" + " An expanded option.\n" + " Expands to: --host=www.google.com");
}
public static class MyURLConverter implements Converter<URL> {
@@ -622,26 +543,38 @@
}
@Test
- public void expansionFunction() throws Exception {
- Options<HttpOptions> options1 = Options.parse(HttpOptions.class, new String[] {"--specialexp"});
- Options<HttpOptions> options2 =
- Options.parse(HttpOptions.class, new String[] {"--specialexp_foo", "--specialexp_bar"});
- assertThat(options1.getOptions()).isEqualTo(options2.getOptions());
+ public void usageForExpansionFunction() {
+ // Expect that the usage text contains the expansion appropriate to the options bases that were
+ // loaded into the options parser.
+ String usage = Options.getUsage(TestOptions.class);
+ assertThat(usage)
+ .contains(
+ " --prefix_expansion\n"
+ + " Expands to all options with a specific prefix.\n"
+ + " Expands to: --specialexp_bar --specialexp_foo");
}
@Test
+ public void expansionFunction() throws Exception {
+ Options<TestOptions> options1 =
+ Options.parse(TestOptions.class, new String[] {"--prefix_expansion"});
+ Options<TestOptions> options2 =
+ Options.parse(TestOptions.class, new String[] {"--specialexp_foo", "--specialexp_bar"});
+ assertThat(options1.getOptions()).isEqualTo(options2.getOptions());
+ }
+ @Test
public void dynamicExpansionFunctionWorks() throws Exception {
- Options<HttpOptions> options1 =
- Options.parse(HttpOptions.class, new String[] {"--dynamicexp=foo_bar"});
- Options<HttpOptions> options2 =
- Options.parse(HttpOptions.class, new String[] {"--specialexp_foo", "--specialexp_bar"});
+ Options<TestOptions> options1 =
+ Options.parse(TestOptions.class, new String[] {"--dynamicexp=foo_bar"});
+ Options<TestOptions> options2 =
+ Options.parse(TestOptions.class, new String[] {"--specialexp_foo", "--specialexp_bar"});
assertThat(options1.getOptions()).isEqualTo(options2.getOptions());
}
@Test
public void dynamicExpansionFunctionUnknowValue() throws Exception {
try {
- Options.parse(HttpOptions.class, new String[] {"--dynamicexp=foo"});
+ Options.parse(TestOptions.class, new String[] {"--dynamicexp=foo"});
fail("Unknown expansion argument should cause a failure.");
} catch (OptionsParsingException e) {
assertThat(e).hasMessage("Unexpected expansion argument: foo");
diff --git a/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java b/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java
new file mode 100644
index 0000000..c720163
--- /dev/null
+++ b/src/test/java/com/google/devtools/common/options/OptionsUsageTest.java
@@ -0,0 +1,247 @@
+// 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.common.options;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.common.options.OptionsParser.HelpVerbosity;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit test for {@link OptionsUsage}. */
+@RunWith(JUnit4.class)
+public final class OptionsUsageTest {
+
+ private OptionsData data;
+
+ @Before
+ public void setUp() {
+ data = OptionsParser.getOptionsDataInternal(TestOptions.class);
+ }
+
+ private String getUsage(String fieldName, HelpVerbosity verbosity) {
+ StringBuilder builder = new StringBuilder();
+ OptionsUsage.getUsage(data.getFieldFromName(fieldName), builder, verbosity, data);
+ return builder.toString();
+ }
+
+ @Test
+ public void stringValue_short() {
+ assertThat(getUsage("test_string", HelpVerbosity.SHORT)).isEqualTo(" --test_string\n");
+ }
+
+ @Test
+ public void stringValue_medium() {
+ assertThat(getUsage("test_string", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_string (a string; default: \"test string default\")\n");
+ }
+
+ @Test
+ public void stringValue_long() {
+ assertThat(getUsage("test_string", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_string (a string; default: \"test string default\")\n"
+ + " a string-valued option to test simple option operations\n");
+ }
+
+ @Test
+ public void intValue_short() {
+ assertThat(getUsage("expanded_c", HelpVerbosity.SHORT)).isEqualTo(" --expanded_c\n");
+ }
+
+ @Test
+ public void intValue_medium() {
+ assertThat(getUsage("expanded_c", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --expanded_c (an integer; default: \"12\")\n");
+ }
+
+ @Test
+ public void intValue_long() {
+ assertThat(getUsage("expanded_c", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --expanded_c (an integer; default: \"12\")\n"
+ + " an int-value'd flag used to test expansion logic\n");
+ }
+
+ @Test
+ public void multipleValue_short() {
+ assertThat(getUsage("test_multiple_string", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_multiple_string\n");
+ }
+
+ @Test
+ public void multipleValue_medium() {
+ assertThat(getUsage("test_multiple_string", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_multiple_string (a string; may be used multiple times)\n");
+ }
+
+ @Test
+ public void multipleValue_long() {
+ assertThat(getUsage("test_multiple_string", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_multiple_string (a string; may be used multiple times)\n"
+ + " a repeatable string-valued flag with its own unhelpful help text\n");
+ }
+
+ @Test
+ public void customConverterValue_short() {
+ assertThat(getUsage("test_list_converters", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_list_converters\n");
+ }
+
+ @Test
+ public void customConverterValue_medium() {
+ assertThat(getUsage("test_list_converters", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_list_converters (a list of strings; may be used multiple times)\n");
+ }
+
+ @Test
+ public void customConverterValue_long() {
+ assertThat(getUsage("test_list_converters", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_list_converters (a list of strings; may be used multiple times)\n"
+ + " a repeatable flag that accepts lists, but doesn't want to have lists of \n"
+ + " lists as a final type\n");
+ }
+
+ @Test
+ public void staticExpansionOption_short() {
+ assertThat(getUsage("test_expansion", HelpVerbosity.SHORT)).isEqualTo(" --test_expansion\n");
+ }
+
+ @Test
+ public void staticExpansionOption_medium() {
+ assertThat(getUsage("test_expansion", HelpVerbosity.MEDIUM)).isEqualTo(" --test_expansion\n");
+ }
+
+ @Test
+ public void staticExpansionOption_long() {
+ assertThat(getUsage("test_expansion", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_expansion\n"
+ + " this expands to an alphabet soup.\n"
+ + " Expands to: --noexpanded_a --expanded_b=false --expanded_c 42 --\n"
+ + " expanded_d bar \n");
+ }
+
+ @Test
+ public void recursiveExpansionOption_short() {
+ assertThat(getUsage("test_recursive_expansion_top_level", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_recursive_expansion_top_level\n");
+ }
+
+ @Test
+ public void recursiveExpansionOption_medium() {
+ assertThat(getUsage("test_recursive_expansion_top_level", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_recursive_expansion_top_level\n");
+ }
+
+ @Test
+ public void recursiveExpansionOption_long() {
+ assertThat(getUsage("test_recursive_expansion_top_level", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_recursive_expansion_top_level\n"
+ + " Lets the children do all the work.\n"
+ + " Expands to: --test_recursive_expansion_middle1 --\n"
+ + " test_recursive_expansion_middle2 \n");
+ }
+
+ @Test
+ public void expansionToMultipleValue_short() {
+ assertThat(getUsage("test_expansion_to_repeatable", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_expansion_to_repeatable\n");
+ }
+
+ @Test
+ public void expansionToMultipleValue_medium() {
+ assertThat(getUsage("test_expansion_to_repeatable", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_expansion_to_repeatable\n");
+ }
+
+ @Test
+ public void expansionToMultipleValue_long() {
+ assertThat(getUsage("test_expansion_to_repeatable", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_expansion_to_repeatable\n"
+ + " Go forth and multiply, they said.\n"
+ + " Expands to: --test_multiple_string=expandedFirstValue --\n"
+ + " test_multiple_string=expandedSecondValue \n");
+ }
+
+ @Test
+ public void implicitRequirementOption_short() {
+ assertThat(getUsage("test_implicit_requirement", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_implicit_requirement\n");
+ }
+
+ @Test
+ public void implicitRequirementOption_medium() {
+ assertThat(getUsage("test_implicit_requirement", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_implicit_requirement (a string; default: \"direct implicit\")\n");
+ }
+
+ @Test
+ public void implicitRequirementOption_long() {
+ assertThat(getUsage("test_implicit_requirement", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_implicit_requirement (a string; default: \"direct implicit\")\n"
+ + " this option really needs that other one, isolation of purpose has failed.\n"
+ + " Using this option will also add: --implicit_requirement_a=implicit \n"
+ + " requirement, required \n");
+ }
+
+ @Test
+ public void expansionFunctionOptionThatReadsUserValue_short() {
+ assertThat(getUsage("test_expansion_function", HelpVerbosity.SHORT))
+ .isEqualTo(" --test_expansion_function\n");
+ }
+
+ @Test
+ public void expansionFunctionOptionThatReadsUserValue_medium() {
+ assertThat(getUsage("test_expansion_function", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --test_expansion_function\n");
+ }
+
+ @Test
+ public void expansionFunctionOptionThatReadsUserValue_long() {
+ assertThat(getUsage("test_expansion_function", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --test_expansion_function\n"
+ + " this is for testing expansion-by-function functionality.\n"
+ + " Expands to unknown options.\n");
+ }
+
+ @Test
+ public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_short() {
+ assertThat(getUsage("prefix_expansion", HelpVerbosity.SHORT))
+ .isEqualTo(" --prefix_expansion\n");
+ }
+
+ @Test
+ public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_medium() {
+ assertThat(getUsage("prefix_expansion", HelpVerbosity.MEDIUM))
+ .isEqualTo(" --prefix_expansion\n");
+ }
+
+ @Test
+ public void expansionFunctionOptionThatExpandsBasedOnOtherLoadedOptions_long() {
+ assertThat(getUsage("prefix_expansion", HelpVerbosity.LONG))
+ .isEqualTo(
+ " --prefix_expansion\n"
+ + " Expands to all options with a specific prefix.\n"
+ + " Expands to: --specialexp_bar --specialexp_foo \n");
+ }
+}
diff --git a/src/test/java/com/google/devtools/common/options/TestOptions.java b/src/test/java/com/google/devtools/common/options/TestOptions.java
index 7dc0228..4fa0309 100644
--- a/src/test/java/com/google/devtools/common/options/TestOptions.java
+++ b/src/test/java/com/google/devtools/common/options/TestOptions.java
@@ -16,6 +16,8 @@
import com.google.common.collect.ImmutableList;
import com.google.devtools.common.options.InvocationPolicyEnforcerTestBase.ToListConverter;
import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
/** Options for testing. */
public class TestOptions extends OptionsBase {
@@ -30,7 +32,8 @@
name = "test_string",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.NO_OP},
- defaultValue = TEST_STRING_DEFAULT
+ defaultValue = TEST_STRING_DEFAULT,
+ help = "a string-valued option to test simple option operations"
)
public String testString;
@@ -43,7 +46,8 @@
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.NO_OP},
defaultValue = "", // default value is ignored when allowMultiple=true.
- allowMultiple = true
+ allowMultiple = true,
+ help = "a repeatable string-valued flag with its own unhelpful help text"
)
public List<String> testMultipleString;
@@ -57,7 +61,10 @@
effectTags = {OptionEffectTag.NO_OP},
defaultValue = "",
allowMultiple = true,
- converter = ToListConverter.class
+ converter = ToListConverter.class,
+ help =
+ "a repeatable flag that accepts lists, but doesn't want to have lists of lists "
+ + "as a final type"
)
public List<String> testListConverters;
@@ -82,7 +89,8 @@
"42",
"--expanded_d",
"bar"
- }
+ },
+ help = "this expands to an alphabet soup."
)
public Void testExpansion;
@@ -99,7 +107,8 @@
expansion = {
"--test_recursive_expansion_middle1",
"--test_recursive_expansion_middle2",
- }
+ },
+ help = "Lets the children do all the work."
)
public Void testRecursiveExpansionTopLevel;
@@ -153,7 +162,8 @@
name = "expanded_c",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "12"
+ defaultValue = "12",
+ help = "an int-value'd flag used to test expansion logic"
)
public int expandedC;
@@ -182,7 +192,8 @@
expansion = {
"--test_multiple_string=expandedFirstValue",
"--test_multiple_string=expandedSecondValue"
- }
+ },
+ help = "Go forth and multiply, they said."
)
public Void testExpansionToRepeatable;
@@ -198,7 +209,8 @@
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.NO_OP},
defaultValue = TEST_IMPLICIT_REQUIREMENT_DEFAULT,
- implicitRequirements = {"--implicit_requirement_a=" + IMPLICIT_REQUIREMENT_A_REQUIRED}
+ implicitRequirements = {"--implicit_requirement_a=" + IMPLICIT_REQUIREMENT_A_REQUIRED},
+ help = "this option really needs that other one, isolation of purpose has failed."
)
public String testImplicitRequirement;
@@ -227,6 +239,10 @@
public static final String TEST_EXPANSION_FUNCTION_ACCEPTED_VALUE = "valueA";
public static final String EXPANDED_D_EXPANSION_FUNCTION_VALUE = "expanded valueA";
+ /*
+ * Expansion function flags
+ */
+
/** Used for testing an expansion flag that requires a value. */
public static class TestExpansionFunction implements ExpansionFunction {
@Override
@@ -248,7 +264,8 @@
defaultValue = "null",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.NO_OP},
- expansionFunction = TestExpansionFunction.class
+ expansionFunction = TestExpansionFunction.class,
+ help = "this is for testing expansion-by-function functionality."
)
public Void testExpansionFunction;
@@ -270,4 +287,79 @@
expansionFunction = TestVoidExpansionFunction.class
)
public Void testVoidExpansionFunction;
+
+ // Interestingly, the class needs to be public, or else the default constructor ends up not
+ // being public and the expander can't be instantiated.
+ /**
+ * Defines an expansion function that looks at other options defined with it and expands to
+ * options that match a pattern.
+ */
+ public static class ExpansionDependsOnOtherOptionDefinitions implements ExpansionFunction {
+ @Override
+ public ImmutableList<String> getExpansion(ExpansionContext context) {
+ TreeSet<String> flags = new TreeSet<>();
+ for (Map.Entry<String, ?> entry : context.getOptionsData().getAllNamedFields()) {
+ if (entry.getKey().startsWith("specialexp_")) {
+ flags.add("--" + entry.getKey());
+ }
+ }
+ return ImmutableList.copyOf(flags);
+ }
+ }
+
+ @Option(
+ name = "prefix_expansion",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "null",
+ expansionFunction = ExpansionDependsOnOtherOptionDefinitions.class,
+ help = "Expands to all options with a specific prefix."
+ )
+ public Void specialExp;
+
+ /**
+ * Defines an expansion function that adapts its expansion to the value assigned to the original
+ * expansion option.
+ */
+ public static class ExpansionDependsOnFlagValue implements ExpansionFunction {
+ @Override
+ public ImmutableList<String> getExpansion(ExpansionContext context)
+ throws OptionsParsingException {
+ String value = context.getUnparsedValue();
+ if (value == null) {
+ throw new ExpansionNeedsValueException("Expansion value not set.");
+ }
+ if (value.equals("foo_bar")) {
+ return ImmutableList.<String>of("--specialexp_foo", "--specialexp_bar");
+ }
+
+ throw new OptionsParsingException("Unexpected expansion argument: " + value);
+ }
+ }
+
+ @Option(
+ name = "dynamicexp",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "null",
+ expansionFunction = ExpansionDependsOnFlagValue.class,
+ help = "Expands depending on the value provided."
+ )
+ public Void variableExpansion;
+
+ @Option(
+ name = "specialexp_foo",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "false"
+ )
+ public boolean specialExpFoo;
+
+ @Option(
+ name = "specialexp_bar",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "false"
+ )
+ public boolean specialExpBar;
}