123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- #! /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
|