Commit 57253730 authored by Paweł Hajdan, Jr's avatar Paweł Hajdan, Jr Committed by Commit Bot

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: 's avatarDirk Pranke <dpranke@chromium.org>
parent f69860bd
...@@ -157,6 +157,36 @@ def ast2str(node, indent=0): ...@@ -157,6 +157,36 @@ def ast2str(node, indent=0):
% (node.lineno, node.col_offset, t)) % (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 GClientKeywords(object):
class VarImpl(object): class VarImpl(object):
def __init__(self, custom_vars, local_scope): def __init__(self, custom_vars, local_scope):
...@@ -307,6 +337,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -307,6 +337,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
# Calculates properties: # Calculates properties:
self._parsed_url = None self._parsed_url = None
self._dependencies = [] self._dependencies = []
self._vars = {}
# A cache of the files affected by the current operation, necessary for # A cache of the files affected by the current operation, necessary for
# hooks. # hooks.
self._file_list = [] self._file_list = []
...@@ -315,6 +346,9 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -315,6 +346,9 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
# hosts will be allowed. Non-empty set means whitelist of hosts. # 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. # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
self._allowed_hosts = frozenset() 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 # If it is not set to True, the dependency wasn't processed for its child
# dependency, i.e. its DEPS wasn't read. # dependency, i.e. its DEPS wasn't read.
self._deps_parsed = False self._deps_parsed = False
...@@ -582,6 +616,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -582,6 +616,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
'ParseDepsFile(%s): Strict mode disallows %r -> %r' % 'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
(self.name, key, val)) (self.name, key, val))
self._vars = local_scope.get('vars', {})
deps = local_scope.get('deps', {}) deps = local_scope.get('deps', {})
if 'recursion' in local_scope: if 'recursion' in local_scope:
self.recursion_override = local_scope.get('recursion') self.recursion_override = local_scope.get('recursion')
...@@ -657,6 +693,9 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -657,6 +693,9 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
'ParseDepsFile(%s): allowed_hosts must be absent ' 'ParseDepsFile(%s): allowed_hosts must be absent '
'or a non-empty iterable' % self.name) '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. # Convert the deps into real Dependency.
deps_to_add = [] deps_to_add = []
for name, dep_value in deps.iteritems(): for name, dep_value in deps.iteritems():
...@@ -790,6 +829,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -790,6 +829,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
# Always parse the DEPS file. # Always parse the DEPS file.
self.ParseDepsFile() self.ParseDepsFile()
if self._gn_args_file and command == 'update':
self.WriteGNArgsFile()
self._run_is_done(file_list or [], parsed_url) self._run_is_done(file_list or [], parsed_url)
if command in ('update', 'revert') and not options.noprehooks: if command in ('update', 'revert') and not options.noprehooks:
self.RunPreDepsHooks() self.RunPreDepsHooks()
...@@ -855,6 +896,12 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): ...@@ -855,6 +896,12 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
else: else:
print('Skipped missing %s' % cwd, file=sys.stderr) 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 @gclient_utils.lockedmethod
def _run_is_done(self, file_list, parsed_url): def _run_is_done(self, file_list, parsed_url):
......
...@@ -59,6 +59,12 @@ _GCLIENT_SCHEMA = schema.Schema({ ...@@ -59,6 +59,12 @@ _GCLIENT_SCHEMA = schema.Schema({
} }
}, },
# Path to GN args file to write selected variables.
schema.Optional('gclient_gn_args_file'): basestring,
# Subset of variables to write to the GN args file (see above).
schema.Optional('gclient_gn_args'): [schema.Optional(basestring)],
# Hooks executed after gclient sync (unless suppressed), or explicitly # Hooks executed after gclient sync (unless suppressed), or explicitly
# on gclient hooks. See _GCLIENT_HOOKS_SCHEMA for details. # on gclient hooks. See _GCLIENT_HOOKS_SCHEMA for details.
# Also see 'pre_deps_hooks'. # Also see 'pre_deps_hooks'.
......
...@@ -324,6 +324,8 @@ class FakeRepos(FakeReposBase): ...@@ -324,6 +324,8 @@ class FakeRepos(FakeReposBase):
vars = { vars = {
'DummyVariable': 'repo', 'DummyVariable': 'repo',
} }
gclient_gn_args_file = 'src/gclient.args'
gclient_gn_args = ['DummyVariable']
deps = { deps = {
'src/repo2': { 'src/repo2': {
'url': '%(git_base)srepo_2', 'url': '%(git_base)srepo_2',
......
...@@ -319,6 +319,10 @@ class GClientSmokeGIT(GClientSmokeBase): ...@@ -319,6 +319,10 @@ class GClientSmokeGIT(GClientSmokeBase):
('repo_3@1', 'src/repo2/repo3'), ('repo_3@1', 'src/repo2/repo3'),
('repo_4@2', 'src/repo4')) ('repo_4@2', 'src/repo4'))
tree['src/git_hooked2'] = 'git_hooked2' tree['src/git_hooked2'] = 'git_hooked2'
tree['src/gclient.args'] = '\n'.join([
'# Generated from \'DEPS\'',
'DummyVariable = "repo"',
])
self.assertTree(tree) self.assertTree(tree)
# Test incremental sync: delete-unversioned_trees isn't there. # Test incremental sync: delete-unversioned_trees isn't there.
self.parseGclient( self.parseGclient(
...@@ -331,6 +335,10 @@ class GClientSmokeGIT(GClientSmokeBase): ...@@ -331,6 +335,10 @@ class GClientSmokeGIT(GClientSmokeBase):
('repo_4@2', 'src/repo4')) ('repo_4@2', 'src/repo4'))
tree['src/git_hooked1'] = 'git_hooked1' tree['src/git_hooked1'] = 'git_hooked1'
tree['src/git_hooked2'] = 'git_hooked2' tree['src/git_hooked2'] = 'git_hooked2'
tree['src/gclient.args'] = '\n'.join([
'# Generated from \'DEPS\'',
'DummyVariable = "repo"',
])
self.assertTree(tree) self.assertTree(tree)
def testSyncIgnoredSolutionName(self): def testSyncIgnoredSolutionName(self):
...@@ -364,6 +372,10 @@ class GClientSmokeGIT(GClientSmokeBase): ...@@ -364,6 +372,10 @@ class GClientSmokeGIT(GClientSmokeBase):
('repo_2@2', 'src/repo2'), ('repo_2@2', 'src/repo2'),
('repo_3@1', 'src/repo2/repo3'), ('repo_3@1', 'src/repo2/repo3'),
('repo_4@2', 'src/repo4')) ('repo_4@2', 'src/repo4'))
tree['src/gclient.args'] = '\n'.join([
'# Generated from \'DEPS\'',
'DummyVariable = "repo"',
])
self.assertTree(tree) self.assertTree(tree)
def testSyncJobs(self): def testSyncJobs(self):
...@@ -400,6 +412,10 @@ class GClientSmokeGIT(GClientSmokeBase): ...@@ -400,6 +412,10 @@ class GClientSmokeGIT(GClientSmokeBase):
('repo_3@1', 'src/repo2/repo3'), ('repo_3@1', 'src/repo2/repo3'),
('repo_4@2', 'src/repo4')) ('repo_4@2', 'src/repo4'))
tree['src/git_hooked2'] = 'git_hooked2' tree['src/git_hooked2'] = 'git_hooked2'
tree['src/gclient.args'] = '\n'.join([
'# Generated from \'DEPS\'',
'DummyVariable = "repo"',
])
self.assertTree(tree) self.assertTree(tree)
# Test incremental sync: delete-unversioned_trees isn't there. # Test incremental sync: delete-unversioned_trees isn't there.
self.parseGclient( self.parseGclient(
...@@ -413,6 +429,10 @@ class GClientSmokeGIT(GClientSmokeBase): ...@@ -413,6 +429,10 @@ class GClientSmokeGIT(GClientSmokeBase):
('repo_4@2', 'src/repo4')) ('repo_4@2', 'src/repo4'))
tree['src/git_hooked1'] = 'git_hooked1' tree['src/git_hooked1'] = 'git_hooked1'
tree['src/git_hooked2'] = 'git_hooked2' tree['src/git_hooked2'] = 'git_hooked2'
tree['src/gclient.args'] = '\n'.join([
'# Generated from \'DEPS\'',
'DummyVariable = "repo"',
])
self.assertTree(tree) self.assertTree(tree)
def testRunHooks(self): def testRunHooks(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment