chromium的部署工具depot_tools和gclient

depot_tools是个工具包,里面包含gclient、gcl、gn和ninja等工具。其中gclient是代码获取工具,它其实是利用了svn和git。主要涉及的depot_tools文件夹下的文件有:gclient、gclient.py、subcommand.py、gclient_utils.py。

gclient文件是个bash脚本:

[plain] view plaincopy

  1. #########glcient###########
  2. /usr/bin/bash
  3. base_dir=$(dirname "$0")
  4. if [[ "#grep#fetch#cleanup#diff#" != *"#$1#"* ]]; then
  5. "$base_dir"/update_depot_tools
  6. fi
  7. PYTHONDONTWRITEBYTECODE=1 exec python "$base_dir/gclient.py" "[email protected]"

首先,获取脚本的目录并赋值给base_dir,然后判断命令参数1是否为grep|fetch|cleanup|diff,如不是则执行base_dir下的updat_depot_tools脚本,该脚本更新git、svn工具。最后调用当前脚本目录(depot_tools)下的python脚本gclient.py,并把参数都传递给该脚本。

[plain] view plaincopy

  1. ###############glcient.py#######
  2. # 文件最后
  3. def Main(argv):
  4. .....
  5. dispatcher = subcommand.CommandDispatcher(__name__)
  6. try:
  7. return dispatcher.execute(OptionParser(), argv)
  8. ......
  9. if ‘__main__‘ == __name__:
  10. sys.exit(Main(sys.argv[1:]))

在gclient.py脚本开始,调用函数Main,参数为bash脚本传递来的。Main主要执行两个过程,一是建立dispatcher对象,参数为当前模块;然后调用dispatcher的execute方法,参数一是OptionParser对象,参数二为传递给Main的参数。

下面进入subcommand模块下的类CommandDispatcher,分析execute方法。

