blob: 6f410b48b3a87c204a5035ae5b5ed4a8df87c537 [file] [log] [blame]
Alexander Afanasyev58ea4582014-05-30 08:38:56 +03001# Alexander Afanasyev (UCLA), 2014
Alexander Afanasyev8552a382014-05-15 20:13:42 -07002
Alexander Afanasyev58ea4582014-05-30 08:38:56 +03003"""
4Enable precompiled C++ header support (currently only clang++ and g++ are supported)
5
6To use this tool, wscript should look like:
7
8 def options(opt):
9 opt.load('pch')
10 # This will add `--with-pch` configure option.
11 # Unless --with-pch during configure stage specified, the precompiled header support is disabled
12
13 def configure(conf):
14 conf.load('pch')
15 # this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
16 # Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
17
18 def build(bld):
19 bld(features='cxx pch',
20 target='precompiled-headers',
21 name='precompiled-headers',
22 headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
23
24 # Other parameters to compile precompiled headers
25 # includes=...,
26 # export_includes=...,
27 # use=...,
28 # ...
29
30 # Exported parameters will be propagated even if precompiled headers are disabled
31 )
32
33 bld(
34 target='test',
35 features='cxx cxxprogram',
36 source='a.cpp b.cpp d.cpp main.cpp',
37 use='precompiled-headers',
38 )
39
40 # or
41
42 bld(
43 target='test',
44 features='pch cxx cxxprogram',
45 source='a.cpp b.cpp d.cpp main.cpp',
46 headers='a.h b.h c.h',
47 )
48
49Note 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.
50"""
51
52import os
Davide Pesavento0064c1d2018-03-03 18:43:53 -050053from waflib import Task, TaskGen, Utils
Alexander Afanasyev8552a382014-05-15 20:13:42 -070054from waflib.Tools import c_preproc, cxx
55
Alexander Afanasyev8552a382014-05-15 20:13:42 -070056
57PCH_COMPILER_OPTIONS = {
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030058 'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
59 'g++': [['-include'], '.gch', ['-x', 'c++-header']],
Alexander Afanasyev8552a382014-05-15 20:13:42 -070060}
61
Alexander Afanasyev8552a382014-05-15 20:13:42 -070062
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030063def options(opt):
64 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++)''')
Alexander Afanasyev8552a382014-05-15 20:13:42 -070065
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030066def configure(conf):
67 if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
68 conf.env.WITH_PCH = True
69 flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
70 conf.env.CXXPCH_F = flags[0]
71 conf.env.CXXPCH_EXT = flags[1]
72 conf.env.CXXPCH_FLAGS = flags[2]
Alexander Afanasyev8552a382014-05-15 20:13:42 -070073
Alexander Afanasyev8552a382014-05-15 20:13:42 -070074
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030075@TaskGen.feature('pch')
76@TaskGen.before('process_source')
77def apply_pch(self):
78 if not self.env.WITH_PCH:
79 return
Alexander Afanasyev8552a382014-05-15 20:13:42 -070080
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030081 if getattr(self.bld, 'pch_tasks', None) is None:
82 self.bld.pch_tasks = {}
Alexander Afanasyev8552a382014-05-15 20:13:42 -070083
Davide Pesaventodc26b272014-06-27 16:41:51 +020084 if getattr(self, 'headers', None) is None:
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030085 return
Alexander Afanasyev8552a382014-05-15 20:13:42 -070086
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030087 self.headers = self.to_nodes(self.headers)
Alexander Afanasyev8552a382014-05-15 20:13:42 -070088
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030089 if getattr(self, 'name', None):
90 try:
Davide Pesavento0064c1d2018-03-03 18:43:53 -050091 task = self.bld.pch_tasks["%s.%s" % (self.name, self.idx)]
92 self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030093 except KeyError:
94 pass
Alexander Afanasyev8552a382014-05-15 20:13:42 -070095
Alexander Afanasyev58ea4582014-05-30 08:38:56 +030096 out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
97 out = self.path.find_or_declare(out)
98 task = self.create_task('gchx', self.headers, out)
Alexander Afanasyev8552a382014-05-15 20:13:42 -070099
Alexander Afanasyev58ea4582014-05-30 08:38:56 +0300100 # target should be an absolute path of `out`, but without precompiled header extension
101 task.target = out.abspath()[:-len(out.suffix())]
102
Davide Pesaventodc26b272014-06-27 16:41:51 +0200103 self.pch_task = task
Alexander Afanasyev58ea4582014-05-30 08:38:56 +0300104 if getattr(self, 'name', None):
Davide Pesavento0064c1d2018-03-03 18:43:53 -0500105 self.bld.pch_tasks["%s.%s" % (self.name, self.idx)] = task
Alexander Afanasyev58ea4582014-05-30 08:38:56 +0300106
107@TaskGen.feature('cxx')
108@TaskGen.after_method('process_source', 'propagate_uselib_vars')
109def add_pch(self):
110 if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
111 return
112
113 pch = None
114 # find pch task, if any
115
116 if getattr(self, 'pch_task', None):
117 pch = self.pch_task
118 else:
119 for use in Utils.to_list(self.use):
120 try:
121 pch = self.bld.pch_tasks[use]
122 except KeyError:
123 pass
124
125 if pch:
126 for x in self.compiled_tasks:
127 x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
128
129class gchx(Task.Task):
Davide Pesavento0064c1d2018-03-03 18:43:53 -0500130 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}'
Alexander Afanasyev58ea4582014-05-30 08:38:56 +0300131 scan = c_preproc.scan
132 color = 'BLUE'
133 ext_out=['.h']
134
135 def runnable_status(self):
136 try:
137 node_deps = self.generator.bld.node_deps[self.uid()]
138 except KeyError:
139 node_deps = []
140 ret = Task.Task.runnable_status(self)
141 if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
142 t = os.stat(self.outputs[0].abspath()).st_mtime
143 for n in self.inputs + node_deps:
144 if os.stat(n.abspath()).st_mtime > t:
145 return Task.RUN_ME
146 return ret