summaryrefslogtreecommitdiff
path: root/acprep
diff options
context:
space:
mode:
Diffstat (limited to 'acprep')
-rwxr-xr-xacprep1609
1 files changed, 1570 insertions, 39 deletions
diff --git a/acprep b/acprep
index aa3741c1..20be2db4 100755
--- a/acprep
+++ b/acprep
@@ -1,39 +1,1570 @@
-#!/bin/sh
-
-touch AUTHORS COPYING
-
-aclocal
-autoheader
-if [ "$1" = "--dist" ]; then
- automake -a -c -f -i
-else
- automake -a -c -f
-fi
-autoconf
-
-INCDIRS="-I/sw/include -I/usr/include/httpd/xml -I/usr/include/python2.3"
-#INCDIRS="$INCDIRS -I/sw/include/libofx"
-INCDIRS="$INCDIRS -Wno-long-double"
-LIBDIRS="-L/sw/lib -L/usr/lib/python2.3/config"
-
-if [ "$1" = "--debug" ]; then
- ./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" CXXFLAGS="-g" \
- --enable-debug
-elif [ "$1" = "--debug-python" ]; then
- ./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" CXXFLAGS="-g" \
- --enable-debug --enable-python
-elif [ "$1" = "--opt" ]; then
- ./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450 -fPIC"
-elif [ "$1" = "--flat-opt" ]; then
- ./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450"
-elif [ "$1" = "--safe-opt" ]; then
- ./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450 -fPIC -DDEBUG_LEVEL=1" \
- --enable-python
-elif [ "$1" = "--perf" ]; then
- ./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" CXXFLAGS="-g -pg"
-fi
-
-rm AUTHORS COPYING
+#!/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}
+
+
+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
+
+ 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.products_dir = None
+ self.build_dir = self.source_dir
+ self.configure_args = ['--with-included-gettext', '--enable-python']
+ 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',
+ 'BOOST_SUFFIX': None,
+ 'BOOST_HOME': '/usr',
+ '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)
+
+ op = self.option_parser
+ 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('', '--boost', metavar='SUFFIX',
+ action="callback", type="string",
+ callback=self.option_boost,
+ help='Set Boost library suffix (ex: "--boost=-mt")')
+ 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('', '--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('', '--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')
+
+ self.force = False
+ self.no_pch = False
+ self.source_dir = os.getcwd()
+
+ self.initialize()
+
+ def main(self, *args):
+ 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):
+ cmd = 'config'
+ 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 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 confgure.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()))
+ 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 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.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')
+
+ if not exists('Makefile.am') or \
+ self.isnewer('tools/Makefile.am', 'Makefile.am'):
+ shutil.copyfile('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',
+ 'boost-jam',
+ 'boost', '+icu+python26+st+universal',
+ 'cppunit', '+universal',
+ #'texlive',
+ #'doxygen',
+ #'graphviz',
+ 'texinfo',
+ 'lcov',
+ 'sloccount'
+ ]
+ 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!")
+
+ 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',
+ 'bjam',
+ 'gettext',
+ 'cvs',
+ 'libboost1.40-dev',
+ 'libboost-regex1.40-dev',
+ 'libboost-date-time1.40-dev',
+ 'libboost-filesystem1.40-dev',
+ 'libboost-python1.40-dev',
+ 'libedit-dev',
+ 'libcppunit-dev',
+ #'texlive-full',
+ #'doxygen',
+ #'graphviz',
+ 'texinfo',
+ 'lcov',
+ 'sloccount'
+ ]
+ 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',
+ 'bjam',
+ 'cvs',
+ 'gettext',
+ 'libgmp3-dev',
+ 'libmpfr-dev',
+ 'libboost1.35-dev',
+ 'libboost-python1.35-dev',
+ 'libboost-regex1.35-dev',
+ 'libboost-date-time1.35-dev',
+ 'libboost-filesystem1.35-dev',
+ 'libedit-dev',
+ 'libcppunit-dev',
+ #'texlive-full',
+ #'doxygen',
+ #'graphviz',
+ 'texinfo',
+ 'lcov',
+ 'sloccount'
+ ]
+ 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',
+ 'bboost-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)
+
+ def phase_buildlibs(self, *args):
+ self.log.info('Executing phase: buildlibs')
+
+ try:
+ os.chdir('lib')
+
+ environ, conf_args = self.configure_environment()
+
+ boost = 'boost_1_41_0'
+ tarball = boost + '.7z'
+
+ if not exists(boost):
+ if not exists(tarball):
+ self.log.info('Downloading Boost source tarball ...')
+ self.execute('curl', '-L', '-o', tarball,
+ 'http://downloads.sourceforge.net/boost/' +
+ boost + '.7z?use_mirror=ufpr')
+
+ if not exists(tarball):
+ self.log.error('Failed to locate the Boost source tarball')
+ sys.exit(1)
+
+ fd = open(tarball)
+ try:
+ csum = hashlib.md5()
+ except:
+ csum = md5.md5()
+ csum.update(fd.read())
+ fd.close()
+ digest = csum.hexdigest()
+
+ if digest != 'b74ee2f0f46cef601544dd4ac2d7dec4':
+ self.log.error('Boost source tarball fails to match checksum')
+ sys.exit(1)
+
+ self.log.info('Extracting Boost source tarball ...')
+ self.execute('7za', 'x', tarball)
+
+ if not exists(boost):
+ self.log.error('Failed to locate the Boost sources')
+ sys.exit(1)
+
+ if not exists('cppunit') and self.git_working_tree():
+ self.execute('git', 'clone', 'git://github.com/jwiegley/cppunit.git')
+
+ if not exists('cppunit'):
+ self.log.error('Failed to locate the CppUnit sources')
+ sys.exit(1)
+
+ self.execute('make',
+ 'BOOST_SOURCE=%s' % boost,
+ 'CC=%s' % environ['CC'],
+ 'CXX=%s' % environ['CXX'],
+ 'LD=%s' % environ['LD'],
+ 'build-all')
+ finally:
+ os.chdir(self.source_dir)
+
+ #########################################################################
+ # Determine the system's basic configuration #
+ #########################################################################
+
+ def locate_boost_in_dir(self, path):
+ if exists(path) and isdir(path):
+ entries = os.listdir(path)
+ entries.sort()
+ for entry in entries:
+ if re.search('boost_regex', entry):
+ self.log.info('Found a Boost library: ' + entry)
+
+ match = re.match('libboost_regex([^.]*)\.(a|so|dylib)', entry)
+ if match:
+ suffix = match.group(1)
+ self.log.info('Found Boost suffix => ' + suffix)
+ self.envvars['BOOST_HOME'] = dirname(path)
+ return suffix
+ else:
+ self.log.debug('The directory "%s" is not valid, skipping' %
+ path)
+ return None
+
+ def inform_boost_location(self, text, suffix):
+ self.log.info('Boost %s here:' % text)
+ self.log.info('BOOST_HOME => ' + self.envvars['BOOST_HOME'])
+ self.log.info('BOOST_SUFFIX => ' + suffix)
+
+ def locate_boost(self):
+ if self.envvars['BOOST_SUFFIX']:
+ self.log.info(("Not looking for Boost, since " +
+ "a suffix of '%s' was given") %
+ self.envvars['BOOST_SUFFIX'])
+ else:
+ suffix = None
+ for path in ['/usr/local/lib', '/opt/local/lib',
+ '/sw/lib', '/usr/lib']:
+ self.log.info('Looking for Boost in %s...' % path)
+ suffix = self.locate_boost_in_dir(path)
+ if suffix is not None:
+ self.inform_boost_location('was found', suffix)
+ break
+ if suffix is None:
+ self.log.error("Boost could not be found.")
+ self.envvars['BOOST_SUFFIX'] = suffix
+ return self.envvars['BOOST_SUFFIX']
+
+ def setup_system_directories(self):
+ boost_suffix = self.locate_boost()
+
+ # Each of these becomes '-isystem <name>'
+ for path in ['/usr/local/include',
+ '%s/include/boost' %
+ self.envvars['BOOST_HOME'],
+ '%s/include' % self.envvars['BOOST_HOME'],
+ '%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',
+ '/sw/lib']:
+ if exists(path) and isdir(path):
+ self.log.info('Noticing library directory => ' + path)
+ self.sys_library_dirs.append(path)
+
+ def setup_for_johnw(self):
+ if self.current_flavor == 'debug' or self.current_flavor == 'gcov':
+ if exists('/usr/local/stow/cppunit/include'):
+ self.sys_include_dirs.insert(0, '/usr/local/stow/cppunit/include')
+ self.sys_library_dirs.insert(0, '/usr/local/stow/cppunit/lib')
+
+ if exists('/usr/local/stow/icu/include'):
+ self.sys_include_dirs.insert(0, '/usr/local/stow/icu/include')
+ self.sys_library_dirs.insert(0, '/usr/local/stow/icu/lib')
+
+ 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.configure_args.append('--enable-doxygen')
+ self.configure_args.append('--enable-python')
+
+ def setup_for_system(self):
+ self.setup_system_directories()
+
+ 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 exists('/Users/johnw/Dropbox/Accounts/ledger.dat'):
+ self.setup_for_johnw()
+
+ self.locate_darwin_libraries()
+
+ if (self.current_flavor == 'opt' or \
+ self.current_flavor == 'default') 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.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 '--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_boost(self, option=None, opt_str=None, value=None, parser=None):
+ self.log.debug('Saw option --boost')
+ self.envvars['BOOST_SUFFIX'] = value
+
+ 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_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):
+ if self.current_flavor == 'debug' or self.current_flavor == 'gcov':
+ self.log.debug('We are using GLIBCXX_DEBUG, so setting up flags')
+
+ if self.options.use_glibcxx_debug:
+ self.CPPFLAGS.append('-D_GLIBCXX_DEBUG=1')
+
+ if exists('/usr/local/lib/libboost_regex-xgcc44-sd-1_41.a'):
+ self.envvars['BOOST_HOME'] = '/usr/local'
+ self.envvars['BOOST_SUFFIX'] = '-xgcc44-sd-1_41'
+ self.sys_include_dirs.append('/usr/local/include/boost-1_41')
+ self.inform_boost_location('is really located',
+ self.envvars['BOOST_SUFFIX'])
+
+ elif exists('/usr/local/lib/libboost_regex-xgcc44-d-1_41.a'):
+ self.envvars['BOOST_HOME'] = '/usr/local'
+ self.envvars['BOOST_SUFFIX'] = '-xgcc44-d-1_41'
+ self.sys_include_dirs.append('/usr/local/include/boost-1_41')
+ self.inform_boost_location('is really located',
+ self.envvars['BOOST_SUFFIX'])
+
+ elif exists('/opt/local/lib/libboost_regex-d.a'):
+ self.envvars['BOOST_HOME'] = '/opt/local'
+ self.envvars['BOOST_SUFFIX'] = '-d'
+ self.sys_include_dirs.append('/opt/local/include/boost')
+ self.inform_boost_location('is really located',
+ self.envvars['BOOST_SUFFIX'])
+
+ else:
+ if exists('/opt/local/lib/libboost_regex.a'):
+ self.envvars['BOOST_HOME'] = '/opt/local'
+ self.envvars['BOOST_SUFFIX'] = ''
+ self.sys_include_dirs.append('/opt/local/include/boost')
+ self.inform_boost_location('is really located',
+ self.envvars['BOOST_SUFFIX'])
+
+ elif exists('/usr/local/lib/libboost_regex-xgcc44-s-1_41.a'):
+ self.envvars['BOOST_HOME'] = '/usr/local'
+ self.envvars['BOOST_SUFFIX'] = '-xgcc44-s-1_41'
+ self.sys_include_dirs.append('/usr/local/include/boost-1_41')
+ self.inform_boost_location('is really located',
+ self.envvars['BOOST_SUFFIX'])
+
+ elif exists('/usr/local/lib/libboost_regex-xgcc44-1_41.a'):
+ self.envvars['BOOST_HOME'] = '/usr/local'
+ self.envvars['BOOST_SUFFIX'] = '-xgcc44-1_41'
+ self.sys_include_dirs.append('/usr/local/include/boost-1_41')
+ self.inform_boost_location('is really located',
+ self.envvars['BOOST_SUFFIX'])
+
+ def setup_flavor_default(self):
+ pass
+
+ def setup_flavor_debug(self):
+ self.configure_args.append('--enable-debug')
+
+ self.CXXFLAGS.append('-g')
+ self.LDFLAGS.append('-g')
+
+ def setup_flavor_opt(self):
+ 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')
+ 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)
+ else:
+ for i in ['-O3', '-fomit-frame-pointer']:
+ 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')
+
+ #########################################################################
+ # Prettify the output from automake, by rewriting the Makefile #
+ #########################################################################
+
+ def phase_patch(self, *args):
+ """Alter the Makefile so that it's not nearly so verbose.
+
+ This makes errors and warnings much easier to spot."""
+ self.log.info('Executing phase: patch')
+
+ if exists('Makefile'):
+ self.log.debug('Patching generated Makefile')
+ Makefile = open('Makefile')
+ Makefile_new = open('Makefile.new', 'w')
+ for line in Makefile.readlines():
+ line = re.sub('^\t(\$\((LIBTOOL|CXX)\).*?\.(cc|cpp))$',
+ '\t@echo " " CXX \$@;\\1 > /dev/null', line)
+ line = re.sub('^\tmv -f', '\t@mv -f', line)
+ line = re.sub('^\t\$\(am__mv\)', '\t@$(am__mv)', line)
+ line = re.sub('^\t(\$\((.*?)LINK\).*)',
+ '\t@echo " LD " \$@;\\1 > /dev/null', line)
+ Makefile_new.write(line)
+ Makefile_new.close()
+ Makefile.close()
+
+ os.remove('Makefile')
+ os.rename('Makefile.new', 'Makefile')
+
+ stamp = open('.timestamp', 'w')
+ stamp.write('timestamp')
+ stamp.close()
+
+ #########################################################################
+ # 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 environ.has_key('BOOST_SUFFIX') and environ['BOOST_SUFFIX']:
+ conf_args.append('--with-boost-suffix=%s' %
+ environ['BOOST_SUFFIX'])
+
+ 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
+
+ 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)
+
+ environ, conf_args = self.configure_environment()
+ for arg in args:
+ if arg: conf_args.append(arg)
+
+ 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)
+
+ if not self.options.no_patch:
+ self.phase_patch()
+
+ # Wipe the pre-compiled header, if there is one
+ pch = join(self.build_directory(), 'system.hh.gch')
+ if exists(pch):
+ os.remove(pch)
+ else:
+ if not self.options.no_patch and \
+ self.isnewer('Makefile', '.timestamp'):
+ self.phase_patch()
+
+ 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
+
+ if reset and exists(self.build_directory()) and \
+ isdir(self.build_directory()):
+ self.option_release()
+
+ 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')
+ self.log.info('=== Testing opt ===')
+ self.phase_make('fullcheck')
+
+ self.configure_flavor('gcov')
+ self.log.info('=== Testing gcov ===')
+ self.phase_make('check')
+
+ self.configure_flavor('debug')
+ self.log.info('=== Testing debug ===')
+ self.phase_make('fullcheck')
+
+ self.configure_flavor('default')
+ self.log.info('=== Testing default ===')
+ self.phase_make('fullcheck')
+
+ 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()