#!/usr/bin/python2.7 -B

# Build cross toolchain for AmigaOS 4.x / PowerPC target.

from fnmatch import fnmatch
from logging import info, getLogger
from os import environ
import argparse
import logging
import platform
import sys

URLS = \
  ['https://ftp.gnu.org/gnu/gmp/gmp-5.1.3.tar.bz2',
   'https://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz',
   'https://ftp.gnu.org/gnu/mpfr/mpfr-3.1.3.tar.bz2',
   'https://ftp.gnu.org/gnu/texinfo/texinfo-4.12.tar.gz',
   'http://isl.gforge.inria.fr/isl-0.12.2.tar.bz2',
   'http://www.bastoul.net/cloog/pages/download/cloog-0.18.4.tar.gz',
   'https://ftp.gnu.org/gnu/automake/automake-1.15.tar.gz',
   ('http://hyperion-entertainment.biz/index.php/downloads' +
    '?view=download&amp;format=raw&amp;file=69', 'SDK_53.24.lha'),
   ('svn://svn.code.sf.net/p/adtools/code/trunk/binutils', 'binutils-2.18'),
   ('svn://svn.code.sf.net/p/adtools/code/trunk/gcc', 'gcc-4.2.4'),
   ('svn://svn.code.sf.net/p/adtools/code/branches/binutils/2.23.2',
    'binutils-2.23.2'),
   ('svn://svn.code.sf.net/p/adtools/code/branches/gcc/4.9.x', 'gcc-4.9.1')]


from common import * # NOQA


def update_autotools(dst):
  remove(path.join(dst, 'config.guess'), path.join(dst, 'config.sub'))
  copy('{sources}/{automake}/lib/config.guess', path.join(dst, 'config.guess'))
  copy('{sources}/{automake}/lib/config.sub', path.join(dst, 'config.sub'))


@recipe('{sdk}-prepare')
def prepare_sdk():
  info('preparing AmigaOS 4.x SDK')

  target = path.join('{prefix}', 'ppc-amigaos/SDK')

  unpack('{sdk}', work_dir='{archives}', top_dir='SDK_Install', dst_dir='SDK')
  unpack('SDK/base', top_dir='Include', dst_dir=path.join(target, 'include'))
  unpack('SDK/clib2', top_dir='clib2', dst_dir=path.join(target, 'clib2'))
  unpack('SDK/newlib', top_dir='newlib', dst_dir=path.join(target, 'newlib'))


def download():
  with cwd('{archives}'):
    for url in URLS:
      if type(url) == tuple:
        url, name = url[0], url[1]
      else:
        name = path.basename(url)
      fetch(name, url)


def build():
  for var in environ.keys():
    if var not in ['_', 'LOGNAME', 'HOME', 'SHELL', 'TMPDIR', 'PWD']:
      del environ[var]

  PATH = ['/usr/bin', '/bin']

  environ['PATH'] = ":".join(PATH)
  environ['LANG'] = 'C'
  environ['TERM'] = 'xterm'

  add_site_dir('{prefix}')

  """
  Make sure we always choose known compiler (from the distro) and not one in
  user's path that could shadow the original one.
  """
  if platform.system() == 'Darwin':
    CC, CXX = 'clang', 'clang++'
  else:
    CC, CXX = 'gcc', 'g++'

  CC = find_executable(CC)
  CXX = find_executable(CXX)
  FLAGS = '-g -O2'

  if getLogger().isEnabledFor(logging.DEBUG):
    FLAGS += ' -Wall'
  else:
    FLAGS += ' -w'
    environ['MAKEFLAGS'] = '--silent'

  environ['CC'] = CC
  environ['CXX'] = CXX
  environ['PATH'] = ':'.join([path.join('{prefix}', 'bin'),
                              path.join('{host}', 'bin'),
                              environ['PATH']])

  setvar(cc=environ['CC'], cxx=environ['CXX'])

  """
  When we have a working compiler in our path, we shoule also check if the
  required programs, headers and libraries are present.
  """
  find_executable('patch')
  find_executable('bison')
  find_executable('flex')
  find_executable('make')
  find_executable('svn')

  py_ver = 'python%d.%d' % (sys.version_info.major, sys.version_info.minor)
  require_header([path.join(py_ver, 'Python.h')],
                 lang='c', errmsg='python-dev package missing')

  execute('git', 'submodule', 'init', 'submodules/python-lhafile');
  execute('git', 'submodule', 'update', 'submodules/python-lhafile');
  unpack('python-lha', work_dir='{build}')
  pysetup('python-lha')

  download()

  unpack('{automake}')

  unpack('{texinfo}')
  update_autotools('{sources}/{texinfo}/build-aux')
  configure('{texinfo}', '--prefix={host}')
  make('{texinfo}', parallel=True)
  make('{texinfo}', 'install')

  unpack('{gmp}')
  update_autotools('{sources}/{gmp}')
  configure('{gmp}',
            '--disable-shared',
            '--prefix={host}')
  make('{gmp}', parallel=True)
  make('{gmp}', 'install')

  unpack('{mpfr}')
  update_autotools('{sources}/{mpfr}')
  configure('{mpfr}',
            '--disable-shared',
            '--prefix={host}',
            '--with-gmp={host}')
  make('{mpfr}', parallel=True)
  make('{mpfr}', 'install')

  unpack('{mpc}')
  update_autotools('{sources}/{mpc}')
  configure('{mpc}',
            '--disable-shared',
            '--prefix={host}',
            '--with-gmp={host}',
            '--with-mpfr={host}')
  make('{mpc}', parallel=True)
  make('{mpc}', 'install')

  unpack('{isl}')
  update_autotools('{sources}/{isl}')
  configure('{isl}',
            '--disable-shared',
            '--prefix={host}',
            '--with-gmp-prefix={host}')
  make('{isl}', parallel=True)
  make('{isl}', 'install')

  unpack('{cloog}')
  update_autotools('{sources}/{cloog}')
  configure('{cloog}',
            '--disable-shared',
            '--prefix={host}',
            '--with-isl=system',
            '--with-gmp-prefix={host}',
            '--with-isl-prefix={host}')
  make('{cloog}', parallel=True)
  make('{cloog}', 'install')

  with env(CFLAGS='-Wno-error'):
    update_autotools('{archives}/{binutils}')
    configure('{binutils}',
              '--prefix={prefix}',
              '--target={target}',
              from_dir='{archives}/{binutils}')
    make('{binutils}', parallel=True)
    make('{binutils}', 'install')

  prepare_sdk()

  gcc_env = {}
  if cmpver('eq', '{gcc_ver}', '4.2.4'):
    gcc_env.update(CFLAGS='-std=gnu89',
                   CC=find_executable(CC) + ' -m32',
                   CXX=find_executable(CXX) + ' -m32')

  update_autotools('{archives}/{gcc}')
  with env(**gcc_env):
    configure('{gcc}',
              '--prefix={prefix}',
              '--target={target}',
              '--with-bugurl="http://sf.net/p/adtools"',
              '--with-gmp={host}',
              '--with-mpfr={host}',
              '--with-isl={host}',
              '--with-cloog={host}',
              '--enable-languages=c,c++',
              '--enable-haifa',
              '--enable-sjlj-exceptions',
              '--disable-libstdcxx-pch',
              '--disable-tls',
              from_dir='{archives}/{gcc}')
  make('{gcc}')
  make('{gcc}', 'install')


