Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 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.build.lib.syntax; |
| 15 | |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 16 | import com.google.common.base.Joiner; |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 17 | import com.google.common.base.Preconditions; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableList; |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 19 | import com.google.common.collect.ImmutableMap; |
Lukacs Berki | 4833822 | 2015-06-12 11:37:46 +0000 | [diff] [blame] | 20 | import com.google.common.hash.HashCode; |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 21 | import com.google.devtools.build.lib.cmdline.RepositoryName; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 22 | import com.google.devtools.build.lib.events.Event; |
| 23 | import com.google.devtools.build.lib.events.EventHandler; |
| 24 | import com.google.devtools.build.lib.events.Location; |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 25 | import com.google.devtools.build.lib.syntax.Parser.ParseResult; |
| 26 | import com.google.devtools.build.lib.syntax.SkylarkImports.SkylarkImportSyntaxException; |
| 27 | import com.google.devtools.build.lib.util.Pair; |
Carmi Grushko | 46bf88c | 2017-02-20 22:37:15 +0000 | [diff] [blame] | 28 | import com.google.devtools.build.lib.vfs.PathFragment; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 29 | import java.io.IOException; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 30 | import java.util.List; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 31 | import javax.annotation.Nullable; |
| 32 | |
| 33 | /** |
| 34 | * Abstract syntax node for an entire BUILD file. |
| 35 | */ |
brandjon | 540aac6 | 2017-06-12 23:08:09 +0200 | [diff] [blame] | 36 | // TODO(bazel-team): Consider breaking this up into two classes: One that extends ASTNode and does |
| 37 | // not include import info; and one that wraps that object with additional import info but that |
| 38 | // does not itself extend ASTNode. This would help keep the AST minimalistic. |
brandjon | a64b1c4 | 2017-05-15 21:27:33 +0200 | [diff] [blame] | 39 | public class BuildFileAST extends ASTNode { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 40 | |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 41 | private final ImmutableList<Statement> statements; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 42 | |
| 43 | private final ImmutableList<Comment> comments; |
| 44 | |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 45 | @Nullable private final ImmutableList<SkylarkImport> imports; |
Laurent Le Brun | 6190ffb | 2015-07-01 16:24:56 +0000 | [diff] [blame] | 46 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 47 | /** |
| 48 | * Whether any errors were encountered during scanning or parsing. |
| 49 | */ |
| 50 | private final boolean containsErrors; |
| 51 | |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 52 | @Nullable private final String contentHashCode; |
Lukacs Berki | f445ea1 | 2015-07-09 07:16:41 +0000 | [diff] [blame] | 53 | |
Damien Martin-Guillerez | 5e95a46 | 2016-02-05 22:32:08 +0000 | [diff] [blame] | 54 | private BuildFileAST( |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 55 | ImmutableList<Statement> statements, |
Damien Martin-Guillerez | 5e95a46 | 2016-02-05 22:32:08 +0000 | [diff] [blame] | 56 | boolean containsErrors, |
| 57 | String contentHashCode, |
Laurent Le Brun | 8c8857d | 2016-08-04 10:22:16 +0000 | [diff] [blame] | 58 | Location location, |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 59 | ImmutableList<Comment> comments, |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 60 | @Nullable ImmutableList<SkylarkImport> imports) { |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 61 | this.statements = statements; |
Damien Martin-Guillerez | 5e95a46 | 2016-02-05 22:32:08 +0000 | [diff] [blame] | 62 | this.containsErrors = containsErrors; |
| 63 | this.contentHashCode = contentHashCode; |
Laurent Le Brun | 8c8857d | 2016-08-04 10:22:16 +0000 | [diff] [blame] | 64 | this.comments = comments; |
Damien Martin-Guillerez | 5e95a46 | 2016-02-05 22:32:08 +0000 | [diff] [blame] | 65 | this.setLocation(location); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 66 | this.imports = imports; |
| 67 | } |
| 68 | |
| 69 | private static BuildFileAST create( |
| 70 | List<Statement> preludeStatements, |
| 71 | ParseResult result, |
| 72 | String contentHashCode, |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 73 | ImmutableMap<RepositoryName, RepositoryName> repositoryMapping, |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 74 | EventHandler eventHandler) { |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 75 | ImmutableList<Statement> statements = |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 76 | ImmutableList.<Statement>builder() |
| 77 | .addAll(preludeStatements) |
| 78 | .addAll(result.statements) |
| 79 | .build(); |
| 80 | |
| 81 | boolean containsErrors = result.containsErrors; |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 82 | Pair<Boolean, ImmutableList<SkylarkImport>> skylarkImports = |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 83 | fetchLoads(statements, repositoryMapping, eventHandler); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 84 | containsErrors |= skylarkImports.first; |
| 85 | return new BuildFileAST( |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 86 | statements, |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 87 | containsErrors, |
| 88 | contentHashCode, |
| 89 | result.location, |
| 90 | ImmutableList.copyOf(result.comments), |
| 91 | skylarkImports.second); |
Damien Martin-Guillerez | 5e95a46 | 2016-02-05 22:32:08 +0000 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Extract a subtree containing only statements from {@code firstStatement} (included) up to |
| 96 | * {@code lastStatement} excluded. |
| 97 | */ |
| 98 | public BuildFileAST subTree(int firstStatement, int lastStatement) { |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 99 | ImmutableList<Statement> statements = this.statements.subList(firstStatement, lastStatement); |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 100 | ImmutableList.Builder<SkylarkImport> imports = ImmutableList.builder(); |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 101 | for (Statement stmt : statements) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 102 | if (stmt instanceof LoadStatement) { |
Laurent Le Brun | 7b1708c | 2016-10-13 10:05:12 +0000 | [diff] [blame] | 103 | String str = ((LoadStatement) stmt).getImport().getValue(); |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 104 | try { |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 105 | imports.add(SkylarkImports.create(str, /* repositoryMapping= */ ImmutableMap.of())); |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 106 | } catch (SkylarkImportSyntaxException e) { |
| 107 | throw new IllegalStateException( |
dmaclach | 6a4b151 | 2018-09-04 08:51:37 -0700 | [diff] [blame] | 108 | "Cannot create SkylarkImport for '" + str + "'. This is an internal error."); |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 109 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 110 | } |
| 111 | } |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 112 | return new BuildFileAST( |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 113 | statements, |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 114 | containsErrors, |
| 115 | null, |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 116 | this.statements.get(firstStatement).getLocation(), |
| 117 | ImmutableList.of(), |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 118 | imports.build()); |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Collects all load statements. Returns a pair with a boolean saying if there were errors and the |
| 123 | * imports that could be resolved. |
| 124 | */ |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 125 | private static Pair<Boolean, ImmutableList<SkylarkImport>> fetchLoads( |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 126 | List<Statement> statements, |
| 127 | ImmutableMap<RepositoryName, RepositoryName> repositoryMapping, |
| 128 | EventHandler eventHandler) { |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 129 | ImmutableList.Builder<SkylarkImport> imports = ImmutableList.builder(); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 130 | boolean error = false; |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 131 | for (Statement stmt : statements) { |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 132 | if (stmt instanceof LoadStatement) { |
Laurent Le Brun | 7b1708c | 2016-10-13 10:05:12 +0000 | [diff] [blame] | 133 | String importString = ((LoadStatement) stmt).getImport().getValue(); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 134 | try { |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 135 | imports.add(SkylarkImports.create(importString, repositoryMapping)); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 136 | } catch (SkylarkImportSyntaxException e) { |
| 137 | eventHandler.handle(Event.error(stmt.getLocation(), e.getMessage())); |
| 138 | error = true; |
| 139 | } |
| 140 | } |
| 141 | } |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 142 | return Pair.of(error, imports.build()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Returns true if any errors were encountered during scanning or parsing. If |
| 147 | * set, clients should not rely on the correctness of the AST for builds or |
| 148 | * BUILD-file editing. |
| 149 | */ |
| 150 | public boolean containsErrors() { |
| 151 | return containsErrors; |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Returns an (immutable, ordered) list of statements in this BUILD file. |
| 156 | */ |
| 157 | public ImmutableList<Statement> getStatements() { |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 158 | return statements; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Returns an (immutable, ordered) list of comments in this BUILD file. |
| 163 | */ |
| 164 | public ImmutableList<Comment> getComments() { |
| 165 | return comments; |
| 166 | } |
| 167 | |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 168 | /** Returns a list of loads in this BUILD file. */ |
| 169 | public ImmutableList<SkylarkImport> getImports() { |
| 170 | Preconditions.checkNotNull(imports, "computeImports Should be called in parse* methods"); |
Laurent Le Brun | 7c3d668 | 2016-10-13 14:55:19 +0000 | [diff] [blame] | 171 | return imports; |
Laurent Le Brun | 6190ffb | 2015-07-01 16:24:56 +0000 | [diff] [blame] | 172 | } |
| 173 | |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 174 | /** Returns a list of loads as strings in this BUILD file. */ |
Laurent Le Brun | 7b1708c | 2016-10-13 10:05:12 +0000 | [diff] [blame] | 175 | public ImmutableList<StringLiteral> getRawImports() { |
| 176 | ImmutableList.Builder<StringLiteral> imports = ImmutableList.builder(); |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 177 | for (Statement stmt : statements) { |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 178 | if (stmt instanceof LoadStatement) { |
| 179 | imports.add(((LoadStatement) stmt).getImport()); |
| 180 | } |
| 181 | } |
| 182 | return imports.build(); |
| 183 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 184 | /** |
| 185 | * Executes this build file in a given Environment. |
| 186 | * |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 187 | * <p>If, for any reason, execution of a statement cannot be completed, an {@link EvalException} |
laurentlb | 919d1b7 | 2017-09-01 20:22:11 +0200 | [diff] [blame] | 188 | * is thrown by {@link Eval#exec(Statement)}. This exception is caught here and reported |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 189 | * through reporter and execution continues on the next statement. In effect, there is a |
| 190 | * "try/except" block around every top level statement. Such exceptions are not ignored, though: |
| 191 | * they are visible via the return value. Rules declared in a package containing any error |
| 192 | * (including loading-phase semantical errors that cannot be checked here) must also be considered |
| 193 | * "in error". |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 194 | * |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 195 | * <p>Note that this method will not affect the value of {@link #containsErrors()}; that refers |
| 196 | * only to lexer/parser errors. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 197 | * |
| 198 | * @return true if no error occurred during execution. |
| 199 | */ |
| 200 | public boolean exec(Environment env, EventHandler eventHandler) throws InterruptedException { |
| 201 | boolean ok = true; |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 202 | for (Statement stmt : statements) { |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 203 | if (!execTopLevelStatement(stmt, env, eventHandler)) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 204 | ok = false; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 205 | } |
| 206 | } |
| 207 | return ok; |
| 208 | } |
| 209 | |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 210 | /** |
| 211 | * Executes tol-level statement of this build file in a given Environment. |
| 212 | * |
| 213 | * <p>If, for any reason, execution of a statement cannot be completed, an {@link EvalException} |
laurentlb | 919d1b7 | 2017-09-01 20:22:11 +0200 | [diff] [blame] | 214 | * is thrown by {@link Eval#exec(Statement)}. This exception is caught here and reported |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 215 | * through reporter. In effect, there is a |
| 216 | * "try/except" block around every top level statement. Such exceptions are not ignored, though: |
| 217 | * they are visible via the return value. Rules declared in a package containing any error |
| 218 | * (including loading-phase semantical errors that cannot be checked here) must also be considered |
| 219 | * "in error". |
| 220 | * |
| 221 | * <p>Note that this method will not affect the value of {@link #containsErrors()}; that refers |
| 222 | * only to lexer/parser errors. |
| 223 | * |
| 224 | * @return true if no error occurred during execution. |
| 225 | */ |
| 226 | |
| 227 | public boolean execTopLevelStatement(Statement stmt, Environment env, |
| 228 | EventHandler eventHandler) throws InterruptedException { |
| 229 | try { |
Googler | 29eafdf | 2018-05-23 12:32:07 -0700 | [diff] [blame] | 230 | Eval.fromEnvironment(env).exec(stmt); |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 231 | return true; |
| 232 | } catch (EvalException e) { |
| 233 | // Do not report errors caused by a previous parsing error, as it has already been |
| 234 | // reported. |
| 235 | if (e.isDueToIncompleteAST()) { |
| 236 | return false; |
| 237 | } |
| 238 | // When the exception is raised from another file, report first the location in the |
| 239 | // BUILD file (as it is the most probable cause for the error). |
| 240 | Location exnLoc = e.getLocation(); |
| 241 | Location nodeLoc = stmt.getLocation(); |
| 242 | eventHandler.handle(Event.error( |
| 243 | (exnLoc == null || !nodeLoc.getPath().equals(exnLoc.getPath())) ? nodeLoc : exnLoc, |
| 244 | e.getMessage())); |
| 245 | return false; |
| 246 | } |
| 247 | } |
| 248 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 249 | @Override |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 250 | public void prettyPrint(Appendable buffer, int indentLevel) throws IOException { |
| 251 | // Only statements are printed, not comments and processed import data. |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 252 | for (Statement stmt : statements) { |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 253 | stmt.prettyPrint(buffer, indentLevel); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | @Override |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 258 | public String toString() { |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 259 | return "<BuildFileAST with " + statements.size() + " statements>"; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | @Override |
| 263 | public void accept(SyntaxTreeVisitor visitor) { |
| 264 | visitor.visit(this); |
| 265 | } |
| 266 | |
| 267 | /** |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 268 | * Parse the specified build file, returning its AST. All errors during scanning or parsing will |
| 269 | * be reported to the reporter. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 270 | */ |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 271 | public static BuildFileAST parseBuildFile( |
| 272 | ParserInputSource input, |
| 273 | List<Statement> preludeStatements, |
| 274 | ImmutableMap<RepositoryName, RepositoryName> repositoryMapping, |
| 275 | EventHandler eventHandler) { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 276 | Parser.ParseResult result = Parser.parseFile(input, eventHandler); |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 277 | return create( |
| 278 | preludeStatements, result, /* contentHashCode= */ null, repositoryMapping, eventHandler) |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 279 | .validateBuildFile(eventHandler); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 280 | } |
| 281 | |
Laurent Le Brun | f1112b3 | 2016-08-03 13:16:02 +0000 | [diff] [blame] | 282 | public static BuildFileAST parseBuildFile(ParserInputSource input, EventHandler eventHandler) { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 283 | Parser.ParseResult result = Parser.parseFile(input, eventHandler); |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 284 | return create( |
| 285 | /* preludeStatements= */ ImmutableList.<Statement>of(), |
| 286 | result, |
| 287 | /* contentHashCode= */ null, |
| 288 | /* repositoryMapping= */ ImmutableMap.of(), |
| 289 | eventHandler) |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 290 | .validateBuildFile(eventHandler); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 291 | } |
| 292 | |
tomlu | 67c84b1 | 2017-11-06 19:49:16 +0100 | [diff] [blame] | 293 | public static BuildFileAST parseSkylarkFile( |
| 294 | byte[] bytes, byte[] digest, PathFragment path, EventHandler eventHandler) |
Laurent Le Brun | 8c8857d | 2016-08-04 10:22:16 +0000 | [diff] [blame] | 295 | throws IOException { |
tomlu | 67c84b1 | 2017-11-06 19:49:16 +0100 | [diff] [blame] | 296 | ParserInputSource input = ParserInputSource.create(bytes, path); |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 297 | Parser.ParseResult result = Parser.parseFile(input, eventHandler); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 298 | return create( |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 299 | /* preludeStatements= */ ImmutableList.of(), |
| 300 | result, |
| 301 | HashCode.fromBytes(digest).toString(), |
| 302 | /* repositoryMapping= */ ImmutableMap.of(), |
| 303 | eventHandler); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 304 | } |
| 305 | |
laurentlb | 04b4c66 | 2017-08-07 20:03:40 +0200 | [diff] [blame] | 306 | public static BuildFileAST parseSkylarkFile(ParserInputSource input, EventHandler eventHandler) { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 307 | Parser.ParseResult result = Parser.parseFile(input, eventHandler); |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 308 | return create( |
| 309 | /* preludeStatements= */ ImmutableList.<Statement>of(), |
| 310 | result, |
| 311 | /* contentHashCode= */ null, |
| 312 | /* repositoryMapping= */ ImmutableMap.of(), |
| 313 | eventHandler); |
laurentlb | 04b4c66 | 2017-08-07 20:03:40 +0200 | [diff] [blame] | 314 | } |
| 315 | |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 316 | /** |
| 317 | * Parse the specified non-build Skylark file but avoid the validation of the imports, returning |
| 318 | * its AST. All errors during scanning or parsing will be reported to the reporter. |
| 319 | * |
| 320 | * <p>This method should not be used in Bazel code, since it doesn't validate that the imports are |
| 321 | * syntactically valid. |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 322 | */ |
| 323 | public static BuildFileAST parseSkylarkFileWithoutImports( |
brandjon | 540aac6 | 2017-06-12 23:08:09 +0200 | [diff] [blame] | 324 | ParserInputSource input, EventHandler eventHandler) { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 325 | ParseResult result = Parser.parseFile(input, eventHandler); |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 326 | return new BuildFileAST( |
| 327 | ImmutableList.<Statement>builder() |
| 328 | .addAll(ImmutableList.<Statement>of()) |
| 329 | .addAll(result.statements) |
| 330 | .build(), |
brandjon | 540aac6 | 2017-06-12 23:08:09 +0200 | [diff] [blame] | 331 | result.containsErrors, |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 332 | /* contentHashCode= */null, |
Miguel Alcon Pinto | 927f3b2 | 2016-08-22 14:21:30 +0000 | [diff] [blame] | 333 | result.location, |
brandjon | 540aac6 | 2017-06-12 23:08:09 +0200 | [diff] [blame] | 334 | ImmutableList.copyOf(result.comments), |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 335 | /* imports= */null); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 336 | } |
| 337 | |
Laurent Le Brun | 8c8857d | 2016-08-04 10:22:16 +0000 | [diff] [blame] | 338 | /** |
| 339 | * Run static checks on the AST. |
| 340 | * |
| 341 | * @return a new AST (or the same), with the containsErrors flag updated. |
| 342 | */ |
laurentlb | bde7c41 | 2017-06-12 15:22:37 +0200 | [diff] [blame] | 343 | public BuildFileAST validate(Environment env, EventHandler eventHandler) { |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 344 | boolean valid = ValidationEnvironment.validateAst(env, statements, eventHandler); |
Laurent Le Brun | 8c8857d | 2016-08-04 10:22:16 +0000 | [diff] [blame] | 345 | if (valid || containsErrors) { |
| 346 | return this; |
| 347 | } |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 348 | return new BuildFileAST(statements, true, contentHashCode, getLocation(), comments, imports); |
Laurent Le Brun | 8c8857d | 2016-08-04 10:22:16 +0000 | [diff] [blame] | 349 | } |
| 350 | |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 351 | /** |
| 352 | * Run static checks for a BUILD file. |
| 353 | * |
| 354 | * @return a new AST (or the same), with the containsErrors flag updated. |
| 355 | */ |
| 356 | public BuildFileAST validateBuildFile(EventHandler eventHandler) { |
| 357 | boolean valid = ValidationEnvironment.checkBuildSyntax(statements, eventHandler); |
| 358 | if (valid || containsErrors) { |
| 359 | return this; |
| 360 | } |
| 361 | return new BuildFileAST(statements, true, contentHashCode, getLocation(), comments, imports); |
| 362 | } |
| 363 | |
| 364 | public static BuildFileAST parseString(EventHandler eventHandler, String... content) { |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 365 | String str = Joiner.on("\n").join(content); |
Carmi Grushko | 46bf88c | 2017-02-20 22:37:15 +0000 | [diff] [blame] | 366 | ParserInputSource input = ParserInputSource.create(str, PathFragment.EMPTY_FRAGMENT); |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 367 | Parser.ParseResult result = Parser.parseFile(input, eventHandler); |
dannark | f4b9ff4 | 2018-06-12 09:28:45 -0700 | [diff] [blame] | 368 | return create( |
| 369 | /* preludeStatements= */ ImmutableList.of(), |
| 370 | result, |
| 371 | /* contentHashCode= */ null, |
| 372 | /* repositoryMapping= */ ImmutableMap.of(), |
| 373 | eventHandler); |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 374 | } |
| 375 | |
brandjon | 733a97d | 2017-06-27 17:11:27 +0200 | [diff] [blame] | 376 | public static BuildFileAST parseBuildString(EventHandler eventHandler, String... content) { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 377 | return parseString(eventHandler, content).validateBuildFile(eventHandler); |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 378 | } |
| 379 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 380 | /** |
| 381 | * Parse the specified build file, without building the AST. |
| 382 | * |
| 383 | * @return true if the input file is syntactically valid |
| 384 | */ |
Laurent Le Brun | b566c7d | 2016-10-07 16:31:03 +0000 | [diff] [blame] | 385 | public static boolean checkSyntax(ParserInputSource input, EventHandler eventHandler) { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 386 | Parser.ParseResult result = Parser.parseFile(input, eventHandler); |
Laurent Le Brun | f1112b3 | 2016-08-03 13:16:02 +0000 | [diff] [blame] | 387 | return !result.containsErrors; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 388 | } |
| 389 | |
| 390 | /** |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 391 | * Evaluates the code and return the value of the last statement if it's an |
| 392 | * Expression or else null. |
| 393 | */ |
| 394 | @Nullable public Object eval(Environment env) throws EvalException, InterruptedException { |
| 395 | Object last = null; |
Googler | 29eafdf | 2018-05-23 12:32:07 -0700 | [diff] [blame] | 396 | Eval evaluator = Eval.fromEnvironment(env); |
brandjon | 990622b | 2017-07-11 19:56:45 +0200 | [diff] [blame] | 397 | for (Statement statement : statements) { |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 398 | if (statement instanceof ExpressionStatement) { |
| 399 | last = ((ExpressionStatement) statement).getExpression().eval(env); |
| 400 | } else { |
laurentlb | 919d1b7 | 2017-09-01 20:22:11 +0200 | [diff] [blame] | 401 | evaluator.exec(statement); |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 402 | last = null; |
| 403 | } |
| 404 | } |
| 405 | return last; |
| 406 | } |
| 407 | |
Laurent Le Brun | d4d7fca | 2017-02-14 19:12:02 +0000 | [diff] [blame] | 408 | /** |
| 409 | * Evaluates the lines from input and return the value of the last statement if it's an |
| 410 | * Expression or else null. In case of error (either during validation or evaluation), it |
| 411 | * throws an EvalException. |
| 412 | */ |
| 413 | @Nullable |
Laurent Le Brun | a6d8fe4 | 2016-11-28 18:14:54 +0000 | [diff] [blame] | 414 | public static Object eval(Environment env, String... input) |
| 415 | throws EvalException, InterruptedException { |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 416 | BuildFileAST ast = parseAndValidateSkylarkString(env, input); |
| 417 | return ast.eval(env); |
| 418 | } |
| 419 | |
| 420 | /** |
| 421 | * Parses and validates the lines from input and return the the AST |
| 422 | * In case of error during validation, it throws an EvalException. |
| 423 | */ |
| 424 | public static BuildFileAST parseAndValidateSkylarkString(Environment env, String[] input) |
| 425 | throws EvalException { |
laurentlb | 17d975e | 2017-09-01 17:49:23 +0200 | [diff] [blame] | 426 | BuildFileAST ast = parseString(env.getEventHandler(), input); |
laurentlb | bde7c41 | 2017-06-12 15:22:37 +0200 | [diff] [blame] | 427 | ValidationEnvironment.validateAst(env, ast.getStatements()); |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 428 | return ast; |
Laurent Le Brun | a6d8fe4 | 2016-11-28 18:14:54 +0000 | [diff] [blame] | 429 | } |
| 430 | |
Laurent Le Brun | 8e965b8 | 2016-08-03 11:50:24 +0000 | [diff] [blame] | 431 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 432 | * Returns a hash code calculated from the string content of the source file of this AST. |
| 433 | */ |
| 434 | @Nullable public String getContentHashCode() { |
| 435 | return contentHashCode; |
| 436 | } |
| 437 | } |