Add a converter for relative, null-able path fragments.
RELNOTES: None.
PiperOrigin-RevId: 238653219
diff --git a/src/main/java/com/google/devtools/build/lib/util/OptionsUtils.java b/src/main/java/com/google/devtools/build/lib/util/OptionsUtils.java
index 8594a9c..4ce2631 100644
--- a/src/main/java/com/google/devtools/build/lib/util/OptionsUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/util/OptionsUtils.java
@@ -15,16 +15,16 @@
package com.google.devtools.build.lib.util;
import com.google.common.base.Preconditions;
+import com.google.common.base.StandardSystemProperty;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.common.options.Converter;
+import com.google.devtools.common.options.OptionsParsingException;
import com.google.devtools.common.options.OptionsParsingResult;
import com.google.devtools.common.options.ParsedOptionDescription;
import java.util.List;
-/**
- * Blaze-specific option utilities.
- */
+/** Blaze-specific option utilities. */
public final class OptionsUtils {
/**
@@ -46,8 +46,8 @@
}
/**
- * Returns a string representation of the non-hidden explicitly or implicitly
- * specified options; option values are shell-escaped.
+ * Returns a string representation of the non-hidden explicitly or implicitly specified options;
+ * option values are shell-escaped.
*/
public static String asShellEscapedString(OptionsParsingResult options) {
return asShellEscapedString(options.asCompleteListOfParsedOptions());
@@ -86,27 +86,19 @@
}
/**
- * Returns a string representation of the non-hidden explicitly or implicitly
- * specified options, filtering out any sensitive options; option values are
- * shell-escaped.
+ * Returns a string representation of the non-hidden explicitly or implicitly specified options,
+ * filtering out any sensitive options; option values are shell-escaped.
*/
public static String asFilteredShellEscapedString(OptionsParsingResult options) {
return asFilteredShellEscapedString(options, options.asCompleteListOfParsedOptions());
}
- /**
- * Converter from String to PathFragment.
- */
- public static class PathFragmentConverter
- implements Converter<PathFragment> {
+ /** Converter from String to PathFragment. */
+ public static class PathFragmentConverter implements Converter<PathFragment> {
@Override
public PathFragment convert(String input) {
- String path = Preconditions.checkNotNull(input);
- if (!path.isEmpty() && path.startsWith("~/")) {
- path = path.replace("~", System.getProperty("user.home"));
- }
- return PathFragment.create(path);
+ return convertOptionsPathFragment(Preconditions.checkNotNull(input));
}
@Override
@@ -115,6 +107,30 @@
}
}
+ /** Converter from String to PathFragment. If the input is empty returns {@code null} instead. */
+ public static class EmptyToNullRelativePathFragmentConverter implements Converter<PathFragment> {
+
+ @Override
+ public PathFragment convert(String input) throws OptionsParsingException {
+ if (input.isEmpty()) {
+ return null;
+ }
+
+ PathFragment pathFragment = convertOptionsPathFragment(input);
+
+ if (pathFragment.isAbsolute()) {
+ throw new OptionsParsingException("Expected relative path but got '" + input + "'.");
+ }
+
+ return pathFragment;
+ }
+
+ @Override
+ public String getTypeDescription() {
+ return "a relative path";
+ }
+ }
+
/** Converts from a colon-separated list of strings into a list of PathFragment instances. */
public static class PathFragmentListConverter implements Converter<ImmutableList<PathFragment>> {
@@ -123,10 +139,7 @@
ImmutableList.Builder<PathFragment> result = ImmutableList.builder();
for (String piece : input.split(":")) {
if (!piece.isEmpty()) {
- if (piece.startsWith("~/")) {
- piece = piece.replace("~", System.getProperty("user.home"));
- }
- result.add(PathFragment.create(piece));
+ result.add(convertOptionsPathFragment(piece));
}
}
return result.build();
@@ -137,4 +150,11 @@
return "a colon-separated list of paths";
}
}
+
+ private static PathFragment convertOptionsPathFragment(String path) {
+ if (!path.isEmpty() && path.startsWith("~/")) {
+ path = path.replace("~", StandardSystemProperty.USER_HOME.value());
+ }
+ return PathFragment.create(path);
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/util/OptionsUtilsTest.java b/src/test/java/com/google/devtools/build/lib/util/OptionsUtilsTest.java
index fb69921..24a2b25 100644
--- a/src/test/java/com/google/devtools/build/lib/util/OptionsUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/util/OptionsUtilsTest.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.util;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
import com.google.devtools.build.lib.util.OptionsUtils.PathFragmentConverter;
import com.google.devtools.build.lib.util.OptionsUtils.PathFragmentListConverter;
@@ -25,58 +26,52 @@
import com.google.devtools.common.options.OptionPriority.PriorityCategory;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
+import com.google.devtools.common.options.OptionsParsingException;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Test for {@link OptionsUtils}.
- */
+/** Test for {@link OptionsUtils}. */
@RunWith(JUnit4.class)
public class OptionsUtilsTest {
public static class IntrospectionExample extends OptionsBase {
@Option(
- name = "alpha",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "alpha"
- )
+ name = "alpha",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "alpha")
public String alpha;
@Option(
- name = "beta",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "beta"
- )
+ name = "beta",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "beta")
public String beta;
@Option(
- name = "gamma",
- documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "gamma"
- )
+ name = "gamma",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "gamma")
public String gamma;
@Option(
- name = "delta",
- documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "delta"
- )
+ name = "delta",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "delta")
public String delta;
@Option(
- name = "echo",
- metadataTags = {OptionMetadataTag.HIDDEN},
- documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "echo"
- )
+ name = "echo",
+ metadataTags = {OptionMetadataTag.HIDDEN},
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "echo")
public String echo;
}
@@ -103,19 +98,17 @@
public static class BooleanOpts extends OptionsBase {
@Option(
- name = "b_one",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "true"
- )
+ name = "b_one",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "true")
public boolean bOne;
@Option(
- name = "b_two",
- documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
- effectTags = {OptionEffectTag.NO_OP},
- defaultValue = "false"
- )
+ name = "b_two",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.NO_OP},
+ defaultValue = "false")
public boolean bTwo;
}
@@ -194,4 +187,25 @@
assertThat(convertOne("foo/bar/baz")).isEqualTo(fragment("foo/bar/baz"));
assertThat(convertOne("~/foo")).isEqualTo(fragment(System.getProperty("user.home") + "/foo"));
}
+
+ @Test
+ public void emptyPathFragmentToNull() throws Exception {
+ assertThat(new OptionsUtils.EmptyToNullRelativePathFragmentConverter().convert("")).isNull();
+ }
+
+ @Test
+ public void absolutePathFragmentThrows() throws Exception {
+ OptionsParsingException exception =
+ assertThrows(
+ OptionsParsingException.class,
+ () -> new OptionsUtils.EmptyToNullRelativePathFragmentConverter().convert("/abs"));
+
+ assertThat(exception).hasMessageThat().contains("/abs");
+ }
+
+ @Test
+ public void relativePathFragment() throws Exception {
+ assertThat(new OptionsUtils.EmptyToNullRelativePathFragmentConverter().convert("path/to/me"))
+ .isEqualTo(PathFragment.create("path/to/me"));
+ }
}