[plain] view plaincopy

  1. ################# subcommand.py ###########
  2. class CommandDispatcher(object):
  3. def __init__(self, module):
  4. """module is the name of the main python module where to look for commands.
  5. The python builtin variable __name__ MUST be used for |module|. If the
  6. script is executed in the form ‘python script.py‘, __name__ == ‘__main__‘
  7. and sys.modules[‘script‘] doesn‘t exist. On the other hand if it is unit
  8. tested, __main__ will be the unit test‘s module so it has to reference to
  9. itself with ‘script‘. __name__ always match the right value.
  10. """
  11. self.module = sys.modules[module]
  12. def enumerate_commands(self):
  13. """Returns a dict of command and their handling function.
  14. The commands must be in the ‘__main__‘ modules. To import a command from a
  15. submodule, use:
  16. from mysubcommand import CMDfoo
  17. Automatically adds ‘help‘ if not already defined.
  18. A command can be effectively disabled by defining a global variable to None,
  19. e.g.:
  20. CMDhelp = None
  21. """
  22. cmds = dict(
  23. (fn[3:], getattr(self.module, fn))
  24. for fn in dir(self.module) if fn.startswith(‘CMD‘))
  25. cmds.setdefault(‘help‘, CMDhelp)
  26. return cmds
  27. def find_nearest_command(self, name):
  28. """Retrieves the function to handle a command.
  29. It automatically tries to guess the intended command by handling typos or
  30. incomplete names.
  31. """
  32. # Implicitly replace foo-bar to foo_bar since foo-bar is not a valid python
  33. # symbol but it‘s faster to type.
  34. name = name.replace(‘-‘, ‘_‘)
  35. commands = self.enumerate_commands()
  36. if name in commands:
  37. return commands[name]
  38. # An exact match was not found. Try to be smart and look if there‘s
  39. # something similar.
  40. commands_with_prefix = [c for c in commands if c.startswith(name)]
  41. if len(commands_with_prefix) == 1:
  42. return commands[commands_with_prefix[0]]
  43. # A #closeenough approximation of levenshtein distance.
  44. def close_enough(a, b):
  45. return difflib.SequenceMatcher(a=a, b=b).ratio()
  46. hamming_commands = sorted(
  47. ((close_enough(c, name), c) for c in commands),
  48. reverse=True)
  49. if (hamming_commands[0][0] - hamming_commands[1][0]) < 0.3:
  50. # Too ambiguous.
  51. return
  52. if hamming_commands[0][0] < 0.8:
  53. # Not similar enough. Don‘t be a fool and run a random command.
  54. return
  55. return commands[hamming_commands[0][1]]
  56. def _gen_commands_list(self):
  57. """Generates the short list of supported commands."""
  58. commands = self.enumerate_commands()
  59. docs = sorted(
  60. (name, self._create_command_summary(name, handler))
  61. for name, handler in commands.iteritems())
  62. # Skip commands without a docstring.
  63. docs = [i for i in docs if i[1]]
  64. # Then calculate maximum length for alignment:
  65. length = max(len(c) for c in commands)
  66. # Look if color is supported.
  67. colors = _get_color_module()
  68. green = reset = ‘‘
  69. if colors:
  70. green = colors.Fore.GREEN
  71. reset = colors.Fore.RESET
  72. return (
  73. ‘Commands are:\n‘ +
  74. ‘‘.join(
  75. ‘  %s%-*s%s %s\n‘ % (green, length, name, reset, doc)
  76. for name, doc in docs))
  77. def _add_command_usage(self, parser, command):
  78. """Modifies an OptionParser object with the function‘s documentation."""
  79. name = command.__name__[3:]
  80. if name == ‘help‘:
  81. name = ‘<command>‘
  82. # Use the module‘s docstring as the description for the ‘help‘ command if
  83. # available.
  84. parser.description = (self.module.__doc__ or ‘‘).rstrip()
  85. if parser.description:
  86. parser.description += ‘\n\n‘
  87. parser.description += self._gen_commands_list()
  88. # Do not touch epilog.
  89. else:
  90. # Use the command‘s docstring if available. For commands, unlike module
  91. # docstring, realign.
  92. lines = (command.__doc__ or ‘‘).rstrip().splitlines()
  93. if lines[:1]:
  94. rest = textwrap.dedent(‘\n‘.join(lines[1:]))
  95. parser.description = ‘\n‘.join((lines[0], rest))
  96. else:
  97. parser.description = lines[0]
  98. if parser.description:
  99. parser.description += ‘\n‘
  100. parser.epilog = getattr(command, ‘epilog‘, None)
  101. if parser.epilog:
  102. parser.epilog = ‘\n‘ + parser.epilog.strip() + ‘\n‘
  103. more = getattr(command, ‘usage_more‘, ‘‘)
  104. parser.set_usage(
  105. ‘usage: %%prog %s [options]%s‘ % (name, ‘‘ if not more else ‘ ‘ + more))
  106. @staticmethod
  107. def _create_command_summary(name, command):
  108. """Creates a oneline summary from the command‘s docstring."""
  109. if name != command.__name__[3:]:
  110. # Skip aliases.
  111. return ‘‘
  112. doc = command.__doc__ or ‘‘
  113. line = doc.split(‘\n‘, 1)[0].rstrip(‘.‘)
  114. if not line:
  115. return line
  116. return (line[0].lower() + line[1:]).strip()
  117. def execute(self, parser, args):
  118. """Dispatches execution to the right command.
  119. Fallbacks to ‘help‘ if not disabled.
  120. """
  121. # Unconditionally disable format_description() and format_epilog().
  122. # Technically, a formatter should be used but it‘s not worth (yet) the
  123. # trouble.
  124. parser.format_description = lambda _: parser.description or ‘‘
  125. parser.format_epilog = lambda _: parser.epilog or ‘‘
  126. if args:
  127. if args[0] in (‘-h‘, ‘--help‘) and len(args) > 1:
  128. # Inverse the argument order so ‘tool --help cmd‘ is rewritten to
  129. # ‘tool cmd --help‘.
  130. args = [args[1], args[0]] + args[2:]
  131. command = self.find_nearest_command(args[0])
  132. if command:
  133. if command.__name__ == ‘CMDhelp‘ and len(args) > 1:
  134. # Inverse the arguments order so ‘tool help cmd‘ is rewritten to
  135. # ‘tool cmd --help‘. Do it here since we want ‘tool hel cmd‘ to work
  136. # too.
  137. args = [args[1], ‘--help‘] + args[2:]
  138. command = self.find_nearest_command(args[0]) or command
  139. # "fix" the usage and the description now that we know the subcommand.
  140. self._add_command_usage(parser, command)
  141. return command(parser, args[1:])
  142. cmdhelp = self.enumerate_commands().get(‘help‘)
  143. if cmdhelp:
  144. # Not a known command. Default to help.
  145. self._add_command_usage(parser, cmdhelp)
  146. return cmdhelp(parser, args)
  147. # Nothing can be done.
  148. return 2

