waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

clang_compilation_database.py (3412B)


      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 			ClangDbContext.variant = self.variant
    130 			Scripting.run_command(self.cmd.replace('build','clangdb'))
    131 
    132 		old_execute_build(self)
    133 
    134 	old_execute_build = getattr(Build.BuildContext, 'execute_build', None)
    135 	setattr(Build.BuildContext, 'execute_build', new_execute_build)
    136 	EXECUTE_PATCHED = True
    137 
    138 patch_execute()