Add '--output=tmp/' syntax to CliParser. Change-Id: Ifa3747a9b3a231a5f42601b2c47c262a41cf0c0b Bug: b/510714591
diff --git a/src/main/java/com/android/tools/r8/utils/internal/CliParser.java b/src/main/java/com/android/tools/r8/utils/internal/CliParser.java index 99f7192..127513a 100644 --- a/src/main/java/com/android/tools/r8/utils/internal/CliParser.java +++ b/src/main/java/com/android/tools/r8/utils/internal/CliParser.java
@@ -122,23 +122,39 @@ private void parseInternal(Deque<String> args, B builder, Consumer<String> errorReporter) { while (!args.isEmpty()) { - String arg = args.removeFirst(); + String rawArg = args.removeFirst(); + String arg = rawArg; + String eqValue = null; + + if (rawArg.startsWith("-")) { + int equalsIndex = rawArg.indexOf('='); + if (equalsIndex > 0) { + arg = rawArg.substring(0, equalsIndex); + eqValue = rawArg.substring(equalsIndex + 1); + } + } if (options0.containsKey(arg)) { - options0.get(arg).accept(builder); + if (eqValue != null) { + errorReporter.accept("Option " + arg + " does not take a value."); + } else { + options0.get(arg).accept(builder); + } } else if (options1.containsKey(arg)) { - if (!args.isEmpty()) { + if (eqValue != null) { + options1.get(arg).accept(builder, eqValue); + } else if (!args.isEmpty()) { options1.get(arg).accept(builder, args.removeFirst()); } else { errorReporter.accept("Missing parameter for " + arg + "."); break; } - } else if (arg.startsWith("-")) { - errorReporter.accept("Unknown option: " + arg); + } else if (rawArg.startsWith("-")) { + errorReporter.accept("Unknown option: " + rawArg); } else if (positionalHandler != null) { - positionalHandler.accept(builder, arg); + positionalHandler.accept(builder, rawArg); } else { - errorReporter.accept("Unexpected argument: " + arg); + errorReporter.accept("Unexpected argument: " + rawArg); } } } @@ -151,6 +167,7 @@ private boolean assertValidName(String name) { assert name.startsWith("-") : name + " does not start with -"; + assert !name.contains("=") : name + " contains '='"; return true; }
diff --git a/src/test/java/com/android/tools/r8/utils/CliParserTest.java b/src/test/java/com/android/tools/r8/utils/CliParserTest.java new file mode 100644 index 0000000..9bebabd --- /dev/null +++ b/src/test/java/com/android/tools/r8/utils/CliParserTest.java
@@ -0,0 +1,127 @@ +// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.internal.CliParser; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class CliParserTest extends TestBase { + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public CliParserTest(TestParameters parameters) { + parameters.assertNoneRuntime(); + } + + private static class Builder { + String output; + boolean help; + List<String> positionals = new ArrayList<>(); + } + + @Test + public void testOption1WithEquals() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + parser.option1("--output", "<file>", "Output file", (builder, arg) -> builder.output = arg); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"--output=foo.bar"}, builder, errors::add); + + assertTrue(errors.isEmpty()); + assertEquals("foo.bar", builder.output); + } + + @Test + public void testOption1WithSpace() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + parser.option1("--output", "<file>", "Output file", (builder, arg) -> builder.output = arg); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"--output", "foo.bar"}, builder, errors::add); + + assertTrue(errors.isEmpty()); + assertEquals("foo.bar", builder.output); + } + + @Test + public void testOption0() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + parser.option0("--help", "Help", builder -> builder.help = true); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"--help"}, builder, errors::add); + + assertTrue(errors.isEmpty()); + assertTrue(builder.help); + } + + @Test + public void testOption0WithEquals() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + parser.option0("--help", "Help", builder -> builder.help = true); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"--help=true"}, builder, errors::add); + + assertEquals(1, errors.size()); + assertEquals("Option --help does not take a value.", errors.get(0)); + } + + @Test + public void testPositional() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + parser.positional((builder, arg) -> builder.positionals.add(arg)); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"foo", "bar"}, builder, errors::add); + + assertTrue(errors.isEmpty()); + assertEquals(ImmutableList.of("foo", "bar"), builder.positionals); + } + + @Test + public void testUnknownOption() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"--unknown"}, builder, errors::add); + + assertEquals(1, errors.size()); + assertEquals("Unknown option: --unknown", errors.get(0)); + } + + @Test + public void testUnknownOptionWithEquals() { + CliParser<Builder> parser = new CliParser<>("Usage: test"); + + Builder builder = new Builder(); + List<String> errors = new ArrayList<>(); + parser.parse(new String[] {"--unknown=value"}, builder, errors::add); + + assertEquals(1, errors.size()); + assertEquals("Unknown option: --unknown=value", errors.get(0)); + } +}