#!/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+python27+st+universal' ] if system == 'centos': return [ 'boost-devel' ] elif system == 'ubuntu-lucid': return [ 'bjam', 'autopoint', 'libboost-dev', 'libboost-regex-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-dev', 'libboost-python-dev' ] elif system == 'ubuntu-karmic': return [ 'bjam', 'libboost-dev', 'libboost-regex-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-dev', 'libboost-python-dev' ] elif system == 'ubuntu-hardy': return [ 'bjam', 'libboost-dev', 'libboost-python-dev', 'libboost-regex-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-dev' ] elif system == 'ubuntu-oneiric': return [ 'libboost-dev', 'libboost-python-dev', 'libboost-regex-dev', 'libboost-date-time-dev', 'libboost-filesystem-dev', 'libboost-iostreams-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_48_0" boost_major = "1_48" 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.make_command = None 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': '/usr/bin/python', 'PYTHON_HOME': '/usr', 'PYTHON_VERSION': '2.7', '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('', '--clang', action='store_true', dest='use_clang', default=False, help='Use the Clang C++ compiler') 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('', '--make', metavar='FILE', action="callback", callback=self.option_make, type="string", help='Use custom make command') 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('', '--gcc46', action='store_true', dest='gcc46', default=False, help='Require the use of gcc 4.6') op.add_option('', '--gcc47', action='store_true', dest='gcc47', default=False, help='Require the use of gcc 4.7') op.add_option('', '--cpp0x', action='store_true', dest='cpp0x', default=False, help='Use C++0x extensions (requires Clang or gcc 4.6/7)') 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): if self.prefix_dir: return self.prefix_dir else: return 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: version_m4 = open('version.m4', 'r') for line in version_m4.readlines(): match = re.match('m4_define\(\[VERSION_NUMBER\], \[([0-9.]+[-abgrc0-9]*)\]\)', line) assert(match) self.current_ver = match.group(1) version_m4.close() 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 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 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_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', 'python27', '+universal', 'libiconv', '+universal', 'zlib', '+universal', 'gmp' ,'+universal', 'mpfr', '+universal', 'ncurses', '+universal', 'ncursesw', '+universal', 'gettext' ,'+universal', 'libedit' ,'+universal', 'texlive-xetex', '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', #'texlive-full', #'doxygen', #'graphviz', 'texinfo', 'lcov', 'sloccount' ] + self.boost_info.dependencies('ubuntu-karmic') elif re.search('hardy', info): self.log.info('Looks like you are using APT on Ubuntu Hardy') packages = [ 'sudo', 'apt-get', 'install', 'build-essential', 'libtool', 'autoconf', 'automake', 'autopoint', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'cvs', 'gettext', 'libgmp3-dev', 'libmpfr-dev', 'libedit-dev', #'texlive-full', #'doxygen', #'graphviz', 'texinfo', 'lcov', 'sloccount' ] + self.boost_info.dependencies('ubuntu-hardy') elif re.search('oneiric', info): self.log.info('Looks like you are using APT on Ubuntu Oneiric') packages = [ 'sudo', 'apt-get', 'install', 'build-essential', 'libtool', 'autoconf', 'automake', 'autopoint', 'zlib1g-dev', 'libbz2-dev', 'python-dev', 'cvs', 'gettext', 'libgmp3-dev', 'libmpfr-dev', 'libedit-dev', #'texlive-full', #'doxygen', #'graphviz', 'texinfo', 'lcov', 'sloccount' ] + self.boost_info.dependencies('ubuntu-oneiric') else: self.log.info('I do not recognize your version of Ubuntu!') packages = None if packages: 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', #'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('/(usr|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 ' 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) includes = string.split(self.get_stdout('python-config', '--includes'), '-I') for include in includes: include = include.strip() if include and include not in self.sys_include_dirs: self.sys_include_dirs.append(include) # Each of these becomes '-L' 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): self.envvars['PYTHON'] = '/opt/local/bin/python' self.envvars['PYTHON_HOME'] = '/opt/local' if self.options.use_clang: self.boost_inc_ident = "clang" self.boost_lib_ident = "clang-darwin28" self.log.debug("Setting Python home to /opt/local") self.log.debug('Using Clang ident: %s/%s' % (self.boost_inc_ident, self.boost_lib_ident)) else: match = re.search('gcc-mp-([0-9]+)\.([0-9]+)', self.envvars['CC']) if match: self.boost_inc_ident = "gcc" + match.group(1) + match.group(2) self.boost_lib_ident = "x" + self.boost_inc_ident else: self.boost_inc_ident = "gcc42" self.boost_lib_ident = "xgcc42" self.log.debug('Using Boost ident: %s/%s' % (self.boost_inc_ident, self.boost_lib_ident)) if self.current_flavor == 'debug': if exists('/usr/local/stow/icu-%s/include' % self.boost_inc_ident): self.sys_include_dirs.insert( 0, '/usr/local/stow/icu-%s/include' % self.boost_inc_ident) self.sys_library_dirs.insert( 0, '/usr/local/stow/icu-%s/lib' % self.boost_inc_ident) self.configure_args.append('--disable-shared') elif self.current_flavor == 'gcov': self.configure_args.append('--disable-shared') 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.options.use_clang: if exists('/opt/local/bin/clang++-mp-3.1'): self.envvars['CC'] = '/opt/local/bin/clang-mp-3.1' self.envvars['CXX'] = '/opt/local/bin/clang++-mp-3.1' self.envvars['LD'] = '/opt/local/bin/clang++-mp-3.1' elif exists('/usr/local/bin/clang++'): self.envvars['CC'] = '/usr/local/bin/clang' self.envvars['CXX'] = '/usr/local/bin/clang++' self.envvars['LD'] = '/usr/local/bin/clang++' else: self.envvars['CC'] = 'clang' self.envvars['CXX'] = 'clang++' self.envvars['LD'] = 'llvm-ld' elif (self.current_flavor == 'opt' or \ self.current_flavor == 'default') and \ not self.options.gcc45 and \ not self.options.gcc46 and \ not self.options.gcc47 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('/usr/local/bin/g++-mp-4.7') and \ self.options.gcc47: self.envvars['CC'] = '/usr/local/bin/gcc-mp-4.7' self.envvars['CXX'] = '/usr/local/bin/g++-mp-4.7' self.envvars['LD'] = '/usr/local/bin/g++-mp-4.7' elif exists('/opt/local/bin/g++-mp-4.7') and \ self.options.gcc47: self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.7' self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.7' self.envvars['LD'] = '/opt/local/bin/g++-mp-4.7' elif exists('/opt/local/bin/g++-mp-4.6') and \ self.options.gcc46: self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.6' self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.6' self.envvars['LD'] = '/opt/local/bin/g++-mp-4.6' elif exists('/opt/local/bin/g++-mp-4.5') and \ self.options.gcc45: 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('/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 self.options.cpp0x: self.CXXFLAGS.append('-std=c++0x') 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.LDFLAGS.append('-fpch-deps') if not self.options.use_clang: self.CXXFLAGS.append('-Wconversion') else: self.CXXFLAGS.append('-Weverything') self.CXXFLAGS.append('-Wno-padded') self.CXXFLAGS.append('-Wno-weak-vtables') self.CXXFLAGS.append('-Wno-exit-time-destructors') self.CXXFLAGS.append('-Wno-global-constructors') self.CXXFLAGS.append('-Wno-switch-enum') self.CXXFLAGS.append('-Wno-missing-prototypes') self.CXXFLAGS.append('-Wno-missing-noreturn') self.CXXFLAGS.append('-fno-limit-debug-info') #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_make(self, option=None, opt_str=None, value=None, parser=None): self.log.debug('Saw option --make') self.make_command = value 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): if self.current_flavor == 'debug': if self.boost_info.configure( home_path = '/usr/local/stow/boost_%s-%s' % \ (self.boost_version, self.boost_inc_ident), suffix = '-%s-sd-%s' % \ (self.boost_lib_ident, self.boost_major), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_major): pass elif self.boost_info.configure( home_path = '/usr/local/stow/boost_%s-%s' % \ (self.boost_version, self.boost_inc_ident), suffix = '-%s-d-%s' % \ (self.boost_lib_ident, self.boost_major), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_major): 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-%s' % \ (self.boost_version, self.boost_inc_ident), suffix = '-%s-s-%s' % \ (self.boost_lib_ident, self.boost_major), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_major): pass elif self.boost_info.configure( home_path = '/usr/local/stow/boost_%s-%s' % \ (self.boost_version, self.boost_inc_ident), suffix = '-%s-%s' % \ (self.boost_lib_ident, self.boost_major), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_major): 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') if self.options.gcc45 or self.options.gcc46 or self.options.gcc47: self.CXXFLAGS.append('-g2') self.CXXFLAGS.append('-ggdb') self.LDFLAGS.append('-g2') self.LDFLAGS.append('-ggdb') else: 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.options.gcc46: # for i in ['-flto']: # self.CXXFLAGS.append(i) # self.CFLAGS.append(i) # self.LDFLAGS.append(i) #if self.options.gcc47: # 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): # NDEBUG is set so that branch coverage ignores the never-taken else # branch inside assert statements. self.CPPFLAGS.append('-DNDEBUG=1') 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 and self.current_flavor != 'gcov': make_args.append('-j%d' % self.options.jobs) make_args.append('JOBS=%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(*([self.make_command or '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()