在gcient.py中,dispatcher = subcomamnd.CommandDispatcher(__name__),可以看出传入的参数是gclient.py这个模块。在CommandDispatcher的__init__中,self.module就是gclent。了解这点对理解后面的enumrate_commands函数有用。从它的名字就可以看出是枚举所有支持的命令,返回的结果是dict,键是命令名称,值为对应的处理函数,如{"sync" : CMDsync}。其他函数如_add_command_usage, _create_command_summary, _gen_commands_list是些辅助函数,功能也比较明确。比较复杂的是find_nearest_command,它是从enumerate_commands生成的dict字典里查找命令,如果没有精确匹配的,就通过模糊查找,并返回对应命令的处理函数。

[plain] view plaincopy

  1. ############# gclient.py ##################
  2. class OptionParser(optparse.OptionParser):
  3. gclientfile_default = os.environ.get(‘GCLIENT_FILE‘, ‘.gclient‘)
  4. def __init__(self, **kwargs):
  5. optparse.OptionParser.__init__(
  6. self, version=‘%prog ‘ + __version__, **kwargs)
  7. # Some arm boards have issues with parallel sync.
  8. if platform.machine().startswith(‘arm‘):
  9. jobs = 1
  10. else:
  11. jobs = max(8, gclient_utils.NumLocalCpus())
  12. # cmp: 2013/06/19
  13. # Temporary workaround to lower bot-load on SVN server.
  14. # Bypassed if a bot_update flag is detected.
  15. if (os.environ.get(‘CHROME_HEADLESS‘) == ‘1‘ and
  16. not os.path.exists(‘update.flag‘)):
  17. jobs = 1
  18. self.add_option(
  19. ‘-j‘, ‘--jobs‘, default=jobs, type=‘int‘,
  20. help=‘Specify how many SCM commands can run in parallel; defaults to ‘
  21. ‘%default on this machine‘)
  22. self.add_option(
  23. ‘-v‘, ‘--verbose‘, action=‘count‘, default=0,
  24. help=‘Produces additional output for diagnostics. Can be used up to ‘
  25. ‘three times for more logging info.‘)
  26. self.add_option(
  27. ‘--gclientfile‘, dest=‘config_filename‘,
  28. help=‘Specify an alternate %s file‘ % self.gclientfile_default)
  29. self.add_option(
  30. ‘--spec‘,
  31. help=‘create a gclient file containing the provided string. Due to ‘
  32. ‘Cygwin/Python brokenness, it can\‘t contain any newlines.‘)
  33. self.add_option(
  34. ‘--no-nag-max‘, default=False, action=‘store_true‘,
  35. help=‘Ignored for backwards compatibility.‘)

参数OptionParser是gclient.py中的一个类,它继承子模块的optparser.OptionParser。主要功能是解析参数,并返回tuple(options, args),关于OptionParser这个例子说明的很清楚。返回值就是输入参数被格式化分析后的结果。这个函数处理分两个层面,一是如果是帮助查询,一个是执行命令。在帮助查询时,用户输入的命令如:gclient --help sync等。其实,传到execute的时候,参数变成--help sync,那么首先是调换参数位置,使得结果为sync --help,也就是args[0]为"sync", args[1]为--help,然后调用self.fine_nearest_command函数,查找精确命令或者最相似的模糊匹配的命令,并把结果(命令处理函数以CMD开头)赋值给command变量。

