#!/usr/bin/env python
# encoding: utf-8
#
# partially based on boost.py written by Gernot Vormayr
# written by Ruediger Sonderfeld <ruediger@c-plusplus.de>, 2008
# modified by Bjoern Michaelsen, 2008
# modified by Luca Fossati, 2008
# rewritten for waf 1.5.1, Thomas Nagy, 2008
#
#def set_options(opt):
# opt.tool_options('boost')
# # ...
#
#def configure(conf):
# # ... (e.g. conf.check_tool('g++'))
# conf.check_tool('boost')
# conf.check_boost(lib='signals filesystem', static='onlystatic', score_version=(-1000, 1000), tag_minscore=1000)
#
#def build(bld):
# bld(source='main.c', target='bar', uselib="BOOST BOOST_SYSTEM")
#
#ISSUES:
# * find_includes should be called only once!
# * support mandatory
######## boost update ###########
## ITA: * the method get_boost_version_number does work
## * the rest of the code has not really been tried
# * make certain a demo is provided (in demos/adv for example)
# TODO: bad and underdocumented code -> boost.py will be removed in waf 1.6 to be rewritten later
import os . path , glob , types , re , sys
import Configure , config_c , Options , Utils , Logs
from Logs import warn
from Configure import conf
boost_code = '''
#include <iostream>
#include <boost/version.hpp>
int main ( ) { std : : cout << BOOST_VERSION << std : : endl ; }
'''
boost_libpath = [ ' /usr/lib ' , ' /usr/local/lib ' , ' /opt/local/lib ' , ' /sw/lib ' , ' /lib ' ]
boost_cpppath = [ ' /usr/include ' , ' /usr/local/include ' , ' /opt/local/include ' , ' /sw/include ' ]
STATIC_NOSTATIC = ' nostatic '
STATIC_BOTH = ' both '
STATIC_ONLYSTATIC = ' onlystatic '
is_versiontag = re . compile ( ' ^ \ d+_ \ d+_? \ d*$ ' )
is_threadingtag = re . compile ( ' ^mt$ ' )
is_abitag = re . compile ( ' ^[sgydpn]+$ ' )
is_toolsettag = re . compile ( ' ^(acc|borland|como|cw|dmc|darwin|gcc|hp_cxx|intel|kylix|vc|mgw|qcc|sun|vacpp) \ d*$ ' )
is_pythontag = re . compile ( ' ^py[0-9] {2} $ ' )
def set_options ( opt ) :
opt . add_option ( ' --boost-includes ' , type = ' string ' , default = ' ' , dest = ' boostincludes ' , help = ' path to the boost directory where the includes are e.g. /usr/local/include/boost-1_35 ' )
opt . add_option ( ' --boost-libs ' , type = ' string ' , default = ' ' , dest = ' boostlibs ' , help = ' path to the directory where the boost libs are e.g. /usr/local/lib ' )
def string_to_version ( s ) :
version = s . split ( ' . ' )
if len ( version ) < 3 : return 0
return int ( version [ 0 ] ) * 100000 + int ( version [ 1 ] ) * 100 + int ( version [ 2 ] )
def version_string ( version ) :
major = version / 100000
minor = version / 100 % 1000
minor_minor = version % 100
if minor_minor == 0 :
return " %d _ %d " % ( major , minor )
else :
return " %d _ %d _ %d " % ( major , minor , minor_minor )
def libfiles ( lib , pattern , lib_paths ) :
result = [ ]
for lib_path in lib_paths :
libname = pattern % ( ' boost_ %s [!_]* ' % lib )
result + = glob . glob ( os . path . join ( lib_path , libname ) )
return result
@conf
def get_boost_version_number ( self , dir ) :
""" silently retrieve the boost version number """
try :
return self . run_c_code ( compiler = ' cxx ' , code = boost_code , includes = dir , execute = 1 , env = self . env . copy ( ) , type = ' cprogram ' , compile_mode = ' cxx ' , compile_filename = ' test.cpp ' )
except Configure . ConfigurationError , e :
return - 1
def set_default ( kw , var , val ) :
if not var in kw :
kw [ var ] = val
def tags_score ( tags , kw ) :
"""
checks library tags
see http : / / www . boost . org / doc / libs / 1_35_0 / more / getting_started / unix - variants . html 6.1
"""
score = 0
needed_tags = {
' threading ' : kw [ ' tag_threading ' ] ,
' abi ' : kw [ ' tag_abi ' ] ,
' toolset ' : kw [ ' tag_toolset ' ] ,
' version ' : kw [ ' tag_version ' ] ,
' python ' : kw [ ' tag_python ' ]
}
if kw [ ' tag_toolset ' ] is None :
v = kw [ ' env ' ]
toolset = v [ ' CXX_NAME ' ]
if v [ ' CXX_VERSION ' ] :
version_no = v [ ' CXX_VERSION ' ] . split ( ' . ' )
toolset + = version_no [ 0 ]
if len ( version_no ) > 1 :
toolset + = version_no [ 1 ]
needed_tags [ ' toolset ' ] = toolset
found_tags = { }
for tag in tags :
if is_versiontag . match ( tag ) : found_tags [ ' version ' ] = tag
if is_threadingtag . match ( tag ) : found_tags [ ' threading ' ] = tag
if is_abitag . match ( tag ) : found_tags [ ' abi ' ] = tag
if is_toolsettag . match ( tag ) : found_tags [ ' toolset ' ] = tag
if is_pythontag . match ( tag ) : found_tags [ ' python ' ] = tag
for tagname in needed_tags . iterkeys ( ) :
if needed_tags [ tagname ] is not None and tagname in found_tags :
if re . compile ( needed_tags [ tagname ] ) . match ( found_tags [ tagname ] ) :
score + = kw [ ' score_ ' + tagname ] [ 0 ]
else :
score + = kw [ ' score_ ' + tagname ] [ 1 ]
return score
@conf
def validate_boost ( self , kw ) :
ver = kw . get ( ' version ' , ' ' )
for x in ' min_version max_version version ' . split ( ) :
set_default ( kw , x , ver )
set_default ( kw , ' lib ' , ' ' )
kw [ ' lib ' ] = Utils . to_list ( kw [ ' lib ' ] )
set_default ( kw , ' env ' , self . env )
set_default ( kw , ' libpath ' , boost_libpath )
set_default ( kw , ' cpppath ' , boost_cpppath )
for x in ' tag_threading tag_version tag_toolset ' . split ( ) :
set_default ( kw , x , None )
set_default ( kw , ' tag_abi ' , ' ^[^d]*$ ' )
set_default ( kw , ' python ' , str ( sys . version_info [ 0 ] ) + str ( sys . version_info [ 1 ] ) )
set_default ( kw , ' tag_python ' , ' ^py ' + kw [ ' python ' ] + ' $ ' )
set_default ( kw , ' score_threading ' , ( 10 , - 10 ) )
set_default ( kw , ' score_abi ' , ( 10 , - 10 ) )
set_default ( kw , ' score_python ' , ( 10 , - 10 ) )
set_default ( kw , ' score_toolset ' , ( 1 , - 1 ) )
set_default ( kw , ' score_version ' , ( 100 , - 100 ) )
set_default ( kw , ' score_min ' , 0 )
set_default ( kw , ' static ' , STATIC_NOSTATIC )
set_default ( kw , ' found_includes ' , False )
set_default ( kw , ' min_score ' , 0 )
set_default ( kw , ' errmsg ' , ' not found ' )
set_default ( kw , ' okmsg ' , ' ok ' )
@conf
def find_boost_includes ( self , kw ) :
"""
check every path in kw [ ' cpppath ' ] for subdir
that either starts with boost - or is named boost .
Then the version is checked and selected accordingly to
min_version / max_version . The highest possible version number is
selected !
If no versiontag is set the versiontag is set accordingly to the
selected library and CPPPATH_BOOST is set .
"""
boostPath = getattr ( Options . options , ' boostincludes ' , ' ' )
if boostPath :
boostPath = [ os . path . normpath ( os . path . expandvars ( os . path . expanduser ( boostPath ) ) ) ]
else :
boostPath = Utils . to_list ( kw [ ' cpppath ' ] )
min_version = string_to_version ( kw . get ( ' min_version ' , ' ' ) )
max_version = string_to_version ( kw . get ( ' max_version ' , ' ' ) ) or ( sys . maxint - 1 )
version = 0
for include_path in boostPath :
boost_paths = glob . glob ( os . path . join ( include_path , ' boost* ' ) )
for path in boost_paths :
pathname = os . path . split ( path ) [ - 1 ]
ret = - 1
if pathname == ' boost ' :
path = include_path
ret = self . get_boost_version_number ( path )
elif pathname . startswith ( ' boost- ' ) :
ret = self . get_boost_version_number ( path )
ret = int ( ret )
if ret != - 1 and ret > = min_version and ret < = max_version and ret > version :
boost_path = path
version = ret
if not version :
self . fatal ( ' boost headers not found! (required version min: %s max: %s ) '
% ( kw [ ' min_version ' ] , kw [ ' max_version ' ] ) )
return False
found_version = version_string ( version )
versiontag = ' ^ ' + found_version + ' $ '
if kw [ ' tag_version ' ] is None :
kw [ ' tag_version ' ] = versiontag
elif kw [ ' tag_version ' ] != versiontag :
warn ( ' boost header version %r and tag_version %r do not match! ' % ( versiontag , kw [ ' tag_version ' ] ) )
env = self . env
env [ ' CPPPATH_BOOST ' ] = boost_path
env [ ' BOOST_VERSION ' ] = found_version
self . found_includes = 1
ret = ' Version %s ( %s ) ' % ( found_version , boost_path )
return ret
@conf
def find_boost_library ( self , lib , kw ) :
def find_library_from_list ( lib , files ) :
lib_pattern = re . compile ( ' .*boost_(.*?) \ ..* ' )
result = ( None , None )
resultscore = kw [ ' min_score ' ] - 1
for file in files :
m = lib_pattern . search ( file , 1 )
if m :
libname = m . group ( 1 )
libtags = libname . split ( ' - ' ) [ 1 : ]
currentscore = tags_score ( libtags , kw )
if currentscore > resultscore :
result = ( libname , file )
resultscore = currentscore
return result
lib_paths = getattr ( Options . options , ' boostlibs ' , ' ' )
if lib_paths :
lib_paths = [ os . path . normpath ( os . path . expandvars ( os . path . expanduser ( lib_paths ) ) ) ]
else :
lib_paths = Utils . to_list ( kw [ ' libpath ' ] )
v = kw . get ( ' env ' , self . env )
( libname , file ) = ( None , None )
if kw [ ' static ' ] in [ STATIC_NOSTATIC , STATIC_BOTH ] :
st_env_prefix = ' LIB '
files = libfiles ( lib , v [ ' shlib_PATTERN ' ] , lib_paths )
( libname , file ) = find_library_from_list ( lib , files )
if libname is None and kw [ ' static ' ] in [ STATIC_ONLYSTATIC , STATIC_BOTH ] :
st_env_prefix = ' STATICLIB '
staticLibPattern = v [ ' staticlib_PATTERN ' ]
if self . env [ ' CC_NAME ' ] == ' msvc ' :
staticLibPattern = ' lib ' + staticLibPattern
files = libfiles ( lib , staticLibPattern , lib_paths )
( libname , file ) = find_library_from_list ( lib , files )
if libname is not None :
v [ ' LIBPATH_BOOST_ ' + lib . upper ( ) ] = [ os . path . split ( file ) [ 0 ] ]
if self . env [ ' CC_NAME ' ] == ' msvc ' and os . path . splitext ( file ) [ 1 ] == ' .lib ' :
v [ st_env_prefix + ' _BOOST_ ' + lib . upper ( ) ] = [ ' libboost_ ' + libname ]
else :
v [ st_env_prefix + ' _BOOST_ ' + lib . upper ( ) ] = [ ' boost_ ' + libname ]
return
self . fatal ( ' lib boost_ ' + lib + ' not found! ' )
@conf
def check_boost ( self , * k , * * kw ) :
"""
This should be the main entry point
- min_version
- max_version
- version
- include_path
- lib_path
- lib
- toolsettag - None or a regexp
- threadingtag - None or a regexp
- abitag - None or a regexp
- versiontag - WARNING : you should rather use version or min_version / max_version
- static - look for static libs ( values :
' nostatic ' or STATIC_NOSTATIC - ignore static libs ( default )
' both ' or STATIC_BOTH - find static libs , too
' onlystatic ' or STATIC_ONLYSTATIC - find only static libs
- score_version
- score_abi
- scores_threading
- score_toolset
* the scores are tuples ( match_score , nomatch_score )
match_score is the added to the score if the tag is matched
nomatch_score is added when a tag is found and does not match
- min_score
"""
if not self . env [ ' CXX ' ] :
self . fatal ( ' load a c++ compiler tool first, for example conf.check_tool( " g++ " ) ' )
self . validate_boost ( kw )
ret = None
try :
if not kw . get ( ' found_includes ' , None ) :
self . check_message_1 ( kw . get ( ' msg_includes ' , ' boost headers ' ) )
ret = self . find_boost_includes ( kw )
except Configure . ConfigurationError , e :
if ' errmsg ' in kw :
self . check_message_2 ( kw [ ' errmsg ' ] , ' YELLOW ' )
if ' mandatory ' in kw :
if Logs . verbose > 1 :
raise
else :
self . fatal ( ' the configuration failed (see %r ) ' % self . log . name )
else :
if ' okmsg ' in kw :
self . check_message_2 ( kw . get ( ' okmsg_includes ' , ret ) )
for lib in kw [ ' lib ' ] :
self . check_message_1 ( ' library boost_ ' + lib )
try :
self . find_boost_library ( lib , kw )
except Configure . ConfigurationError , e :
ret = False
if ' errmsg ' in kw :
self . check_message_2 ( kw [ ' errmsg ' ] , ' YELLOW ' )
if ' mandatory ' in kw :
if Logs . verbose > 1 :
raise
else :
self . fatal ( ' the configuration failed (see %r ) ' % self . log . name )
else :
if ' okmsg ' in kw :
self . check_message_2 ( kw [ ' okmsg ' ] )
return ret