def clean():
  rmtree('{stamps}')
  rmtree('{sources}')
  rmtree('{host}')
  rmtree('{build}')
  rmtree('{tmpdir}')


if __name__ == "__main__":
  logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')

  if not sys.version_info[:2] == (2, 7):
    panic('I need Python 2.7 to run!')

  if not any(fnmatch(platform.system(), pat)
             for pat in ['Darwin', 'Linux', 'CYGWIN_NT*', 'MSYS_NT*']):
    panic('Build on %s not supported!', platform.system())

  if platform.machine() not in ['i686', 'x86_64']:
    panic('Build on %s architecture not supported!', platform.machine())

  parser = argparse.ArgumentParser(description='Build cross toolchain.')
  parser.add_argument('action',
                      choices=['build', 'clean', 'download'],
                      default='build', help='perform action')
  parser.add_argument('args', metavar='ARGS', type=str, nargs='*',
                      help='action arguments')
  parser.add_argument('--binutils', choices=['2.18', '2.23.2'], default='2.18',
                      help='desired binutils version')
  parser.add_argument('--gcc', choices=['4.2.4', '4.9.1'], default='4.2.4',
                      help='desired gcc version')
  parser.add_argument('-q', '--quiet', action='store_true')
  parser.add_argument('--prefix', type=str, default=None,
                      help='installation directory')
  args = parser.parse_args()

  setvar(top=path.abspath(path.dirname(sys.argv[0])),
         binutils_ver=args.binutils,
         gcc_ver=args.gcc,
         py_ver='python%d.%d' % (sys.version_info.major, sys.version_info.minor))

  setvar(gmp='gmp-5.1.3',
         mpfr='mpfr-3.1.3',
         mpc='mpc-1.0.3',
         isl='isl-0.12.2',
         cloog='cloog-0.18.4',
         texinfo='texinfo-4.12',
         automake='automake-1.15',
         binutils='binutils-{binutils_ver}',
         sdk='SDK_53.24',
         gcc='gcc-{gcc_ver}',
         gpp='g++-{gcc_ver}',
         target='ppc-amigaos',
         python=sys.executable,
         sitedir=path.join('lib', '{py_ver}', 'site-packages'),
         patches=path.join('{top}', 'patches'),
         stamps=path.join('{top}', '.build-ppc', 'stamps'),
         build=path.join('{top}', '.build-ppc', 'build'),
         sources=path.join('{top}', '.build-ppc', 'sources'),
         host=path.join('{top}', '.build-ppc', 'host'),
         tmpdir=path.join('{top}', '.build-ppc', 'tmp'),
         prefix=path.join('{top}', 'ppc-amigaos'),
         archives=path.join('{top}', '.build-ppc', 'archives'),
         submodules=path.join('{top}', 'submodules'))

  if args.quiet:
    getLogger().setLevel(logging.INFO)

  if args.prefix is not None:
    setvar(prefix=args.prefix)

  if not path.exists('{prefix}'):
    mkdir('{prefix}')

  action = args.action.replace('-', '_')
  globals()[action].__call__(*args.args)