进入到execute(),解析参数,find_nearest_commands()得到命令后,判断是不是帮助命令,如果不是就直接调用对应的处理函数return command(parser, args[1:])。用上例来说就是调用CMDsync函数,参数是parser 和sync后的参数。

看看CMDsync是如何处理输入参数的?

[plain] view plaincopy

  1. #############gclient.py#############
  2. def CMDsync(parser, args):
  3. """Checkout/update all modules."""
  4. parser.add_option(‘-f‘, ‘--force‘, action=‘store_true‘,
  5. help=‘force update even for unchanged modules‘)
  6. parser.add_option(‘-n‘, ‘--nohooks‘, action=‘store_true‘,
  7. help=‘don\‘t run hooks after the update is complete‘)
  8. parser.add_option(‘-p‘, ‘--noprehooks‘, action=‘store_true‘,
  9. help=‘don\‘t run pre-DEPS hooks‘, default=False)
  10. parser.add_option(‘-r‘, ‘--revision‘, action=‘append‘,
  11. dest=‘revisions‘, metavar=‘REV‘, default=[],
  12. help=‘Enforces revision/hash for the solutions with the ‘
  13. ‘format [email protected] The [email protected] part is optional and can be ‘
  14. ‘skipped. -r can be used multiple times when .gclient ‘
  15. ‘has multiple solutions configured and will work even ‘
  16. ‘if the [email protected] part is skipped. Note that specifying ‘
  17. ‘--revision means your safesync_url gets ignored.‘)
  18. parser.add_option(‘--with_branch_heads‘, action=‘store_true‘,
  19. help=‘Clone git "branch_heads" refspecs in addition to ‘
  20. ‘the default refspecs. This adds about 1/2GB to a ‘
  21. ‘full checkout. (git only)‘)
  22. parser.add_option(‘-t‘, ‘--transitive‘, action=‘store_true‘,
  23. help=‘When a revision is specified (in the DEPS file or ‘
  24. ‘with the command-line flag), transitively update ‘
  25. ‘the dependencies to the date of the given revision. ‘
  26. ‘Only supported for SVN repositories.‘)
  27. parser.add_option(‘-H‘, ‘--head‘, action=‘store_true‘,
  28. help=‘skips any safesync_urls specified in ‘
  29. ‘configured solutions and sync to head instead‘)
  30. parser.add_option(‘-D‘, ‘--delete_unversioned_trees‘, action=‘store_true‘,
  31. help=‘Deletes from the working copy any dependencies that ‘
  32. ‘have been removed since the last sync, as long as ‘
  33. ‘there are no local modifications. When used with ‘
  34. ‘--force, such dependencies are removed even if they ‘
  35. ‘have local modifications. When used with --reset, ‘
  36. ‘all untracked directories are removed from the ‘
  37. ‘working copy, excluding those which are explicitly ‘
  38. ‘ignored in the repository.‘)
  39. parser.add_option(‘-R‘, ‘--reset‘, action=‘store_true‘,
  40. help=‘resets any local changes before updating (git only)‘)
  41. parser.add_option(‘-M‘, ‘--merge‘, action=‘store_true‘,
  42. help=‘merge upstream changes instead of trying to ‘
  43. ‘fast-forward or rebase‘)
  44. parser.add_option(‘--deps‘, dest=‘deps_os‘, metavar=‘OS_LIST‘,
  45. help=‘override deps for the specified (comma-separated) ‘
  46. ‘platform(s); \‘all\‘ will process all deps_os ‘
  47. ‘references‘)
  48. parser.add_option(‘-m‘, ‘--manually_grab_svn_rev‘, action=‘store_true‘,
  49. help=‘Skip svn up whenever possible by requesting ‘
  50. ‘actual HEAD revision from the repository‘)
  51. parser.add_option(‘--upstream‘, action=‘store_true‘,
  52. help=‘Make repo state match upstream branch.‘)
  53. parser.add_option(‘--output-json‘,
  54. help=‘Output a json document to this path containing ‘
  55. ‘summary information about the sync.‘)
  56. (options, args) = parser.parse_args(args)
  57. client = GClient.LoadCurrentConfig(options)
  58. if not client:
  59. raise gclient_utils.Error(‘client not configured; see \‘gclient config\‘‘)
  60. if options.revisions and options.head:
  61. # TODO(maruel): Make it a parser.error if it doesn‘t break any builder.
  62. print(‘Warning: you cannot use both --head and --revision‘)
  63. if options.verbose:
  64. # Print out the .gclient file.  This is longer than if we just printed the
  65. # client dict, but more legible, and it might contain helpful comments.
  66. print(client.config_content)
  67. ret = client.RunOnDeps(‘update‘, args)
  68. if options.output_json:
  69. slns = {}
  70. for d in client.subtree(True):
  71. normed = d.name.replace(‘\\‘, ‘/‘).rstrip(‘/‘) + ‘/‘
  72. slns[normed] = {
  73. ‘revision‘: d.got_revision,
  74. ‘scm‘: d.used_scm.name if d.used_scm else None,
  75. }
  76. with open(options.output_json, ‘wb‘) as f:
  77. json.dump({‘solutions‘: slns}, f)
  78. return ret
  79. CMDupdate = CMDsync

