| // Copyright 2014 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.devtools.build.singlejar; |
| |
| import static java.nio.charset.StandardCharsets.ISO_8859_1; |
| |
| import com.google.devtools.build.lib.shell.ShellUtils; |
| import com.google.devtools.build.lib.shell.ShellUtils.TokenizationException; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.annotation.concurrent.Immutable; |
| |
| /** |
| * A utility class to parse option files and expand them. |
| */ |
| @Immutable |
| final class OptionFileExpander { |
| |
| /** |
| * An interface that allows injecting different implementations for reading |
| * files. This is mostly used for testing. |
| */ |
| interface OptionFileProvider { |
| |
| /** |
| * Opens a file for reading and returns an input stream. |
| */ |
| InputStream getInputStream(String filename) throws IOException; |
| } |
| |
| private final OptionFileProvider fileSystem; |
| |
| /** |
| * Creates an instance with the given option file provider. |
| */ |
| public OptionFileExpander(OptionFileProvider fileSystem) { |
| this.fileSystem = fileSystem; |
| } |
| |
| /** |
| * Pre-processes an argument list, expanding options of the form &at;filename |
| * to read in the content of the file and add it to the list of arguments. |
| * |
| * @param args the List of arguments to pre-process. |
| * @return the List of pre-processed arguments. |
| * @throws IOException if one of the files containing options cannot be read. |
| */ |
| public List<String> expandArguments(List<String> args) throws IOException { |
| List<String> expanded = new ArrayList<>(args.size()); |
| for (String arg : args) { |
| expandArgument(arg, expanded); |
| } |
| return expanded; |
| } |
| |
| /** |
| * Expands a single argument, expanding options &at;filename to read in |
| * the content of the file and add it to the list of processed arguments. |
| * |
| * @param arg the argument to pre-process. |
| * @param expanded the List of pre-processed arguments. |
| * @throws IOException if one of the files containing options cannot be read. |
| */ |
| private void expandArgument(String arg, List<String> expanded) throws IOException { |
| if (arg.startsWith("@")) { |
| InputStream in = fileSystem.getInputStream(arg.substring(1)); |
| try { |
| // TODO(bazel-team): This code doesn't handle escaped newlines correctly. |
| // ShellUtils doesn't support them either. |
| for (String line : readAllLines(new InputStreamReader(in, ISO_8859_1))) { |
| List<String> parsedTokens = new ArrayList<>(); |
| try { |
| ShellUtils.tokenize(parsedTokens, line); |
| } catch (TokenizationException e) { |
| throw new IOException("Could not tokenize parameter file!", e); |
| } |
| for (String token : parsedTokens) { |
| expandArgument(token, expanded); |
| } |
| } |
| InputStream inToClose = in; |
| in = null; |
| inToClose.close(); |
| } finally { |
| if (in != null) { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| // Ignore the exception. It can only occur if an exception already |
| // happened and in that case, we want to preserve the original one. |
| } |
| } |
| } |
| } else { |
| expanded.add(arg); |
| } |
| } |
| |
| private List<String> readAllLines(Reader in) throws IOException { |
| List<String> result = new ArrayList<>(); |
| BufferedReader reader = new BufferedReader(in); |
| String line; |
| while ((line = reader.readLine()) != null) { |
| result.add(line); |
| } |
| return result; |
| } |
| } |