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