| /* |
| * 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.idea.blaze.base.sync.aspects; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableMap; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * Parses any compiler options that were not extracted by the build system earlier. Do minimal |
| * parsing to extract what we need. |
| */ |
| final class UnfilteredCompilerOptions { |
| |
| static class Builder { |
| private final BaseOptionParser baseOptionParser = new BaseOptionParser(); |
| private final ImmutableMap.Builder<String, OptionParser> registeredParsers = |
| ImmutableMap.builder(); |
| |
| /** Have the options parser handle the given one-or-two-token option (e.g., -Ifoo or -I foo). */ |
| Builder registerSingleOrSplitOption(String optionName) { |
| registeredParsers.put( |
| optionName, new SingleOrSplitOptionParser(optionName, baseOptionParser)); |
| return this; |
| } |
| |
| /** Parse the given options and build extracted compiler options */ |
| UnfilteredCompilerOptions build(Iterable<String> unfilteredOptions) { |
| ImmutableMap<String, OptionParser> registered = registeredParsers.build(); |
| baseOptionParser.setRegisteredOptionParsers(registered.values()); |
| UnfilteredCompilerOptions options = |
| new UnfilteredCompilerOptions(baseOptionParser, registered); |
| options.parse(unfilteredOptions); |
| return options; |
| } |
| } |
| |
| /** Make a new builder to register options to extract. */ |
| static Builder builder() { |
| return new Builder(); |
| } |
| |
| private final OptionParser baseOptionParser; |
| private final ImmutableMap<String, OptionParser> registeredParsers; |
| |
| private UnfilteredCompilerOptions( |
| OptionParser baseOptionParser, ImmutableMap<String, OptionParser> registeredParsers) { |
| this.baseOptionParser = baseOptionParser; |
| this.registeredParsers = registeredParsers; |
| } |
| |
| private void parse(Iterable<String> unfilteredOptions) { |
| OptionParser nextOptionParser = baseOptionParser; |
| for (String unfilteredOption : unfilteredOptions) { |
| nextOptionParser = nextOptionParser.parseValue(unfilteredOption); |
| } |
| } |
| |
| /** |
| * Return the list of arguments that are not extracted (don't correspond to a registered option), |
| * in the original order. |
| */ |
| List<String> getUninterpretedOptions() { |
| return baseOptionParser.values(); |
| } |
| |
| /** |
| * Return the extracted option values for the given registered option name. E.g., if -I is |
| * registered, and ["-foo", "-Ibar"] is parsed then getExtractedOptionValues("-I") returns |
| * ["bar"]. List is in the original order. |
| * |
| * @param optionName the name of a flag that was registered to be extracted |
| * @return option values corresponding to the flag. |
| */ |
| List<String> getExtractedOptionValues(String optionName) { |
| OptionParser parser = registeredParsers.get(optionName); |
| Preconditions.checkNotNull(parser); |
| return parser.values(); |
| } |
| |
| private interface OptionParser { |
| /** Checks if the parser handles the next option value. */ |
| boolean handlesOptionValue(String optionValue); |
| |
| /** |
| * Parses the option and returns the next handler (assumes {@link #handlesOptionValue} is true). |
| */ |
| OptionParser parseValue(String optionValue); |
| |
| /** Return a list of option values captured by the parser. */ |
| List<String> values(); |
| } |
| |
| /** |
| * A base option parser that defers to a list of more-specific registered flag parsers, before |
| * handling the flag itself. |
| */ |
| private static class BaseOptionParser implements OptionParser { |
| private final List<String> values = new ArrayList<>(); |
| private Collection<OptionParser> registeredOptionParsers; |
| |
| void setRegisteredOptionParsers(Collection<OptionParser> registeredOptionParsers) { |
| this.registeredOptionParsers = registeredOptionParsers; |
| } |
| |
| @Override |
| public boolean handlesOptionValue(String optionValue) { |
| return true; |
| } |
| |
| @Override |
| public OptionParser parseValue(String optionValue) { |
| for (OptionParser registeredParser : registeredOptionParsers) { |
| if (registeredParser.handlesOptionValue(optionValue)) { |
| return registeredParser.parseValue(optionValue); |
| } |
| } |
| values.add(optionValue); |
| return this; |
| } |
| |
| @Override |
| public List<String> values() { |
| return values; |
| } |
| } |
| |
| /** |
| * A parser that handles flags that can be one or two tokens (e.g., "-Ihdrs", vs "-I", "hdrs"). |
| */ |
| private static class SingleOrSplitOptionParser implements OptionParser { |
| private final String optionName; |
| private final BaseOptionParser baseOptionParser; |
| private final List<String> values = new ArrayList<>(); |
| private boolean consumeNext; |
| |
| SingleOrSplitOptionParser(String optionName, BaseOptionParser baseOptionParser) { |
| this.optionName = optionName; |
| this.baseOptionParser = baseOptionParser; |
| } |
| |
| @Override |
| public boolean handlesOptionValue(String optionValue) { |
| return consumeNext || optionValue.startsWith(optionName); |
| } |
| |
| @Override |
| public OptionParser parseValue(String optionValue) { |
| if (consumeNext) { |
| consumeNext = false; |
| values.add(optionValue); |
| return baseOptionParser; |
| } |
| if (optionValue.equals(optionName)) { |
| consumeNext = true; |
| return this; |
| } |
| if (optionValue.startsWith(optionName)) { |
| values.add(optionValue.substring(optionName.length())); |
| return baseOptionParser; |
| } |
| Preconditions.checkState( |
| false, "Should check handlesOptionValue before attempting to parseValue"); |
| return null; |
| } |
| |
| @Override |
| public List<String> values() { |
| return values; |
| } |
| } |
| } |