3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Alexander Afanasyev (UCLA), 2014
10 Enable precompiled C++ header support (currently only clang++ and g++ are supported)
12 To use this tool, wscript should look like:
16 # This will add `--with-pch` configure option.
17 # Unless --with-pch during configure stage specified, the precompiled header support is disabled
21 # this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
22 # Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
25 bld(features='cxx pch',
26 target='precompiled-headers',
27 name='precompiled-headers',
28 headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
30 # Other parameters to compile precompiled headers
32 # export_includes=...,
36 # Exported parameters will be propagated even if precompiled headers are disabled
41 features='cxx cxxprogram',
42 source='a.cpp b.cpp d.cpp main.cpp',
43 use='precompiled-headers',
50 features='pch cxx cxxprogram',
51 source='a.cpp b.cpp d.cpp main.cpp',
52 headers='a.h b.h c.h',
55 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.
59 from waflib import Task, TaskGen, Utils
60 from waflib.Tools import c_preproc, cxx
63 PCH_COMPILER_OPTIONS = {
64 'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
65 'g++': [['-include'], '.gch', ['-x', 'c++-header']],
70 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++)''')
73 if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
74 conf.env.WITH_PCH = True
75 flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
76 conf.env.CXXPCH_F = flags[0]
77 conf.env.CXXPCH_EXT = flags[1]
78 conf.env.CXXPCH_FLAGS = flags[2]
81 @TaskGen.feature('pch')
82 @TaskGen.before('process_source')
84 if not self.env.WITH_PCH:
87 if getattr(self.bld, 'pch_tasks', None) is None:
88 self.bld.pch_tasks = {}
90 if getattr(self, 'headers', None) is None:
93 self.headers = self.to_nodes(self.headers)
95 if getattr(self, 'name', None):
97 task = self.bld.pch_tasks[self.name]
98 self.bld.fatal("Duplicated 'pch' task with name %r" % self.name)
102 out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
103 out = self.path.find_or_declare(out)
104 task = self.create_task('gchx', self.headers, out)
106 # target should be an absolute path of `out`, but without precompiled header extension
107 task.target = out.abspath()[:-len(out.suffix())]
110 if getattr(self, 'name', None):
111 self.bld.pch_tasks[self.name] = task
113 @TaskGen.feature('cxx')
114 @TaskGen.after_method('process_source', 'propagate_uselib_vars')
116 if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
120 # find pch task, if any
122 if getattr(self, 'pch_task', None):
125 for use in Utils.to_list(self.use):
127 pch = self.bld.pch_tasks[use]
132 for x in self.compiled_tasks:
133 x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
135 class gchx(Task.Task):
136 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}'
137 scan = c_preproc.scan
141 def runnable_status(self):
143 node_deps = self.generator.bld.node_deps[self.uid()]
146 ret = Task.Task.runnable_status(self)
147 if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
148 t = os.stat(self.outputs[0].abspath()).st_mtime
149 for n in self.inputs + node_deps:
150 if os.stat(n.abspath()).st_mtime > t: