waf: upgrade to 2.0.21
[vlendec/samba-autobuild/.git] / third_party / waf / waflib / extras / pch.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # Alexander Afanasyev (UCLA), 2014
4
5 """
6 Enable precompiled C++ header support (currently only clang++ and g++ are supported)
7
8 To use this tool, wscript should look like:
9
10         def options(opt):
11                 opt.load('pch')
12                 # This will add `--with-pch` configure option.
13                 # Unless --with-pch during configure stage specified, the precompiled header support is disabled
14
15         def configure(conf):
16                 conf.load('pch')
17                 # this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
18                 # Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
19
20         def build(bld):
21                 bld(features='cxx pch',
22                         target='precompiled-headers',
23                         name='precompiled-headers',
24                         headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
25
26                         # Other parameters to compile precompiled headers
27                         # includes=...,
28                         # export_includes=...,
29                         # use=...,
30                         # ...
31
32                         # Exported parameters will be propagated even if precompiled headers are disabled
33                 )
34
35                 bld(
36                         target='test',
37                         features='cxx cxxprogram',
38                         source='a.cpp b.cpp d.cpp main.cpp',
39                         use='precompiled-headers',
40                 )
41
42                 # or
43
44                 bld(
45                         target='test',
46                         features='pch cxx cxxprogram',
47                         source='a.cpp b.cpp d.cpp main.cpp',
48                         headers='a.h b.h c.h',
49                 )
50
51 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.
52 """
53
54 import os
55 from waflib import Task, TaskGen, Utils
56 from waflib.Tools import c_preproc, cxx
57
58
59 PCH_COMPILER_OPTIONS = {
60         'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
61         'g++':     [['-include'], '.gch', ['-x', 'c++-header']],
62 }
63
64
65 def options(opt):
66         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++)''')
67
68 def configure(conf):
69         if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
70                 conf.env.WITH_PCH = True
71                 flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
72                 conf.env.CXXPCH_F = flags[0]
73                 conf.env.CXXPCH_EXT = flags[1]
74                 conf.env.CXXPCH_FLAGS = flags[2]
75
76
77 @TaskGen.feature('pch')
78 @TaskGen.before('process_source')
79 def apply_pch(self):
80         if not self.env.WITH_PCH:
81                 return
82
83         if getattr(self.bld, 'pch_tasks', None) is None:
84                 self.bld.pch_tasks = {}
85
86         if getattr(self, 'headers', None) is None:
87                 return
88
89         self.headers = self.to_nodes(self.headers)
90
91         if getattr(self, 'name', None):
92                 try:
93                         task = self.bld.pch_tasks[self.name]
94                         self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
95                 except KeyError:
96                         pass
97
98         out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
99         out = self.path.find_or_declare(out)
100         task = self.create_task('gchx', self.headers, out)
101
102         # target should be an absolute path of `out`, but without precompiled header extension
103         task.target = out.abspath()[:-len(out.suffix())]
104
105         self.pch_task = task
106         if getattr(self, 'name', None):
107                 self.bld.pch_tasks[self.name] = task
108
109 @TaskGen.feature('cxx')
110 @TaskGen.after_method('process_source', 'propagate_uselib_vars')
111 def add_pch(self):
112         if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
113                 return
114
115         pch = None
116         # find pch task, if any
117
118         if getattr(self, 'pch_task', None):
119                 pch = self.pch_task
120         else:
121                 for use in Utils.to_list(self.use):
122                         try:
123                                 pch = self.bld.pch_tasks[use]
124                         except KeyError:
125                                 pass
126
127         if pch:
128                 for x in self.compiled_tasks:
129                         x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
130
131 class gchx(Task.Task):
132         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}'
133         scan    = c_preproc.scan
134         color   = 'BLUE'
135         ext_out=['.h']
136
137         def runnable_status(self):
138                 try:
139                         node_deps = self.generator.bld.node_deps[self.uid()]
140                 except KeyError:
141                         node_deps = []
142                 ret = Task.Task.runnable_status(self)
143                 if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
144                         t = os.stat(self.outputs[0].abspath()).st_mtime
145                         for n in self.inputs + node_deps:
146                                 if os.stat(n.abspath()).st_mtime > t:
147                                         return Task.RUN_ME
148                 return ret