blob: e76ead198466d2b9714948b79af476042de81b64 [file] [log] [blame]
/*
* 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;
}
}
}