1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
#! /usr/bin/env python3
# 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
|