1 // Copyright 2017 The Bazel Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 package com.google.devtools.common.options;
15 
16 import java.io.IOException;
17 import java.nio.file.FileSystem;
18 import java.nio.file.Path;
19 import java.util.List;
20 
21 /**
22  * Defines an {@link ArgsPreProcessor} that will determine if the arguments list contains a "params"
23  * file that contains a list of options to be parsed.
24  *
25  * <p>Params files are used when the argument list of {@link Option} exceed the shells commandline
26  * length. A params file argument is defined as a path starting with @. It will also be the only
27  * entry in an argument list.
28  */
29 public abstract class ParamsFilePreProcessor implements ArgsPreProcessor {
30 
31   static final String ERROR_MESSAGE_FORMAT = "Error reading params file: %s %s";
32 
33   static final String TOO_MANY_ARGS_ERROR_MESSAGE_FORMAT =
34       "A params file must be the only argument: %s";
35 
36   static final String UNFINISHED_QUOTE_MESSAGE_FORMAT = "Unfinished quote %s at %s";
37 
38   private final FileSystem fs;
39 
ParamsFilePreProcessor(FileSystem fs)40   ParamsFilePreProcessor(FileSystem fs) {
41     this.fs = fs;
42   }
43 
44   /**
45    * Parses the param file path and replaces the arguments list with the contents if one exists.
46    *
47    * @param args A list of arguments that may contain @&lt;path&gt; to a params file.
48    * @return A list of arguments suitable for parsing.
49    * @throws OptionsParsingException if the path does not exist.
50    */
51   @Override
preProcess(List<String> args)52   public List<String> preProcess(List<String> args) throws OptionsParsingException {
53     if (!args.isEmpty() && args.get(0).startsWith("@")) {
54       if (args.size() > 1) {
55         throw new OptionsParsingException(
56             String.format(TOO_MANY_ARGS_ERROR_MESSAGE_FORMAT, args), args.get(0));
57       }
58       Path path = fs.getPath(args.get(0).substring(1));
59       try {
60         return parse(path);
61       } catch (RuntimeException | IOException e) {
62         throw new OptionsParsingException(
63             String.format(ERROR_MESSAGE_FORMAT, path, e.getMessage()), args.get(0), e);
64       }
65     }
66     return args;
67   }
68 
69   /**
70    * Parses the paramsFile and returns a list of argument tokens to be further processed by the
71    * {@link OptionsParser}.
72    *
73    * @param paramsFile The path of the params file to parse.
74    * @return a list of argument tokens.
75    * @throws IOException if there is an error reading paramsFile.
76    * @throws OptionsParsingException if there is an error reading paramsFile.
77    */
parse(Path paramsFile)78   protected abstract List<String> parse(Path paramsFile)
79       throws IOException, OptionsParsingException;
80 }
81