通过options,args = parser.parse_args(args),得到对CMDsync命令参数的解析,然后调用Gclient.LoadCurrentConfig(options),为简单起见,我们输入的是gclient sync,那么options和args为空。

[plain] view plaincopy

  1. ############glcient.py#############
  2. def LoadCurrentConfig(options):
  3. """Searches for and loads a .gclient file relative to the current working
  4. dir. Returns a GClient object."""
  5. if options.spec:
  6. client = GClient(‘.‘, options)
  7. client.SetConfig(options.spec)
  8. else:
  9. path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
  10. if not path:
  11. return None
  12. client = GClient(path, options)
  13. client.SetConfig(gclient_utils.FileRead(
  14. os.path.join(path, options.config_filename)))
  15. if (options.revisions and
  16. len(client.dependencies) > 1 and
  17. any(‘@‘ not in r for r in options.revisions)):
  18. print >> sys.stderr, (
  19. ‘You must specify the full solution name like --revision %[email protected]%s\n‘
  20. ‘when you have multiple solutions setup in your .gclient file.\n‘
  21. ‘Other solutions present are: %s.‘) % (
  22. client.dependencies[0].name,
  23. options.revisions[0],
  24. ‘, ‘.join(s.name for s in client.dependencies[1:]))
  25. return client

该函数查询当前目录下.glcient文件,最终返回client对象。该函数会调用到GClient(path, options):

