Move the canonicalization of an option value to the option value itself.

Ideally, the canonical form we output from OptionUtils would be the same as for the command canonicalize-flags, but that must wait for dependencies to be cleaned up. Still, in the meantime, keep the --foo=1 normalization of --foo, and apply it to all other boolean flag values (t,true,yes,y, and false equivalents), so that the canoncalize-flags output is more canonical, even if it isn't using the --[no]foo form yet.

RELNOTES: Boolean flag values will now get normalized to 1 or 0 in canonicalize-flags output.
PiperOrigin-RevId: 170084599
GitOrigin-RevId: 659feca7dee332f75801010b018505b7ba5a185e
Change-Id: I65e969918c0ce0129d52e3fcdda4d95cde5175b7
diff --git a/java/com/google/devtools/common/options/OptionsParserImpl.java b/java/com/google/devtools/common/options/OptionsParserImpl.java
index 176d51e..221dcf0 100644
--- a/java/com/google/devtools/common/options/OptionsParserImpl.java
+++ b/java/com/google/devtools/common/options/OptionsParserImpl.java
@@ -158,12 +158,7 @@
             })
         // Ignore expansion options.
         .filter(value -> !value.getOptionDefinition().isExpansionOption())
-        .map(
-            value ->
-                "--"
-                    + value.getOptionDefinition().getOptionName()
-                    + "="
-                    + value.getUnconvertedValue())
+        .map(ParsedOptionDescription::getDeprecatedCanonicalForm)
         .collect(toCollection(ArrayList::new));
   }
 
diff --git a/java/com/google/devtools/common/options/ParsedOptionDescription.java b/java/com/google/devtools/common/options/ParsedOptionDescription.java
index 0910579..1f43172 100644
--- a/java/com/google/devtools/common/options/ParsedOptionDescription.java
+++ b/java/com/google/devtools/common/options/ParsedOptionDescription.java
@@ -15,6 +15,7 @@
 package com.google.devtools.common.options;
 
 import com.google.common.collect.ImmutableList;
+import java.util.function.Function;
 import javax.annotation.Nullable;
 
 /**
@@ -49,6 +50,46 @@
     return commandLineForm;
   }
 
+  public String getCanonicalForm() {
+    return getCanonicalFormWithValueEscaper(s -> s);
+  }
+
+  public String getCanonicalFormWithValueEscaper(Function<String, String> escapingFunction) {
+    // For boolean flags (note that here we do not check for TriState flags, only flags with actual
+    // boolean values, so that we know the return type of getConvertedValue), use the --[no]flag
+    // form for the canonical value.
+    if (optionDefinition.getType().equals(boolean.class)) {
+      try {
+        return ((boolean) getConvertedValue() ? "--" : "--no") + optionDefinition.getOptionName();
+      } catch (OptionsParsingException e) {
+        throw new RuntimeException("Unexpected parsing exception", e);
+      }
+    } else {
+      String optionString = "--" + optionDefinition.getOptionName();
+      if (unconvertedValue != null) { // Can be null for Void options.
+        optionString += "=" + escapingFunction.apply(unconvertedValue);
+      }
+      return optionString;
+    }
+  }
+
+  @Deprecated
+  // TODO(b/65646296) Once external dependencies are cleaned up, use getCanonicalForm()
+  String getDeprecatedCanonicalForm() {
+    String value = unconvertedValue;
+    // For boolean flags (note that here we do not check for TriState flags, only flags with actual
+    // boolean values, so that we know the return type of getConvertedValue), set them all to 1 or
+    // 0, instead of keeping the wide variety of values we accept in their original form.
+    if (optionDefinition.getType().equals(boolean.class)) {
+      try {
+        value = (boolean) getConvertedValue() ? "1" : "0";
+      } catch (OptionsParsingException e) {
+        throw new RuntimeException("Unexpected parsing exception", e);
+      }
+    }
+    return String.format("--%s=%s", optionDefinition.getOptionName(), value);
+  }
+
   public boolean isBooleanOption() {
     return optionDefinition.getType().equals(boolean.class);
   }