summaryrefslogtreecommitdiffstats
path: root/extras/pch.py
diff options
context:
space:
mode:
Diffstat (limited to 'extras/pch.py')
-rw-r--r--extras/pch.py148
1 files changed, 148 insertions, 0 deletions
diff --git a/extras/pch.py b/extras/pch.py
new file mode 100644
index 0000000..103e752
--- /dev/null
+++ b/extras/pch.py
@@ -0,0 +1,148 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# Alexander Afanasyev (UCLA), 2014
+
+"""
+Enable precompiled C++ header support (currently only clang++ and g++ are supported)
+
+To use this tool, wscript should look like:
+
+ def options(opt):
+ opt.load('pch')
+ # This will add `--with-pch` configure option.
+ # Unless --with-pch during configure stage specified, the precompiled header support is disabled
+
+ def configure(conf):
+ conf.load('pch')
+ # this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
+ # Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
+
+ def build(bld):
+ bld(features='cxx pch',
+ target='precompiled-headers',
+ name='precompiled-headers',
+ headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
+
+ # Other parameters to compile precompiled headers
+ # includes=...,
+ # export_includes=...,
+ # use=...,
+ # ...
+
+ # Exported parameters will be propagated even if precompiled headers are disabled
+ )
+
+ bld(
+ target='test',
+ features='cxx cxxprogram',
+ source='a.cpp b.cpp d.cpp main.cpp',
+ use='precompiled-headers',
+ )
+
+ # or
+
+ bld(
+ target='test',
+ features='pch cxx cxxprogram',
+ source='a.cpp b.cpp d.cpp main.cpp',
+ headers='a.h b.h c.h',
+ )
+
+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.
+"""
+
+import os
+from waflib import Task, TaskGen, Utils
+from waflib.Tools import c_preproc, cxx
+
+
+PCH_COMPILER_OPTIONS = {
+ 'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
+ 'g++': [['-include'], '.gch', ['-x', 'c++-header']],
+}
+
+
+def options(opt):
+ 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++)''')
+
+def configure(conf):
+ if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
+ conf.env.WITH_PCH = True
+ flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
+ conf.env.CXXPCH_F = flags[0]
+ conf.env.CXXPCH_EXT = flags[1]
+ conf.env.CXXPCH_FLAGS = flags[2]
+
+
+@TaskGen.feature('pch')
+@TaskGen.before('process_source')
+def apply_pch(self):
+ if not self.env.WITH_PCH:
+ return
+
+ if getattr(self.bld, 'pch_tasks', None) is None:
+ self.bld.pch_tasks = {}
+
+ if getattr(self, 'headers', None) is None:
+ return
+
+ self.headers = self.to_nodes(self.headers)
+
+ if getattr(self, 'name', None):
+ try:
+ task = self.bld.pch_tasks["%s.%s" % (self.name, self.idx)]
+ self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
+ except KeyError:
+ pass
+
+ out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
+ out = self.path.find_or_declare(out)
+ task = self.create_task('gchx', self.headers, out)
+
+ # target should be an absolute path of `out`, but without precompiled header extension
+ task.target = out.abspath()[:-len(out.suffix())]
+
+ self.pch_task = task
+ if getattr(self, 'name', None):
+ self.bld.pch_tasks["%s.%s" % (self.name, self.idx)] = task
+
+@TaskGen.feature('cxx')
+@TaskGen.after_method('process_source', 'propagate_uselib_vars')
+def add_pch(self):
+ if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
+ return
+
+ pch = None
+ # find pch task, if any
+
+ if getattr(self, 'pch_task', None):
+ pch = self.pch_task
+ else:
+ for use in Utils.to_list(self.use):
+ try:
+ pch = self.bld.pch_tasks[use]
+ except KeyError:
+ pass
+
+ if pch:
+ for x in self.compiled_tasks:
+ x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
+
+class gchx(Task.Task):
+ 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}'
+ scan = c_preproc.scan
+ color = 'BLUE'
+ ext_out=['.h']
+
+ def runnable_status(self):
+ try:
+ node_deps = self.generator.bld.node_deps[self.uid()]
+ except KeyError:
+ node_deps = []
+ ret = Task.Task.runnable_status(self)
+ if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
+ t = os.stat(self.outputs[0].abspath()).st_mtime
+ for n in self.inputs + node_deps:
+ if os.stat(n.abspath()).st_mtime > t:
+ return Task.RUN_ME
+ return ret