[plain] view plaincopy

  1. ############## gclient.py ##################
  2. class GClient(Dependency):
  3. """Object that represent a gclient checkout. A tree of Dependency(), one per
  4. solution or DEPS entry."""
  5. DEPS_OS_CHOICES = {
  6. "win32": "win",
  7. "win": "win",
  8. "cygwin": "win",
  9. "darwin": "mac",
  10. "mac": "mac",
  11. "unix": "unix",
  12. "linux": "unix",
  13. "linux2": "unix",
  14. "linux3": "unix",
  15. "android": "android",
  16. }
  17. DEFAULT_CLIENT_FILE_TEXT = ("""\
  18. solutions = [
  19. { "name"        : "%(solution_name)s",
  20. "url"         : "%(solution_url)s",
  21. "deps_file"   : "%(deps_file)s",
  22. "managed"     : %(managed)s,
  23. "custom_deps" : {
  24. },
  25. "safesync_url": "%(safesync_url)s",
  26. },
  27. ]
  28. cache_dir = %(cache_dir)r
  29. """)
  30. DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
  31. { "name"        : "%(solution_name)s",
  32. "url"         : "%(solution_url)s",
  33. "deps_file"   : "%(deps_file)s",
  34. "managed"     : %(managed)s,
  35. "custom_deps" : {
  36. %(solution_deps)s    },
  37. "safesync_url": "%(safesync_url)s",
  38. },
  39. """)
  40. DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
  41. # Snapshot generated with gclient revinfo --snapshot
  42. solutions = [
  43. %(solution_list)s]
  44. """)
  45. def __init__(self, root_dir, options):
  46. # Do not change previous behavior. Only solution level and immediate DEPS
  47. # are processed.
  48. self._recursion_limit = 2
  49. Dependency.__init__(self, None, None, None, None, True, None, None, None,
  50. ‘unused‘, True)
  51. self._options = options
  52. if options.deps_os:
  53. enforced_os = options.deps_os.split(‘,‘)
  54. else:
  55. enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, ‘unix‘)]
  56. if ‘all‘ in enforced_os:
  57. enforced_os = self.DEPS_OS_CHOICES.itervalues()
  58. self._enforced_os = tuple(set(enforced_os))
  59. self._root_dir = root_dir
  60. self.config_content = None
  61. def SetConfig(self, content):
  62. assert not self.dependencies
  63. config_dict = {}
  64. self.config_content = content
  65. try:
  66. exec(content, config_dict)
  67. except SyntaxError, e:
  68. gclient_utils.SyntaxErrorToError(‘.gclient‘, e)
  69. # Append any target OS that is not already being enforced to the tuple.
  70. target_os = config_dict.get(‘target_os‘, [])
  71. if config_dict.get(‘target_os_only‘, False):
  72. self._enforced_os = tuple(set(target_os))
  73. else:
  74. self._enforced_os = tuple(set(self._enforced_os).union(target_os))
  75. gclient_scm.GitWrapper.cache_dir = config_dict.get(‘cache_dir‘)
  76. if not target_os and config_dict.get(‘target_os_only‘, False):
  77. raise gclient_utils.Error(‘Can\‘t use target_os_only if target_os is ‘
  78. ‘not specified‘)
  79. deps_to_add = []
  80. for s in config_dict.get(‘solutions‘, []):
  81. try:
  82. deps_to_add.append(Dependency(
  83. self, s[‘name‘], s[‘url‘],
  84. s.get(‘safesync_url‘, None),
  85. s.get(‘managed‘, True),
  86. s.get(‘custom_deps‘, {}),
  87. s.get(‘custom_vars‘, {}),
  88. s.get(‘custom_hooks‘, []),
  89. s.get(‘deps_file‘, ‘DEPS‘),
  90. True))
  91. except KeyError:
  92. raise gclient_utils.Error(‘Invalid .gclient file. Solution is ‘
  93. ‘incomplete: %s‘ % s)
  94. self.add_dependencies_and_close(deps_to_add, config_dict.get(‘hooks‘, []))
  95. logging.info(‘SetConfig() done‘)
  96. def SaveConfig(self):
  97. gclient_utils.FileWrite(os.path.join(self.root_dir,
  98. self._options.config_filename),
  99. self.config_content)
  100. def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
  101. """Runs a command on each dependency in a client and its dependencies.
  102. Args:
  103. command: The command to use (e.g., ‘status‘ or ‘diff‘)
  104. args: list of str - extra arguments to add to the command line.
  105. """
  106. if not self.dependencies:
  107. raise gclient_utils.Error(‘No solution specified‘)
  108. revision_overrides = {}
  109. # It‘s unnecessary to check for revision overrides for ‘recurse‘.
  110. # Save a few seconds by not calling _EnforceRevisions() in that case.
  111. if command not in (‘diff‘, ‘recurse‘, ‘runhooks‘, ‘status‘):
  112. revision_overrides = self._EnforceRevisions()
  113. pm = None
  114. # Disable progress for non-tty stdout.
  115. if (sys.stdout.isatty() and not self._options.verbose and progress):
  116. if command in (‘update‘, ‘revert‘):
  117. pm = Progress(‘Syncing projects‘, 1)
  118. elif command == ‘recurse‘:
  119. pm = Progress(‘ ‘.join(args), 1)
  120. work_queue = gclient_utils.ExecutionQueue(
  121. self._options.jobs, pm, ignore_requirements=ignore_requirements)
  122. for s in self.dependencies:
  123. work_queue.enqueue(s)
  124. work_queue.flush(revision_overrides, command, args, options=self._options)
  125. # Once all the dependencies have been processed, it‘s now safe to run the
  126. # hooks.
  127. if not self._options.nohooks:
  128. self.RunHooksRecursively(self._options)
  129. if command == ‘update‘:
  130. # Notify the user if there is an orphaned entry in their working copy.
  131. # Only delete the directory if there are no changes in it, and
  132. # delete_unversioned_trees is set to true.
  133. entries = [i.name for i in self.root.subtree(False) if i.url]
  134. full_entries = [os.path.join(self.root_dir, e.replace(‘/‘, os.path.sep))
  135. for e in entries]
  136. for entry, prev_url in self._ReadEntries().iteritems():
  137. if not prev_url:
  138. # entry must have been overridden via .gclient custom_deps
  139. continue
  140. # Fix path separator on Windows.
  141. entry_fixed = entry.replace(‘/‘, os.path.sep)
  142. e_dir = os.path.join(self.root_dir, entry_fixed)
  143. def _IsParentOfAny(parent, path_list):
  144. parent_plus_slash = parent + ‘/‘
  145. return any(
  146. path[:len(parent_plus_slash)] == parent_plus_slash
  147. for path in path_list)
  148. # Use entry and not entry_fixed there.
  149. if (entry not in entries and
  150. (not any(path.startswith(entry + ‘/‘) for path in entries)) and
  151. os.path.exists(e_dir)):
  152. scm = gclient_scm.CreateSCM(prev_url, self.root_dir, entry_fixed)
  153. # Check to see if this directory is now part of a higher-up checkout.
  154. if scm.GetCheckoutRoot() in full_entries:
  155. logging.info(‘%s is part of a higher level checkout, not ‘
  156. ‘removing.‘, scm.GetCheckoutRoot())
  157. continue
  158. file_list = []
  159. scm.status(self._options, [], file_list)
  160. modified_files = file_list != []
  161. if (not self._options.delete_unversioned_trees or
  162. (modified_files and not self._options.force)):
  163. # There are modified files in this entry. Keep warning until
  164. # removed.
  165. print((‘\nWARNING: \‘%s\‘ is no longer part of this client.  ‘
  166. ‘It is recommended that you manually remove it.\n‘) %
  167. entry_fixed)
  168. else:
  169. # Delete the entry
  170. print(‘\n________ deleting \‘%s\‘ in \‘%s\‘‘ % (
  171. entry_fixed, self.root_dir))
  172. gclient_utils.rmtree(e_dir)
  173. # record the current list of entries for next time
  174. self._SaveEntries()
  175. return 0

