gclient: implement exporting variables to .gni files
Bug: 570091
Change-Id: Ib2b966b5bc967de11a295b1636c1901faabea55f
Reviewed-on: https://chromium-review.googlesource.com/525540
Commit-Queue: Paweł Hajdan Jr. <phajdan.jr@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
diff --git a/gclient.py b/gclient.py
index e651c6c..ad8915b 100755
--- a/gclient.py
+++ b/gclient.py
@@ -157,6 +157,36 @@
% (node.lineno, node.col_offset, t))
+class GNException(Exception):
+ pass
+
+
+def ToGNString(value, allow_dicts = True):
+ """Returns a stringified GN equivalent of the Python value.
+
+ allow_dicts indicates if this function will allow converting dictionaries
+ to GN scopes. This is only possible at the top level, you can't nest a
+ GN scope in a list, so this should be set to False for recursive calls."""
+ if isinstance(value, basestring):
+ if value.find('\n') >= 0:
+ raise GNException("Trying to print a string with a newline in it.")
+ return '"' + \
+ value.replace('\\', '\\\\').replace('"', '\\"').replace('$', '\\$') + \
+ '"'
+
+ if isinstance(value, unicode):
+ return ToGNString(value.encode('utf-8'))
+
+ if isinstance(value, bool):
+ if value:
+ return "true"
+ return "false"
+
+ # NOTE: some type handling removed compared to chromium/src copy.
+
+ raise GNException("Unsupported type when printing to GN.")
+
+
class GClientKeywords(object):
class VarImpl(object):
def __init__(self, custom_vars, local_scope):
@@ -307,6 +337,7 @@
# Calculates properties:
self._parsed_url = None
self._dependencies = []
+ self._vars = {}
# A cache of the files affected by the current operation, necessary for
# hooks.
self._file_list = []
@@ -315,6 +346,9 @@
# hosts will be allowed. Non-empty set means whitelist of hosts.
# allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
self._allowed_hosts = frozenset()
+ # Spec for .gni output to write (if any).
+ self._gn_args_file = None
+ self._gn_args = []
# If it is not set to True, the dependency wasn't processed for its child
# dependency, i.e. its DEPS wasn't read.
self._deps_parsed = False
@@ -582,6 +616,8 @@
'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
(self.name, key, val))
+ self._vars = local_scope.get('vars', {})
+
deps = local_scope.get('deps', {})
if 'recursion' in local_scope:
self.recursion_override = local_scope.get('recursion')
@@ -657,6 +693,9 @@
'ParseDepsFile(%s): allowed_hosts must be absent '
'or a non-empty iterable' % self.name)
+ self._gn_args_file = local_scope.get('gclient_gn_args_file')
+ self._gn_args = local_scope.get('gclient_gn_args', [])
+
# Convert the deps into real Dependency.
deps_to_add = []
for name, dep_value in deps.iteritems():
@@ -790,6 +829,8 @@
# Always parse the DEPS file.
self.ParseDepsFile()
+ if self._gn_args_file and command == 'update':
+ self.WriteGNArgsFile()
self._run_is_done(file_list or [], parsed_url)
if command in ('update', 'revert') and not options.noprehooks:
self.RunPreDepsHooks()
@@ -855,6 +896,12 @@
else:
print('Skipped missing %s' % cwd, file=sys.stderr)
+ def WriteGNArgsFile(self):
+ lines = ['# Generated from %r' % self.deps_file]
+ for arg in self._gn_args:
+ lines.append('%s = %s' % (arg, ToGNString(self._vars[arg])))
+ with open(os.path.join(self.root.root_dir, self._gn_args_file), 'w') as f:
+ f.write('\n'.join(lines))
@gclient_utils.lockedmethod
def _run_is_done(self, file_list, parsed_url):