scons: add options to allow controlling link order (and include order) sent by SCons to the linker line, as predictably /usr/lib would come first and take preference over paths such as /usr/local/lib while compiles may have been against /usr/local/include - closes #484

This commit is contained in:
Dane Springmeyer 2010-02-23 02:40:46 +00:00
parent 26c596cbf1
commit f0fb2fa3c6

View file

@ -68,6 +68,72 @@ def shortest_name(libs):
name = lib name = lib
return name return name
DEFAULT_LINK_PRIORITY = ['internal','other','frameworks','user','system']
def sort_paths(items,priority):
"""Sort paths such that compiling and linking will globally prefer custom or local libs
over system libraries by fixing up the order libs are passed to gcc and the linker.
Ideally preference could be by-target instead of global, but our SCons implementation
is not currently utilizing different SCons build env()'s as we should.
Overally the current approach within these scripts is to prepend paths of preference
and append all others, but this does not give enough control (particularly due to the
approach of assuming /usr/LIBSCHEMA and letting paths be parsed and added by pkg-config).
In effect /usr/lib is likely to come before /usr/local/lib which makes linking against
custom built icu or boost impossible when those libraries are available in both places.
Sorting using a priority list allows this to be controlled, and fine tuned.
"""
new = []
path_types = {'internal':[],'other':[],'frameworks':[],'user':[],'system':[]}
# parse types of paths into logical/meaningful groups
# based on commonly encountered lib directories on linux and osx
for i in items:
# internal paths for code kept inside
# the mapnik sources
if i.startswith('#'):
path_types['internal'].append(i)
# Mac OS X user installed frameworks
elif '/Library/Frameworks' in i:
path_types['frameworks'].append(i)
# various 'local' installs like /usr/local or /opt/local
elif 'local' in i or '/sw' in i:
path_types['user'].append(i)
# key system libs (likely others will fall into 'other')
elif '/usr/' in i or '/System' in i or '/lib' in i:
path_types['system'].append(i)
# anything not yet matched...
# likely a combo of rare system lib paths and
# very custom user paths that should ideally be
# in 'user'
else:
path_types['other'].append(i)
# build up new list based on priority list
for path in priority:
if path_types.has_key(path):
dirs = path_types[path]
new.extend(dirs)
path_types.pop(path)
else:
color_print(1,'\nSorry, "%s" is NOT a valid value for option "LINK_PRIORITY": values include: %s' % (path,','.join(path_types.keys())))
color_print(1,'\tinternal: the local directory of the Mapnik sources (prefix #) (eg. used to link internal agg)')
color_print(1,'\tframeworks: on osx the /Library/Frameworks directory')
color_print(1,'\tuser: any path with "local" or "/sw" inside it')
color_print(1,'\tsystem: any path not yet matched with "/usr/","/lib", or "/System" (osx) inside it')
color_print(1,'\tother: any paths you specified not matched by criteria used to parse the others')
color_print(1,'\tother: any paths you specified not matched by criteria used to parse the others')
color_print(1,'The Default priority is: %s' % ','.join(DEFAULT_LINK_PRIORITY))
color_print(1,'Any priority groups not listed will be appended to the list at the end')
Exit(1)
# append remaining paths potentially not requested
# by any custom priority list defined by user
for k,v in path_types.items():
new.extend(v)
return new
if platform.dist()[0] in ('Ubuntu','debian'): if platform.dist()[0] in ('Ubuntu','debian'):
LIBDIR_SCHEMA='lib' LIBDIR_SCHEMA='lib'
elif platform.uname()[4] == 'x86_64' and platform.system() == 'Linux': elif platform.uname()[4] == 'x86_64' and platform.system() == 'Linux':
@ -112,6 +178,8 @@ def pretty_dep(dep):
# local file to hold custom user configuration variables # local file to hold custom user configuration variables
SCONS_LOCAL_CONFIG = 'config.py' SCONS_LOCAL_CONFIG = 'config.py'
# build log
SCONS_LOCAL_LOG = 'config.log'
# local pickled file to cache configured environment # local pickled file to cache configured environment
SCONS_CONFIGURE_CACHE = 'config.cache' SCONS_CONFIGURE_CACHE = 'config.cache'
# directory SCons uses to stash build tests # directory SCons uses to stash build tests
@ -161,6 +229,8 @@ opts.AddVariables(
('CONFIG', "The path to the python file in which to save user configuration options. Currently : '%s'" % SCONS_LOCAL_CONFIG,SCONS_LOCAL_CONFIG), ('CONFIG', "The path to the python file in which to save user configuration options. Currently : '%s'" % SCONS_LOCAL_CONFIG,SCONS_LOCAL_CONFIG),
BoolVariable('USE_CONFIG', "Use SCons user '%s' file (will also write variables after successful configuration)", 'True'), BoolVariable('USE_CONFIG', "Use SCons user '%s' file (will also write variables after successful configuration)", 'True'),
BoolVariable('FAST', "Make scons faster at the cost of less precise dependency tracking", 'False'), BoolVariable('FAST', "Make scons faster at the cost of less precise dependency tracking", 'False'),
BoolVariable('PRIORITIZE_LINKING', 'Sort list of lib and inc directories to ensure preferencial compiling and linking (useful when duplicate libs)', 'True'),
('LINK_PRIORITY','Priority list in which to sort library and include paths (default order is internal, other, frameworks, user, then system - see source of `sort_paths` function for more detail)',','.join(DEFAULT_LINK_PRIORITY)),
# Install Variables # Install Variables
('PREFIX', 'The install path "prefix"', '/usr/local'), ('PREFIX', 'The install path "prefix"', '/usr/local'),
@ -225,6 +295,7 @@ opts.AddVariables(
BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'), BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'),
BoolVariable('COLOR_PRINT', 'Print build status information in color', 'True'), BoolVariable('COLOR_PRINT', 'Print build status information in color', 'True'),
) )
# variables to pickle after successful configure step # variables to pickle after successful configure step
# these include all scons core variables as well as custom # these include all scons core variables as well as custom
# env variables needed in Sconscript files # env variables needed in Sconscript files
@ -332,6 +403,15 @@ elif preconfigured:
#### Custom Configure Checks ### #### Custom Configure Checks ###
def prioritize_paths(context):
env = context.env
prefs = env['LINK_PRIORITY'].split(',')
context.Message( 'Sorting lib and inc compiler paths by priority... %s' % ','.join(prefs) )
env['LIBPATH'] = sort_paths(env['LIBPATH'],prefs)
env['CPPPATH'] = sort_paths(env['CPPPATH'],prefs)
ret = context.Result( True )
return ret
def CheckPKGConfig(context, version): def CheckPKGConfig(context, version):
context.Message( 'Checking for pkg-config... ' ) context.Message( 'Checking for pkg-config... ' )
ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
@ -573,7 +653,8 @@ int main()
major_version = version / 100000 major_version = version / 100000
return [major_version,minor_version,patch_level] return [major_version,minor_version,patch_level]
conf_tests = { 'CheckPKGConfig' : CheckPKGConfig, conf_tests = { 'prioritize_paths' : prioritize_paths,
'CheckPKGConfig' : CheckPKGConfig,
'CheckPKG' : CheckPKG, 'CheckPKG' : CheckPKG,
'FindBoost' : FindBoost, 'FindBoost' : FindBoost,
'CheckBoost' : CheckBoost, 'CheckBoost' : CheckBoost,
@ -723,6 +804,10 @@ if not preconfigured:
[env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'], [env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'],
] ]
# if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests
if env['PRIORITIZE_LINKING']:
conf.prioritize_paths()
for libinfo in LIBSHEADERS: for libinfo in LIBSHEADERS:
if not conf.CheckLibWithHeader(libinfo[0], libinfo[1], libinfo[3]): if not conf.CheckLibWithHeader(libinfo[0], libinfo[1], libinfo[3]):
@ -820,7 +905,7 @@ if not preconfigured:
env.PrependUnique(CPPPATH = '#include', delete_existing=True) env.PrependUnique(CPPPATH = '#include', delete_existing=True)
env.PrependUnique(CPPPATH = '#', delete_existing=True) env.PrependUnique(CPPPATH = '#', delete_existing=True)
env.PrependUnique(LIBPATH = '#src', delete_existing=True) env.PrependUnique(LIBPATH = '#src', delete_existing=True)
# Decide which libagg to use # Decide which libagg to use
# if we are using internal agg, then prepend to make sure # if we are using internal agg, then prepend to make sure
# we link locally # we link locally
@ -1003,6 +1088,11 @@ if not preconfigured:
color_print(4,'Python %s prefix... %s' % (env['PYTHON_VERSION'], env['PYTHON_SYS_PREFIX'])) color_print(4,'Python %s prefix... %s' % (env['PYTHON_VERSION'], env['PYTHON_SYS_PREFIX']))
color_print(4,'Python bindings will install in... %s' % os.path.normpath(env['PYTHON_INSTALL_LOCATION'])) color_print(4,'Python bindings will install in... %s' % os.path.normpath(env['PYTHON_INSTALL_LOCATION']))
# if requested, sort LIBPATH and CPPPATH one last time before saving...
if env['PRIORITIZE_LINKING']:
conf.prioritize_paths()
# finish config stage and pickle results # finish config stage and pickle results
env = conf.Finish() env = conf.Finish()
env_cache = open(SCONS_CONFIGURE_CACHE, 'w') env_cache = open(SCONS_CONFIGURE_CACHE, 'w')
@ -1024,6 +1114,9 @@ if not preconfigured:
try: try:
os.chmod('.sconsign.dblite',0666) os.chmod('.sconsign.dblite',0666)
except: pass except: pass
try:
os.chmod(SCONS_LOCAL_LOG,0666)
except: pass
try: try:
for item in glob('%s/*' % SCONF_TEMP_DIR): for item in glob('%s/*' % SCONF_TEMP_DIR):
os.chmod(item,0666) os.chmod(item,0666)