2014-05-16 03:55:33 +02:00
#! /usr/bin/env python
# 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 .
"""
2014-05-24 09:01:31 +02:00
import os
2014-05-30 20:35:09 +02:00
from waflib import Task , TaskGen , Utils
2014-05-16 03:55:33 +02:00
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 ) :
2014-05-21 01:37:14 +02:00
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++) ''' )
2014-05-16 03:55:33 +02:00
def configure ( conf ) :
if ( conf . options . with_pch and conf . env [ ' COMPILER_CXX ' ] in PCH_COMPILER_OPTIONS . keys ( ) ) :
2014-05-21 01:37:14 +02:00
conf . env . WITH_PCH = True
2014-05-16 03:55:33 +02:00
flags = PCH_COMPILER_OPTIONS [ conf . env [ ' COMPILER_CXX ' ] ]
2014-05-21 01:37:14 +02:00
conf . env . CXXPCH_F = flags [ 0 ]
conf . env . CXXPCH_EXT = flags [ 1 ]
conf . env . CXXPCH_FLAGS = flags [ 2 ]
2014-05-16 03:55:33 +02:00
@TaskGen.feature ( ' pch ' )
@TaskGen.before ( ' process_source ' )
def apply_pch ( self ) :
2014-05-21 01:37:14 +02:00
if not self . env . WITH_PCH :
return
2014-05-16 03:55:33 +02:00
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 [ self . name ]
2014-05-21 01:37:14 +02:00
self . bld . fatal ( " Duplicated ' pch ' task with name %r " % self . name )
2014-05-16 03:55:33 +02:00
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 [ self . name ] = task
@TaskGen.feature ( ' cxx ' )
2014-05-29 07:23:57 +02:00
@TaskGen.after_method ( ' process_source ' , ' propagate_uselib_vars ' )
2014-05-16 03:55:33 +02:00
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 :
2014-05-29 07:23:57 +02:00
for x in self . compiled_tasks :
x . env . append_value ( ' CXXFLAGS ' , self . env [ ' CXXPCH_F ' ] + [ pch . target ] )
2014-05-16 03:55:33 +02:00
class gchx ( Task . Task ) :
run_str = ' $ {CXX} $ { ARCH_ST:ARCH} $ {CXXFLAGS} $ {CPPFLAGS} $ {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()} '
scan = c_preproc . scan
color = ' BLUE '
ext_out = [ ' .h ' ]
2014-05-24 09:01:31 +02:00
def runnable_status ( self ) :
2014-05-30 20:35:09 +02:00
try :
node_deps = self . generator . bld . node_deps [ self . uid ( ) ]
except KeyError :
node_deps = [ ]
2014-05-24 09:01:31 +02:00
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
2014-05-30 20:35:09 +02:00
for n in self . inputs + node_deps :
2014-05-24 09:01:31 +02:00
if os . stat ( n . abspath ( ) ) . st_mtime > t :
return Task . RUN_ME
return ret