waf

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

pch.py (4384B)


      1 #! /usr/bin/env python
      2 # encoding: utf-8
      3 # Alexander Afanasyev (UCLA), 2014
      4 
      5 """
      6 Enable precompiled C++ header support (currently only clang++ and g++ are supported)
      7 
      8 To use this tool, wscript should look like:
      9 
     10 	def options(opt):
     11 		opt.load('pch')
     12 		# This will add `--with-pch` configure option.
     13 		# Unless --with-pch during configure stage specified, the precompiled header support is disabled
     14 
     15 	def configure(conf):
     16 		conf.load('pch')
     17 		# this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
     18 		# Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
     19 
     20 	def build(bld):
     21 		bld(features='cxx pch',
     22 			target='precompiled-headers',
     23 			name='precompiled-headers',
     24 			headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
     25 
     26 			# Other parameters to compile precompiled headers
     27 			# includes=...,
     28 			# export_includes=...,
     29 			# use=...,
     30 			# ...
     31 
     32 			# Exported parameters will be propagated even if precompiled headers are disabled
     33 		)
     34 
     35 		bld(
     36 			target='test',
     37 			features='cxx cxxprogram',
     38 			source='a.cpp b.cpp d.cpp main.cpp',
     39 			use='precompiled-headers',
     40 		)
     41 
     42 		# or
     43 
     44 		bld(
     45 			target='test',
     46 			features='pch cxx cxxprogram',
     47 			source='a.cpp b.cpp d.cpp main.cpp',
     48 			headers='a.h b.h c.h',
     49 		)
     50 
     51 Note that precompiled header must have multiple inclusion guards.  If the guards are missing, any benefit of precompiled header will be voided and compilation may fail in some cases.
     52 """
     53 
     54 import os
     55 from waflib import Task, TaskGen, Utils
     56 from waflib.Tools import c_preproc, cxx
     57 
     58 
     59 PCH_COMPILER_OPTIONS = {
     60 	'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
     61 	'g++':     [['-include'], '.gch', ['-x', 'c++-header']],
     62 }
     63 
     64 
     65 def options(opt):
     66 	opt.add_option('--without-pch', action='store_false', default=True, dest='with_pch', help='''Try to use precompiled header to speed up compilation (only g++ and clang++)''')
     67 
     68 def configure(conf):
     69 	if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
     70 		conf.env.WITH_PCH = True
     71 		flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
     72 		conf.env.CXXPCH_F = flags[0]
     73 		conf.env.CXXPCH_EXT = flags[1]
     74 		conf.env.CXXPCH_FLAGS = flags[2]
     75 
     76 
     77 @TaskGen.feature('pch')
     78 @TaskGen.before('process_source')
     79 def apply_pch(self):
     80 	if not self.env.WITH_PCH:
     81 		return
     82 
     83 	if getattr(self.bld, 'pch_tasks', None) is None:
     84 		self.bld.pch_tasks = {}
     85 
     86 	if getattr(self, 'headers', None) is None:
     87 		return
     88 
     89 	self.headers = self.to_nodes(self.headers)
     90 
     91 	if getattr(self, 'name', None):
     92 		try:
     93 			task = self.bld.pch_tasks[self.name]
     94 			self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
     95 		except KeyError:
     96 			pass
     97 
     98 	out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
     99 	out = self.path.find_or_declare(out)
    100 	task = self.create_task('gchx', self.headers, out)
    101 
    102 	# target should be an absolute path of `out`, but without precompiled header extension
    103 	task.target = out.abspath()[:-len(out.suffix())]
    104 
    105 	self.pch_task = task
    106 	if getattr(self, 'name', None):
    107 		self.bld.pch_tasks[self.name] = task
    108 
    109 @TaskGen.feature('cxx')
    110 @TaskGen.after_method('process_source', 'propagate_uselib_vars')
    111 def add_pch(self):
    112 	if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
    113 		return
    114 
    115 	pch = None
    116 	# find pch task, if any
    117 
    118 	if getattr(self, 'pch_task', None):
    119 		pch = self.pch_task
    120 	else:
    121 		for use in Utils.to_list(self.use):
    122 			try:
    123 				pch = self.bld.pch_tasks[use]
    124 			except KeyError:
    125 				pass
    126 
    127 	if pch:
    128 		for x in self.compiled_tasks:
    129 			x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
    130 
    131 class gchx(Task.Task):
    132 	run_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${CXXPCH_FLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXXPCH_F:SRC} ${CXX_SRC_F}${SRC[0].abspath()} ${CXX_TGT_F}${TGT[0].abspath()} ${CPPFLAGS}'
    133 	scan    = c_preproc.scan
    134 	color   = 'BLUE'
    135 	ext_out=['.h']
    136 
    137 	def runnable_status(self):
    138 		try:
    139 			node_deps = self.generator.bld.node_deps[self.uid()]
    140 		except KeyError:
    141 			node_deps = []
    142 		ret = Task.Task.runnable_status(self)
    143 		if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
    144 			t = os.stat(self.outputs[0].abspath()).st_mtime
    145 			for n in self.inputs + node_deps:
    146 				if os.stat(n.abspath()).st_mtime > t:
    147 					return Task.RUN_ME
    148 		return ret