Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 1 | # Alexander Afanasyev (UCLA), 2014 |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 2 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 3 | """ |
| 4 | Enable precompiled C++ header support (currently only clang++ and g++ are supported) |
| 5 | |
| 6 | To 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 | |
| 49 | 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. |
| 50 | """ |
| 51 | |
| 52 | import os |
Davide Pesavento | 0064c1d | 2018-03-03 18:43:53 -0500 | [diff] [blame] | 53 | from waflib import Task, TaskGen, Utils |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 54 | from waflib.Tools import c_preproc, cxx |
| 55 | |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 56 | |
| 57 | PCH_COMPILER_OPTIONS = { |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 58 | 'clang++': [['-include'], '.pch', ['-x', 'c++-header']], |
| 59 | 'g++': [['-include'], '.gch', ['-x', 'c++-header']], |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 60 | } |
| 61 | |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 62 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 63 | def 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 Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 65 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 66 | def 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 Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 73 | |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 74 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 75 | @TaskGen.feature('pch') |
| 76 | @TaskGen.before('process_source') |
| 77 | def apply_pch(self): |
| 78 | if not self.env.WITH_PCH: |
| 79 | return |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 80 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 81 | if getattr(self.bld, 'pch_tasks', None) is None: |
| 82 | self.bld.pch_tasks = {} |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 83 | |
Davide Pesavento | dc26b27 | 2014-06-27 16:41:51 +0200 | [diff] [blame] | 84 | if getattr(self, 'headers', None) is None: |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 85 | return |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 86 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 87 | self.headers = self.to_nodes(self.headers) |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 88 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 89 | if getattr(self, 'name', None): |
| 90 | try: |
Davide Pesavento | 0064c1d | 2018-03-03 18:43:53 -0500 | [diff] [blame] | 91 | 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 Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 93 | except KeyError: |
| 94 | pass |
Alexander Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 95 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 96 | 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 Afanasyev | 8552a38 | 2014-05-15 20:13:42 -0700 | [diff] [blame] | 99 | |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 100 | # target should be an absolute path of `out`, but without precompiled header extension |
| 101 | task.target = out.abspath()[:-len(out.suffix())] |
| 102 | |
Davide Pesavento | dc26b27 | 2014-06-27 16:41:51 +0200 | [diff] [blame] | 103 | self.pch_task = task |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 104 | if getattr(self, 'name', None): |
Davide Pesavento | 0064c1d | 2018-03-03 18:43:53 -0500 | [diff] [blame] | 105 | self.bld.pch_tasks["%s.%s" % (self.name, self.idx)] = task |
Alexander Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 106 | |
| 107 | @TaskGen.feature('cxx') |
| 108 | @TaskGen.after_method('process_source', 'propagate_uselib_vars') |
| 109 | def 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 | |
| 129 | class gchx(Task.Task): |
Davide Pesavento | 0064c1d | 2018-03-03 18:43:53 -0500 | [diff] [blame] | 130 | 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 Afanasyev | 58ea458 | 2014-05-30 08:38:56 +0300 | [diff] [blame] | 131 | 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 |