Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(628)

Side by Side Diff: pylib/gyp/input.py

Issue 202059: Add a --check flag. This causes gyp to check syntax of files more carefully,... (Closed) Base URL: http://gyp.googlecode.com/svn/trunk/
Patch Set: '' Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pylib/gyp/__init__.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 from compiler.ast import Const
4 from compiler.ast import Dict
5 from compiler.ast import Discard
6 from compiler.ast import List
7 from compiler.ast import Module
8 from compiler.ast import Node
9 from compiler.ast import Stmt
10 import compiler
3 import copy 11 import copy
4 import gyp.common 12 import gyp.common
5 import optparse 13 import optparse
6 import os.path 14 import os.path
7 import re 15 import re
8 import shlex 16 import shlex
9 import subprocess 17 import subprocess
10 import sys 18 import sys
11 19
12 20
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
100 if build_file_path in included: 108 if build_file_path in included:
101 return included 109 return included
102 110
103 included.append(build_file_path) 111 included.append(build_file_path)
104 112
105 for included_build_file in aux_data[build_file_path].get('included', []): 113 for included_build_file in aux_data[build_file_path].get('included', []):
106 GetIncludedBuildFiles(included_build_file, aux_data, included) 114 GetIncludedBuildFiles(included_build_file, aux_data, included)
107 115
108 return included 116 return included
109 117
118 def CheckedEval(file_contents):
119 """Return the eval of a gyp file.
120
121 The gyp file is restricted to dictionaries and lists only, and
122 repeated keys are not allowed.
123
124 Note that this is slower than eval() is.
125 """
126
127 ast = compiler.parse(file_contents)
128 assert isinstance(ast, Module)
129 c1 = ast.getChildren()
130 assert c1[0] is None
131 assert isinstance(c1[1], Stmt)
132 c2 = c1[1].getChildren()
133 assert isinstance(c2[0], Discard)
134 c3 = c2[0].getChildren()
135 assert len(c3) == 1
136 return CheckNode(c3[0],0)
137
138 def CheckNode(node, level):
139 if isinstance(node, Dict):
140 c = node.getChildren()
141 dict = {}
142 for n in range(0, len(c), 2):
143 assert isinstance(c[n], Const)
144 key = c[n].getChildren()[0]
145 if key in dict:
146 raise KeyError, "Key '" + key + "' repeated at level " \
147 + repr(level)
148 dict[key] = CheckNode(c[n + 1], level + 1)
149 return dict
150 elif isinstance(node, List):
151 c = node.getChildren()
152 list = []
153 for child in c:
154 list.append(CheckNode(child, level + 1))
155 return list
156 elif isinstance(node, Const):
157 return node.getChildren()[0]
158 else:
159 raise TypeError, "Unknown AST node " + repr(node)
110 160
111 def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes, 161 def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
112 is_target): 162 is_target, check):
113 if build_file_path in data: 163 if build_file_path in data:
114 return data[build_file_path] 164 return data[build_file_path]
115 165
116 if os.path.exists(build_file_path): 166 if os.path.exists(build_file_path):
117 build_file_contents = open(build_file_path).read() 167 build_file_contents = open(build_file_path).read()
118 else: 168 else:
119 raise Exception("%s not found (cwd: %s)" % (build_file_path, os.getcwd())) 169 raise Exception("%s not found (cwd: %s)" % (build_file_path, os.getcwd()))
120 170
121 build_file_data = None 171 build_file_data = None
122 try: 172 try:
123 build_file_data = eval(build_file_contents, {'__builtins__': None}, None) 173 if check:
174 build_file_data = CheckedEval(build_file_contents)
175 else:
176 build_file_data = eval(build_file_contents, {'__builtins__': None},
177 None)
124 except SyntaxError, e: 178 except SyntaxError, e:
125 e.filename = build_file_path 179 e.filename = build_file_path
126 raise 180 raise
127 except Exception, e: 181 except Exception, e:
128 gyp.common.ExceptionAppend(e, 'while reading ' + build_file_path) 182 gyp.common.ExceptionAppend(e, 'while reading ' + build_file_path)
129 raise 183 raise
130 184
131 data[build_file_path] = build_file_data 185 data[build_file_path] = build_file_data
132 aux_data[build_file_path] = {} 186 aux_data[build_file_path] = {}
133 187
134 # Scan for includes and merge them in. 188 # Scan for includes and merge them in.
135 try: 189 try:
136 if is_target: 190 if is_target:
137 LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data, 191 LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data,
138 aux_data, variables, includes) 192 aux_data, variables, includes, check)
139 else: 193 else:
140 LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data, 194 LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data,
141 aux_data, variables, None) 195 aux_data, variables, None, check)
142 except Exception, e: 196 except Exception, e:
143 gyp.common.ExceptionAppend(e, 197 gyp.common.ExceptionAppend(e,
144 'while reading includes of ' + build_file_path) 198 'while reading includes of ' + build_file_path)
145 raise 199 raise
146 200
147 return build_file_data 201 return build_file_data
148 202
149 203
150 def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data, 204 def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data,
151 variables, includes): 205 variables, includes, check):
152 includes_list = [] 206 includes_list = []
153 if includes != None: 207 if includes != None:
154 includes_list.extend(includes) 208 includes_list.extend(includes)
155 if 'includes' in subdict: 209 if 'includes' in subdict:
156 for include in subdict['includes']: 210 for include in subdict['includes']:
157 # "include" is specified relative to subdict_path, so compute the real 211 # "include" is specified relative to subdict_path, so compute the real
158 # path to include by appending the provided "include" to the directory 212 # path to include by appending the provided "include" to the directory
159 # in which subdict_path resides. 213 # in which subdict_path resides.
160 relative_include = \ 214 relative_include = \
161 os.path.normpath(os.path.join(os.path.dirname(subdict_path), include)) 215 os.path.normpath(os.path.join(os.path.dirname(subdict_path), include))
162 includes_list.append(relative_include) 216 includes_list.append(relative_include)
163 # Unhook the includes list, it's no longer needed. 217 # Unhook the includes list, it's no longer needed.
164 del subdict['includes'] 218 del subdict['includes']
165 219
166 # Merge in the included files. 220 # Merge in the included files.
167 for include in includes_list: 221 for include in includes_list:
168 if not 'included' in aux_data[subdict_path]: 222 if not 'included' in aux_data[subdict_path]:
169 aux_data[subdict_path]['included'] = [] 223 aux_data[subdict_path]['included'] = []
170 aux_data[subdict_path]['included'].append(include) 224 aux_data[subdict_path]['included'].append(include)
171 MergeDicts(subdict, 225 MergeDicts(subdict,
172 LoadOneBuildFile(include, data, aux_data, variables, None, 226 LoadOneBuildFile(include, data, aux_data, variables, None,
173 False), 227 False, check),
174 subdict_path, include) 228 subdict_path, include)
175 229
176 # Recurse into subdictionaries. 230 # Recurse into subdictionaries.
177 for k, v in subdict.iteritems(): 231 for k, v in subdict.iteritems():
178 if v.__class__ == dict: 232 if v.__class__ == dict:
179 LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data, variables, 233 LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data, variables,
180 None) 234 None, check)
181 elif v.__class__ == list: 235 elif v.__class__ == list:
182 LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data, variables) 236 LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data, variables,
237 check)
183 238
184 239
185 # This recurses into lists so that it can look for dicts. 240 # This recurses into lists so that it can look for dicts.
186 def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data, 241 def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data,
187 variables): 242 variables, check):
188 for item in sublist: 243 for item in sublist:
189 if item.__class__ == dict: 244 if item.__class__ == dict:
190 LoadBuildFileIncludesIntoDict(item, sublist_path, data, aux_data, 245 LoadBuildFileIncludesIntoDict(item, sublist_path, data, aux_data,
191 variables, None) 246 variables, None, check)
192 elif item.__class__ == list: 247 elif item.__class__ == list:
193 LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data, 248 LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data,
194 variables) 249 variables, check)
195 250
196 251
197 # TODO(mark): I don't love this name. It just means that it's going to load 252 # TODO(mark): I don't love this name. It just means that it's going to load
198 # a build file that contains targets and is expected to provide a targets dict 253 # a build file that contains targets and is expected to provide a targets dict
199 # that contains the targets... 254 # that contains the targets...
200 def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes, 255 def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
201 depth): 256 depth, check):
202 global absolute_build_file_paths 257 global absolute_build_file_paths
203 258
204 # If depth is set, predefine the DEPTH variable to be a relative path from 259 # If depth is set, predefine the DEPTH variable to be a relative path from
205 # this build file's directory to the directory identified by depth. 260 # this build file's directory to the directory identified by depth.
206 if depth: 261 if depth:
207 d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path)) 262 d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path))
208 if d == '': 263 if d == '':
209 variables['DEPTH'] = '.' 264 variables['DEPTH'] = '.'
210 else: 265 else:
211 variables['DEPTH'] = d 266 variables['DEPTH'] = d
212 267
213 # If the generator needs absolue paths, then do so. 268 # If the generator needs absolue paths, then do so.
214 if absolute_build_file_paths: 269 if absolute_build_file_paths:
215 build_file_path = os.path.abspath(build_file_path) 270 build_file_path = os.path.abspath(build_file_path)
216 271
217 if build_file_path in data: 272 if build_file_path in data:
218 # Already loaded. 273 # Already loaded.
219 return 274 return
220 275
221 build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables, 276 build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables,
222 includes, True) 277 includes, True, check)
223 278
224 # Store DEPTH for later use in generators. 279 # Store DEPTH for later use in generators.
225 build_file_data['_DEPTH'] = depth 280 build_file_data['_DEPTH'] = depth
226 281
227 # Set up the included_files key indicating which .gyp files contributed to 282 # Set up the included_files key indicating which .gyp files contributed to
228 # this target dict. 283 # this target dict.
229 if 'included_files' in build_file_data: 284 if 'included_files' in build_file_data:
230 raise KeyError, build_file_path + ' must not contain included_files key' 285 raise KeyError, build_file_path + ' must not contain included_files key'
231 286
232 included = GetIncludedBuildFiles(build_file_path, aux_data) 287 included = GetIncludedBuildFiles(build_file_path, aux_data)
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 331
277 if 'targets' in build_file_data: 332 if 'targets' in build_file_data:
278 for target_dict in build_file_data['targets']: 333 for target_dict in build_file_data['targets']:
279 if 'dependencies' not in target_dict: 334 if 'dependencies' not in target_dict:
280 continue 335 continue
281 for dependency in target_dict['dependencies']: 336 for dependency in target_dict['dependencies']:
282 other_build_file = \ 337 other_build_file = \
283 gyp.common.BuildFileAndTarget(build_file_path, dependency)[0] 338 gyp.common.BuildFileAndTarget(build_file_path, dependency)[0]
284 try: 339 try:
285 LoadTargetBuildFile(other_build_file, data, aux_data, variables, 340 LoadTargetBuildFile(other_build_file, data, aux_data, variables,
286 includes, depth) 341 includes, depth, check)
287 except Exception, e: 342 except Exception, e:
288 gyp.common.ExceptionAppend( 343 gyp.common.ExceptionAppend(
289 e, 'while loading dependencies of %s' % build_file_path) 344 e, 'while loading dependencies of %s' % build_file_path)
290 raise 345 raise
291 346
292 return data 347 return data
293 348
294 # Look for the bracket that matches the first bracket seen in a 349 # Look for the bracket that matches the first bracket seen in a
295 # string, and return the start and end as a tuple. For example, if 350 # string, and return the start and end as a tuple. For example, if
296 # the input is something like "<(foo <(bar)) blah", then it would 351 # the input is something like "<(foo <(bar)) blah", then it would
(...skipping 1380 matching lines...) Expand 10 before | Expand all | Expand 10 after
1677 if working_directory and not isinstance(working_directory, str): 1732 if working_directory and not isinstance(working_directory, str):
1678 raise Exception("The 'working_directory' for 'run_as' in target %s " 1733 raise Exception("The 'working_directory' for 'run_as' in target %s "
1679 "in file %s should be a string." % 1734 "in file %s should be a string." %
1680 (target_name, build_file)) 1735 (target_name, build_file))
1681 environment = run_as.get('environment') 1736 environment = run_as.get('environment')
1682 if environment and not isinstance(environment, dict): 1737 if environment and not isinstance(environment, dict):
1683 raise Exception("The 'environment' for 'run_as' in target %s " 1738 raise Exception("The 'environment' for 'run_as' in target %s "
1684 "in file %s should be a dictionary." % 1739 "in file %s should be a dictionary." %
1685 (target_name, build_file)) 1740 (target_name, build_file))
1686 1741
1687 def Load(build_files, variables, includes, depth, generator_input_info): 1742 def Load(build_files, variables, includes, depth, generator_input_info, check):
1688 # Set up path_sections and non_configuration_keys with the default data plus 1743 # Set up path_sections and non_configuration_keys with the default data plus
1689 # the generator-specifc data. 1744 # the generator-specifc data.
1690 global path_sections 1745 global path_sections
1691 path_sections = base_path_sections[:] 1746 path_sections = base_path_sections[:]
1692 path_sections.extend(generator_input_info['path_sections']) 1747 path_sections.extend(generator_input_info['path_sections'])
1693 1748
1694 global non_configuration_keys 1749 global non_configuration_keys
1695 non_configuration_keys = base_non_configuration_keys[:] 1750 non_configuration_keys = base_non_configuration_keys[:]
1696 non_configuration_keys.extend(generator_input_info['non_configuration_keys']) 1751 non_configuration_keys.extend(generator_input_info['non_configuration_keys'])
1697 1752
(...skipping 13 matching lines...) Expand all
1711 # the |data| dictionary such that the keys to |data| are build file names, 1766 # the |data| dictionary such that the keys to |data| are build file names,
1712 # and the values are the entire build file contents after "early" or "pre" 1767 # and the values are the entire build file contents after "early" or "pre"
1713 # processing has been done and includes have been resolved. 1768 # processing has been done and includes have been resolved.
1714 data = {} 1769 data = {}
1715 aux_data = {} 1770 aux_data = {}
1716 for build_file in build_files: 1771 for build_file in build_files:
1717 # Normalize paths everywhere. This is important because paths will be 1772 # Normalize paths everywhere. This is important because paths will be
1718 # used as keys to the data dict and for references between input files. 1773 # used as keys to the data dict and for references between input files.
1719 build_file = os.path.normpath(build_file) 1774 build_file = os.path.normpath(build_file)
1720 try: 1775 try:
1721 LoadTargetBuildFile(build_file, data, aux_data, variables, includes, depth ) 1776 LoadTargetBuildFile(build_file, data, aux_data, variables, includes,
1777 depth, check)
1722 except Exception, e: 1778 except Exception, e:
1723 gyp.common.ExceptionAppend(e, 'while trying to load %s' % build_file) 1779 gyp.common.ExceptionAppend(e, 'while trying to load %s' % build_file)
1724 raise 1780 raise
1725 1781
1726 # Build a dict to access each target's subdict by qualified name. 1782 # Build a dict to access each target's subdict by qualified name.
1727 targets = BuildTargetsDict(data) 1783 targets = BuildTargetsDict(data)
1728 1784
1729 # Fully qualify all dependency links. 1785 # Fully qualify all dependency links.
1730 QualifyDependencies(targets) 1786 QualifyDependencies(targets)
1731 1787
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1777 1833
1778 # Validate run_as sections in targets. 1834 # Validate run_as sections in targets.
1779 for target in flat_list: 1835 for target in flat_list:
1780 build_file = gyp.common.BuildFileAndTarget('', target)[0] 1836 build_file = gyp.common.BuildFileAndTarget('', target)[0]
1781 ValidateRunAsInTarget(target, targets[target], build_file) 1837 ValidateRunAsInTarget(target, targets[target], build_file)
1782 1838
1783 # TODO(mark): Return |data| for now because the generator needs a list of 1839 # TODO(mark): Return |data| for now because the generator needs a list of
1784 # build files that came in. In the future, maybe it should just accept 1840 # build files that came in. In the future, maybe it should just accept
1785 # a list, and not the whole data dict. 1841 # a list, and not the whole data dict.
1786 return [flat_list, targets, data] 1842 return [flat_list, targets, data]
OLDNEW
« no previous file with comments | « pylib/gyp/__init__.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698