1702 lines
66 KiB
Python
Executable file
1702 lines
66 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
# acprep, version 3.0
|
|
#
|
|
# This script configures my ledger source tree on my Mac OS/X machine. This
|
|
# is not necessary, however, since I keep all the files necessary for building
|
|
# checked in to the source tree. Users can just type './configure && make'.
|
|
# This script simply sets up the compiler and linker flags for all the various
|
|
# build permutations I use for testing and profiling.
|
|
|
|
import inspect
|
|
import logging
|
|
import logging.handlers
|
|
import optparse
|
|
import os
|
|
import re
|
|
import shutil
|
|
import string
|
|
import sys
|
|
import time
|
|
import tempfile
|
|
import datetime
|
|
|
|
try:
|
|
import hashlib
|
|
except:
|
|
import md5
|
|
|
|
from os.path import *
|
|
from stat import *
|
|
from subprocess import Popen, PIPE, call
|
|
|
|
LEVELS = {'DEBUG': logging.DEBUG,
|
|
'INFO': logging.INFO,
|
|
'WARNING': logging.WARNING,
|
|
'ERROR': logging.ERROR,
|
|
'CRITICAL': logging.CRITICAL}
|
|
|
|
search_prefixes = [ '/opt/local',
|
|
'/usr/local',
|
|
'/sw',
|
|
'/usr' ]
|
|
|
|
class BoostInfo(object):
|
|
log = None
|
|
suffix = ""
|
|
file_suffix = ".so"
|
|
home_path = "/usr"
|
|
include_path = "include"
|
|
library_path = "lib"
|
|
configured = False
|
|
|
|
def __init__(self, log):
|
|
self.log = log
|
|
|
|
def configure(self, suffix = None, file_suffix = None, home_path = None,
|
|
include_path = None, library_path = None):
|
|
path = library_path or self.library_path
|
|
if not isabs(path):
|
|
path = join(home_path or self.home_path, path)
|
|
if not exists(path) or not isdir(path) or \
|
|
not self.check_for_boost_regex_lib(path, suffix or self.suffix,
|
|
file_suffix or self.file_suffix):
|
|
return False
|
|
|
|
path = include_path or self.include_path
|
|
if not isabs(path):
|
|
path = join(home_path or self.home_path, path)
|
|
if not exists(path) or not isdir(path) or \
|
|
not self.check_for_boost_regex_hpp(path):
|
|
return False
|
|
|
|
self.log.debug('Configuring Boost details')
|
|
if suffix:
|
|
self.log.debug('Setting Boost suffix to => ' + suffix)
|
|
self.suffix = suffix
|
|
if file_suffix:
|
|
self.log.debug('Setting Boost file suffix to => ' + file_suffix)
|
|
self.file_suffix = file_suffix
|
|
if home_path:
|
|
self.log.debug('Setting Boost home to => ' + home_path)
|
|
self.home_path = home_path
|
|
if include_path:
|
|
self.log.debug('Setting Boost include directory to => ' + include_path)
|
|
self.include_path = include_path
|
|
if library_path:
|
|
self.log.debug('Setting Boost lib directory to => ' + library_path)
|
|
self.library_path = library_path
|
|
|
|
self.configured = True
|
|
|
|
return True # The object was configured
|
|
|
|
def option_boost_suffix(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --boost or --boost-suffix')
|
|
self.suffix = value
|
|
|
|
def option_boost_file_suffix(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --boost-file-suffix')
|
|
self.file_suffix = value
|
|
|
|
def option_boost_home(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --boost-home')
|
|
self.home_path = value
|
|
|
|
def option_boost_include(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --boost-include')
|
|
self.include_path = value
|
|
|
|
def option_boost_lib(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --boost-lib')
|
|
self.library_path = value
|
|
|
|
def inform_boost_details(self):
|
|
self.log.info('Boost was found here:')
|
|
self.log.info('Boost home path => ' + self.home_path)
|
|
self.log.info('Boost include path => ' + self.include_directory())
|
|
self.log.info('Boost library path => ' + self.library_directory())
|
|
self.log.info('Boost suffix => ' + self.suffix)
|
|
self.log.info('Boost file suffix => ' + self.file_suffix)
|
|
|
|
def find_boost_in_directory(self, path):
|
|
if exists(path) and isdir(path):
|
|
for entry in reversed(sorted(os.listdir(path))):
|
|
if re.search('boost_regex', entry):
|
|
self.log.info('Found a Boost library: ' + join(path, entry))
|
|
|
|
match = re.match('libboost_regex([^.]*)(\.(a|so|dylib))', entry)
|
|
if match:
|
|
suffix = match.group(1)
|
|
file_suffix = match.group(2)
|
|
self.log.info('Found a Boost suffix => ' + suffix)
|
|
self.log.info('Found a Boost file suffix => ' + file_suffix)
|
|
return [suffix, file_suffix]
|
|
else:
|
|
self.log.debug('The directory "%s" is not valid, skipping' % path)
|
|
return None
|
|
|
|
def locate_boost(self):
|
|
lib64_dirs = map(lambda x: join(x, 'lib64'), search_prefixes)
|
|
lib_dirs = map(lambda x: join(x, 'lib'), search_prefixes)
|
|
result = None
|
|
for path in lib64_dirs + lib_dirs:
|
|
self.log.info('Looking for Boost in %s...' % path)
|
|
result = self.find_boost_in_directory(path)
|
|
if result is not None:
|
|
self.suffix, self.file_suffix = result
|
|
self.library_path = path
|
|
self.home_path = dirname(path)
|
|
self.configured = True
|
|
self.inform_boost_details()
|
|
break
|
|
if result is None:
|
|
self.log.error("Boost not found, try --boost-home (and --boost-suffix, --boost-file-suffix)")
|
|
sys.exit(1)
|
|
|
|
def check_for_boost_regex_lib(self, path, suffix, file_suffix):
|
|
regex_lib = join(path, 'libboost_regex' + suffix)
|
|
return exists(regex_lib + file_suffix)
|
|
|
|
def check_for_boost_regex_hpp(self, path):
|
|
regex_hpp = join(path, 'boost/regex.hpp')
|
|
return exists(regex_hpp)
|
|
|
|
def get_suffix(self):
|
|
if not self.configured:
|
|
self.locate_boost()
|
|
return self.suffix
|
|
|
|
def get_file_suffix(self):
|
|
if not self.configured:
|
|
self.locate_boost()
|
|
return self.file_suffix
|
|
|
|
def include_directory(self):
|
|
if not self.configured:
|
|
self.locate_boost()
|
|
|
|
if isabs(self.include_path):
|
|
path = self.include_path
|
|
else:
|
|
path = join(self.home_path, self.include_path)
|
|
|
|
if not exists(path) or not isdir(path):
|
|
self.log.error("Boost include directory '%s' not found, use --boost-include" % path)
|
|
sys.exit(1)
|
|
|
|
if not self.check_for_boost_regex_hpp(path):
|
|
self.log.error("Could not find Boost header 'boost/regex.hpp' in '%s'; use --boost-* flags" % path)
|
|
sys.exit(1)
|
|
|
|
return path
|
|
|
|
def library_directory(self):
|
|
if not self.configured:
|
|
self.locate_boost()
|
|
|
|
if isabs(self.library_path):
|
|
path = self.library_path
|
|
else:
|
|
path = join(self.home_path, self.library_path)
|
|
|
|
if not exists(path) or not isdir(path):
|
|
self.log.error("Boost library directory '%s' not found, use --boost-include" % path)
|
|
sys.exit(1)
|
|
|
|
if not self.check_for_boost_regex_lib(path, self.suffix, self.file_suffix):
|
|
self.log.error("Could not find Boost library 'boost_regex' in '%s'; use --boost-* flags" % path)
|
|
sys.exit(1)
|
|
|
|
return path
|
|
|
|
def dependencies(self, system):
|
|
if system == 'darwin':
|
|
return [ 'boost-jam', 'boost', '+icu+python26+st+universal' ]
|
|
|
|
if system == 'centos':
|
|
return [ 'boost-devel' ]
|
|
|
|
elif system == 'ubuntu-karmic':
|
|
return [ 'bjam', 'libboost1.40-dev',
|
|
'libboost-regex1.40-dev',
|
|
'libboost-date-time1.40-dev',
|
|
'libboost-filesystem1.40-dev',
|
|
'libboost-python1.40-dev' ]
|
|
|
|
elif system == 'ubuntu-hardy':
|
|
return [ 'bjam', 'libboost1.35-dev',
|
|
'libboost-python1.35-dev',
|
|
'libboost-regex1.35-dev',
|
|
'libboost-date-time1.35-dev',
|
|
'libboost-filesystem1.35-dev' ]
|
|
|
|
class CommandLineApp(object):
|
|
"Base class for building command line applications."
|
|
|
|
force_exit = True # If true, always ends run() with sys.exit()
|
|
log_handler = None
|
|
darwin_gcc = False
|
|
boost_version = "1_43"
|
|
|
|
options = {
|
|
'debug': False,
|
|
'verbose': False,
|
|
'logfile': False,
|
|
'loglevel': False
|
|
}
|
|
|
|
def __init__(self):
|
|
"Initialize CommandLineApp."
|
|
# Create the logger
|
|
self.log = logging.getLogger(os.path.basename(sys.argv[0]))
|
|
ch = logging.StreamHandler()
|
|
formatter = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
|
|
ch.setFormatter(formatter)
|
|
self.log.addHandler(ch)
|
|
self.log_handler = ch
|
|
|
|
# Setup the options parser
|
|
usage = 'usage: %prog [OPTIONS...] [ARGS...]'
|
|
op = self.option_parser = optparse.OptionParser(usage = usage,
|
|
conflict_handler = 'resolve')
|
|
op.add_option('', '--debug',
|
|
action='store_true', dest='debug',
|
|
default=False, help='show debug messages and pass exceptions')
|
|
op.add_option('-v', '--verbose',
|
|
action='store_true', dest='verbose',
|
|
default=False, help='show informational messages')
|
|
op.add_option('-q', '--quiet',
|
|
action='store_true', dest='quiet',
|
|
default=False, help='do not show log messages on console')
|
|
op.add_option('', '--log', metavar='FILE',
|
|
type='string', action='store', dest='logfile',
|
|
default=False, help='append logging data to FILE')
|
|
op.add_option('', '--loglevel', metavar='LEVEL',
|
|
type='string', action='store', dest='loglevel',
|
|
default=False, help='set log level: DEBUG, INFO, WARNING, ERROR, CRITICAL')
|
|
return
|
|
|
|
def main(self, *args):
|
|
"""Main body of your application.
|
|
|
|
This is the main portion of the app, and is run after all of the
|
|
arguments are processed. Override this method to implment the primary
|
|
processing section of your application."""
|
|
pass
|
|
|
|
def handleInterrupt(self):
|
|
"""Called when the program is interrupted via Control-C or SIGINT.
|
|
Returns exit code."""
|
|
self.log.error('Canceled by user.')
|
|
return 1
|
|
|
|
def handleMainException(self):
|
|
"Invoked when there is an error in the main() method."
|
|
if not self.options.debug:
|
|
self.log.exception('Caught exception')
|
|
return 1
|
|
|
|
## INTERNALS (Subclasses should not need to override these methods)
|
|
|
|
def run(self):
|
|
"""Entry point.
|
|
|
|
Process options and execute callback functions as needed. This method
|
|
should not need to be overridden, if the main() method is defined."""
|
|
# Process the options supported and given
|
|
self.options, main_args = self.option_parser.parse_args()
|
|
|
|
if self.options.logfile:
|
|
fh = logging.handlers.RotatingFileHandler(self.options.logfile,
|
|
maxBytes = (1024 * 1024),
|
|
backupCount = 5)
|
|
formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")
|
|
fh.setFormatter(formatter)
|
|
self.log.addHandler(fh)
|
|
|
|
if self.options.quiet:
|
|
self.log.removeHandler(self.log_handler)
|
|
ch = logging.handlers.SysLogHandler()
|
|
formatter = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
|
|
ch.setFormatter(formatter)
|
|
self.log.addHandler(ch)
|
|
self.log_handler = ch
|
|
|
|
if self.options.loglevel:
|
|
self.log.setLevel(LEVELS[self.options.loglevel])
|
|
elif self.options.debug:
|
|
self.log.setLevel(logging.DEBUG)
|
|
elif self.options.verbose:
|
|
self.log.setLevel(logging.INFO)
|
|
|
|
exit_code = 0
|
|
try:
|
|
# We could just call main() and catch a TypeError, but that would
|
|
# not let us differentiate between application errors and a case
|
|
# where the user has not passed us enough arguments. So, we check
|
|
# the argument count ourself.
|
|
argspec = inspect.getargspec(self.main)
|
|
expected_arg_count = len(argspec[0]) - 1
|
|
|
|
if len(main_args) >= expected_arg_count:
|
|
exit_code = self.main(*main_args)
|
|
else:
|
|
self.log.debug('Incorrect argument count (expected %d, got %d)' %
|
|
(expected_arg_count, len(main_args)))
|
|
self.option_parser.print_help()
|
|
exit_code = 1
|
|
|
|
except KeyboardInterrupt:
|
|
exit_code = self.handleInterrupt()
|
|
|
|
except SystemExit, msg:
|
|
exit_code = msg.args[0]
|
|
|
|
except Exception:
|
|
exit_code = self.handleMainException()
|
|
if self.options.debug:
|
|
raise
|
|
|
|
if self.force_exit:
|
|
sys.exit(exit_code)
|
|
return exit_code
|
|
|
|
|
|
class PrepareBuild(CommandLineApp):
|
|
#########################################################################
|
|
# Initialization routines #
|
|
#########################################################################
|
|
|
|
def initialize(self):
|
|
self.log.debug('Initializing all state variables')
|
|
|
|
self.should_clean = False
|
|
self.configured = False
|
|
self.current_ver = None
|
|
#self.current_flavor = 'default'
|
|
self.current_flavor = 'debug'
|
|
self.prefix_dir = None
|
|
self.products_dir = None
|
|
self.build_dir = self.source_dir
|
|
self.configure_args = ['--with-included-gettext', '--enable-python']
|
|
self.boost_info = BoostInfo(self.log)
|
|
self.sys_include_dirs = []
|
|
self.sys_library_dirs = []
|
|
|
|
self.CPPFLAGS = []
|
|
self.CFLAGS = []
|
|
self.CXXFLAGS = []
|
|
self.LDFLAGS = []
|
|
|
|
self.envvars = {
|
|
'PYTHON_HOME': '/usr',
|
|
'PYTHON_VERSION': '2.6',
|
|
'LEDGER_PRODUCTS': None,
|
|
'CC': 'gcc',
|
|
'CPPFLAGS': '',
|
|
'CFLAGS': '',
|
|
'CXX': 'g++',
|
|
'CXXFLAGS': '',
|
|
'LD': 'g++',
|
|
'LDFLAGS': ''
|
|
}
|
|
|
|
for varname in self.envvars.keys():
|
|
if os.environ.has_key(varname):
|
|
self.envvars[varname] = os.environ[varname]
|
|
|
|
if varname.endswith('FLAGS'):
|
|
self.__dict__[varname] = string.split(os.environ[varname])
|
|
self.envvars[varname] = ''
|
|
|
|
# If ~/Products/ or build/ exists, use them instead of the source tree
|
|
# for building
|
|
products = self.default_products_directory()
|
|
if (exists(products) and isdir(products)) or \
|
|
(exists('build') and isdir('build')):
|
|
self.build_dir = None
|
|
|
|
def __init__(self):
|
|
CommandLineApp.__init__(self)
|
|
self.log.setLevel(logging.INFO)
|
|
|
|
self.force = False
|
|
self.no_pch = False
|
|
self.source_dir = os.getcwd()
|
|
|
|
self.initialize()
|
|
|
|
op = self.option_parser
|
|
|
|
# These options call into self.boost_info
|
|
op.add_option('', '--boost', metavar='BOOST_SUFFIX',
|
|
action="callback", type="string",
|
|
callback=self.boost_info.option_boost_suffix,
|
|
help='Set Boost library suffix (ex: "--boost=-mt")')
|
|
op.add_option('', '--boost-suffix', metavar='BOOST_SUFFIX',
|
|
action="callback", type="string",
|
|
callback=self.boost_info.option_boost_suffix,
|
|
help='Set Boost library suffix (ex: "--boost-suffix=-mt")')
|
|
op.add_option('', '--boost-file-suffix', metavar='BOOST_FILE_SUFFIX',
|
|
action="callback", type="string",
|
|
callback=self.boost_info.option_boost_file_suffix,
|
|
help='Set Boost library file suffix (ex: "--boost-file-suffix=.so")')
|
|
op.add_option('', '--boost-home', metavar='BOOST_HOME',
|
|
action="callback", type="string",
|
|
callback=self.boost_info.option_boost_home,
|
|
help='Set Boost home directory (ex: "--boost-home=DIR")')
|
|
op.add_option('', '--boost-include', metavar='BOOST_INCLUDE',
|
|
action="callback", type="string",
|
|
callback=self.boost_info.option_boost_include,
|
|
help='Set Boost include path (ex: "--boost-include=DIR")')
|
|
op.add_option('', '--boost-lib', metavar='BOOST_LIB',
|
|
action="callback", type="string",
|
|
callback=self.boost_info.option_boost_lib,
|
|
help='Set Boost library path (ex: "--boost-lib=DIR")')
|
|
|
|
op.add_option('-j', '--jobs', metavar='N',
|
|
type='int', action='store', dest='jobs',
|
|
default=1, help='Allow N make jobs at once')
|
|
op.add_option('', '--force', action="callback",
|
|
callback=self.option_force,
|
|
help="Perform every action, without checking")
|
|
op.add_option('', '--glibcxx-debug', action='store_true',
|
|
dest='use_glibcxx_debug', default=False,
|
|
help='Define _GLIBCXX_DEBUG=1 during compilation')
|
|
op.add_option('', '--help', action="callback",
|
|
callback=self.option_help,
|
|
help='Show this help text')
|
|
op.add_option('', '--local', action="callback",
|
|
callback=self.option_local,
|
|
help='Build directly within the source tree (default)')
|
|
op.add_option('', '--no-pch', action="callback",
|
|
callback=self.option_no_pch,
|
|
help='Do not use pre-compiled headers')
|
|
op.add_option('', '--no-patch', action='store_true', dest='no_patch',
|
|
default=False,
|
|
help='Do not patch the Makefile for prettier output')
|
|
op.add_option('', '--no-git', action='store_true', dest='no_git',
|
|
default=False,
|
|
help='Do not call out to Git; useful for offline builds')
|
|
op.add_option('', '--no-python', action='store_true', dest='no_python',
|
|
default=False,
|
|
help='Do not enable Python support by default')
|
|
op.add_option('', '--enable-cache', action='store_true',
|
|
dest='enable_cache', default=False,
|
|
help='Enable use of Boost.Serialization (--cache)')
|
|
op.add_option('', '--enable-doxygen', action='store_true',
|
|
dest='enable_doxygen', default=False,
|
|
help='Enable use of Doxygen to build ref manual ("make docs")')
|
|
op.add_option('', '--universal', action='store_true',
|
|
dest='universal', default=False,
|
|
help='Attempt to build universal binaries')
|
|
op.add_option('', '--gcc45', action='store_true',
|
|
dest='gcc45', default=False,
|
|
help='Require the use of gcc 4.5')
|
|
op.add_option('', '--output', metavar='DIR', action="callback",
|
|
callback=self.option_output,
|
|
help='Build in the specified directory')
|
|
op.add_option('', '--pch', action="callback",
|
|
callback=self.option_pch,
|
|
help='Enable use of pre-compiled headers')
|
|
op.add_option('', '--pic', action="callback",
|
|
callback=self.option_pic,
|
|
help='Compile with explicit PIC support')
|
|
op.add_option('', '--prefix', metavar='DIR', action="callback",
|
|
callback=self.option_prefix, type="string",
|
|
help='Use custom installation prefix')
|
|
op.add_option('', '--products', metavar='DIR', action="callback",
|
|
callback=self.option_products,
|
|
help='Collect all build products in this directory')
|
|
op.add_option('', '--trees', action="callback",
|
|
callback=self.option_trees,
|
|
help='Use separate build trees for each flavor')
|
|
op.add_option('', '--release', action="callback",
|
|
callback=self.option_release,
|
|
help='Setup for doing a faster, once-only build')
|
|
op.add_option('', '--warn', action="callback",
|
|
callback=self.option_warn,
|
|
help='Enable full warning flags')
|
|
|
|
def main(self, *args):
|
|
if self.options.no_python:
|
|
self.configure_args.remove('--enable-python')
|
|
|
|
if args and args[0] in ['default', 'debug', 'opt', 'gcov', 'gprof']:
|
|
self.current_flavor = args[0]
|
|
args = args[1:]
|
|
|
|
if args:
|
|
cmd = args[0]
|
|
if not PrepareBuild.__dict__.has_key('phase_' + cmd):
|
|
self.log.error("Unknown build phase: " + cmd + "\n")
|
|
sys.exit(1)
|
|
else:
|
|
args = args[1:]
|
|
else:
|
|
cmd = 'config'
|
|
|
|
self.log.info('Invoking primary phase: ' + cmd)
|
|
PrepareBuild.__dict__['phase_' + cmd](self, *args)
|
|
|
|
#########################################################################
|
|
# General utility code #
|
|
#########################################################################
|
|
|
|
def execute(self, *args):
|
|
try:
|
|
self.log.debug('Executing command: ' + string.join(args, ' '))
|
|
|
|
retcode = call(args, shell=False)
|
|
if retcode < 0:
|
|
self.log.error("Child was terminated by signal", -retcode)
|
|
sys.exit(1)
|
|
elif retcode != 0:
|
|
self.log.error("Execution failed: " + string.join(args, ' '))
|
|
sys.exit(1)
|
|
except OSError, e:
|
|
self.log.error("Execution failed:", e)
|
|
sys.exit(1)
|
|
|
|
def get_stdout(self, *args):
|
|
try:
|
|
self.log.debug('Executing command: ' + string.join(args, ' '))
|
|
|
|
proc = Popen(args, shell=False, stdout=PIPE)
|
|
stdout = proc.stdout.read()
|
|
retcode = proc.wait()
|
|
if retcode < 0:
|
|
self.log.error("Child was terminated by signal",
|
|
-retcode)
|
|
sys.exit(1)
|
|
elif retcode != 0:
|
|
self.log.error("Execution failed: " + string.join(args, ' '))
|
|
sys.exit(1)
|
|
return stdout[:-1]
|
|
except OSError, e:
|
|
self.log.error("Execution failed:", e)
|
|
sys.exit(1)
|
|
|
|
def isnewer(self, file1, file2):
|
|
"Check if file1 is newer than file2."
|
|
if not exists(file2):
|
|
return True
|
|
return os.stat(file1)[ST_MTIME] > os.stat(file2)[ST_MTIME]
|
|
|
|
#########################################################################
|
|
# Determine information about the surroundings #
|
|
#########################################################################
|
|
|
|
def prefix_directory(self):
|
|
return self.prefix_dir if self.prefix_dir else None
|
|
|
|
def default_products_directory(self):
|
|
if self.envvars['LEDGER_PRODUCTS']:
|
|
return self.envvars['LEDGER_PRODUCTS']
|
|
else:
|
|
return join(os.environ['HOME'], "Products")
|
|
|
|
def products_directory(self):
|
|
if not self.products_dir:
|
|
products = self.default_products_directory()
|
|
|
|
if not exists(products) or not isdir(products):
|
|
products = join(self.source_dir, 'build')
|
|
|
|
products = join(products, basename(self.source_dir))
|
|
|
|
self.products_dir = products
|
|
|
|
return self.products_dir
|
|
|
|
def build_directory(self):
|
|
if not self.build_dir:
|
|
self.build_dir = join(self.products_directory(),
|
|
self.current_flavor)
|
|
return self.build_dir
|
|
|
|
def ensure(self, dirname):
|
|
if not exists(dirname):
|
|
self.log.info('Making directory: ' + dirname)
|
|
os.makedirs(dirname)
|
|
elif not isdir(dirname):
|
|
self.log.error('Directory is not a directory: ' + dirname)
|
|
sys.exit(1)
|
|
return dirname
|
|
|
|
def git_working_tree(self):
|
|
return exists('.git') and isdir('.git') and not self.options.no_git
|
|
|
|
def current_version(self):
|
|
if not self.current_ver:
|
|
if self.git_working_tree():
|
|
#date = self.get_stdout('git', 'log', '--format=%ci', '-1', 'HEAD')
|
|
#date = re.sub(" [-+][0-9][0-9][0-9][0-9]$", "", date)
|
|
#when = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
|
#self.current_ver = when.strftime("%Y%m%d_%H%M%S")
|
|
#commit = self.get_stdout('git', 'log', '--format=%h', 'HEAD^..HEAD')
|
|
#self.current_ver += "_" + commit
|
|
tag = self.get_stdout('git', 'describe', '--all', '--long')
|
|
self.current_ver = re.sub('heads/', '', tag)
|
|
else:
|
|
self.current_ver = "no-git"
|
|
return self.current_ver
|
|
|
|
def need_to_prepare_autotools(self):
|
|
if self.force:
|
|
return 'because it was forced'
|
|
elif self.isnewer('acprep', 'configure'):
|
|
self.should_clean = True
|
|
return 'because acprep is newer than configure'
|
|
elif self.isnewer('acprep', 'Makefile.in'):
|
|
self.should_clean = True
|
|
return 'because acprep is newer than Makefile.in'
|
|
elif self.isnewer('configure.ac', 'configure'):
|
|
return 'because configure.ac is newer than configure'
|
|
elif self.isnewer('Makefile.am', 'Makefile.in'):
|
|
return 'because Makefile.am is newer than Makefile.in'
|
|
return False
|
|
|
|
def phase_products(self, *args):
|
|
self.log.info('Executing phase: products')
|
|
print self.products_directory()
|
|
|
|
def phase_info(self, *args):
|
|
self.log.info('Executing phase: info')
|
|
|
|
environ, conf_args = self.configure_environment()
|
|
|
|
self.log.info("Current version => " + self.current_version())
|
|
self.log.info("Current flavor => " + self.current_flavor)
|
|
self.log.info("Source directory => " + self.source_dir)
|
|
self.log.info("Need to run autogen.sh => " +
|
|
str(self.need_to_prepare_autotools()))
|
|
if self.prefix_directory():
|
|
self.log.info("Installation prefix => " + self.prefix_directory())
|
|
self.log.info("Products directory => " + self.products_directory())
|
|
self.log.info("Build directory => " + self.build_directory())
|
|
self.log.info("Need to run configure => " +
|
|
str(self.need_to_run_configure()))
|
|
self.log.info("Use _GLIBCXX_DEBUG => " +
|
|
str(self.options.use_glibcxx_debug))
|
|
self.log.info("Use pre-compiled headers => " +
|
|
str('--enable-pch' in conf_args))
|
|
|
|
self.log.debug('Configure environment =>')
|
|
|
|
keys = environ.keys()
|
|
keys.sort()
|
|
for key in keys:
|
|
if key in ['PATH', 'CC', 'LD', 'CXX'] or \
|
|
key.endswith('FLAGS'):
|
|
self.log.debug(' %s=%s' % (key, environ[key]))
|
|
|
|
self.log.debug('Configure arguments =>')
|
|
|
|
for arg in conf_args + list(args):
|
|
self.log.debug(' %s' % arg)
|
|
|
|
def phase_sloc(self, *args):
|
|
self.log.info('Executing phase: sloc')
|
|
self.execute('sloccount', 'src', 'python', 'lisp', 'test')
|
|
|
|
#########################################################################
|
|
# Configure source tree using autogen #
|
|
#########################################################################
|
|
|
|
def phase_gettext(self, *args):
|
|
self.log.info('Executing phase: gettext')
|
|
|
|
# configure the template files
|
|
assert exists('po') and isdir('po')
|
|
if not exists(join('po', 'Makevars')):
|
|
assert exists(join('po', 'Makevars.template'))
|
|
self.log.info('Moving po/Makevars.template -> po/Makevars')
|
|
os.rename(join('po', 'Makevars.template'),
|
|
join('po', 'Makevars'))
|
|
|
|
POTFILES_in = open('po/POTFILES.in', 'w')
|
|
for filename in (f for f in os.listdir(join(self.source_dir, 'src'))
|
|
if re.search('\.(cc|h)', f)):
|
|
POTFILES_in.write(join('src', filename))
|
|
POTFILES_in.write('\n')
|
|
POTFILES_in.close()
|
|
|
|
def phase_version(self, *args):
|
|
self.log.info('Executing phase: version')
|
|
version_m4 = open('version.m4', 'w')
|
|
version_m4.write("m4_define([VERSION_NUMBER], [%s])\n" %
|
|
self.current_version())
|
|
version_m4.close()
|
|
|
|
def copytimes(self, src, dest):
|
|
os.utime(dest, (os.stat(src)[ST_ATIME], os.stat(src)[ST_MTIME]))
|
|
|
|
def phase_autogen(self, *args):
|
|
self.log.info('Executing phase: autogen')
|
|
|
|
if not exists('autogen.sh') or \
|
|
self.isnewer('tools/autogen.sh', 'autogen.sh'):
|
|
shutil.copyfile('tools/autogen.sh', 'autogen.sh')
|
|
self.copytimes('tools/autogen.sh', 'autogen.sh')
|
|
|
|
self.execute('sh', 'tools/autogen.sh')
|
|
|
|
def phase_aclocal(self, *args):
|
|
self.log.info('Executing phase: aclocal')
|
|
self.execute('aclocal', '-I', 'm4')
|
|
|
|
def phase_autoconf(self, *args):
|
|
self.log.info('Executing phase: autoconf')
|
|
|
|
if not exists('configure.ac') or \
|
|
self.isnewer('tools/configure.ac', 'configure.ac'):
|
|
shutil.copyfile('tools/configure.ac', 'configure.ac')
|
|
self.copytimes('tools/configure.ac', 'configure.ac')
|
|
|
|
if not exists('Makefile.am') or \
|
|
self.isnewer('tools/Makefile.am', 'Makefile.am'):
|
|
shutil.copyfile('tools/Makefile.am', 'Makefile.am')
|
|
self.copytimes('tools/Makefile.am', 'Makefile.am')
|
|
|
|
reason = self.need_to_prepare_autotools()
|
|
if reason:
|
|
self.log.info('autogen.sh must be run ' + reason)
|
|
self.phase_version()
|
|
self.phase_autogen()
|
|
self.phase_gettext()
|
|
self.phase_aclocal()
|
|
else:
|
|
self.log.debug('autogen.sh does not need to be run')
|
|
|
|
#########################################################################
|
|
# Update local files with the latest information #
|
|
#########################################################################
|
|
|
|
def phase_submodule(self, *args):
|
|
self.log.info('Executing phase: submodule')
|
|
if self.git_working_tree():
|
|
self.execute('git', 'submodule', 'init')
|
|
self.execute('git', 'submodule', 'update')
|
|
|
|
def phase_pull(self, *args):
|
|
self.log.info('Executing phase: pull')
|
|
if self.git_working_tree():
|
|
self.execute('git', 'pull')
|
|
self.phase_submodule()
|
|
|
|
#########################################################################
|
|
# Automatic installation of build dependencies #
|
|
#########################################################################
|
|
|
|
def phase_dependencies(self, *args):
|
|
self.log.info('Executing phase: dependencies')
|
|
|
|
self.log.info("Installing Ledger's build dependencies ...")
|
|
|
|
system = self.get_stdout('uname', '-s')
|
|
|
|
if system == 'Darwin':
|
|
if exists('/opt/local/bin/port'):
|
|
self.log.info('Looks like you are using MacPorts on OS X')
|
|
packages = [
|
|
'sudo', 'port', 'install', '-f',
|
|
'automake',
|
|
'autoconf',
|
|
'libtool',
|
|
'python26', '+universal',
|
|
'libiconv', '+universal',
|
|
'zlib', '+universal',
|
|
'gmp' ,'+universal',
|
|
'mpfr', '+universal',
|
|
'ncurses', '+universal',
|
|
'ncursesw', '+universal',
|
|
'gettext' ,'+universal',
|
|
'libedit' ,'+universal',
|
|
'cppunit', '+universal',
|
|
#'texlive',
|
|
#'doxygen',
|
|
#'graphviz',
|
|
'texinfo',
|
|
'lcov',
|
|
'sloccount'
|
|
] + self.boost_info.dependencies('darwin')
|
|
self.log.info('Executing: ' + string.join(packages, ' '))
|
|
self.execute(*packages)
|
|
elif exists('/sw/bin/fink'):
|
|
self.log.info('Looks like you are using Fink on OS X')
|
|
self.log.error("I don't know the package names for Fink yet!")
|
|
sys.exit(1)
|
|
|
|
elif system == 'Linux':
|
|
if exists('/etc/issue'):
|
|
issue = open('/etc/issue')
|
|
if issue.readline().startswith('Ubuntu'):
|
|
release = open('/etc/lsb-release')
|
|
info = release.read()
|
|
release.close()
|
|
if re.search('karmic', info):
|
|
self.log.info('Looks like you are using APT on Ubuntu Karmic')
|
|
packages = [
|
|
'sudo', 'apt-get', 'install',
|
|
'build-essential',
|
|
'libtool',
|
|
'autoconf',
|
|
'automake',
|
|
'zlib1g-dev',
|
|
'libbz2-dev',
|
|
'python-dev',
|
|
'libgmp3-dev',
|
|
'libmpfr-dev',
|
|
'gettext',
|
|
'cvs',
|
|
'libedit-dev',
|
|
'libcppunit-dev',
|
|
#'texlive-full',
|
|
#'doxygen',
|
|
#'graphviz',
|
|
'texinfo',
|
|
'lcov',
|
|
'sloccount'
|
|
] + self.boost_info.dependencies('ubuntu-karmic')
|
|
else:
|
|
self.log.info('Looks like you are using APT on Ubuntu Hardy')
|
|
packages = [
|
|
'sudo', 'apt-get', 'install',
|
|
'build-essential',
|
|
'libtool',
|
|
'autoconf',
|
|
'automake',
|
|
'zlib1g-dev',
|
|
'libbz2-dev',
|
|
'python-dev',
|
|
'cvs',
|
|
'gettext',
|
|
'libgmp3-dev',
|
|
'libmpfr-dev',
|
|
'libedit-dev',
|
|
'libcppunit-dev',
|
|
#'texlive-full',
|
|
#'doxygen',
|
|
#'graphviz',
|
|
'texinfo',
|
|
'lcov',
|
|
'sloccount'
|
|
] + self.boost_info.dependencies('ubuntu-hardy')
|
|
self.log.info('Executing: ' + string.join(packages, ' '))
|
|
self.execute(*packages)
|
|
|
|
if exists('/etc/redhat-release'):
|
|
release = open('/etc/redhat-release')
|
|
if release.readline().startswith('CentOS'):
|
|
self.log.info('Looks like you are using YUM on CentOS')
|
|
packages = [
|
|
'sudo', 'yum', 'install',
|
|
'gcc',
|
|
'gcc-c++',
|
|
'compat-gcc-*',
|
|
'make',
|
|
'libtool',
|
|
'autoconf',
|
|
'automake',
|
|
'zlib-devel',
|
|
'bzip2-devel',
|
|
'python-devel',
|
|
'gmp-devel',
|
|
'gettext-devel',
|
|
#'mpfr-devel'
|
|
'libedit-devel',
|
|
'cppunit-devel',
|
|
#'texlive-full',
|
|
#'doxygen',
|
|
#'graphviz',
|
|
'texinfo',
|
|
#'lcov',
|
|
#'sloccount'
|
|
]
|
|
self.log.info('Executing: ' + string.join(packages, ' '))
|
|
self.execute(*packages)
|
|
|
|
#########################################################################
|
|
# Determine the system's basic configuration #
|
|
#########################################################################
|
|
|
|
def setup_system_directories(self):
|
|
boost_include = self.boost_info.include_directory()
|
|
boost_library = self.boost_info.library_directory()
|
|
|
|
if re.match('/opt/local', self.boost_info.home_path):
|
|
self.log.debug("Setting Python home to /opt/local based on Boost's location")
|
|
self.envvars['PYTHON_HOME'] = '/opt/local'
|
|
|
|
# Each of these becomes '-isystem <name>'
|
|
for path in ['/usr/local/include',
|
|
boost_include,
|
|
'%s/include/python%s' %
|
|
(self.envvars['PYTHON_HOME'],
|
|
self.envvars['PYTHON_VERSION'].strip()),
|
|
'/opt/local/include',
|
|
'/sw/include']:
|
|
if exists(path) and isdir(path) and \
|
|
path != '/usr/include':
|
|
self.log.info('Noticing include directory => ' + path)
|
|
self.sys_include_dirs.append(path)
|
|
|
|
# Each of these becomes '-L<name>'
|
|
for path in ['/usr/local/lib',
|
|
'%s/lib' % self.envvars['PYTHON_HOME'],
|
|
'%s/lib/python%s/config'
|
|
% (self.envvars['PYTHON_HOME'],
|
|
self.envvars['PYTHON_VERSION'].strip()),
|
|
'/opt/local/lib',
|
|
boost_library,
|
|
'/sw/lib']:
|
|
if exists(path) and isdir(path) and \
|
|
path not in self.sys_library_dirs:
|
|
self.log.info('Noticing library directory => ' + path)
|
|
self.sys_library_dirs.append(path)
|
|
|
|
def setup_for_johnw(self):
|
|
match = re.search('gcc-mp-([0-9]+)\.([0-9]+)', self.envvars['CC'])
|
|
if match:
|
|
gcc_version = match.group(1) + match.group(2)
|
|
else:
|
|
gcc_version = "42"
|
|
|
|
if self.current_flavor == 'debug' or self.current_flavor == 'gcov':
|
|
if exists('/usr/local/stow/cppunit-gcc%s/include' % gcc_version):
|
|
self.sys_include_dirs.insert(
|
|
0, '/usr/local/stow/cppunit-gcc%s/include' % gcc_version)
|
|
self.sys_library_dirs.insert(
|
|
0, '/usr/local/stow/cppunit-gcc%s/lib' % gcc_version)
|
|
|
|
if exists('/usr/local/stow/icu-gcc%s/include' % gcc_version):
|
|
self.sys_include_dirs.insert(
|
|
0, '/usr/local/stow/icu-gcc%s/include' % gcc_version)
|
|
self.sys_library_dirs.insert(
|
|
0, '/usr/local/stow/icu-gcc%s/lib' % gcc_version)
|
|
|
|
self.CPPFLAGS.append('-D_GLIBCXX_FULLY_DYNAMIC_STRING=1')
|
|
self.configure_args.append('--disable-shared')
|
|
|
|
self.options.use_glibcxx_debug = True
|
|
else:
|
|
self.CXXFLAGS.append('-march=nocona')
|
|
self.CXXFLAGS.append('-msse3')
|
|
|
|
self.locate_darwin_libraries()
|
|
|
|
def setup_for_system(self):
|
|
system = self.get_stdout('uname', '-s')
|
|
|
|
self.log.info('System type is => ' + system)
|
|
|
|
# These options are global defaults at the moment
|
|
#self.option_warn()
|
|
if not self.no_pch:
|
|
self.option_pch()
|
|
|
|
if system == 'Linux':
|
|
arch = self.get_stdout('uname', '-m')
|
|
if arch == 'x86_64':
|
|
if '--disable-shared' in self.configure_args:
|
|
self.configure_args.remove('--disable-shared')
|
|
self.configure_args.append('--disable-static')
|
|
self.CXXFLAGS.append('-pthread')
|
|
|
|
elif system == 'Solaris':
|
|
self.CXXFLAGS.append('-pthread')
|
|
|
|
elif system == 'Darwin':
|
|
if (self.current_flavor == 'opt' or \
|
|
self.current_flavor == 'default') and \
|
|
not self.options.gcc45 and \
|
|
exists('/usr/bin/g++-4.2'):
|
|
self.envvars['CC'] = '/usr/bin/gcc-4.2'
|
|
self.envvars['CXX'] = '/usr/bin/g++-4.2'
|
|
self.envvars['LD'] = '/usr/bin/g++-4.2'
|
|
self.darwin_gcc = True
|
|
elif exists('/opt/local/bin/g++-mp-4.5'):
|
|
self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.5'
|
|
self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.5'
|
|
self.envvars['LD'] = '/opt/local/bin/g++-mp-4.5'
|
|
elif exists('/opt/local/bin/g++-mp-4.4'):
|
|
self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.4'
|
|
self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.4'
|
|
self.envvars['LD'] = '/opt/local/bin/g++-mp-4.4'
|
|
elif exists('/opt/local/bin/g++-mp-4.3'):
|
|
self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.3'
|
|
self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.3'
|
|
self.envvars['LD'] = '/opt/local/bin/g++-mp-4.3'
|
|
elif exists('/usr/bin/g++-4.2'):
|
|
self.envvars['CC'] = '/usr/bin/gcc-4.2'
|
|
self.envvars['CXX'] = '/usr/bin/g++-4.2'
|
|
self.envvars['LD'] = '/usr/bin/g++-4.2'
|
|
self.darwin_gcc = True
|
|
else:
|
|
# g++ 4.0.1 cannot use PCH headers on OS X 10.5
|
|
self.option_no_pch()
|
|
|
|
if self.options.enable_doxygen:
|
|
self.configure_args.append('--enable-doxygen')
|
|
if self.options.enable_cache:
|
|
self.configure_args.append('--enable-cache')
|
|
|
|
if exists('/Users/johnw/Projects/ledger/plan/TODO'):
|
|
self.setup_for_johnw()
|
|
self.setup_system_directories()
|
|
|
|
if '--enable-pch' not in self.configure_args and \
|
|
(exists('/opt/local/bin/ccache') or \
|
|
exists('/usr/local/bin/ccache')):
|
|
self.envvars['CC'] = 'ccache ' + self.envvars['CC']
|
|
self.envvars['CXX'] = 'ccache ' + self.envvars['CXX']
|
|
self.envvars['LD'] = 'ccache ' + self.envvars['LD']
|
|
|
|
def setup_flags(self):
|
|
for path in self.sys_include_dirs:
|
|
self.CPPFLAGS.append('-isystem')
|
|
self.CPPFLAGS.append(path)
|
|
|
|
self.CXXFLAGS.append('-pipe')
|
|
|
|
for path in self.sys_library_dirs:
|
|
self.LDFLAGS.append('-L' + path)
|
|
|
|
def setup_flavor(self):
|
|
self.setup_for_system()
|
|
|
|
if not PrepareBuild.__dict__.has_key('setup_flavor_' +
|
|
self.current_flavor):
|
|
self.log.error('Unknown build flavor "%s"' % self.current_flavor)
|
|
sys.exit(1)
|
|
|
|
self.log.info('Setting up build flavor => ' + self.current_flavor)
|
|
PrepareBuild.__dict__['setup_flavor_' + self.current_flavor](self)
|
|
|
|
self.setup_flags()
|
|
|
|
def escape_string(self, data):
|
|
return re.sub('(["\\\\])', '\\\\\\1', data)
|
|
|
|
def finalize_config(self):
|
|
self.setup_flavor()
|
|
|
|
for var in ('CPPFLAGS', 'CFLAGS', 'CXXFLAGS', 'LDFLAGS'):
|
|
value = self.__dict__[var]
|
|
if value:
|
|
first = not self.envvars[var]
|
|
for member in value:
|
|
#escaped = self.escape_string(member)
|
|
#if member != escaped:
|
|
# member = escaped
|
|
if first:
|
|
first = False
|
|
else:
|
|
self.envvars[var] += ' '
|
|
self.envvars[var] += member
|
|
self.log.debug('Final value of %s: %s' %
|
|
(var, self.envvars[var]))
|
|
|
|
elif self.envvars.has_key(var):
|
|
del self.envvars[var]
|
|
|
|
#########################################################################
|
|
# Options that can modify any build flavor #
|
|
#########################################################################
|
|
|
|
def option_pch(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --pch')
|
|
|
|
self.configure_args.append('--enable-pch')
|
|
|
|
self.CXXFLAGS.append('-fpch-deps')
|
|
self.CXXFLAGS.append('-Wconversion')
|
|
#self.CXXFLAGS.append('-Wold-style-cast')
|
|
|
|
system = self.get_stdout('uname', '-s')
|
|
|
|
def option_no_pch(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --no-pch')
|
|
|
|
self.no_pch = True
|
|
|
|
if '--enable-pch' in self.configure_args:
|
|
self.configure_args.remove('--enable-pch')
|
|
|
|
if '-Wconversion' in self.configure_args:
|
|
self.CXXFLAGS.remove('-Wconversion')
|
|
#if '-Wold-style-cast' in self.configure_args:
|
|
# self.CXXFLAGS.remove('-Wold-style-cast')
|
|
|
|
system = self.get_stdout('uname', '-s')
|
|
|
|
if system == "Darwin":
|
|
self.envvars['CC'] = 'gcc'
|
|
self.envvars['CXX'] = 'g++'
|
|
self.envvars['LD'] = 'g++'
|
|
|
|
def option_force(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --force')
|
|
self.force = True
|
|
|
|
def option_warn(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --warn')
|
|
self.CXXFLAGS.append('-ansi')
|
|
self.CXXFLAGS.append('-pedantic')
|
|
self.CXXFLAGS.append('-pedantic-errors')
|
|
self.CXXFLAGS.append('-Wall')
|
|
self.CXXFLAGS.append('-Winvalid-pch')
|
|
self.CXXFLAGS.append('-Wextra')
|
|
self.CXXFLAGS.append('-Wcast-align')
|
|
self.CXXFLAGS.append('-Wcast-qual')
|
|
self.CXXFLAGS.append('-Wfloat-equal')
|
|
self.CXXFLAGS.append('-Wmissing-field-initializers')
|
|
self.CXXFLAGS.append('-Wno-endif-labels')
|
|
self.CXXFLAGS.append('-Woverloaded-virtual')
|
|
self.CXXFLAGS.append('-Wsign-compare')
|
|
self.CXXFLAGS.append('-Wsign-promo')
|
|
self.CXXFLAGS.append('-Wstrict-null-sentinel')
|
|
self.CXXFLAGS.append('-Wwrite-strings')
|
|
self.CXXFLAGS.append('-Wno-old-style-cast')
|
|
self.CXXFLAGS.append('-Wno-deprecated')
|
|
self.CXXFLAGS.append('-Wno-strict-aliasing')
|
|
self.CXXFLAGS.append('-Werror')
|
|
|
|
def option_pic(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --pic')
|
|
self.CXXFLAGS.append('-fPIC')
|
|
|
|
def option_output(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --output')
|
|
self.build_dir = value
|
|
|
|
def option_prefix(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --prefix')
|
|
self.prefix_dir = value
|
|
|
|
def option_products(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --products')
|
|
self.products_dir = value
|
|
|
|
def option_local(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --local')
|
|
self.build_dir = self.source_dir
|
|
|
|
def option_trees(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --trees')
|
|
self.build_dir = None
|
|
|
|
def option_release(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.log.debug('Saw option --release')
|
|
if '--disable-shared' in self.configure_args:
|
|
self.configure_args.remove('--disable-shared')
|
|
self.configure_args.append('--disable-dependency-tracking')
|
|
|
|
def option_help(self, option=None, opt_str=None, value=None, parser=None):
|
|
self.phase_help()
|
|
|
|
#########################################################################
|
|
# The various build flavors #
|
|
#########################################################################
|
|
|
|
def locate_darwin_libraries(self):
|
|
match = re.search('gcc-mp-([0-9]+)\.([0-9]+)', self.envvars['CC'])
|
|
if match:
|
|
gcc_version = match.group(1) + match.group(2)
|
|
else:
|
|
gcc_version = "42"
|
|
|
|
self.log.debug('Using gcc version: %s' % gcc_version)
|
|
|
|
if self.current_flavor == 'debug' or self.current_flavor == 'gcov':
|
|
if self.options.use_glibcxx_debug:
|
|
self.log.debug('We are using GLIBCXX_DEBUG, so setting up flags')
|
|
self.CPPFLAGS.append('-D_GLIBCXX_DEBUG=1')
|
|
|
|
if self.boost_info.configure(
|
|
home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \
|
|
(self.boost_version, gcc_version),
|
|
suffix = '-xgcc%s-sd-%s' % \
|
|
(gcc_version, self.boost_version),
|
|
file_suffix = '.dylib',
|
|
include_path = 'include/boost-%s' % self.boost_version):
|
|
pass
|
|
elif self.boost_info.configure(
|
|
home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \
|
|
(self.boost_version, gcc_version),
|
|
suffix = '-xgcc%s-d-%s' % \
|
|
(gcc_version, self.boost_version),
|
|
file_suffix = '.dylib',
|
|
include_path = 'include/boost-%s' % self.boost_version):
|
|
pass
|
|
elif self.boost_info.configure(suffix = '-d', file_suffix = '.dylib'):
|
|
pass
|
|
|
|
else:
|
|
if self.boost_info.configure():
|
|
pass
|
|
elif self.boost_info.configure(
|
|
home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \
|
|
(self.boost_version, gcc_version),
|
|
suffix = '-xgcc%s-s-%s' % \
|
|
(gcc_version, self.boost_version),
|
|
file_suffix = '.dylib',
|
|
include_path = 'include/boost-%s' % self.boost_version):
|
|
pass
|
|
elif self.boost_info.configure(
|
|
home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \
|
|
(self.boost_version, gcc_version),
|
|
suffix = '-xgcc%s-%s' % \
|
|
(gcc_version, self.boost_version),
|
|
file_suffix = '.dylib',
|
|
include_path = 'include/boost-%s' % self.boost_version):
|
|
pass
|
|
|
|
def setup_flavor_default(self):
|
|
if self.darwin_gcc:
|
|
self.option_no_pch()
|
|
|
|
def setup_flavor_debug(self):
|
|
self.configure_args.append('--enable-debug')
|
|
|
|
self.CXXFLAGS.append('-g')
|
|
self.LDFLAGS.append('-g')
|
|
|
|
def setup_flavor_opt(self):
|
|
self.CPPFLAGS.append('-DNDEBUG=1')
|
|
for i in ['-O3', '-fomit-frame-pointer']:
|
|
self.CXXFLAGS.append(i)
|
|
self.CFLAGS.append(i)
|
|
self.LDFLAGS.append(i)
|
|
#if self.options.gcc45:
|
|
# for i in ['-flto']:
|
|
# self.CXXFLAGS.append(i)
|
|
# self.CFLAGS.append(i)
|
|
# self.LDFLAGS.append(i)
|
|
if self.darwin_gcc:
|
|
self.option_no_pch()
|
|
if '--disable-shared' in self.configure_args:
|
|
self.configure_args.remove('--disable-shared')
|
|
self.configure_args.append('--disable-dependency-tracking')
|
|
if self.options.universal:
|
|
for i in ['-fast']:
|
|
self.CXXFLAGS.append(i)
|
|
self.CFLAGS.append(i)
|
|
self.LDFLAGS.append(i)
|
|
for i in ['-arch', 'i386', '-arch', 'x86_64']:
|
|
self.CXXFLAGS.append(i)
|
|
self.CFLAGS.append(i)
|
|
self.LDFLAGS.append(i)
|
|
|
|
def setup_flavor_gcov(self):
|
|
self.CXXFLAGS.append('-g')
|
|
self.CXXFLAGS.append('-fprofile-arcs')
|
|
self.CXXFLAGS.append('-ftest-coverage')
|
|
self.LDFLAGS.append('-g')
|
|
|
|
def setup_flavor_gprof(self):
|
|
self.CXXFLAGS.append('-g')
|
|
self.CXXFLAGS.append('-pg')
|
|
self.LDFLAGS.append('-g')
|
|
self.LDFLAGS.append('-pg')
|
|
|
|
#########################################################################
|
|
# Configure build tree using autoconf #
|
|
#########################################################################
|
|
|
|
def configure_environment(self):
|
|
self.finalize_config()
|
|
|
|
environ = dict(os.environ)
|
|
for key, value in self.envvars.items():
|
|
if value:
|
|
environ[key] = value
|
|
|
|
environ['PATH'] = ('%s/bin:%s' %
|
|
(environ['PYTHON_HOME'], environ['PATH']))
|
|
|
|
if self.build_directory() == self.source_dir:
|
|
conf_args = ['sh', 'configure']
|
|
else:
|
|
conf_args = ['sh', join(self.source_dir, 'configure'),
|
|
'--srcdir', self.source_dir]
|
|
|
|
for var in ('CC', 'CPPFLAGS', 'CFLAGS', 'CXX', 'CXXFLAGS',
|
|
'LD', 'LDFLAGS'):
|
|
if self.envvars.has_key(var) and self.envvars[var] and \
|
|
(var.endswith('FLAGS') or exists(self.envvars[var])):
|
|
conf_args.append('%s=%s' % (var, self.envvars[var]))
|
|
|
|
if self.boost_info.get_suffix():
|
|
conf_args.append('--with-boost-suffix=%s' %
|
|
self.boost_info.get_suffix())
|
|
|
|
if self.prefix_directory():
|
|
conf_args.append('--prefix=%s' % self.prefix_directory())
|
|
|
|
return (environ, conf_args + self.configure_args)
|
|
|
|
def need_to_run_configure(self):
|
|
Makefile = join(self.build_directory(), 'Makefile')
|
|
if self.force:
|
|
return 'because it was forced'
|
|
elif not exists(Makefile):
|
|
return 'because Makefile does not exist'
|
|
elif self.isnewer(join(self.source_dir, 'configure'), Makefile):
|
|
return 'because configure is newer than Makefile'
|
|
elif self.isnewer(join(self.source_dir, 'Makefile.in'), Makefile):
|
|
return 'because Makefile.in is newer than Makefile'
|
|
return False
|
|
|
|
def phase_configure(self, *args):
|
|
self.log.info('Executing phase: configure')
|
|
|
|
self.configured = True
|
|
|
|
environ, conf_args = self.configure_environment()
|
|
for arg in args:
|
|
if arg: conf_args.append(arg)
|
|
|
|
build_dir = self.ensure(self.build_directory())
|
|
try:
|
|
os.chdir(build_dir)
|
|
|
|
reason = self.need_to_run_configure()
|
|
if reason:
|
|
self.log.info('./configure must be run ' + reason)
|
|
self.log.debug('Source => ' + self.source_dir)
|
|
self.log.debug('Build => ' + build_dir)
|
|
self.log.debug('configure env => ' + str(environ))
|
|
self.log.debug('configure args => ' + str(conf_args))
|
|
|
|
configure = Popen(conf_args, shell=False, env=environ)
|
|
retcode = configure.wait()
|
|
if retcode < 0:
|
|
self.log.error("Child was terminated by signal", -retcode)
|
|
sys.exit(1)
|
|
elif retcode != 0:
|
|
self.log.error("Execution failed: " + string.join(conf_args, ' '))
|
|
sys.exit(1)
|
|
|
|
# Wipe the pre-compiled header, if there is one
|
|
pch = join(self.build_directory(), 'system.hh.gch')
|
|
if exists(pch):
|
|
os.remove(pch)
|
|
else:
|
|
self.log.debug('configure does not need to be run')
|
|
|
|
finally:
|
|
os.chdir(self.source_dir)
|
|
|
|
def phase_config(self, *args):
|
|
self.log.info('Executing phase: config')
|
|
self.phase_submodule()
|
|
self.phase_autoconf()
|
|
self.phase_configure(*args)
|
|
if self.should_clean:
|
|
self.phase_clean()
|
|
|
|
#########################################################################
|
|
# Builds products from the sources #
|
|
#########################################################################
|
|
|
|
def phase_make(self, *args):
|
|
self.log.info('Executing phase: make')
|
|
|
|
config_args = []
|
|
make_args = []
|
|
|
|
for arg in args:
|
|
if arg.startswith('--'):
|
|
config_args.append(arg)
|
|
else:
|
|
make_args.append(arg)
|
|
|
|
if self.options.jobs > 1:
|
|
make_args.append('-j%d' % self.options.jobs)
|
|
|
|
self.log.debug('Configure arguments => ' + str(config_args))
|
|
self.log.debug('Makefile arguments => ' + str(make_args))
|
|
|
|
if not self.configured:
|
|
self.phase_config(*config_args)
|
|
|
|
build_dir = self.ensure(self.build_directory())
|
|
try:
|
|
self.log.debug('Changing directory to ' + build_dir)
|
|
os.chdir(build_dir)
|
|
|
|
self.execute(*(['make'] + make_args))
|
|
finally:
|
|
os.chdir(self.source_dir)
|
|
|
|
def phase_update(self, *args):
|
|
self.log.info('Executing phase: update')
|
|
self.phase_pull()
|
|
self.phase_make(*args)
|
|
|
|
#########################################################################
|
|
# Build directory cleaning phases #
|
|
#########################################################################
|
|
|
|
def phase_clean(self, *args):
|
|
self.log.info('Executing phase: clean')
|
|
self.phase_make('clean')
|
|
|
|
def phase_distclean(self, *args):
|
|
self.log.info('Executing phase: distclean')
|
|
self.phase_make('distclean')
|
|
|
|
def phase_gitclean(self, *args):
|
|
self.log.info('Executing phase: gitclean')
|
|
if self.git_working_tree():
|
|
self.execute('git', 'clean', '-dfx')
|
|
|
|
#########################################################################
|
|
# Packaging phases #
|
|
#########################################################################
|
|
|
|
def translate_file(self, path, dest):
|
|
dest_file = join(dest, basename(path))
|
|
|
|
self.log.debug("Translating file %s -> %s" % (path, dest_file))
|
|
|
|
if not exists(dest_file):
|
|
shutil.copyfile(path, dest_file)
|
|
os.chmod(dest_file, 0755)
|
|
|
|
for line in self.get_stdout('otool', '-L', dest_file).split('\n'):
|
|
match = re.search('/opt/local/lib/(.+?)\.dylib', line)
|
|
if not match:
|
|
continue
|
|
|
|
lib = match.group(0)
|
|
base = basename(lib)
|
|
|
|
if lib != path:
|
|
self.translate_file(lib, dest)
|
|
|
|
self.execute('install_name_tool', '-change', lib,
|
|
'@loader_path/' + base, dest_file)
|
|
|
|
def phase_bindmg(self, *args):
|
|
self.log.info('Executing phase: bindmg')
|
|
|
|
self.phase_make()
|
|
|
|
binary = join(self.build_directory(), 'ledger')
|
|
if not exists(binary):
|
|
self.log.error('Failed to build Ledger: ' + binary)
|
|
sys.exit(1)
|
|
|
|
cwd = os.getcwd()
|
|
tempdir = tempfile.mkdtemp()
|
|
try:
|
|
name = 'ledger-%s' % self.current_version()
|
|
dest = join(tempdir, name)
|
|
|
|
os.makedirs(dest)
|
|
self.translate_file(binary, dest)
|
|
|
|
self.execute('hdiutil', 'create', '-srcfolder', dest,
|
|
'-ov', join(cwd, name + '.dmg'))
|
|
finally:
|
|
os.chdir(cwd)
|
|
shutil.rmtree(tempdir)
|
|
|
|
def phase_upload(self, *args):
|
|
self.log.info('Executing phase: upload')
|
|
|
|
self.phase_bindmg()
|
|
|
|
dmg = 'ledger-%s.dmg' % self.current_version()
|
|
self.execute('zip', '%s.zip' % dmg, dmg)
|
|
dmg = '%s.zip' % dmg
|
|
|
|
self.execute('ssh', 'jw', 'rm', '-f', '/srv/ftp/pub/ledger/ledger-*.dmg.zip')
|
|
self.execute('scp', dmg, 'jw:/srv/ftp/pub/ledger')
|
|
self.execute('ssh', 'jw',
|
|
'(cd /srv/ftp/pub/ledger; ln -sf %s ledger-current.dmg.zip)' %
|
|
basename(dmg))
|
|
|
|
#########################################################################
|
|
# Other build phases #
|
|
#########################################################################
|
|
|
|
def configure_flavor(self, flavor, reset=True):
|
|
self.initialize() # reset everything
|
|
self.build_dir = None # use the build/ tree
|
|
self.current_flavor = flavor
|
|
self.option_release()
|
|
self.prefix_dir = None
|
|
|
|
if reset and exists(self.build_directory()) and \
|
|
isdir(self.build_directory()):
|
|
self.log.info('=== Wiping build directory %s ===' %
|
|
self.build_directory())
|
|
shutil.rmtree(self.build_directory())
|
|
|
|
def phase_distcheck(self, *args):
|
|
self.log.info('Executing phase: distcheck')
|
|
|
|
self.configure_flavor('default', False)
|
|
|
|
environ, conf_args = self.configure_environment()
|
|
|
|
configure_args = []
|
|
|
|
skip_next = False
|
|
for arg in conf_args[2:]: # skip "sh configure"
|
|
if arg == '--srcdir':
|
|
skip_next = True
|
|
elif skip_next:
|
|
skip_next = False
|
|
else:
|
|
configure_args.append('"' + self.escape_string(arg) + '"')
|
|
|
|
make_args = ['DISTCHECK_CONFIGURE_FLAGS=%s' %
|
|
string.join(configure_args, ' '), 'distcheck']
|
|
|
|
self.log.debug('make_args for distcheck => ' + str(make_args))
|
|
|
|
self.phase_make(*make_args)
|
|
|
|
def phase_rsync(self, *args):
|
|
self.log.info('Executing phase: rsync')
|
|
|
|
source_copy_dir = join(self.ensure(self.products_directory()),
|
|
'ledger-proof')
|
|
|
|
self.execute('rsync', '-a', '--delete',
|
|
'--exclude=.git/', '--exclude=b/',
|
|
'%s/' % self.source_dir, '%s/' % source_copy_dir)
|
|
|
|
self.source_dir = source_copy_dir
|
|
|
|
def phase_proof(self, *args):
|
|
self.log.info('Executing phase: proof')
|
|
|
|
self.log.info('=== Copying source tree ===')
|
|
self.phase_rsync()
|
|
|
|
self.phase_makeall(reset=True, *args)
|
|
|
|
self.configure_flavor('opt', reset=False)
|
|
self.log.info('=== Testing opt ===')
|
|
self.phase_make('fullcheck')
|
|
|
|
self.configure_flavor('gcov', reset=False)
|
|
self.log.info('=== Testing gcov ===')
|
|
self.phase_make('check')
|
|
|
|
self.configure_flavor('debug', reset=False)
|
|
self.log.info('=== Testing debug ===')
|
|
self.phase_make('fullcheck')
|
|
|
|
self.configure_flavor('default', reset=False)
|
|
self.log.info('=== Testing default ===')
|
|
self.phase_make('fullcheck')
|
|
self.phase_make('docs')
|
|
|
|
self.log.info('=== Building final distcheck ===')
|
|
self.phase_distcheck()
|
|
|
|
def phase_makeall(self, reset=False, *args):
|
|
self.log.info('Executing phase: makeall')
|
|
|
|
self.configure_flavor('opt', reset)
|
|
|
|
system_hh_gch = join(self.source_dir, 'src', 'system.hh.gch')
|
|
if exists(system_hh_gch):
|
|
os.remove(system_hh_gch)
|
|
|
|
self.log.info('=== Building opt ===')
|
|
self.phase_make(*args)
|
|
|
|
self.configure_flavor('gcov', reset)
|
|
|
|
system_hh_gch = join(self.source_dir, 'src', 'system.hh.gch')
|
|
if exists(system_hh_gch):
|
|
os.remove(system_hh_gch)
|
|
|
|
self.log.info('=== Building gcov ===')
|
|
self.phase_make(*args)
|
|
|
|
system_hh_gch = join(self.source_dir, 'src', 'system.hh.gch')
|
|
if exists(system_hh_gch):
|
|
os.remove(system_hh_gch)
|
|
|
|
self.log.info('=== Building default ===')
|
|
self.phase_make(*args)
|
|
|
|
self.configure_flavor('debug', reset)
|
|
|
|
system_hh_gch = join(self.source_dir, 'src', 'system.hh.gch')
|
|
if exists(system_hh_gch):
|
|
os.remove(system_hh_gch)
|
|
|
|
self.log.info('=== Building debug ===')
|
|
self.phase_make(*args)
|
|
|
|
self.configure_flavor('default', reset)
|
|
|
|
#########################################################################
|
|
# Help #
|
|
#########################################################################
|
|
|
|
def phase_help(self, *args):
|
|
self.option_parser.print_help()
|
|
|
|
print """
|
|
Of the optional ARGS, the first is an optional build FLAVOR, with the default
|
|
being 'debug':
|
|
|
|
default Regular autoconf settings
|
|
debug Debugging and --verify support (default)
|
|
opt Full optimizations
|
|
gcov Coverage analysis
|
|
gprof Code profiling (for OS X, just use: 'shark -i ledger ...')
|
|
|
|
Next is the optional build PHASE, with 'config' being the default:
|
|
|
|
clean Runs 'make clean' in the build directory
|
|
config Configure the environment for building
|
|
dependencies Automatically install all necessary build dependencies
|
|
distcheck Properly does 'make distcheck', carrying all flags
|
|
distclean Runs 'make distclean' in the build directory
|
|
gitclean Runs 'git clean -dfx', which *really* cleans things
|
|
help Displays this help text
|
|
info Show information about the build environment
|
|
make Do a make in the build directory
|
|
proof Proves Ledger by building and testing every flavor
|
|
pull Pulls the latest, and updates local config if need be
|
|
update Does it all, updates your environment and re-make's
|
|
|
|
There are many other build phases, though most are not of interest to the
|
|
typical user:
|
|
|
|
aclocal Runs aclocal -I m4
|
|
autoconf Prepare the autoconf subsystem
|
|
autogen Runs autogen.sh
|
|
configure Runs just ./configure
|
|
do_all Runs makeall followed by proof
|
|
gettext Initialize gettext support
|
|
makeall Build every flavor there is
|
|
patch Patches the automake Makefile to be less verbose
|
|
products Report the products directory path
|
|
rsync Rsync a copy of the source tree into Products
|
|
sloc Report total Source Lines Of Code
|
|
submodule Updates Git submodules (better to use 'pull')
|
|
version Output current HEAD version to version.m4
|
|
|
|
NOTE: If you wish to pass options to configure or make, add "--" followed by
|
|
your options. Here are some real-world examples:
|
|
|
|
./acprep
|
|
./acprep opt -- make -j3
|
|
./acprep --enable-doxygen"""
|
|
sys.exit(0)
|
|
|
|
PrepareBuild().run()
|