scons: move all configure options to 'scons configure' stage, using pickling to maintain configure options across builds

This commit is contained in:
Dane Springmeyer 2009-02-26 23:35:09 +00:00
parent 6a84586489
commit 9f40e90def
2 changed files with 513 additions and 483 deletions

View file

@ -19,8 +19,13 @@
# $Id$ # $Id$
import os, sys, platform import os
import sys
import platform
from glob import glob
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from SCons.SConf import SetCacheMode
import pickle
def color_print(color,text,newline=True): def color_print(color,text,newline=True):
# 1 - red # 1 - red
@ -40,17 +45,6 @@ def call(cmd):
else: else:
color_print(1,'Problem encounted with SCons scripts, please post bug report to: http://trac.mapnik.org') color_print(1,'Problem encounted with SCons scripts, please post bug report to: http://trac.mapnik.org')
# Helper function for uniquely appending paths to a SCons path listing.
def uniq_add(env, key, val):
if not val in env[key]: env[key].append(val)
# Helper function for building up paths to add for a lib (plugin or required)
def add_paths(prereq):
inc_path = env['%s_INCLUDES' % prereq]
lib_path = env['%s_LIBS' % prereq]
uniq_add(env, 'CPPPATH', inc_path)
uniq_add(env, 'LIBPATH', lib_path)
if platform.uname()[4] == 'x86_64': if platform.uname()[4] == 'x86_64':
LIBDIR_SCHEMA='lib64' LIBDIR_SCHEMA='lib64'
elif platform.uname()[4] == 'ppc64': elif platform.uname()[4] == 'ppc64':
@ -58,15 +52,12 @@ elif platform.uname()[4] == 'ppc64':
else: else:
LIBDIR_SCHEMA='lib' LIBDIR_SCHEMA='lib'
#### SCons build options and initial setup #### # local file to hold custom user configuration variables
SCONS_LOCAL_CONFIG = 'config.py' SCONS_LOCAL_CONFIG = 'config.py'
# local pickled file to cache configured environment
# Warn user of current set of build options. SCONS_CONFIGURE_CACHE = 'config.cache'
if os.path.exists(SCONS_LOCAL_CONFIG): # directory SCons uses to stash build tests
optfile = file(SCONS_LOCAL_CONFIG) SCONF_TEMP_DIR = '.sconf_temp'
print "Saved options:", optfile.read().replace("\n", ", ")[:-2]
optfile.close()
# Core plugin build configuration # Core plugin build configuration
# opts.AddVariables still hardcoded however... # opts.AddVariables still hardcoded however...
@ -89,14 +80,14 @@ for k,v in PLUGINS.items():
if v['default']: if v['default']:
DEFAULT_PLUGINS.append(k) DEFAULT_PLUGINS.append(k)
#### SCons build options and initial setup ####
color_print(4,'\nWelcome to Mapnik...\n')
env = Environment(ENV=os.environ)
# All of the following options may be modified at the command-line, for example: # All of the following options may be modified at the command-line, for example:
# `python scons/scons.py PREFIX=/opt` # `python scons/scons.py PREFIX=/opt`
opts = Variables() opts = Variables()
def OptionalPath(key, val, env):
if val:
PathOption.PathIsDir(key, val, env)
opts.AddVariables( opts.AddVariables(
# Compiler options # Compiler options
('CXX', 'The C++ compiler to use (defaults to g++).', 'g++'), ('CXX', 'The C++ compiler to use (defaults to g++).', 'g++'),
@ -108,8 +99,7 @@ opts.AddVariables(
# SCons build behavior options # SCons build behavior options
('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('SCONS_CACHE', 'Use SCons dependency caching to speed build process', 'False'), BoolVariable('FAST', "Make scons faster at the cost of less precise dependency tracking", 'False'),
BoolVariable('USE_USER_ENV', 'Allow the SCons build environment to inherit from the current user environment', 'True'),
# Install Variables # Install Variables
('PREFIX', 'The install path "prefix"', '/usr/local'), ('PREFIX', 'The install path "prefix"', '/usr/local'),
@ -163,92 +153,85 @@ opts.AddVariables(
ListVariable('BINDINGS','Language bindings to build','all',['python']), ListVariable('BINDINGS','Language bindings to build','all',['python']),
EnumVariable('THREADING','Set threading support','multi', ['multi','single']), EnumVariable('THREADING','Set threading support','multi', ['multi','single']),
EnumVariable('XMLPARSER','Set xml parser ','libxml2', ['tinyxml','spirit','libxml2']), EnumVariable('XMLPARSER','Set xml parser ','libxml2', ['tinyxml','spirit','libxml2']),
('JOBS', 'Set the number of parallel compilations', "1", lambda key, value, env: int(value), int),
) )
# Construct the SCons build environment as a union of the users environment and the `opts` # variables to pickle after successful configure step
# these include all scons core variables as well as custom
# env variables needed in Sconscript files
pickle_store = [# Scons internal variables
'CC',
'CXX',
'CCFLAGS',
'CPPDEFINES',
'CPPFLAGS',
'CPPPATH',
'CXXFLAGS',
'LIBPATH',
'LIBS',
'LINKFLAGS',
# Mapnik's SConstruct build variables
'ABI_VERSION',
'PLATFORM',
'BOOST_ABI',
'BOOST_APPEND',
'LIBDIR_SCHEMA',
'REQUESTED_PLUGINS',
'SUNCC',
'PYTHON_VERSION',
'PYTHON_INCLUDES',
'PYTHON_INSTALL_LOCATION',
]
# Add all other user configurable options to pickle pickle_store
# We add here more options than are needed for the build stage
# but helpful so that scons -h shows the exact cached options
for opt in opts.options:
if opt.key not in pickle_store:
pickle_store.append(opt.key)
# Build up base environment, then reinitiate based on user customizations. # Method of adding configure behavoir to Scons adapted from:
# http://freeorion.svn.sourceforge.net/svnroot/freeorion/trunk/FreeOrion/SConstruct
preconfigured = False
force_configure = False
command_line_args = sys.argv[1:]
# This method seems un-pythonic and a bit dodgy, but it works. if 'configure' in command_line_args:
# It seems that creating an alternative environment that loads user options force_configure = True
# and then updating the main env using those options is the more logical route elif ('-h' in command_line_args) or ('--help' in command_line_args):
# But my testing indicate that something like: preconfigured = True # this is just to ensure config gets skipped when help is requested
# >>>user_opts = Variables([env['CONFIG']])
# >>> user_opts.Update(env)
# does not seem to work as expected.
# Create clean environment with only the options within this SConstruct file if not force_configure:
color_print(4,'Constructing build environment...') if os.path.exists(SCONS_CONFIGURE_CACHE):
env = Environment(options=opts) try:
pickled_environment = open(SCONS_CONFIGURE_CACHE, 'r')
# Unless USE_USER_ENV=False, recreate the base environment pickled_values = pickle.load(pickled_environment)
# using all variables (for example) in the shell users .profile or /etc/profile for key, value in pickled_values.items():
if env['USE_USER_ENV']: #if key == 'BINDINGS': import pdb;pdb.set_trace()
env = Environment(ENV=os.environ,options=opts) env[key] = value
else: preconfigured = True
# Default has been overridden, so print a warning if ('-h' not in command_line_args) and ('--help' not in command_line_args):
color_print(4,'USER_USER_ENV specified as false, will not inherit variables from user environment...') color_print(4,'Using previous successful configuration...')
color_print(4,'Re-configure by running "python scons/scons.py configure".')
# Unless USE_USER_ENV=False, recreate the base environment except:
# using all variables (for example) in the shell users .profile or /etc/profile # unpickling failed, so reconfigure as fallback
user_conf = env['CONFIG'] preconfigured = False
if env['USE_CONFIG']:
if not user_conf.endswith('.py'):
color_print(1,'SCons CONFIG file specified is not a python file, will not be read...')
else: else:
# Accept more than one file as comma-delimited list preconfigured = False
user_confs = user_conf.split(',')
# If they exist add the files to the existing `opts`
for conf in user_confs: if preconfigured:
if os.path.exists(conf): if opts.args:
opts.files.append(conf) color_print(1,'Arguments ignored, please run "configure" with arguments to customize build')
color_print(4,"SCons CONFIG found: '%s', variables will be inherited..." % conf) Exit(0)
elif not conf == SCONS_LOCAL_CONFIG:
# if default missing, no worries
# but if the default is overridden and the file is not found, give warning
color_print(1,"SCons CONFIG not found: '%s'" % conf)
# Recreate the base environment using modified `opts`
env = Environment(ENV=os.environ,options=opts)
env['USE_CONFIG'] = True
else: else:
color_print(4,'SCons USE_CONFIG specified as false, will not inherit variables python config file...') # if we are not preconfigured update the environment based on commandline options
opts.Update(env)
env['MISSING_DEPS'] = []
env['SKIPPED_DEPS'] = []
env['LIBDIR_SCHEMA'] = LIBDIR_SCHEMA
env['PLATFORM'] = platform.uname()[0]
if env['DEBUG']:
mode = 'debug mode'
else:
mode = 'release mode'
color_print (4,"Building on %s in *%s*..." % (env['PLATFORM'],mode))
Help(opts.GenerateHelpText(env)) Help(opts.GenerateHelpText(env))
if env['SCONS_CACHE']:
# caching is 'auto' by default in SCons
# But let's also cache implicit deps...
SetOption('implicit_cache', 1)
# uncomment for more speed improvements
#env.Decider('MD5-timestamp')
#SetOption('max_drift', 1)
else: #### Custom Configure Checks ###
# Set the cache mode to 'force' unless requested, avoiding hidden caching of Scons 'opts' in '.sconsign.dblite'
# This allows for a SCONS_LOCAL_CONFIG, if present, to be used as the primary means of storing paths to successful build dependencies
from SCons.SConf import SetCacheMode
SetCacheMode('force')
thread_suffix = 'mt'
if env['PLATFORM'] == 'FreeBSD':
thread_suffix = ''
env.Append(LIBS = 'pthread')
def CheckPKGConfig(context, version): def CheckPKGConfig(context, version):
context.Message( 'Checking for pkg-config... ' ) context.Message( 'Checking for pkg-config... ' )
@ -333,32 +316,99 @@ 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 = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, conf_tests = { 'CheckPKGConfig' : CheckPKGConfig,
'CheckPKG' : CheckPKG, 'CheckPKG' : CheckPKG,
'CheckBoost' : CheckBoost, 'CheckBoost' : CheckBoost,
'GetBoostLibVersion' : GetBoostLibVersion, 'GetBoostLibVersion' : GetBoostLibVersion,
'GetMapnikLibVersion' : GetMapnikLibVersion }) 'GetMapnikLibVersion' : GetMapnikLibVersion
}
#### Libraries and headers dependency checks #### if not env.GetOption('clean'):
if not preconfigured:
# Libraries and headers dependency checks color_print(4,'Configuring build environment...')
env['CPPPATH'] = ['#include', '#']
env['LIBPATH'] = ['#src']
# Solaris & Sun Studio settings (the `SUNCC` flag will only be if env['USE_CONFIG']:
# set if the `CXX` option begins with `CC`) if not env['CONFIG'].endswith('.py'):
SOLARIS = env['PLATFORM'] == 'SunOS' color_print(1,'SCons CONFIG file specified is not a python file, will not be read...')
SUNCC = SOLARIS and env['CXX'].startswith('CC') else:
# Accept more than one file as comma-delimited list
user_confs = env['CONFIG'].split(',')
# If they exist add the files to the existing `opts`
for conf in user_confs:
if os.path.exists(conf):
opts.files.append(conf)
color_print(4,"SCons CONFIG found: '%s', variables will be inherited..." % conf)
optfile = file(conf)
print optfile.read().replace("\n", " ").replace("'","").replace(" = ","=")
optfile.close()
# For Solaris include paths (e.g., for freetype2, ltdl, etc.). elif not conf == SCONS_LOCAL_CONFIG:
if SOLARIS: # if default missing, no worries
# but if the default is overridden and the file is not found, give warning
color_print(1,"SCons CONFIG not found: '%s'" % conf)
# Recreate the base environment using modified `opts`
env = Environment(ENV=os.environ,options=opts)
env['USE_CONFIG'] = True
else:
color_print(4,'SCons USE_CONFIG specified as false, will not inherit variables python config file...')
conf = Configure(env, custom_tests = conf_tests)
if env['DEBUG']:
mode = 'debug mode'
else:
mode = 'release mode'
color_print (4,"Configuring on %s in *%s*..." % (env['PLATFORM'],mode))
env['MISSING_DEPS'] = []
env['SKIPPED_DEPS'] = []
env['LIBDIR_SCHEMA'] = LIBDIR_SCHEMA
env['PLATFORM'] = platform.uname()[0]
if env['FAST']:
# caching is 'auto' by default in SCons
# But let's also cache implicit deps...
EnsureSConsVersion(0,98)
SetOption('implicit_cache', 1)
env.Decider('MD5-timestamp')
SetOption('max_drift', 1)
else:
# Set the cache mode to 'force' unless requested, avoiding hidden caching of Scons 'opts' in '.sconsign.dblite'
# This allows for a SCONS_LOCAL_CONFIG, if present, to be used as the primary means of storing paths to successful build dependencies
SetCacheMode('force')
if env['JOBS'] > 1:
SetOption("num_jobs", env['JOBS'])
thread_suffix = 'mt'
if env['PLATFORM'] == 'FreeBSD':
thread_suffix = ''
env.Append(LIBS = 'pthread')
#### Libraries and headers dependency checks ####
# Set up for libraries and headers dependency checks
env['CPPPATH'] = ['#include', '#']
env['LIBPATH'] = ['#src']
# Solaris & Sun Studio settings (the `SUNCC` flag will only be
# set if the `CXX` option begins with `CC`)
SOLARIS = env['PLATFORM'] == 'SunOS'
env['SUNCC'] = SOLARIS and env['CXX'].startswith('CC')
# For Solaris include paths (e.g., for freetype2, ltdl, etc.).
if SOLARIS:
blastwave_dir = '/opt/csw/%s' blastwave_dir = '/opt/csw/%s'
uniq_add(env, 'CPPPATH', blastwave_dir % 'include') env.AppendUnique(CPPPATH = blastwave_dir % 'include')
uniq_add(env, 'LIBPATH', blastwave_dir % LIBDIR_SCHEMA) env.AppendUnique(LIBPATH = blastwave_dir % LIBDIR_SCHEMA)
# If the Sun Studio C++ compiler (`CC`) is used instead of GCC. # If the Sun Studio C++ compiler (`CC`) is used instead of GCC.
if SUNCC: if env['SUNCC']:
env['CC'] = 'cc' env['CC'] = 'cc'
# To be compatible w/Boost everything needs to be compiled # To be compatible w/Boost everything needs to be compiled
# with the `-library=stlport4` flag (which needs to come # with the `-library=stlport4` flag (which needs to come
@ -367,92 +417,81 @@ if SUNCC:
if env['THREADING'] == 'multi': if env['THREADING'] == 'multi':
env['CXXFLAGS'] = ['-mt'] env['CXXFLAGS'] = ['-mt']
# Adding the required prerequisite library directories to the include path for # Adding the required prerequisite library directories to the include path for
# compiling and the library path for linking, respectively. # compiling and the library path for linking, respectively.
for required in ('BOOST', 'PNG', 'JPEG', 'TIFF','PROJ'): for required in ('BOOST', 'PNG', 'JPEG', 'TIFF','PROJ'):
add_paths(required) inc_path = env['%s_INCLUDES' % required]
lib_path = env['%s_LIBS' % required]
env.AppendUnique(CPPPATH = inc_path)
env.AppendUnique(LIBPATH = lib_path)
try: try:
env.ParseConfig(env['FREETYPE_CONFIG'] + ' --libs --cflags') env.ParseConfig(env['FREETYPE_CONFIG'] + ' --libs --cflags')
except OSError: except OSError:
env['MISSING_DEPS'].append(env['FREETYPE_CONFIG']) env['MISSING_DEPS'].append(env['FREETYPE_CONFIG'])
if env['XMLPARSER'] == 'tinyxml': if env['XMLPARSER'] == 'tinyxml':
env['CPPPATH'].append('#tinyxml') env['CPPPATH'].append('#tinyxml')
env.Append(CXXFLAGS = '-DBOOST_PROPERTY_TREE_XML_PARSER_TINYXML -DTIXML_USE_STL') env.Append(CXXFLAGS = '-DBOOST_PROPERTY_TREE_XML_PARSER_TINYXML -DTIXML_USE_STL')
elif env['XMLPARSER'] == 'libxml2': elif env['XMLPARSER'] == 'libxml2':
try: try:
env.ParseConfig(env['XML2_CONFIG'] + ' --libs --cflags') env.ParseConfig(env['XML2_CONFIG'] + ' --libs --cflags')
env.Append(CXXFLAGS = '-DHAVE_LIBXML2') env.Append(CXXFLAGS = '-DHAVE_LIBXML2')
except OSError: except OSError:
env['MISSING_DEPS'].append(env['XML2_CONFIG']) env['MISSING_DEPS'].append(env['XML2_CONFIG'])
C_LIBSHEADERS = [ if env['CAIRO'] and conf.CheckPKGConfig('0.15.0') and conf.CheckPKG('cairomm-1.0'):
['m', 'math.h', True],
['ltdl', 'ltdl.h', True],
['png', 'png.h', True],
['tiff', 'tiff.h', True],
['z', 'zlib.h', True],
['jpeg', ['stdio.h', 'jpeglib.h'], True],
['proj', 'proj_api.h', True],
]
if env['CAIRO'] and conf.CheckPKGConfig('0.15.0') and conf.CheckPKG('cairomm-1.0'):
env.ParseConfig('pkg-config --libs --cflags cairomm-1.0') env.ParseConfig('pkg-config --libs --cflags cairomm-1.0')
env.Append(CXXFLAGS = '-DHAVE_CAIRO') env.Append(CXXFLAGS = '-DHAVE_CAIRO')
else: else:
env['SKIPPED_DEPS'].extend(['cairo','cairomm','pycairo']) env['SKIPPED_DEPS'].extend(['cairo','cairomm'])
CXX_LIBSHEADERS = [ LIBSHEADERS = [
['icuuc','unicode/unistr.h',True], ['m', 'math.h', True,'C'],
['icudata','unicode/utypes.h' , True], ['ltdl', 'ltdl.h', True,'C'],
] ['png', 'png.h', True,'C'],
['tiff', 'tiff.h', True,'C'],
['z', 'zlib.h', True,'C'],
['jpeg', ['stdio.h', 'jpeglib.h'], True,'C'],
['proj', 'proj_api.h', True,'C'],
['icuuc','unicode/unistr.h',True,'C++'],
['icudata','unicode/utypes.h' , True,'C++'],
]
# get boost version from boost headers rather than previous approach # get boost version from boost headers rather than previous approach
# of fetching from the user provided INCLUDE path # of fetching from the user provided INCLUDE path
boost_system_required = False boost_system_required = False
boost_lib_version_from_header = conf.GetBoostLibVersion() boost_lib_version_from_header = conf.GetBoostLibVersion()
if boost_lib_version_from_header: if boost_lib_version_from_header:
boost_version_from_header = int(boost_lib_version_from_header.split('_')[1]) boost_version_from_header = int(boost_lib_version_from_header.split('_')[1])
if boost_version_from_header >= 35 and env['PLATFORM'] == 'Darwin': if boost_version_from_header >= 35 and env['PLATFORM'] == 'Darwin':
boost_system_required = True boost_system_required = True
else: else:
boost_system_required = False boost_system_required = False
# The other required boost headers. # The other required boost headers.
BOOST_LIBSHEADERS = [ BOOST_LIBSHEADERS = [
['system', 'boost/system/system_error.hpp', boost_system_required], ['system', 'boost/system/system_error.hpp', boost_system_required],
['filesystem', 'boost/filesystem/operations.hpp', True], ['filesystem', 'boost/filesystem/operations.hpp', True],
['regex', 'boost/regex.hpp', True], ['regex', 'boost/regex.hpp', True],
['iostreams','boost/iostreams/device/mapped_file.hpp',True], ['iostreams','boost/iostreams/device/mapped_file.hpp',True],
['program_options', 'boost/program_options.hpp', False] ['program_options', 'boost/program_options.hpp', False]
] ]
if env['THREADING'] == 'multi': if env['THREADING'] == 'multi':
BOOST_LIBSHEADERS.append(['thread', 'boost/thread/mutex.hpp', True]) BOOST_LIBSHEADERS.append(['thread', 'boost/thread/mutex.hpp', True])
thread_flag = thread_suffix thread_flag = thread_suffix
else: else:
thread_flag = '' thread_flag = ''
for libinfo in C_LIBSHEADERS: for libinfo in LIBSHEADERS:
if not conf.CheckLibWithHeader(libinfo[0], libinfo[1], 'C'): if not conf.CheckLibWithHeader(libinfo[0], libinfo[1], libinfo[3]):
if libinfo[0] == 'pq': if libinfo[0] == 'pq':
libinfo[0] = 'pq (postgres/postgis)' libinfo[0] = 'pq (postgres/postgis)'
if libinfo[2]:
color_print (1,'Could not find required header or shared library for %s' % libinfo[0])
env['MISSING_DEPS'].append(libinfo[0])
else:
color_print(4,'Could not find optional header or shared library for %s' % libinfo[0])
env['SKIPPED_DEPS'].append(libinfo[0])
for libinfo in CXX_LIBSHEADERS:
if not conf.CheckLibWithHeader(libinfo[0], libinfo[1], 'C++'):
if libinfo[0] == 'gdal' and libinfo[1] == 'ogrsf_frmts.h': if libinfo[0] == 'gdal' and libinfo[1] == 'ogrsf_frmts.h':
libinfo[0] = 'ogr' libinfo[0] = 'ogr'
if libinfo[2]: if libinfo[2]:
color_print(1,'Could not find required header or shared library for %s' % libinfo[0]) color_print (1,'Could not find required header or shared library for %s' % libinfo[0])
env['MISSING_DEPS'].append(libinfo[0]) env['MISSING_DEPS'].append(libinfo[0])
else: else:
color_print(4,'Could not find optional header or shared library for %s' % libinfo[0]) color_print(4,'Could not find optional header or shared library for %s' % libinfo[0])
@ -465,33 +504,32 @@ for libinfo in CXX_LIBSHEADERS:
else: else:
print 'gdal raster support... enabled' print 'gdal raster support... enabled'
# Creating BOOST_APPEND according to the Boost library naming order,
# which goes <toolset>-<threading>-<abi>-<version>. See:
# http://www.boost.org/doc/libs/1_35_0/more/getting_started/unix-variants.html#library-naming
append_params = ['']
if env['BOOST_TOOLKIT']: append_params.append(env['BOOST_TOOLKIT'])
if thread_flag: append_params.append(thread_flag)
if env['BOOST_ABI']: append_params.append(env['BOOST_ABI'])
if env['BOOST_VERSION']: append_params.append(env['BOOST_VERSION'])
# Creating BOOST_APPEND according to the Boost library naming order, # if the user is not setting custom boost configuration
# which goes <toolset>-<threading>-<abi>-<version>. See: # enforce boost version greater than or equal to 1.33
# http://www.boost.org/doc/libs/1_35_0/more/getting_started/unix-variants.html#library-naming if not conf.CheckBoost('1.33'):
append_params = ['']
if env['BOOST_TOOLKIT']: append_params.append(env['BOOST_TOOLKIT'])
if thread_flag: append_params.append(thread_flag)
if env['BOOST_ABI']: append_params.append(env['BOOST_ABI'])
if env['BOOST_VERSION']: append_params.append(env['BOOST_VERSION'])
# if the user is not setting custom boost configuration
# enforce boost version greater than or equal to 1.33
if not conf.CheckBoost('1.33'):
color_print (1,'Boost version 1.33 or greater is requred') color_print (1,'Boost version 1.33 or greater is requred')
if not env['BOOST_VERSION']: if not env['BOOST_VERSION']:
env['MISSING_DEPS'].append('boost version >=1.33') env['MISSING_DEPS'].append('boost version >=1.33')
else: else:
color_print (4,'Found boost lib version... %s' % boost_lib_version_from_header ) color_print (4,'Found boost lib version... %s' % boost_lib_version_from_header )
# Constructing the BOOST_APPEND setting that will be used to find the # Constructing the BOOST_APPEND setting that will be used to find the
# Boost libraries. # Boost libraries.
if len(append_params) > 1: if len(append_params) > 1:
env['BOOST_APPEND'] = '-'.join(append_params) env['BOOST_APPEND'] = '-'.join(append_params)
else: else:
env['BOOST_APPEND'] = '' env['BOOST_APPEND'] = ''
for count, libinfo in enumerate(BOOST_LIBSHEADERS): for count, libinfo in enumerate(BOOST_LIBSHEADERS):
if not conf.CheckLibWithHeader('boost_%s%s' % (libinfo[0],env['BOOST_APPEND']), libinfo[1], 'C++'): if not conf.CheckLibWithHeader('boost_%s%s' % (libinfo[0],env['BOOST_APPEND']), libinfo[1], 'C++'):
if libinfo[2]: if libinfo[2]:
color_print(1,'Could not find required header or shared library for boost %s' % libinfo[0]) color_print(1,'Could not find required header or shared library for boost %s' % libinfo[0])
@ -500,10 +538,10 @@ for count, libinfo in enumerate(BOOST_LIBSHEADERS):
color_print(4,'Could not find optional header or shared library for boost %s' % libinfo[0]) color_print(4,'Could not find optional header or shared library for boost %s' % libinfo[0])
env['SKIPPED_DEPS'].append('boost ' + libinfo[0]) env['SKIPPED_DEPS'].append('boost ' + libinfo[0])
requested_plugins = [ driver.strip() for driver in Split(env['INPUT_PLUGINS'])] env['REQUESTED_PLUGINS'] = [ driver.strip() for driver in Split(env['INPUT_PLUGINS'])]
color_print(4,'Checking for requested plugins dependencies...') color_print(4,'Checking for requested plugins dependencies...')
for plugin in requested_plugins: for plugin in env['REQUESTED_PLUGINS']:
details = PLUGINS[plugin] details = PLUGINS[plugin]
if details['path'] and details['lib'] and details['inc']: if details['path'] and details['lib'] and details['inc']:
backup = env.Clone().Dictionary() backup = env.Clone().Dictionary()
@ -515,171 +553,74 @@ for plugin in requested_plugins:
env.Replace(**backup) env.Replace(**backup)
env['SKIPPED_DEPS'].append(details['lib']) env['SKIPPED_DEPS'].append(details['lib'])
# re-append the local paths for mapnik sources to the beginning of the list # re-append the local paths for mapnik sources to the beginning of the list
# to make sure they come before any plugins that were 'prepended' # to make sure they come before any plugins that were 'prepended'
env.PrependUnique(CPPPATH = ['#include', '#'],delete_existing=True) env.PrependUnique(CPPPATH = ['#include', '#'], 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
if env['INTERNAL_LIBAGG']: if env['INTERNAL_LIBAGG']:
env.Prepend(CPPPATH = '#agg/include') env.Prepend(CPPPATH = '#agg/include')
env.Prepend(LIBPATH = '#agg') env.Prepend(LIBPATH = '#agg')
else: else:
env.ParseConfig('pkg-config --libs --cflags libagg') env.ParseConfig('pkg-config --libs --cflags libagg')
if 'python' in env['BINDINGS']: if 'python' in env['BINDINGS']:
# checklibwithheader does not work for boost_python since we can't feed it # checklibwithheader does not work for boost_python since we can't feed it
# multiple header files, so we fall back on a simple check for boost_python headers # multiple header files, so we fall back on a simple check for boost_python headers
if not conf.CheckHeader(header='boost/python/detail/config.hpp',language='C++'): if not conf.CheckHeader(header='boost/python/detail/config.hpp',language='C++'):
color_print(1,'Could not find required header files for boost python') color_print(1,'Could not find required header files for boost python')
env['MISSING_DEPS'].append('boost python') env['MISSING_DEPS'].append('boost python')
# fetch the mapnik version header in order to set the if env['CAIRO'] and conf.CheckPKGConfig('0.15.0') and conf.CheckPKG('pycairo'):
# ABI version used to build libmapnik.so on linux in src/SConscript env.ParseConfig('pkg-config --cflags pycairo')
abi = conf.GetMapnikLibVersion() env.Append(CXXFLAGS = '-DHAVE_PYCAIRO')
abi_fallback = [0,6,0] else:
if not abi: env['SKIPPED_DEPS'].extend(['pycairo'])
color_print(1,'Problem encountered parsing mapnik version, falling back to %s' % abi_fallback)
env['ABI_VERSION'] = abi_fallback
else:
env['ABI_VERSION'] = abi
#### End Config Stage #### #### End Config Stage for Required Dependencies ####
if env['MISSING_DEPS']: if env['MISSING_DEPS']:
# if required dependencies are missing, print warnings and then let SCons finish without building or saving local config # if required dependencies are missing, print warnings and then let SCons finish without building or saving local config
color_print(1,'\nExiting... the following required dependencies were not found:\n - %s' % '\n - '.join(env['MISSING_DEPS'])) color_print(1,'\nExiting... the following required dependencies were not found:\n - %s' % '\n - '.join(env['MISSING_DEPS']))
color_print(1,"\nSee the 'config.log' for details on possible problems.") color_print(1,"\nSee the 'config.log' for details on possible problems.")
if env['SKIPPED_DEPS']: if env['SKIPPED_DEPS']:
color_print(4,'\nAlso, these optional dependencies were not found:\n - %s' % '\n - '.join(env['SKIPPED_DEPS'])) color_print(4,'\nAlso, these optional dependencies were not found:\n - %s' % '\n - '.join(env['SKIPPED_DEPS']))
color_print(4,"\nSet custom paths to these libraries and header files on the command-line or in a file called '%s'" % SCONS_LOCAL_CONFIG) color_print(4,"\nSet custom paths to these libraries and header files on the command-line or in a file called '%s'" % SCONS_LOCAL_CONFIG)
color_print(4," ie. $ python scons/scons.py BOOST_INCLUDES=/usr/local/include/boost-1_37 BOOST_LIBS=/usr/local/lib") color_print(4," ie. $ python scons/scons.py BOOST_INCLUDES=/usr/local/include/boost-1_37 BOOST_LIBS=/usr/local/lib")
color_print(4, "\nOnce all required dependencies are found a local '%s' will be saved and then install:" % SCONS_LOCAL_CONFIG) color_print(4, "\nOnce all required dependencies are found a local '%s' will be saved and then install:" % SCONS_LOCAL_CONFIG)
color_print(4," $ sudo python scons/scons.py install") color_print(4," $ sudo python scons/scons.py install")
color_print(4,"\nTo view available path variables:\n $ python scons/scons.py --help or -h") color_print(4,"\nTo view available path variables:\n $ python scons/scons.py --help or -h")
color_print(4,'\nTo view overall SCons help options:\n $ python scons/scons.py --help-options or -H\n') color_print(4,'\nTo view overall SCons help options:\n $ python scons/scons.py --help-options or -H\n')
color_print(4,'More info: http://trac.mapnik.org/wiki/MapnikInstallation') color_print(4,'More info: http://trac.mapnik.org/wiki/MapnikInstallation')
Exit(0)
# Need some way to exit cleanly here... else:
# calling Exit() does not work because that will abort the users ability to get help # Save the custom variables in a SCONS_LOCAL_CONFIG
env = conf.Finish() # that will be reloaded to allow for `install` without re-specifying custom variables
#Exit() color_print(4,"\nAll Required dependencies found!\n")
else:
# Save the custom variables in a SCONS_LOCAL_CONFIG that will be reloaded to allow for `install` without re-specifying custom variables
color_print(4,"All Required dependencies found!")
if env['USE_CONFIG']: if env['USE_CONFIG']:
if os.path.exists(SCONS_LOCAL_CONFIG): if os.path.exists(SCONS_LOCAL_CONFIG):
action = 'Overwriting and re-saving' action = 'Overwriting and re-saving'
os.unlink(SCONS_LOCAL_CONFIG) os.unlink(SCONS_LOCAL_CONFIG)
# Serialize all user customizations into local config file
else: else:
action = 'Saving new' action = 'Saving new'
color_print(4,"%s file '%s', to hold successful path variables from commandline and python config file(s)..." % (action,SCONS_LOCAL_CONFIG)) color_print(4,"%s file '%s'..." % (action,SCONS_LOCAL_CONFIG))
color_print(4,"Will hold custom path variables from commandline and python config file(s)...")
opts.Save(SCONS_LOCAL_CONFIG,env) opts.Save(SCONS_LOCAL_CONFIG,env)
else: else:
color_print(4,"Did not use user config file(s), therefore no local configurations will be saved...") color_print(4,"Did not use user config file, no custom path variables will be saved...")
Export('env')
Export('conf')
bindings = [ binding.strip() for binding in Split(env['BINDINGS'])]
#### Build instructions & settings ####
# Build agg first, doesn't need anything special
if env['INTERNAL_LIBAGG']:
SConscript('agg/SConscript')
# Build the core library
SConscript('src/SConscript')
# Build shapeindex and remove its dependency from the LIBS
if 'boost_program_options%s' % env['BOOST_APPEND'] in env['LIBS']:
SConscript('utils/shapeindex/SConscript')
env['LIBS'].remove('boost_program_options%s' % env['BOOST_APPEND'])
else :
color_print(1,"WARNING: Cannot find boost_program_options. 'shapeindex' won't be available")
# Build the requested and able-to-be-compiled input plug-ins
if requested_plugins:
color_print(4,'Requested plugins to be built...',newline=False)
for plugin in requested_plugins:
details = PLUGINS[plugin]
if details['lib'] in env['LIBS']:
SConscript('plugins/input/%s/SConscript' % plugin)
env['LIBS'].remove(details['lib'])
color_print(4,'%s' % plugin,newline=False)
elif not details['lib']:
# build internal shape and raster plugins
SConscript('plugins/input/%s/SConscript' % plugin)
color_print(4,'%s' % plugin,newline=False)
if requested_plugins:
print
# Build the Python bindings.
if 'python' in env['BINDINGS']:
if not os.access(env['PYTHON'], os.X_OK):
color_print(1,"Cannot run python interpreter at '%s', make sure that you have the permissions to execute it." % env['PYTHON'])
Exit(1)
sys_prefix = "%s -c 'import sys; print sys.prefix'" % env['PYTHON']
env['PYTHON_SYS_PREFIX'] = call(sys_prefix)
# Note: we use the plat_specific argument here to make sure to respect the arch-specific site-packages location
site_packages = "%s -c 'from distutils.sysconfig import get_python_lib; print get_python_lib(plat_specific=True)'" % env['PYTHON']
env['PYTHON_SITE_PACKAGES'] = call(site_packages)
sys_version = "%s -c 'from distutils.sysconfig import get_python_version; print get_python_version()'" % env['PYTHON']
env['PYTHON_VERSION'] = call(sys_version)
py_includes = "%s -c 'from distutils.sysconfig import get_python_inc; print get_python_inc()'" % env['PYTHON']
env['PYTHON_INCLUDES'] = call(py_includes)
# if user-requested custom prefix fall back to manual concatenation for building subdirectories
py_relative_install = env['LIBDIR_SCHEMA'] + '/python' + env['PYTHON_VERSION'] + '/site-packages/'
if env['PYTHON_PREFIX']:
env['PYTHON_INSTALL_LOCATION'] = env['DESTDIR'] + '/' + env['PYTHON_PREFIX'] + '/' + py_relative_install
# TODO...
# When the env['PREFIX'] is changed should that
# also affect/override where to install python?
# Perhaps env['PREFIX'] should be None by default
# while 'usr/local' would be overridden in the code if
# env['PREFIX'] is set?
# This way we could more easily check for a 'custom' PREFIX
# code here but disabled...
#elif not env['PREFIX'] == '/usr/local':
# env['PYTHON_INSTALL_LOCATION'] = env['DESTDIR'] + '/' + env['PREFIX'] + '/' + py_relative_install
# fetch the mapnik version header in order to set the
# ABI version used to build libmapnik.so on linux in src/SConscript
abi = conf.GetMapnikLibVersion()
abi_fallback = [0,6,0]
if not abi:
color_print(1,'Problem encountered parsing mapnik version, falling back to %s' % abi_fallback)
env['ABI_VERSION'] = abi_fallback
else: else:
env['PYTHON_INSTALL_LOCATION'] = env['DESTDIR'] + '/' + env['PYTHON_SITE_PACKAGES'] env['ABI_VERSION'] = abi
majver, minver = env['PYTHON_VERSION'].split('.')
if (int(majver), int(minver)) < (2, 2):
color_print(1,"Python version 2.2 or greater required")
Exit(1)
color_print(4,'Bindings Python version... %s' % env['PYTHON_VERSION'])
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']))
SConscript('bindings/python/SConscript')
env = conf.Finish()
# Common C++ flags. # Common C++ flags.
if env['THREADING'] == 'multi' : if env['THREADING'] == 'multi' :
@ -705,7 +646,7 @@ else:
# Customizing the C++ compiler flags depending on: # Customizing the C++ compiler flags depending on:
# (1) the C++ compiler used; and # (1) the C++ compiler used; and
# (2) whether debug binaries are requested. # (2) whether debug binaries are requested.
if SUNCC: if env['SUNCC']:
if env['DEBUG']: if env['DEBUG']:
env.Append(CXXFLAGS = common_cxx_flags + debug_flags) env.Append(CXXFLAGS = common_cxx_flags + debug_flags)
else: else:
@ -719,5 +660,95 @@ else:
else: else:
env.Append(CXXFLAGS = gcc_cxx_flags + '-O%s -finline-functions -Wno-inline %s' % (env['OPTIMIZATION'],ndebug_flags)) env.Append(CXXFLAGS = gcc_cxx_flags + '-O%s -finline-functions -Wno-inline %s' % (env['OPTIMIZATION'],ndebug_flags))
if 'python' in env['BINDINGS']:
if not os.access(env['PYTHON'], os.X_OK):
color_print(1,"Cannot run python interpreter at '%s', make sure that you have the permissions to execute it." % env['PYTHON'])
Exit(1)
SConscript('fonts/SConscript') sys_prefix = "%s -c 'import sys; print sys.prefix'" % env['PYTHON']
env['PYTHON_SYS_PREFIX'] = call(sys_prefix)
# Note: we use the plat_specific argument here to make sure to respect the arch-specific site-packages location
site_packages = "%s -c 'from distutils.sysconfig import get_python_lib; print get_python_lib(plat_specific=True)'" % env['PYTHON']
env['PYTHON_SITE_PACKAGES'] = call(site_packages)
sys_version = "%s -c 'from distutils.sysconfig import get_python_version; print get_python_version()'" % env['PYTHON']
env['PYTHON_VERSION'] = call(sys_version)
py_includes = "%s -c 'from distutils.sysconfig import get_python_inc; print get_python_inc()'" % env['PYTHON']
env['PYTHON_INCLUDES'] = call(py_includes)
# if user-requested custom prefix fall back to manual concatenation for building subdirectories
py_relative_install = env['LIBDIR_SCHEMA'] + '/python' + env['PYTHON_VERSION'] + '/site-packages/'
if env['PYTHON_PREFIX']:
env['PYTHON_INSTALL_LOCATION'] = env['DESTDIR'] + '/' + env['PYTHON_PREFIX'] + '/' + py_relative_install
else:
env['PYTHON_INSTALL_LOCATION'] = env['DESTDIR'] + '/' + env['PYTHON_SITE_PACKAGES']
majver, minver = env['PYTHON_VERSION'].split('.')
if (int(majver), int(minver)) < (2, 2):
color_print(1,"Python version 2.2 or greater required")
Exit(1)
color_print(4,'Bindings Python version... %s' % env['PYTHON_VERSION'])
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']))
# finish config stage and pickle results
env = conf.Finish()
env_cache = open(SCONS_CONFIGURE_CACHE, 'w')
pickle_dict = {}
for i in pickle_store:
pickle_dict[i] = env.get(i)
pickle.dump(pickle_dict,env_cache)
env_cache.close()
# fix up permissions on pickled options
try:
os.chmod(SCONS_CONFIGURE_CACHE,0666)
except: pass
# clean up test build targets
if not env['FAST']:
try:
for test in glob('%s/*' % SCONF_TEMP_DIR):
os.unlink(test)
except: pass
if 'configure' in command_line_args:
Exit(0)
#### Builds ####
# export env so it is available in Sconscript files
Export('env')
# Build agg first, doesn't need anything special
if env['INTERNAL_LIBAGG']:
SConscript('agg/SConscript')
# Build the core library
SConscript('src/SConscript')
# Build shapeindex and remove its dependency from the LIBS
if 'boost_program_options%s' % env['BOOST_APPEND'] in env['LIBS']:
SConscript('utils/shapeindex/SConscript')
env['LIBS'].remove('boost_program_options%s' % env['BOOST_APPEND'])
else :
color_print(1,"WARNING: Cannot find boost_program_options. 'shapeindex' won't be available")
# Build the requested and able-to-be-compiled input plug-ins
for plugin in env['REQUESTED_PLUGINS']:
details = PLUGINS[plugin]
if details['lib'] in env['LIBS']:
SConscript('plugins/input/%s/SConscript' % plugin)
env['LIBS'].remove(details['lib'])
elif not details['lib']:
# build internal shape and raster plugins
SConscript('plugins/input/%s/SConscript' % plugin)
# Build the Python bindings
if 'python' in env['BINDINGS']:
SConscript('bindings/python/SConscript')
# Configure fonts and if requested install the bundled DejaVu fonts
SConscript('fonts/SConscript')

View file

@ -24,15 +24,10 @@ import re
import os import os
Import('env') Import('env')
Import('conf')
prefix = env['PREFIX'] prefix = env['PREFIX']
install_prefix = env['DESTDIR'] + '/' + prefix install_prefix = env['DESTDIR'] + '/' + prefix
if env['CAIRO'] and conf.CheckPKGConfig('0.15.0') and conf.CheckPKG('pycairo'):
env.ParseConfig('pkg-config --cflags pycairo')
env.Append(CXXFLAGS = '-DHAVE_PYCAIRO');
linkflags = '' linkflags = ''
libraries = ['mapnik','png','jpeg'] libraries = ['mapnik','png','jpeg']
libraries.append('boost_python%s' % env['BOOST_APPEND']) libraries.append('boost_python%s' % env['BOOST_APPEND'])
@ -72,6 +67,10 @@ exp = r"%s{2,}" % os.sep
mapnik_lib_path = re.sub(exp,os.sep, prefix + '/' + env['LIBDIR_SCHEMA'] + env['LIB_DIR_NAME']) mapnik_lib_path = re.sub(exp,os.sep, prefix + '/' + env['LIBDIR_SCHEMA'] + env['LIB_DIR_NAME'])
file('mapnik/paths.py','w').write(paths % (mapnik_lib_path)) file('mapnik/paths.py','w').write(paths % (mapnik_lib_path))
try:
os.chmod('mapnik/paths.py',0666)
except: pass
# install the core mapnik python files, including '__init__.py' and 'paths.py' # install the core mapnik python files, including '__init__.py' and 'paths.py'
init_files = glob.glob('mapnik/*.py') init_files = glob.glob('mapnik/*.py')
init_module = env.Install(env['PYTHON_INSTALL_LOCATION'] + '/mapnik', init_files) init_module = env.Install(env['PYTHON_INSTALL_LOCATION'] + '/mapnik', init_files)