clang_compilation_database.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Christoph Koke, 2013
  4. # Alibek Omarov, 2019
  5. """
  6. Writes the c and cpp compile commands into build/compile_commands.json
  7. see http://clang.llvm.org/docs/JSONCompilationDatabase.html
  8. Usage:
  9. Load this tool in `options` to be able to generate database
  10. by request in command-line and before build:
  11. $ waf clangdb
  12. def options(opt):
  13. opt.load('clang_compilation_database')
  14. Otherwise, load only in `configure` to generate it always before build.
  15. def configure(conf):
  16. conf.load('compiler_cxx')
  17. ...
  18. conf.load('clang_compilation_database')
  19. """
  20. from waflib import Logs, TaskGen, Task, Build, Scripting
  21. Task.Task.keep_last_cmd = True
  22. class ClangDbContext(Build.BuildContext):
  23. '''generates compile_commands.json by request'''
  24. cmd = 'clangdb'
  25. def write_compilation_database(self):
  26. """
  27. Write the clang compilation database as JSON
  28. """
  29. database_file = self.bldnode.make_node('compile_commands.json')
  30. Logs.info('Build commands will be stored in %s', database_file.path_from(self.path))
  31. try:
  32. root = database_file.read_json()
  33. except IOError:
  34. root = []
  35. clang_db = dict((x['file'], x) for x in root)
  36. for task in self.clang_compilation_database_tasks:
  37. try:
  38. cmd = task.last_cmd
  39. except AttributeError:
  40. continue
  41. f_node = task.inputs[0]
  42. filename = f_node.path_from(task.get_cwd())
  43. entry = {
  44. "directory": task.get_cwd().abspath(),
  45. "arguments": cmd,
  46. "file": filename,
  47. }
  48. clang_db[filename] = entry
  49. root = list(clang_db.values())
  50. database_file.write_json(root)
  51. def execute(self):
  52. """
  53. Build dry run
  54. """
  55. self.restore()
  56. self.cur_tasks = []
  57. self.clang_compilation_database_tasks = []
  58. if not self.all_envs:
  59. self.load_envs()
  60. self.recurse([self.run_dir])
  61. self.pre_build()
  62. # we need only to generate last_cmd, so override
  63. # exec_command temporarily
  64. def exec_command(self, *k, **kw):
  65. return 0
  66. for g in self.groups:
  67. for tg in g:
  68. try:
  69. f = tg.post
  70. except AttributeError:
  71. pass
  72. else:
  73. f()
  74. if isinstance(tg, Task.Task):
  75. lst = [tg]
  76. else: lst = tg.tasks
  77. for tsk in lst:
  78. if tsk.__class__.__name__ == "swig":
  79. tsk.runnable_status()
  80. if hasattr(tsk, 'more_tasks'):
  81. lst.extend(tsk.more_tasks)
  82. # Not all dynamic tasks can be processed, in some cases
  83. # one may have to call the method "run()" like this:
  84. #elif tsk.__class__.__name__ == 'src2c':
  85. # tsk.run()
  86. # if hasattr(tsk, 'more_tasks'):
  87. # lst.extend(tsk.more_tasks)
  88. tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
  89. if isinstance(tsk, tup):
  90. self.clang_compilation_database_tasks.append(tsk)
  91. tsk.nocache = True
  92. old_exec = tsk.exec_command
  93. tsk.exec_command = exec_command
  94. tsk.run()
  95. tsk.exec_command = old_exec
  96. self.write_compilation_database()
  97. EXECUTE_PATCHED = False
  98. def patch_execute():
  99. global EXECUTE_PATCHED
  100. if EXECUTE_PATCHED:
  101. return
  102. def new_execute_build(self):
  103. """
  104. Invoke clangdb command before build
  105. """
  106. if self.cmd.startswith('build'):
  107. Scripting.run_command(self.cmd.replace('build','clangdb'))
  108. old_execute_build(self)
  109. old_execute_build = getattr(Build.BuildContext, 'execute_build', None)
  110. setattr(Build.BuildContext, 'execute_build', new_execute_build)
  111. EXECUTE_PATCHED = True
  112. patch_execute()