Last active
April 12, 2018 19:02
-
-
Save Eloston/e9a627ac94f6eaf46c98c362aeba225b to your computer and use it in GitHub Desktop.
Get all deps and hooks exclusive to Chromium on Android
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: UTF-8 -*- | |
""" | |
Prints out the deps and hooks exclusive to Chromium for Android | |
Usage: get_android_deps.py path/to/DEPS | |
""" | |
import ast | |
import sys | |
class _UnexpectedSyntax(RuntimeError): | |
"""Raised when unexpected syntax is used in DEPS""" | |
pass | |
class _DepsNodeVisitor(ast.NodeVisitor): | |
_valid_syntax_types = ( | |
ast.mod, ast.expr_context, ast.boolop, | |
ast.Assign, ast.Add, ast.Name, ast.Dict, ast.Str, ast.NameConstant, | |
ast.List, ast.BinOp | |
) | |
_allowed_callables = ('Var',) | |
def visit_Call(self, node): | |
if node.func.id not in self._allowed_callables: | |
raise _UnexpectedSyntax( | |
'Unexpected call of "%s" at line %s, column %s' % ( | |
node.func.id, node.lineno, node.col_offset)) | |
def generic_visit(self, node): | |
for ast_type in self._valid_syntax_types: | |
if isinstance(node, ast_type): | |
super().generic_visit(node) | |
return | |
raise _UnexpectedSyntax( | |
'Unexpected {} at line {}, column {}'.format( | |
type(node).__name__, node.lineno, node.col_offset)) | |
def _validate_deps(deps_text): | |
"""Returns True if the DEPS file passes validation; False otherwise""" | |
try: | |
_DepsNodeVisitor().visit(ast.parse(deps_text)) | |
except _UnexpectedSyntax as exc: | |
print('ERROR: %s' % exc) | |
return False | |
return True | |
def _deps_var(deps_globals): | |
"""Return a function that implements DEPS's Var() function""" | |
def _var_impl(var_name): | |
"""Implementation of Var() in DEPS""" | |
return deps_globals['vars'][var_name] | |
return _var_impl | |
def _parse_deps(deps_text): | |
"""Returns a dict of parsed DEPS data""" | |
deps_globals = {'__builtins__': None} | |
deps_globals['Var'] = _deps_var(deps_globals) | |
exec(deps_text, deps_globals) | |
return deps_globals | |
def _get_android_deps(parsed_deps): | |
"""Yields deps that are needed only by Android""" | |
recursive_deps = dict() | |
for recurse_dep in parsed_deps['recursedeps']: | |
if isinstance(recurse_dep, str): | |
recursive_deps[recurse_dep] = 'DEPS' | |
elif isinstance(recurse_dep, list): | |
recursive_deps[recurse_dep[0]] = recurse_dep[1] | |
else: | |
raise ValueError('Unexpected recursedeps value: %s' % recurse_dep) | |
for dep_name, dep_def in parsed_deps['deps'].items(): | |
if isinstance(dep_def, str): | |
# This is just a URL, so skip it | |
continue | |
condition = dep_def.get('condition', '') | |
if 'checkout_linux' not in condition and 'checkout_android' in condition: | |
yield dep_name, dep_def, recursive_deps.get(dep_name) | |
def _get_android_hooks(parsed_deps): | |
"""Yields hooks that are needed only by Android""" | |
for hook in parsed_deps['hooks']: | |
condition = hook.get('condition', '') | |
if 'checkout_linux' not in condition and 'checkout_android' in condition: | |
yield hook['name'], hook | |
def main(args): | |
assert len(args) == 1 | |
with open(args[0]) as deps_file: | |
deps_text = deps_file.read() | |
if not _validate_deps(deps_text): | |
print('ERROR: DEPS does not pass validation. Aborting.') | |
return 1 | |
parsed_deps = _parse_deps(deps_text) | |
print('***Dependencies:') | |
for dep_name, dep_def, recurse_dep in sorted(_get_android_deps(parsed_deps)): | |
if recurse_dep: | |
print(dep_name, '(Contains additional deps file: %s)' % recurse_dep) | |
else: | |
print(dep_name) | |
print('***Hooks:') | |
for hook_name, hook in sorted(_get_android_hooks(parsed_deps)): | |
print(hook_name) | |
return 0 | |
if __name__ == '__main__': | |
exit(main(sys.argv[1:])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment