12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Autocompletion config for YouCompleteMe in Chromium.
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# USAGE:
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   1. Install YCM [https://github.com/Valloric/YouCompleteMe]
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#          (Googlers should check out [go/ycm])
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   2. Point to this config file in your .vimrc:
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#          let g:ycm_global_ycm_extra_conf =
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#              '<chrome_depot>/src/tools/vim/chromium.ycm_extra_conf.py'
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   3. Profit
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Usage notes:
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   * You must use ninja & clang to build Chromium.
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   * You must have run gyp_chromium and built Chromium recently.
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Hacking notes:
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   * The purpose of this script is to construct an accurate enough command line
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#     for YCM to pass to clang so it can build and extract the symbols.
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   * Right now, we only pull the -I and -D flags. That seems to be sufficient
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#     for everything I've used it for.
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   * That whole ninja & clang thing? We could support other configs if someone
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#     were willing to write the correct commands and a parser.
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#   * This has only been tested on gPrecise.
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import os
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import subprocess
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Flags from YCM's default config.
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)flags = [
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'-DUSE_CLANG_COMPLETER',
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'-std=c++11',
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'-x',
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'c++',
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)]
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def PathExists(*args):
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return os.path.exists(os.path.join(*args))
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def FindChromeSrcFromFilename(filename):
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Searches for the root of the Chromium checkout.
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Simply checks parent directories until it finds .gclient and src/.
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Args:
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    filename: (String) Path to source file being edited.
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Returns:
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (String) Path of 'src/', or None if unable to find.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  curdir = os.path.normpath(os.path.dirname(filename))
6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  while not (PathExists(curdir, 'src') and PathExists(curdir, 'src', 'DEPS')
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)             and (PathExists(curdir, '.gclient')
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  or PathExists(curdir, 'src', '.git'))):
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    nextdir = os.path.normpath(os.path.join(curdir, '..'))
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if nextdir == curdir:
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return None
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    curdir = nextdir
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return os.path.join(curdir, 'src')
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
79b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)# Largely copied from ninja-build.vim (guess_configuration)
80b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)def GetNinjaOutputDirectory(chrome_root):
81b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  """Returns either <chrome_root>/out/Release or <chrome_root>/out/Debug.
82b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
83b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  The configuration chosen is the one most recently generated/built."""
84b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  root = os.path.join(chrome_root, 'out')
85b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  debug_path = os.path.join(root, 'Debug')
86b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  release_path = os.path.join(root, 'Release')
87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def is_release_15s_newer(test_path):
89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    try:
90b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      debug_mtime = os.path.getmtime(os.path.join(debug_path, test_path))
91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    except os.error:
92b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      debug_mtime = 0
93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    try:
94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      rel_mtime = os.path.getmtime(os.path.join(release_path, test_path))
95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    except os.error:
96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      rel_mtime = 0
97b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return rel_mtime - debug_mtime >= 15
98b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
99b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if is_release_15s_newer('build.ninja') or is_release_15s_newer('protoc'):
100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return release_path
101b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return debug_path
102b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
103b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def GetClangCommandFromNinjaForFilename(chrome_root, filename):
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Returns the command line to build |filename|.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Asks ninja how it would build the source file. If the specified file is a
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  header, tries to find its companion source file first.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Args:
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    chrome_root: (String) Path to src/.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    filename: (String) Path to source file being edited.
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Returns:
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (List of Strings) Command line arguments for clang.
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if not chrome_root:
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return []
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Generally, everyone benefits from including Chromium's src/, because all of
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Chromium's includes are relative to that.
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome_flags = ['-I' + os.path.join(chrome_root)]
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Header files can't be built. Instead, try to match a header file to its
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # corresponding source file.
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if filename.endswith('.h'):
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    alternates = ['.cc', '.cpp']
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for alt_extension in alternates:
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      alt_name = filename[:-2] + alt_extension
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if os.path.exists(alt_name):
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        filename = alt_name
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # If this is a standalone .h file with no source, the best we can do is
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # try to use the default flags.
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return chrome_flags
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Ninja needs the path to the source file from the output build directory.
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Cut off the common part and /.
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  subdir_filename = filename[len(chrome_root)+1:]
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  rel_filename = os.path.join('..', '..', subdir_filename)
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
143b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  out_dir = GetNinjaOutputDirectory(chrome_root)
144b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Ask ninja how it would build our source file.
146b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  p = subprocess.Popen(['ninja', '-v', '-C', out_dir, '-t',
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        'commands', rel_filename + '^'],
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       stdout=subprocess.PIPE)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  stdout, stderr = p.communicate()
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if p.returncode:
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return chrome_flags
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Ninja might execute several commands to build something. We want the last
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # clang command.
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  clang_line = None
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for line in reversed(stdout.split('\n')):
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'clang' in line:
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      clang_line = line
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else:
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return chrome_flags
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Parse out the -I and -D flags. These seem to be the only ones that are
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # important for YCM's purposes.
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for flag in clang_line.split(' '):
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if flag.startswith('-I'):
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Relative paths need to be resolved, because they're relative to the
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # output dir, not the source.
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if flag[2] == '/':
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        chrome_flags.append(flag)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
172b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        abs_path = os.path.normpath(os.path.join(out_dir, flag[2:]))
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        chrome_flags.append('-I' + abs_path)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif flag.startswith('-') and flag[1] in 'DWFfmO':
1757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if flag == '-Wno-deprecated-register' or flag == '-Wno-header-guard':
1767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        # These flags causes libclang (3.3) to crash. Remove it until things
1777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        # are fixed.
1787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        continue
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome_flags.append(flag)
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return chrome_flags
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def FlagsForFile(filename):
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """This is the main entry point for YCM. Its interface is fixed.
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Args:
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    filename: (String) Path to source file being edited.
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Returns:
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (Dictionary)
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'flags': (List of Strings) Command line flags.
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'do_cache': (Boolean) True if the result should be cached.
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome_root = FindChromeSrcFromFilename(filename)
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome_flags = GetClangCommandFromNinjaForFilename(chrome_root,
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                     filename)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  final_flags = flags + chrome_flags
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return {
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    'flags': final_flags,
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    'do_cache': True
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
204