client.SetConfig()读取配置文件里的solutions,构建dependencies的list变量deps_to_add,然后调用.add_dependencies_and_close,参数就是包含deps_to_add。在add_dependencies_and_close函数里验证deps,如果是有效的,就加入到依赖树中。

在CMDsync函数里,执行client.RunOnDeps()。它将dependengcies的每个任务加入到gclient_tuils.ExecutionQueue队列并执行。完成所有任务后执行runhook。

[plain] view plaincopy

  1. ######## src/DEPS #############
  2. hooks = [
  3. {
  4. # A change to a .gyp, .gypi, or to GYP itself should run the generator.
  5. "pattern": ".",
  6. "action": ["python", "src/build/gyp_chromium"],
  7. },
  8. ]

上面是src下的DEPS关于hooks的部分,也就是执行完sync命令后,文件更新就执行src/build/gyp_chromium文件。

时间: 2024-08-11 09:33:09

chromium的部署工具depot_tools和gclient的相关文章

Capistrano初探--Ruby快速部署工具

1.Capistrano介绍 是什么?---一种部署工具.(部署就是在生产服务器上安装应用程序,或是更新最新版本:web服务器的启动重启与停止:使网站进入维护状态或将其恢复为常态) 在进行 Rails 部署的时候你可以直接从 svn 或者 git 下面更新代码,运行 db:migrate  来进行数据库的更新,然后进行这样那样的操作后,再启动服务器,便可进行部署,即便你只有一台机器,你也会觉得太麻烦,如果你需要多台机器来运行,那你可 能就会觉得每次手工部署都是一场恶梦,你可以使用 shell 脚

fabric --- Python中的批量远程管理和部署工具

Fabric是Python中一个非常强大的批量远程管理和部署工具,常用于在多个远程PC上批量执行SSH任务. 常见的使用方法大概总结如下: 1, 首先,要将批量执行的任务写入到一个fabfile.py中, # -*- coding:utf-8 -*- from fabric.api import run, local, roles, env, cd env.hosts=[ '192.168.1.110', '192.168.1.111', '192.168.1.112' ] env.user="

OpenStack部署工具总结

目前感觉比较简单直观的部署工具有RDO.devstack.Fuel等: 1. RDO https://openstack.redhat.com/Quickstart REDHAT出品,支持Redhat.CentOS等系统.RDO基于puppet部署各个组件,支持单节点或多节点部署,在Redhat系操作系统上使用非常方便. 2. devstack http://docs.openstack.org/developer/devstack/ 这个应该是最老的Openstack部署工具了,可以用来快速部

Openstack部署工具

Openstack发展很猛,很多朋友都很认同,2013年,会很好的解决OpenStack部署的问题,让安装,配置变得更加简单易用. 很多公司都投入人力去做这个,新浪也计划做一个Openstack的iso,集成OS,当你决定去做这个的时候,那么先了解一下目前的现状.说到部 署,肯定和OS有关,对于Openstack来说,无非就是Ubuntu还是CentOS,当然也会和OpenStack版本有关. 其实部署工具,最麻烦的地方,不是软件的配置和安装,而且网络.用户的网络情况太多,还有Openstack

获取Windows 10(3)之部署工具介绍

Windows 10最终版本将在7月29日即本周三正式发布,相信包括我在内的很多管理员已经对Windows 10预览版进行相关测试,并期望找到一种最佳的部署工具以最快的速度部署到企业内部.熟悉微软产品的IT爱好者都知道微软提供了很多部署工具,如Windows部署服务.Windows To Go,Windows评估和部署工具包(Windows ADK).微软部署工具包(MDT).System Center Configuration Manager(SCCM). Windows 部署服务(WDS)

python 自动化部署工具Fabric简介

自动化部署工具Fabric简介 Fabric就是一个帮助我们在上线时减少重复/繁琐操作的自动化部署利器,对于缺乏成熟运维平台的众多小公司的运维或开发人员来说,掌握这个工具是有必要的. 1. Fabric是什么 Fabric官方文档的描述如下:      Fabric is a Python (2.5-2.7) library and command-line tool for streamlining the use of SSH for application deployment or sy

自动化批量部署工具Ansible笔记之ansible安装与Inventory文件

一.ansible简介 ansible是一款自动化运维部署工具,与saltstack,pupet等不同的是,ansible没有采用C/S的架构,即没有客户端与服务端之分.这也就意味着,ansible的安装更加方便,管理节点更加灵活(任何一台安装了ansible的机器都可以充当管理节点). ansible提供了丰富的模块来方便的完成管理任务,对于复杂的管理任务来说,ansible通过编写playbook的方式来批量执行.而且ansible也可以并发的执行操作,可以同时在多台机器上执行playboo

Spark1.0.0 应用程序部署工具spark-submit

原文链接:http://blog.csdn.net/book_mmicky/article/details/25714545 随着Spark的应用越来越广泛,对支持多资源管理器应用程序部署工具的需求也越来越迫切.Spark1.0.0的出现,这个问题得到了逐步改善.从Spark1.0.0开始,Spark提供了一个容易上手的应用程序部署工具bin/spark-submit,可以完成Spark应用程序在local.Standalone.YARN.Mesos上的快捷部署. 1:使用说明 进入$SPARK

制作U盘版Windows 10部署工具

微软在2012年发布了Windows 7 USB DVD Download Tool,是U盘版Windows 7部署工具,同样适用于Windows 10部署,特别适合个人用户.企业批量部署可以使用MDT或SCCM. 下面来说说Windows 7 USB DVD Download Tool这个工具的使用方法: 下载安装完成后打开这个工具 选择Windows 10安装镜像文件 选择安装介质,这里选择USB设备 选择USB设备,这里使用的是一个8GB  U盘 注意U盘如果有数据请先进行备份,因为系统需