Initial import.

This commit is contained in:
Chris Johns 2012-10-30 10:37:12 +11:00
commit bf13d27ab0
19 changed files with 3157 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~
*.pyc

34
config/base.cfg Normal file
View File

@ -0,0 +1,34 @@
#
# Base set up for all configurations.
#
# _prefix is set in the tool
%define _exec_prefix %{_prefix}
%define _bindir %{_exec_prefix}/bin
%define _sbindir %{_exec_prefix}/sbin
%define _libexecdir %{_exec_prefix}/libexec
%define _datarootdir %{_prefix}/share
%define _datadir %{_datarootdir}
%define _sysconfdir %{_prefix}/etc
%define _sharedstatedir %{_prefix}/com
%define _localstatedir %{_prefix}/var
%define _includedir %{_prefix}/include
%define _libdir %{_exec_prefix}/%{_lib}
%define _mandir %{_datarootdir}/man
%define _infodir %{_datarootdir}/info
%define _localedir %{_datarootdir}/locale
%ifos mingw mingw32
%define _exeext .exe
%define debug_package %{nil}
%define _libdir %{_exec_prefix}/lib
%else
%define _exeext %{nil}
%endif
%if "%{_build}" != "%{_host}"
%define _host_prefix %{_host}-
%else
%define _host_prefix %{nil}
%endif

86
config/binutils-2-1.cfg Normal file
View File

@ -0,0 +1,86 @@
#
# Binutils 2.xx Version 1.
#
# This configuration file configure's, make's and install's binutils.
#
%include %{_configdir}/checks.cfg
Summary: Binutils v%{binutils_version} for target %{_target} on host %{_host}
Version: %{binutils_version}
Release: %{release}
URL: http://sources.redhat.com/binutils
BuildRoot: %{_tmppath}/%{name}-root-%(%{__id_u} -n)
#
# Source
#
Source0: ftp://ftp.gnu.org/gnu/binutils/binutils-%{binutils_version}.tar.bz2
#
# Prepare the source code.
#
%prep
%setup -q -c -T -n %{name}-%{version}
%setup -q -D -T -n %{name}-%{version} -a0
cd binutils-%{binutils_version}
%{?patch0:%patch0 -p1}
cd ..
%build
export PATH="%{_bindir}:${PATH}"
mkdir -p build
cd build
%if "%{_build}" != "%{_host}"
CFLAGS_FOR_BUILD="-g -O2 -Wall" \
%endif
CFLAGS="$TB_OPT_FLAGS" \
../binutils-%{binutils_version}/configure \
--build=%{_build} --host=%{_host} \
--target=%{_target} \
--verbose --disable-nls \
--without-included-gettext \
--disable-win32-registry \
--disable-werror \
--prefix=%{_prefix} --bindir=%{_bindir} \
--exec-prefix=%{_exec_prefix} \
--includedir=%{_includedir} --libdir=%{_libdir} \
--mandir=%{_mandir} --infodir=%{_infodir}
%{__make} %{?_smp_mflags} all
cd ..
%install
export PATH="%{_bindir}:${PATH}"
rm -rf $TB_BUILD_ROOT
cd build
%{__make} DESTDIR=$TB_BUILD_ROOT install
# Dropped in FSF-binutils-2.9.5, but Cygwin still ships it.
rm -rf $TB_BUILD_ROOT%{_infodir}/configure.info*
rm -f $TB_BUILD_ROOT%{_infodir}/dir
touch $TB_BUILD_ROOT%{_infodir}/dir
# binutils does not install share/locale, however it uses it
mkdir -p $TB_BUILD_ROOT%{_prefix}/share/locale
# We don't ship host files
rm -f ${TB_BUILD_ROOT}%{_libdir}/libiberty*
# manpages without corresponding tools
if test ! -f ${TB_BUILD_ROOT}%{_bindir}/%{_target}-dlltool%{_exeext}; then
rm -f ${TB_BUILD_ROOT}%{_mandir}/man1/%{_target}-dlltool*
fi
if test ! -f ${TB_BUILD_ROOT}%{_bindir}/%{_target}-nlmconv%{_exeext}; then
rm -f ${TB_BUILD_ROOT}%{_mandir}/man1/%{_target}-nlmconv*
fi
if test ! -f ${TB_BUILD_ROOT}%{_bindir}/%{_target}-windres%{_exeext}; then
rm -f ${TB_BUILD_ROOT}%{_mandir}/man1/%{_target}-windres*
fi
if test ! -f ${TB_BUILD_ROOT}%{_bindir}/%{_target}-windmc%{_exeext}; then
rm -f ${TB_BUILD_ROOT}%{_mandir}/man1/%{_target}-windmc*
fi
cd ..

View File

@ -0,0 +1,18 @@
#
# Binutils 2.22.
#
%include %{_configdir}/checks.cfg
%include %{_configdir}/base.cfg
%define binutils_version 2.22
Name: %{_target}-binutils-%{binutils_version}-%{release}
%description
Cross binutils for target %{_target}.
#
# The binutils build instructions. We use 2.xx Release 1.
#
%include %{_configdir}/binutils-2-1.cfg

11
config/checks.cfg Normal file
View File

@ -0,0 +1,11 @@
#
# Standard checks.
#
%if %{_target} == %{nil}
%error No 'target' defined
%endif
%ifn %{defined release}
%error No 'release' defined
%endif

175
config/gcc-4.6-1.cfg Normal file
View File

@ -0,0 +1,175 @@
#
# GCC 4.6 Version 1.
#
# This configuration file configure's, make's and install's gcc. It uses
# newlib, MPFR, MPC, and GMP in a one-tree build configuration.
#
%include %{_configdir}/checks.cfg
%ifn %{defined gcc_version_message}
%error No GCC Version message defined.
%endif
Summary: GCC v%{gcc_version} and Newlib v%{newlib_version} for target %{_target} on host %{_host}
Version: %{gcc_version}
Release: %{release}
URL: http://gcc.gnu.org/
BuildRoot: %{_tmppath}/%{name}-root-%(%{__id_u} -n)
#
# Source
#
#
# GCC core and G++
#
Source0: ftp://ftp.gnu.org/gnu/gcc/gcc-%{gcc_version}/gcc-core-%{gcc_version}.tar.bz2
%if %{enable_cxx}
Source1: ftp://ftp.gnu.org/gnu/gcc/gcc-%{gcc_pkgvers}/%{gcc_version}/gcc-g++-%{gcc_version}.tar.bz2
%endif
#
# Newlib
#
Source10: ftp://sourceware.org/pub/newlib/newlib-%{newlib_version}.tar.gz
#
# Packages GCC requires
#
Source20: http://www.mpfr.org/mpfr-%{mpfr_version}/mpfr-%{mpfr_version}.tar.bz2
Source21: http://www.multiprecision.org/mpc/download/mpc-%{mpc_version}.tar.gz
Source22: ftp://ftp.gnu.org/gnu/gmp/gmp-%{gmp_version}.tar.bz2
#
# The GCC library directory
#
%global _gcclibdir %{_prefix}/lib
#
# Prepare the source code.
#
%prep
%setup -q -c -T -n %{name}-%{version}
# gcc core
%setup -q -T -D -n %{name}-%{version} -a0
cd gcc-%{gcc_version}
%{?patch0:%patch0 -p1}
cd ..
# g++
%{?source1:%setup -q -T -D -n %{name}-%{version} -a1}
cd gcc-%{gcc_version}
%{?patch1:%patch1 -p1}
cd ..
# newlib
%setup -q -T -D -n %{name}-%{version} -a10
cd newlib-%{newlib_version}
%{?patch10:%patch10 -p1}
cd ..
# Link newlib into the gcc source tree
ln -s ../newlib-%{newlib_version}/newlib gcc-%{gcc_version}
# MPFR
%setup -q -T -D -n %{name}-%{version} -a20
cd mpfr-%{mpfr_version}
%{?patch20:%patch20 -p1}
cd ..
# Build MPFR one-tree style
ln -s ../mpfr-%{mpfr_version} gcc-%{gcc_version}/mpfr
# MPC
%setup -q -T -D -n %{name}-%{version} -a21
cd mpc-%{mpc_version}
%{?patch21:%patch21 -p1}
cd ..
# Build MPC one-tree style
ln -s ../mpc-%{mpc_version} gcc-%{gcc_version}/mpc
# GMP
%setup -q -T -D -n %{name}-%{version} -a22
cd gmp-%{gmp_version}
%{?patch22:%patch22 -p1}
cd ..
# Build GMP one-tree style
ln -s ../gmp-%{gmp_version} gcc-%{gcc_version}/gmp
echo "%{gcc_version_message}" > gcc-%{gcc_version}/gcc/DEV-PHASE
# Fix timestamps
cd gcc-%{gcc_version}
contrib/gcc_update --touch
cd ..
%build
export PATH="%{_bindir}:${PATH}"
mkdir -p build
cd build
languages="c"
%if %{enable_cxx}
languages="$languages,c++"
%endif
%if "%{_build}" != "%{_host}"
CFLAGS_FOR_BUILD="-g -O2 -Wall" \
CC="%{_host}-gcc ${TB_OPT_FLAGS}" \
%else
# gcc is not ready to be compiled with -std=gnu99
CC=$(echo "%{__cc} ${TB_OPT_FLAGS}" | sed -e 's,-std=gnu99 ,,') \
%endif
../gcc-%{gcc_version}/configure \
--prefix=%{_prefix} \
--bindir=%{_bindir} \
--exec_prefix=%{_exec_prefix} \
--includedir=%{_includedir} \
--libdir=%{_gcclibdir} \
--libexecdir=%{_libexecdir} \
--mandir=%{_mandir} \
--infodir=%{_infodir} \
--datadir=%{_datadir} \
--build=%_build --host=%_host \
--target=%{_target} \
--disable-libstdcxx-pch \
--with-gnu-as --with-gnu-ld --verbose \
--with-newlib \
--with-system-zlib \
--disable-nls --without-included-gettext \
--disable-win32-registry \
--enable-version-specific-runtime-libs \
--disable-lto \
%{?with_threads:--enable-threads}%{!?with_threads:--disable-threads} \
%{?with_plugin:--enable-plugin}%{!?with_plugin:--disable-plugin} \
--enable-newlib-io-c99-formats \
%{?with_iconv:--enable-newlib-iconv} \
--enable-languages="$languages"
%if "%_host" != "%_build"
# Bug in gcc-3.2.1:
# Somehow, gcc doesn't get syslimits.h right for Cdn-Xs
mkdir -p gcc/include
cp ../gcc-%{gcc_version}/gcc/gsyslimits.h gcc/include/syslimits.h
%endif
%{__make} %{?_smp_mflags} all
cd ..
%install
export PATH="%{_bindir}:${PATH}"
rm -rf $TB_BUILD_ROOT
cd build
%{__make} DESTDIR=$TB_BUILD_ROOT install
cd ..
# libiberty doesn't honor --libdir, but always installs to a
# magically guessed _libdir
rm -f ${TB_BUILD_ROOT}%{_libdir}/libiberty.a
# We use the version from binutils
rm -f $TB_BUILD_ROOT%{_bindir}/%{_target}-c++filt%{_exeext}
# We don't ship info/dir
rm -f $TB_BUILD_ROOT%{_infodir}/dir
# Don't want libffi's man-pages
rm -f $TB_BUILD_ROOT%{_mandir}/man3/*ffi*

View File

@ -0,0 +1,19 @@
#
# GCC 2.6, Newlib 1.20
#
%include %{_configdir}/checks.cfg
%include %{_configdir}/base.cfg
%define gcc_version 4.6.3
%define newlib_version 1.20.0
%define mpfr_version 3.0.1
%define mpc_version 0.8.2
%define gmp_version 5.0.1
Name: %{_target}-gcc-%{gcc_version}-newlib-%{newlib_version}-%{release}
#
# The gcc/newlib build instructions. We use 4.6 Release 1.
#
%include %{_configdir}/gcc-4.6-1.cfg

29
tb-builder Normal file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import sys, os
base = os.path.dirname(sys.argv[0])
sys.path.insert(0, base + '/tb')
try:
import build
build.run(sys.argv)
except ImportError:
print >> sys.stderr, "Incorrect Tool Builder installation"
sys.exit(1)

29
tb-crossgcc Executable file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import sys, os
base = os.path.dirname(sys.argv[0])
sys.path.insert(0, base + '/tb')
try:
import crossgcc
crossgcc.run()
except ImportError:
print >> sys.stderr, "Incorrect Tools Bulder installation"
sys.exit(1)

428
tb/build.py Normal file
View File

@ -0,0 +1,428 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# This code builds a package given a config file. It only builds to be
# installed not to be package unless you run a packager around this.
#
import getopt
import os
import shutil
import stat
import sys
import urllib2
import urlparse
import config
import defaults
import error
import execute
import log
#
# Version of Tools Builder.
#
version = '0.1'
def _notice(opts, text):
if not opts.quiet() and not log.default.has_stdout():
print text
log.output(text)
log.flush()
class script:
"""Create and manage a shell script."""
def __init__(self, quiet = True):
self.quiet = quiet
self.reset()
def reset(self):
self.body = []
self.lc = 0
def append(self, text):
if type(text) is str:
text = text.splitlines()
if not self.quiet:
i = 0
for l in text:
i += 1
log.output('script:%3d: ' % (self.lc + i) + l)
self.lc += len(text)
self.body.extend(text)
def write(self, name, check_for_errors = False):
s = None
try:
s = open(name, 'w')
s.write('\n'.join(self.body))
s.close()
os.chmod(name, stat.S_IRWXU | \
stat.S_IRGRP | stat.S_IXGRP | \
stat.S_IROTH | stat.S_IXOTH)
except IOError, err:
raise error.general('creating script: ' + name)
except:
if s is not None:
s.close()
raise
if s is not None:
s.close()
class build:
"""Build a package given a config file."""
def __init__(self, name, _defaults, opts):
self.opts = opts
_notice(opts, 'building: ' + name)
self.config = config.file(name, _defaults = _defaults, opts = opts)
self.script = script(quiet = opts.quiet())
def _output(self, text):
if not self.opts.quiet():
log.output(text)
def rmdir(self, path):
self._output('removing: ' + path)
if not self.opts.dry_run():
if os.path.exists(path):
try:
shutil.rmtree(path)
except IOError, err:
raise error.error('error removing: ' + path)
def mkdir(self, path):
self._output('making dir: ' + path)
if not self.opts.dry_run():
try:
os.makedirs(path)
except IOError, err:
raise error.general('error creating path: ' + path)
def get_file(self, url, local):
if not os.path.isdir(os.path.dirname(local)):
raise error.general('source path not found: ' + os.path.dirname(local))
if not os.path.exists(local):
#
# Not localy found so we need to download it. Check if a URL
# has been provided on the command line.
#
url_bases = self.opts.urls()
urls = []
if url_bases is not None:
for base in url_bases:
if base[-1:] != '/':
base += '/'
url_path = urlparse.urlsplit(url)[2]
slash = url_path.rfind('/')
if slash < 0:
url_file = url_path
else:
url_file = url_path[slash + 1:]
urls.append(urlparse.urljoin(base, url_file))
urls.append(url)
for url in urls:
_notice(self.opts, 'download: ' + url + ' -> ' + local)
if not self.opts.dry_run():
failed = False
_in = None
_out = None
try:
_in = urllib2.urlopen(url)
_out = open(local, 'wb')
_out.write(_in.read())
except IOError, err:
_notice(self.opts, 'download: ' + url + ': failed: ' + str(err))
if os.path.exists(local):
os.remove(local)
failed = True
except:
if _out is not None:
_out.close()
raise
if _out is not None:
_out.close()
if _in is not None:
del _in
if not failed:
if not os.path.isfile(local):
raise error.general('source is not a file: ' + local)
return
raise error.general('downloading ' + url + ': all paths have failed, giving up')
def parse_url(self, url):
#
# Split the source up into the parts we need.
#
source = {}
source['url'] = url
source['path'] = os.path.dirname(url)
source['file'] = os.path.basename(url)
source['name'], source['ext'] = os.path.splitext(source['file'])
#
# Get the file. Checks the local source directory first.
#
source['local'] = os.path.join(self.config.abspath('_sourcedir'),
source['file'])
#
# Is the file compressed ?
#
esl = source['ext'].split('.')
if esl[-1:][0] == 'gz':
source['compressed'] = '%{__gzip} -dc'
elif esl[-1:][0] == 'bz2':
source['compressed'] = '%{__bzip2} -dc'
elif esl[-1:][0] == 'bz2':
source['compressed'] = '%{__zip} -u'
elif esl[-1:][0] == 'xz':
source['compressed'] = '%{__xz} -dc'
source['script'] = ''
return source
def source(self, package, source_tag):
#
# Scan the sources found in the config file for the one we are
# after. Infos or tags are lists.
#
sources = package.sources()
url = None
for s in sources:
tag = s[len('source'):]
if tag.isdigit():
if int(tag) == source_tag:
url = sources[s][0]
break
if url is None:
raise error.general('source tag not found: source' + str(source_tag))
source = self.parse_url(url)
self.get_file(source['url'], source['local'])
if 'compressed' in source:
source['script'] = source['compressed'] + ' ' + \
source['local'] + ' | %{__tar_extract} -'
else:
source['script'] = '%{__tar_extract} ' + source['local']
return source
def patch(self, package, args):
#
# Scan the patches found in the config file for the one we are
# after. Infos or tags are lists.
#
patches = package.patches()
url = None
for p in patches:
if args[0][1:].lower() == p:
url = patches[p][0]
break
if url is None:
raise error.general('patch tag not found: ' + args[0])
patch = self.parse_url(url)
self.get_file(patch['url'], patch['local'])
if 'compressed' in patch:
patch['script'] = patch['compressed'] + ' ' + patch['local']
else:
patch['script'] = '%{__cat} ' + patch['local']
patch['script'] += ' | %{__patch} ' + ' '.join(args[1:])
self.script.append(self.config.expand(patch['script']))
def setup(self, package, args):
self._output('prep: ' + package.name() + ': ' + ' '.join(args))
opts, args = getopt.getopt(args[1:], 'qDcTn:b:a:')
source_tag = 0
quiet = False
unpack_default_source = True
delete_before_unpack = True
create_dir = False
name = None
unpack_before_chdir = True
for o in opts:
if o[0] == '-q':
quiet = True
elif o[0] == '-D':
delete_before_unpack = False
elif o[0] == '-c':
create_dir = True
elif o[0] == '-T':
unpack_default_source = False
elif o[0] == '-n':
name = o[1]
elif o[0] == '-b':
unpack_before_chdir = True
if not o[1].isdigit():
raise error.general('setup source tag no a number: ' + o[1])
source_tag = int(o[1])
elif o[0] == '-a':
unpack_before_chdir = False
source_tag = int(o[1])
source0 = None
source = self.source(package, source_tag)
if name is None:
if source:
name = source['name']
else:
name = source0['name']
self.script.append(self.config.expand('cd %{_builddir}'))
if delete_before_unpack:
self.script.append(self.config.expand('%{__rm} -rf ' + name))
if create_dir:
self.script.append(self.config.expand('%{__mkdir_p} ' + name))
#
# If -a? then change directory before unpacking.
#
if not unpack_before_chdir:
self.script.append(self.config.expand('cd ' + name))
#
# Unpacking the source. Note, treated the same as -a0.
#
if unpack_default_source and source_tag != 0:
source0 = self.source(package, 0)
if source0 is None:
raise error.general('no setup source0 tag found')
self.script.append(self.config.expand(source0['script']))
self.script.append(self.config.expand(source['script']))
if unpack_before_chdir:
self.script.append(self.config.expand('cd ' + name))
self.script.append(self.config.expand('%{__setup_post}'))
if create_dir:
self.script.append(self.config.expand('cd ..'))
def run(self, command, shell_opts = '', cwd = None):
e = execute.capture_execution(log = log.default, dump = self.opts.quiet())
cmd = self.config.expand('%{___build_shell} -ex ' + shell_opts + ' ' + command)
self._output('run: ' + cmd)
exit_code, proc, output = e.shell(cmd, cwd = cwd)
if exit_code != 0:
raise error.general('shell cmd failed: ' + cmd)
def builddir(self):
builddir = self.config.abspath('_builddir')
self.rmdir(builddir)
if not self.opts.dry_run():
self.mkdir(builddir)
def prep(self, package):
self.script.append('echo "==> %prep:"')
_prep = package.prep()
for l in _prep:
args = l.split()
if args[0] == '%setup':
self.setup(package, args)
elif args[0].startswith('%patch'):
self.patch(package, args)
else:
self.script.append(' '.join(args))
def build(self, package):
self.script.append('echo "==> %build:"')
_build = package.build()
for l in _build:
args = l.split()
self.script.append(' '.join(args))
def install(self, package):
self.script.append('echo "==> %install:"')
_install = package.install()
for l in _install:
args = l.split()
self.script.append(' '.join(args))
def files(self, package):
self.script.append('echo "==> %files:"')
prefixbase = self.opts.prefixbase()
if prefixbase is None:
prefixbase = ''
inpath = os.path.join('%{buildroot}', prefixbase)
tardir = os.path.abspath(self.config.expand('%{_tardir}'))
self.script.append('mkdir -p %s' % tardir)
self.script.append(self.config.expand('cd ' + inpath))
tar = os.path.join(tardir, package.long_name() + '.tar.bz2')
cmd = self.config.expand('%{__tar} -cf - . ' + '| %{__bzip2} > ' + tar)
self.script.append(cmd)
self.script.append(self.config.expand('cd %{_builddir}'))
def clean(self, package):
self.script.append('echo "==> %clean:"')
_clean = package.clean()
if _clean is not None:
for l in _clean:
args = l.split()
self.script.append(' '.join(args))
def cleanup(self):
if not self.opts.no_clean():
buildroot = self.config.abspath('buildroot')
builddir = self.config.abspath('_builddir')
_notice(self.opts, 'cleanup: %s' % (buildroot))
self.rmdir(buildroot)
_notice(self.opts, 'cleanup: %s' % (builddir))
self.rmdir(builddir)
def make(self):
packages = self.config.packages()
package = packages['main']
name = package.name()
_notice(self.opts, 'package: %s' % (name))
self.script.reset()
self.script.append(self.config.expand('%{___build_template}'))
self.script.append('echo "=> ' + name + ':"')
self.prep(package)
self.build(package)
self.install(package)
self.files(package)
if not self.opts.no_clean():
self.clean(package)
if not self.opts.dry_run():
self.builddir()
sn = self.config.expand(os.path.join('%{_builddir}', 'doit'))
self._output('write script: ' + sn)
self.script.write(sn)
_notice(self.opts, 'building: ' + name)
self.run(sn)
def name(self):
packages = self.config.packages()
package = packages['main']
return package.name()
def run(args):
try:
opts, _defaults = defaults.load(args)
log.default = log.log(opts.logfiles())
_notice(opts, 'Tools Builder, v%s' % (version))
for config_file in opts.config_files():
b = build(config_file, _defaults = _defaults, opts = opts)
b.make()
del b
except error.general, gerr:
print gerr
sys.exit(1)
except error.internal, ierr:
print ierr
sys.exit(1)
except error.exit, eerr:
pass
except KeyboardInterrupt:
_notice(opts, 'user terminated')
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
run(sys.argv)

858
tb/config.py Normal file
View File

@ -0,0 +1,858 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# This code is based on a tool I wrote to parse RPM spec files in the RTEMS
# project. This is now a configuration file format that has moved away from the
# spec file format to support the specific needs of cross-compiling GCC. This
# module parses a configuration file into Python data types that can be used by
# other software modules.
#
import os
import re
import sys
import defaults
import error
import execute
import log
class package:
def __init__(self, name, arch):
self._name = name
self._arch = arch
self.directives = {}
self.infos = {}
def __str__(self):
def _dictlist(dl):
s = ''
dll = dl.keys()
dll.sort()
for d in dll:
if d:
s += ' ' + d + ':\n'
for l in dl[d]:
s += ' ' + l + '\n'
return s
s = '\npackage: ' + self._name + \
'\n directives:\n' + _dictlist(self.directives) + \
'\n infos:\n' + _dictlist(self.infos)
return s
def directive_extend(self, dir, data):
if dir not in self.directives:
self.directives[dir] = []
for i in range(0, len(data)):
data[i] = data[i].strip()
self.directives[dir].extend(data)
def info_append(self, info, data):
if info not in self.infos:
self.infos[info] = []
self.infos[info].append(data)
def get_info(self, info):
if not info in self.infos:
raise error.general('no ' + info + ' in package "' + self.name + '"')
return self.info
def version(self):
return self.get_info('Version')
def extract_info(self, label):
infos = {}
for i in self.infos:
il = i.lower()
if il.startswith(label):
if il == label:
il = label + '0'
elif not il[len(label):].isdigit():
continue
infos[il] = self.infos[i]
return infos
def find_info(self, label):
for i in self.infos:
if i.lower() == label:
return self.infos[i]
return None
def find_directive(self, label):
for d in self.directives:
if d.lower() == label:
return self.directives[d]
return None
def name(self):
info = self.find_info('name')
if info:
return info[0]
return self._name
def version(self):
info = self.find_info('version')
if not info:
return None
return info[0]
def release(self):
info = self.find_info('release')
if not info:
return None
return info[0]
def buildarch(self):
info = self.find_info('buildarch')
if not info:
return self._arch
return info[0]
def sources(self):
return self.extract_info('source');
def patches(self):
return self.extract_info('patch')
def prep(self):
return self.find_directive('%prep')
def build(self):
return self.find_directive('%build')
def install(self):
return self.find_directive('%install')
def clean(self):
return self.find_directive('%clean')
def post(self):
return self.find_directive('%post')
def include(self):
return self.find_directive('%include')
def long_name(self):
return self.name()
class file:
"""Parse a config file."""
_directive = [ '%description',
'%changelog',
'%prep',
'%build',
'%check',
'%include',
'%install',
'%clean',
'%post',
'%preun',
'%files' ]
_ignore = [ re.compile('%setup'),
re.compile('%configure'),
re.compile('%source[0-9]*'),
re.compile('%patch[0-9]*') ]
def __init__(self, name, _defaults, opts):
self.opts = opts
if self.opts.trace():
print 'config: %s' % (name)
self.configpath = []
self.wss = re.compile(r'\s+')
self.tags = re.compile(r':+')
self.sf = re.compile(r'%\([^\)]+\)')
self.default_defines = {}
for d in _defaults:
self.default_defines[self._label(d)] = _defaults[d]
for arg in self.opts.args:
if arg.startswith('--with-') or arg.startswith('--without-'):
label = arg[2:].lower().replace('-', '_')
self.default_defines[self._label(label)] = label
self.load_depth = 0
self.load(name)
def __str__(self):
def _dict(dd):
s = ''
ddl = dd.keys()
ddl.sort()
for d in ddl:
s += ' ' + d + ': ' + dd[d] + '\n'
return s
s = 'config: %s' % ('.'.join(self.configpath)) + \
'\n' + str(self.opts) + \
'\nlines parsed: %d' % (self.lc) + \
'\nname: ' + self.name + \
'\ndefines:\n' + _dict(self.defines)
for _package in self._packages:
s += str(self._packages[_package])
return s
def _output(self, text):
if not self.opts.quiet():
log.output(text)
def _warning(self, msg):
self._output('warning: ' + self.name + ':' + str(self.lc) + ': ' + msg)
def _error(self, msg):
print >> sys.stderr, \
'error: ' + self.name + ':' + str(self.lc) + ': ' + msg
self.in_error = True
if not self.opts.dry_run():
print >> sys.stderr, 'warning: switched to dry run due to errors'
self.opts.set_dry_run()
def _label(self, name):
return '%{' + name.lower() + '}'
def _macro_split(self, s):
'''Split the string (s) up by macros. Only split on the
outter level. Nested levels will need to split with futher calls.'''
trace_me = False
macros = []
nesting = []
has_braces = False
c = 0
while c < len(s):
if trace_me:
print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting
#
# We need to watch for shell type variables or the form '${var}' because
# they can upset the brace matching.
#
if s[c] == '%' or s[c] == '$':
start = s[c]
c += 1
if c == len(s):
continue
#
# Do we have '%%' or '%(' or '$%' or '$(' or not '${' ?
#
if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'):
continue
elif not s[c].isspace():
#
# If this is a shell macro and we are at the outter
# level or is '$var' forget it and move on.
#
if start == '$' and (s[c] != '{' or len(nesting) == 0):
continue
if s[c] == '{':
this_has_braces = True
else:
this_has_braces = False
nesting.append((c - 1, has_braces))
has_braces = this_has_braces
elif len(nesting) > 0:
if s[c] == '}' or (s[c].isspace() and not has_braces):
#
# Can have '%{?test: something %more}' where the
# nested %more ends with the '}' which also ends
# the outter macro.
#
if not has_braces:
if s[c] == '}':
macro_start, has_braces = nesting[len(nesting) - 1]
nesting = nesting[:-1]
if len(nesting) == 0:
macros.append(s[macro_start:c].strip())
if len(nesting) > 0:
macro_start, has_braces = nesting[len(nesting) - 1]
nesting = nesting[:-1]
if len(nesting) == 0:
macros.append(s[macro_start:c + 1].strip())
c += 1
if trace_me:
print 'ms:', macros
return macros
def _shell(self, line):
sl = self.sf.findall(line)
if len(sl):
e = execute.capture_execution()
for s in sl:
exit_code, proc, output = e.shell(s[2:-1])
if exit_code == 0:
line = line.replace(s, output)
else:
raise error.general('shell macro failed: ' + s + ': ' + output)
return line
def _expand(self, s):
expanded = True
while expanded:
expanded = False
ms = self._macro_split(s)
for m in ms:
mn = m
#
# A macro can be '%{macro}' or '%macro'. Turn the later into
# the former.
#
show_warning = True
if mn[1] != '{':
for r in self._ignore:
if r.match(mn) is not None:
mn = None
break
else:
mn = self._label(mn[1:])
show_warning = False
elif m.startswith('%{expand'):
colon = m.find(':')
if colon < 8:
self._warning('malformed expand macro, no colon found')
else:
e = self._expand(m[colon + 1:-1].strip())
s = s.replace(m, e)
expanded = True
mn = None
elif m.startswith('%{with '):
#
# Change the ' ' to '_' because the macros have no spaces.
#
n = self._label('with_' + m[7:-1].strip())
if n in self.defines:
s = s.replace(m, '1')
else:
s = s.replace(m, '0')
expanded = True
mn = None
elif m.startswith('%{echo'):
mn = None
elif m.startswith('%{defined'):
n = self._label(m[9:-1].strip())
if n in self.defines:
s = s.replace(m, '1')
else:
s = s.replace(m, '0')
expanded = True
mn = None
elif m.startswith('%{?') or m.startswith('%{!?'):
if m[2] == '!':
start = 4
else:
start = 3
colon = m[start:].find(':')
if colon < 0:
if not m.endswith('}'):
self._warning("malform conditional macro'" + m)
mn = None
else:
mn = self._label(m[start:-1])
else:
mn = self._label(m[start:start + colon])
if mn:
if m.startswith('%{?'):
if mn in self.defines:
if colon >= 0:
s = s.replace(m, m[start + colon + 1:-1])
expanded = True
mn = None
else:
mn = '%{nil}'
else:
if mn not in self.defines:
if colon >= 0:
s = s.replace(m, m[start + colon + 1:-1])
expanded = True
mn = None
else:
mn = '%{nil}'
if mn:
if mn.lower() in self.defines:
s = s.replace(m, self.defines[mn.lower()])
expanded = True
elif show_warning:
self._error("macro '" + mn + "' not found")
return self._shell(s)
def _define(self, config, ls):
if len(ls) <= 1:
self._warning('invalid macro definition')
else:
d = self._label(ls[1])
if d not in self.defines:
if len(ls) == 2:
self.defines[d] = '1'
else:
self.defines[d] = ls[2].strip()
else:
if self.opts.warn_all():
self._warning("macro '" + d + "' already defined")
def _undefine(self, config, ls):
if len(ls) <= 1:
self._warning('invalid macro definition')
else:
mn = self._label(ls[1])
if mn in self.defines:
self._error("macro '" + mn + "' not defined")
del self.defines[mn]
def _ifs(self, config, ls, label, iftrue, isvalid):
text = []
in_iftrue = True
while True:
if isvalid and \
((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
this_isvalid = True
else:
this_isvalid = False
r = self._parse(config, roc = True, isvalid = this_isvalid)
if r[0] == 'control':
if r[1] == '%end':
self._error(label + ' without %endif')
raise error.general('terminating build')
if r[1] == '%endif':
return text
if r[1] == '%else':
in_iftrue = False
elif r[0] == 'data':
if this_isvalid:
text.extend(r[1])
def _if(self, config, ls, isvalid, invert = False):
def add(x, y):
return x + ' ' + str(y)
def check_bool(value):
if value.isdigit():
if int(value) == 0:
istrue = False
else:
istrue = True
else:
istrue = None
return istrue
istrue = False
if isvalid:
if len(ls) == 2:
s = ls[1]
else:
s = (ls[1] + ' ' + ls[2])
ifls = s.split()
if len(ifls) == 1:
#
# Check if '%if %{x} == %{nil}' has both parts as nothing
# which means '%if ==' is always True and '%if !=' is always false.
#
if ifls[0] == '==':
istrue = True
elif ifls[0] == '==':
istrue = False
else:
istrue = check_bool(ifls[0])
if istrue == None:
self._error('invalid if bool value: ' + reduce(add, ls, ''))
istrue = False
elif len(ifls) == 2:
if ifls[0] == '!':
istrue = check_bool(ifls[1])
if istrue == None:
self._error('invalid if bool value: ' + reduce(add, ls, ''))
istrue = False
else:
istrue = not istrue
else:
#
# Check is something is being checked against empty,
# ie '%if %{x} == %{nil}'
# The logic is 'something == nothing' is False and
# 'something != nothing' is True.
#
if ifls[1] == '==':
istrue = False
elif ifls[1] == '!=':
istrue = True
else:
self._error('invalid if bool operator: ' + reduce(add, ls, ''))
elif len(ifls) == 3:
if ifls[1] == '==':
if ifls[0] == ifls[2]:
istrue = True
else:
istrue = False
elif ifls[1] == '!=' or ifls[1] == '=!':
if ifls[0] != ifls[2]:
istrue = True
else:
istrue = False
elif ifls[1] == '>':
if ifls[0] > ifls[2]:
istrue = True
else:
istrue = False
elif ifls[1] == '>=' or ifls[1] == '=>':
if ifls[0] >= ifls[2]:
istrue = True
else:
istrue = False
elif ifls[1] == '<=' or ifls[1] == '=<':
if ifls[0] <= ifls[2]:
istrue = True
else:
istrue = False
elif ifls[1] == '<':
if ifls[0] < ifls[2]:
istrue = True
else:
istrue = False
else:
self._error('invalid %if operator: ' + reduce(add, ls, ''))
else:
self._error('malformed if: ' + reduce(add, ls, ''))
if invert:
istrue = not istrue
if self.opts.trace():
print '_if: ', ifls, istrue
return self._ifs(config, ls, '%if', istrue, isvalid)
def _ifos(self, config, ls, isvalid):
isos = False
if isvalid:
os = self.define('_os')
if ls[0].find(os) >= 0 or ls[1].find(os) >= 0:
isos = True
else:
isos = False
return self._ifs(config, ls, '%ifos', isos, isvalid)
def _ifarch(self, config, positive, ls, isvalid):
isarch = False
if isvalid:
arch = self.define('_arch')
if ls[0].find(arch) >= 0 or ls[1].find(arch) >= 0:
isarch = True
else:
isarch = False
if not positive:
isarch = not isarch
return self._ifs(config, ls, '%ifarch', isarch, isvalid)
def _parse(self, config, roc = False, isvalid = True):
# roc = return on control
def _clean(line):
line = line[0:-1]
b = line.find('#')
if b >= 0:
line = line[1:b]
return line.strip()
#
# Need to add code to count matching '{' and '}' and if they
# do not match get the next line and add to the string until
# they match. This closes an opening '{' that is on another
# line.
#
for l in config:
self.lc += 1
l = _clean(l)
if len(l) == 0:
continue
if self.opts.trace():
print '%03d: %d %s' % (self.lc, isvalid, l)
if isvalid:
l = self._expand(l)
if len(l) == 0:
continue
if l[0] == '%':
ls = self.wss.split(l, 2)
if ls[0] == '%package':
if isvalid:
if ls[1] == '-n':
name = ls[2]
else:
name = self.name + '-' + ls[1]
return ('package', name)
elif ls[0] == '%error':
if isvalid:
return ('data', ['%%error %s:%d: %s' % (self.name, self.lc, l[7:])])
elif ls[0] == '%define' or ls[0] == '%global':
if isvalid:
self._define(config, ls)
elif ls[0] == '%undefine':
if isvalid:
self._undefine(config, ls)
elif ls[0] == '%if':
d = self._if(config, ls, isvalid)
if len(d):
return ('data', d)
elif ls[0] == '%ifn':
d = self._if(config, ls, isvalid, True)
if len(d):
return ('data', d)
elif ls[0] == '%ifos':
d = self._ifos(config, ls, isvalid)
if len(d):
return ('data', d)
elif ls[0] == '%ifarch':
d = self._ifarch(config, True, ls, isvalid)
if len(d):
return ('data', d)
elif ls[0] == '%ifnarch':
d = self._ifarch(config, False, ls, isvalid)
if len(d):
return ('data', d)
elif ls[0] == '%endif':
if roc:
return ('control', '%endif')
self._warning("unexpected '" + ls[0] + "'")
elif ls[0] == '%else':
if roc:
return ('control', '%else')
self._warning("unexpected '" + ls[0] + "'")
elif ls[0].startswith('%defattr'):
return ('data', [l])
elif ls[0] == '%bcond_with':
if isvalid:
#
# Check if already defined. Would be by the command line or
# even a host specific default.
#
if self._label('with_' + ls[1]) not in self.defines:
self._define(config, (ls[0], 'without_' + ls[1]))
elif ls[0] == '%bcond_without':
if isvalid:
if self._label('without_' + ls[1]) not in self.defines:
self._define(config, (ls[0], 'with_' + ls[1]))
else:
for r in self._ignore:
if r.match(ls[0]) is not None:
return ('data', [l])
if isvalid:
for d in self._directive:
if ls[0].strip() == d:
return ('directive', ls[0].strip(), ls[1:])
self._warning("unknown directive: '" + ls[0] + "'")
return ('data', [l])
else:
return ('data', [l])
return ('control', '%end')
def _set_package(self, _package):
if self.package == 'main' and \
self._packages[self.package].name() != None:
if self._packages[self.package].name() == _package:
return
if _package not in self._packages:
self._packages[_package] = package(_package,
self.define('%{_arch}'))
self.package = _package
def _directive_extend(self, dir, data):
self._packages[self.package].directive_extend(dir, data)
def _info_append(self, info, data):
self._packages[self.package].info_append(info, data)
def load(self, name):
if self.load_depth == 0:
self.in_error = False
self.lc = 0
self.name = name
self.defines = self.default_defines
self.conditionals = {}
self._packages = {}
self.package = 'main'
self._packages[self.package] = package(self.package,
self.define('%{_arch}'))
self.load_depth += 1
save_name = self.name
save_lc = self.lc
self.name = name
self.lc = 0
#
# Locate the config file. Expand any macors then Add the
# extension. Check if the file exists, therefore directly
# referenced. If not see if the file contains ':' or the path
# separator. If it does split the path else use the standard config dir
# path in the defaults.
#
exname = self.expand(name)
if exname.endswith('.cfg'):
configname = exname
else:
configname = '%s.cfg' % (exname)
cfgname = os.path.basename(configname)
if not os.path.exists(configname):
if ':' in configname:
configdirs = os.path.dirname(configname).split(':')
else:
configdirs = self.define('_configdir').split(':')
for cp in configdirs:
configname = os.path.join(os.path.abspath(cp), cfgname)
if os.path.exists(configname):
break
configname = None
if configname is None:
raise error.general('no config file found: %s' % (cfgname))
try:
if self.opts.trace():
print '_open: %s' % (configname)
config = open(configname, 'r')
except IOError, err:
raise error.general('error opening config file: %s' % (configname))
self.configpath += [configname]
try:
dir = None
data = []
while True:
r = self._parse(config)
if r[0] == 'package':
self._set_package(r[1])
dir = None
elif r[0] == 'control':
if r[1] == '%end':
break
self._warning("unexpected '" + r[1] + "'")
elif r[0] == 'directive':
new_data = []
if r[1] == '%description':
new_data = [' '.join(r[2])]
elif r[1] == '%include':
self.load(r[2][0])
continue
else:
if len(r[2]) == 0:
_package = 'main'
elif len(r[2]) == 1:
_package = r[2][0]
else:
if r[2][0].strip() != '-n':
self._warning("unknown directive option: '" + ' '.join(r[2]) + "'")
_package = r[2][1].strip()
self._set_package(_package)
if dir and dir != r[1]:
self._directive_extend(dir, data)
dir = r[1]
data = new_data
elif r[0] == 'data':
for l in r[1]:
l = self._expand(l)
if l.startswith('%error'):
raise error.general('config error: %s' % (l[7:]))
if not dir:
ls = self.tags.split(l, 1)
if self.opts.trace():
print '_tag: ', l, ls
if len(ls) > 1:
i = ls[0]
self._info_append(i, ls[1].strip())
# It seems like the info's also appear as
# defines or can be accessed via macros.
if ls[0][len(ls[0]) - 1] == ':':
ls[0] = ls[0][:-1]
ls[0] = ls[0].lower()
self._define(None, ('', ls[0], ls[1]))
else:
self._warning("invalid format: '" + l[:-1] + "'")
else:
data.append(l)
else:
self._error("invalid parse state: '" + r[0] + "'")
if dir is not None:
self._directive_extend(dir, data)
except:
config.close()
raise
config.close()
self.name = save_name
self.lc = save_lc
self.load_depth -= 1
def define(self, name):
if name.lower() in self.defines:
d = self.defines[name.lower()]
else:
n = self._label(name)
if n in self.defines:
d = self.defines[n]
else:
raise error.general('macro "' + name + '" not found')
return self._expand(d)
def expand(self, line):
return self._expand(line)
def directive(self, _package, name):
if _package not in self._packages:
raise error.general('package "' + _package + '" not found')
if name not in self._packages[_package].directives:
raise error.general('directive "' + name + \
'" not found in package "' + _package + '"')
return self._packages[_package].directives[name]
def abspath(self, path):
return os.path.abspath(self.define(path))
def packages(self):
return self._packages
def run():
import sys
try:
opts, _defaults = defaults.load(sys.argv)
if opts.trace():
print 'config: count %d' % (len(opts.config_files()))
for config_file in opts.config_files():
s = file(config_file, _defaults = _defaults, opts = opts)
print s
del s
except error.general, gerr:
print gerr
sys.exit(1)
except error.internal, ierr:
print ierr
sys.exit(1)
except KeyboardInterrupt:
print 'user terminated'
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
run()

245
tb/crossgcc.py Normal file
View File

@ -0,0 +1,245 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# This code builds a cross-gcc compiler tool suite given a tool set. A tool
# set lists the various tools. These are specific tool configurations.
#
import distutils.dir_util
import operator
import os
import build
import defaults
import error
import log
#
# Version of Tools Builder CrossGCC Builder.
#
version = '0.1'
def _trace(opts, text):
if opts.trace():
print text
def _notice(opts, text):
if not opts.quiet() and not log.default.has_stdout():
print text
log.output(text)
log.flush()
class crossgcc:
"""Build a Cross GCC Compiler tool suite."""
_order = { 'binutils': 0,
'gcc' : 1,
'gdb' : 2 }
def __init__(self, toolset, _defaults, opts):
_trace(opts, '_cgcc:%s: init' % (toolset))
self.opts = opts
self.defaults = _defaults
self.toolset = toolset
self.toolset_pkg = '%s-%s-tools' % (self.opts.expand('%{_target}', _defaults),
self.toolset)
def _output(self, text):
if not self.opts.quiet():
log.output(text)
def copy(self, src, dst):
what = '%s -> %s' % (src, dst)
_notice(self.opts, 'coping: %s' % (what))
if not self.opts.dry_run():
try:
files = distutils.dir_util.copy_tree(src, dst)
for f in files:
self._output(f)
except IOError, err:
raise error.general('coping tree: %s: %s' % (what, str(err)))
except distutils.errors.DistutilsFileError, err:
raise error.general('coping tree: %s' % (str(err)))
def first_package(self, _build):
what = _build.config.expand('crossgcc-%(%{__id_u} -n)-' + _build.name())
path = os.path.join(_build.config.abspath('%{_tmppath}'), what)
_build.rmdir(path)
_build.mkdir(path)
prefix = os.path.join(_build.config.expand('%{_prefix}'), 'bin')
if prefix[0] == os.sep:
prefix = prefix[1:]
binpath = os.path.join(path, prefix)
os.environ['PATH'] = binpath + os.pathsep + os.environ['PATH']
self._output('path: ' + os.environ['PATH'])
return path
def every_package(self, _build, path):
self.copy(_build.config.abspath('%{buildroot}'), path)
def last_package(self, _build, path):
tar = os.path.join(_build.config.expand('%{_tardir}'),
_build.config.expand('%s.tar.bz2' % (self.toolset_pkg)))
_notice(self.opts, 'tarball: %s' % (tar))
if not self.opts.dry_run():
cmd = _build.config.expand("'cd " + path + \
" && %{__tar} -cf - . | %{__bzip2} > " + tar + "'")
_build.run(cmd, shell_opts = '-c', cwd = path)
#if not self.opts.no_clean():
# _build.rmdir(path)
def load(self):
def _clean(line):
line = line[0:-1]
b = line.find('#')
if b >= 0:
line = line[1:b]
return line.strip()
extoolset = self.opts.expand(self.toolset, self.defaults)
root, ext = os.path.splitext(extoolset)
if extoolset.endswith('.cfg'):
toolsetcfg = extoolset
else:
toolsetcfg = '%s.cfg' % (extoolset)
toolsetname = toolsetcfg
if not os.path.exists(toolsetname):
for cp in self.opts.expand('%{_configdir}', self.defaults).split(':'):
configdir = os.path.abspath(cp)
toolsetname = os.path.join(configdir, toolsetcfg)
if os.path.exists(toolsetname):
break
toolsetname = None
if toolsetname is None:
raise error.general('no tool set file found: %s' % (toolsetcfg))
try:
if self.opts.trace():
print '_cgcc:%s: open: %s' % (self.toolset, toolsetname)
toolset = open(toolsetname, 'r')
except IOError, err:
raise error.general('error opening toolset file: %s' + toolsetname)
configs = []
try:
lc = 0
for l in toolset:
lc += 1
l = _clean(l)
if len(l) == 0:
continue
if self.opts.trace():
print '%03d: %s' % (lc, l)
if ':' in l:
ls = l.split(':')
if ls[0].strip() == 'package':
self.toolset_pkg = self.opts.expand(ls[1].strip(), self.defaults)
self.defaults['package'] = self.toolset_pkg
elif l[0] == '%':
if l.startswith('%define'):
ls = l.split()
self.defaults[ls[1].strip()] = ls[2].strip()
else:
raise error.general('invalid directive in tool set files: %s' % (l))
else:
configs += [l.strip()]
except:
toolset.close()
raise
toolset.close()
return configs
def make(self):
def _sort(configs):
_configs = {}
for config in configs:
for order in crossgcc._order:
if config.lower().find(order) >= 0:
_configs[config] = crossgcc._order[order]
sorted_configs = sorted(_configs.iteritems(), key = operator.itemgetter(1))
configs = []
for s in range(0, len(sorted_configs)):
configs.append(sorted_configs[s][0])
return configs
_trace(self.opts, '_cgcc:%s: make' % (self.toolset))
_notice(self.opts, 'toolset: %s' % (self.toolset))
configs = self.load()
_trace(self.opts, '_cgcc:%s: configs: %s' % (self.toolset, ','.join(configs)))
current_path = os.environ['PATH']
try:
builds = []
for s in range(0, len(configs)):
b = build.build(configs[s], _defaults = self.defaults, opts = self.opts)
if s == 0:
path = self.first_package(b)
b.make()
self.every_package(b, path)
if s == len(configs) - 1:
self.last_package(b, path)
builds += [b]
if not self.opts.no_clean():
for b in builds:
_notice(self.opts, 'cleaning: %s' % (b.name()))
b.cleanup()
for b in builds:
del b
except:
os.environ['PATH'] = current_path
raise
os.environ['PATH'] = current_path
def run():
import sys
try:
opts, _defaults = defaults.load(sys.argv)
log.default = log.log(opts.logfiles())
_notice(opts, 'Tools Builder - CrossGCC Tool Sets, v%s' % (version))
for toolset in opts.params():
c = crossgcc(toolset, _defaults = _defaults, opts = opts)
c.make()
del c
except error.general, gerr:
print gerr
sys.exit(1)
except error.internal, ierr:
print ierr
sys.exit(1)
except error.exit, eerr:
pass
except KeyboardInterrupt:
_notice(opts, 'user terminated')
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
run()

57
tb/darwin.py Normal file
View File

@ -0,0 +1,57 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# This code is based on what ever doco about spec files I could find and
# RTEMS project's spec files.
#
import pprint
import os
import execute
def load():
uname = os.uname()
sysctl = '/usr/sbin/sysctl '
e = execute.capture_execution()
exit_code, proc, output = e.shell(sysctl + 'hw.ncpu')
if exit_code == 0:
smp_mflags = '-j' + output.split(' ')[1].strip()
else:
smp_mflags = ''
defines = {
'_os': 'darwin',
'_host': uname[4] + '-apple-darwin' + uname[2],
'_host_vendor': 'apple',
'_host_os': 'darwin',
'_host_cpu': uname[4],
'_host_alias': '%{nil}',
'_host_arch': uname[4],
'_usr': '/opt/local',
'_var': '/opt/local/var',
'optflags': '-O2',
'_smp_mflags': smp_mflags,
'__xz': '/usr/local/bin/xz',
'with_zlib': '--with-zlib=no',
}
return defines
if __name__ == '__main__':
pprint.pprint(load())

510
tb/defaults.py Normal file
View File

@ -0,0 +1,510 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Determine the defaults and load the specific file.
#
import glob
import pprint
import re
import os
import error
import execute
basepath = 'tb'
defaults = {
# Nothing
'nil': '',
# Set to invalid values.
'_host': '',
'_build': '%{_host}',
'_target': '',
# Paths
'_host_platform': '%{_host_cpu}-%{_host_vendor}-%{_host_os}%{?_gnu}',
'_build': '%{_host}',
'_arch': '%{_host_arch}',
'_tbdir': '',
'_topdir': os.getcwd(),
'_configdir': '%{_topdir}/config:%{_tbdir}/config',
'_tardir': '%{_topdir}/tar',
'_sourcedir': '%{_topdir}/sources',
'_builddir': '%{_topdir}/build/%{name}-%{version}-%{release}',
'_docdir': '%{_defaultdocdir}',
'_tmppath': '%{_topdir}/build/tmp',
'buildroot:': '%{_tmppath}/%{name}-root-%(%{__id_u} -n)',
# Defaults, override in platform specific modules.
'___setup_shell': '/bin/sh',
'__aclocal': 'aclocal',
'__ar': 'ar',
'__arch_install_post': '%{nil}',
'__as': 'as',
'__autoconf': 'autoconf',
'__autoheader': 'autoheader',
'__automake': 'automake',
'__awk': 'awk',
'__bash': '/bin/bash',
'__bzip2': '/usr/bin/bzip2',
'__cat': '/bin/cat',
'__cc': '/usr/bin/gcc',
'__chgrp': '/usr/bin/chgrp',
'__chmod': '/bin/chmod',
'__chown': '/usr/sbin/chown',
'__cp': '/bin/cp',
'__cpio': '/usr/bin/cpio',
'__cpp': '/usr/bin/gcc -E',
'__cxx': '/usr/bin/g++',
'__grep': '/usr/bin/grep',
'__gzip': '/usr/bin/gzip',
'__id': '/usr/bin/id',
'__id_u': '%{__id} -u',
'__install': '/usr/bin/install',
'__install_info': '/usr/bin/install-info',
'__ld': '/usr/bin/ld',
'__ldconfig': '/sbin/ldconfig',
'__ln_s': 'ln -s',
'__make': 'make',
'__mkdir': '/bin/mkdir',
'__mkdir_p': '/bin/mkdir -p',
'__mv': '/bin/mv',
'__nm': '/usr/bin/nm',
'__objcopy': '%{_bindir}/objcopy',
'__objdump': '%{_bindir}/objdump',
'__patch': '/usr/bin/patch',
'__perl': 'perl',
'__perl_provides': '%{_usrlibrpm}/perl.prov',
'__perl_requires': '%{_usrlibrpm}/perl.req',
'__ranlib': 'ranlib',
'__remsh': '%{__rsh}',
'__rm': '/bin/rm',
'__rsh': '/usr/bin/rsh',
'__sed': '/usr/bin/sed',
'__setup_post': '%{__chmod} -R a+rX,g-w,o-w .',
'__sh': '/bin/sh',
'__tar': '/usr/bin/tar',
'__tar_extract': '%{__tar} -xvvf',
'__unzip': '/usr/bin/unzip',
'__xz': '/usr/bin/xz',
'_datadir': '%{_prefix}/share',
'_defaultdocdir': '%{_prefix}/share/doc',
'_exeext': '',
'_exec_prefix': '%{_prefix}',
'_lib': 'lib',
'_libdir': '%{_exec_prefix}/%{_lib}',
'_libexecdir': '%{_exec_prefix}/libexec',
'_localedir': '%{_datadir}/locale',
'_localstatedir': '%{_prefix}/var',
'_prefix': '%{_usr}',
'_usr': '/usr/local',
'_usrsrc': '%{_usr}/src',
'_var': '/usr/local/var',
'_varrun': '%{_var}/run',
# Shell Build Settings.
'___build_args': '-e',
'___build_cmd': '%{?_sudo:%{_sudo} }%{?_remsh:%{_remsh} %{_remhost} }%{?_remsudo:%{_remsudo} }%{?_remchroot:%{_remchroot} %{_remroot} }%{___build_shell} %{___build_args}',
'___build_post': 'exit 0',
# Prebuild set up script.
'___build_pre': '''# ___build_pre in as set up in defaults.py
# Directories
TB_SOURCE_DIR="%{_sourcedir}"
TB_BUILD_DIR="%{_builddir}"
TB_OPT_FLAGS="%{optflags}"
TB_ARCH="%{_arch}"
TB_OS="%{_os}"
export TB_SOURCE_DIR TB_BUILD_DIR TB_OPT_FLAGS TB_ARCH TB_OS
# Documentation
TB_DOC_DIR="%{_docdir}"
export TB_DOC_DIR
# Packages
TB_PACKAGE_NAME="%{name}"
TB_PACKAGE_VERSION="%{version}"
TB_PACKAGE_RELEASE="%{release}"
export TBPACKAGE_NAME TB_PACKAGE_VERSION TB_PACKAGE_RELEASE
# Build root directory
%{?buildroot:TB_BUILD_ROOT="%{buildroot}"}
export TB_BUILD_ROOT
# The compiler flags
%{?_targetcflags:CFLAGS_FOR_TARGET="%{_targetcflags}"}
%{?_targetcxxflags:CXXFLAGS_FOR_TARGET="%{_targetcxxflags}"}
export CFLAGS_FOR_TARGET
# Default environment set up.
LANG=C
export LANG
unset DISPLAY || :
umask 022
cd "%{_builddir}"''',
'___build_shell': '%{?_buildshell:%{_buildshell}}%{!?_buildshell:/bin/sh}',
'___build_template': '''#!%{___build_shell}
%{___build_pre}
%{nil}''',
# Configure command
'configure': '''
CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ;
CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ;
FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ;
./configure --build=%{_build} --host=%{_host} \
--target=%{_target_platform} \
--program-prefix=%{?_program_prefix} \
--prefix=%{_prefix} \
--exec-prefix=%{_exec_prefix} \
--bindir=%{_bindir} \
--sbindir=%{_sbindir} \
--sysconfdir=%{_sysconfdir} \
--datadir=%{_datadir} \
--includedir=%{_includedir} \
--libdir=%{_libdir} \
--libexecdir=%{_libexecdir} \
--localstatedir=%{_localstatedir} \
--sharedstatedir=%{_sharedstatedir} \
--mandir=%{_mandir} \
--infodir=%{_infodir}'''
}
class command_line:
"""Process the command line in a common way for all Tool Builder commands."""
_defaults = { 'params' : [],
'warn-all' : '0',
'quiet' : '0',
'trace' : '0',
'dry-run' : '0',
'no-clean' : '0',
'no-smp' : '0',
'rebuild' : '0' }
_long_opts = { '--prefix' : '_prefix',
'--prefixbase' : '_prefixbase',
'--topdir' : '_topdir',
'--configdir' : '_configdir',
'--builddir' : '_builddir',
'--sourcedir' : '_sourcedir',
'--usrlibrpm' : '_usrlibrpm', # XXX remove ?
'--tmppath' : '_tmppath',
'--log' : '_logfile',
'--url' : '_url_base',
'--targetcflags' : '_targetcflags',
'--targetcxxflags' : '_targetcxxflags',
'--libstdcxxflags' : '_libstdcxxflags' }
_long_true_opts = { '--trace' : '_trace',
'--dry-run' : '_dry_run',
'--warn-all' : '_warn_all',
'--no-clean' : '_no_clean',
'--no-smp' : '_no_smp',
'--rebuild' : '_rebuild' }
_target_triplets = { '--host' : '_host',
'--build' : '_build',
'--target' : '_target' }
def _help(self):
print '%s: [options] [args]' % (self.command_name)
print 'Options and arguments:'
print '--trace : Trace the execution (not current used)'
print '--dry-run : Do everything but actually run the build'
print '--warn-all : Generate warnings'
print '--no-clean : Do not clean up the build tree'
print '--no-smp : Run with 1 job and not as many as CPUs'
print '--rebuild : Rebuild (not used)'
print '--host : Set the host triplet'
print '--build : Set the build triplet'
print '--target : Set the target triplet'
print '--prefix path : Tools build prefix, ie where they are installed'
print '--prefixbase path : '
print '--topdir path : Top of the build tree, default is $PWD'
print '--configdir path : Path to the configuration directory, default: ./config'
print '--builddir path : Path to the build directory, default: ./build'
print '--sourcedir path : Path to the source directory, default: ./source'
print '--tmppath path : Path to the temp directory, default: ./tmp'
print '--log file : Log file where all build out is written too'
print '--url url : URL to look for source'
print '--targetcflags flags : List of C flags for the target code'
print '--targetcxxflags flags : List of C++ flags for the target code'
print '--libstdcxxflags flags : List of C++ flags to build the target libstdc++ code'
print '--with-<label> : Add the --with-<label> to the build'
print '--without-<label> : Add the --without-<label> to the build'
raise error.exit()
def __init__(self, argv):
self.command_path = os.path.dirname(argv[0])
if len(self.command_path) == 0:
self.command_path = '.'
self.command_name = os.path.basename(argv[0])
self.args = argv[1:]
self.defaults = {}
for to in command_line._long_true_opts:
self.defaults[command_line._long_true_opts[to]] = '0'
self.defaults['_tbdir'] = self.command_path
self._process()
def __str__(self):
def _dict(dd):
s = ''
ddl = dd.keys()
ddl.sort()
for d in ddl:
s += ' ' + d + ': ' + str(dd[d]) + '\n'
return s
s = 'command: ' + self.command() + \
'\nargs: ' + str(self.args) + \
'\nopts:\n' + _dict(self.opts)
return s
def _process(self):
def _process_lopt(opt, arg, long_opts, args, values = False):
for lo in long_opts:
if values and opt.startswith(lo):
equals = opt.find('=')
if equals < 0:
if arg == len(args) - 1:
raise error.general('missing option value: ' + lo)
arg += 1
value = args[arg]
else:
value = opt[equals + 1:]
return lo, long_opts[lo], value, arg
elif opt == lo:
return lo, long_opts[lo], True, arg
return None, None, None, arg
self.opts = command_line._defaults
i = 0
while i < len(self.args):
a = self.args[i]
if a.startswith('-'):
if a.startswith('--'):
if a.startswith('--warn-all'):
self.opts['warn-all'] = True
elif a == '--help':
self._help()
else:
lo, macro, value, i = _process_lopt(a, i,
command_line._long_true_opts,
self.args)
if lo:
self.defaults[macro] = '1'
self.opts[lo[2:]] = '1'
else:
lo, macro, value, i = _process_lopt(a, i,
command_line._long_opts,
self.args, True)
if lo:
self.defaults[macro] = value
self.opts[lo[2:]] = value
else:
#
# The target triplet is 'cpu-vendor-os'.
#
lo, macro, value, i = _process_lopt(a, i,
command_line._target_triplets,
self.args, True)
if lo:
#
# This is a target triplet. Run it past config.sub to make
# make sure it is ok.
#
e = execute.capture_execution()
config_sub = os.path.join(self.command_path,
basepath, 'config.sub')
exit_code, proc, output = e.shell(config_sub + ' ' + value)
if exit_code == 0:
value = output
self.defaults[macro] = value
self.opts[lo[2:]] = value
_arch = macro + '_cpu'
_vendor = macro + '_vendor'
_os = macro + '_os'
_arch_value = ''
_vendor_value = ''
_os_value = ''
dash = value.find('-')
if dash >= 0:
_arch_value = value[:dash]
value = value[dash + 1:]
dash = value.find('-')
if dash >= 0:
_vendor_value = value[:dash]
value = value[dash + 1:]
if len(value):
_os_value = value
self.defaults[_arch] = _arch_value
self.defaults[_vendor] = _vendor_value
self.defaults[_os] = _os_value
if not lo:
raise error.general('invalid argument: ' + a)
else:
if a == '-n':
self.opts['dry-run'] = '1'
elif a == '-q':
self.opts['quiet'] = '1'
elif a == '-?':
self._help()
else:
self.opts['params'].append(a)
i += 1
def _post_process(self, _defaults):
if self.no_smp():
_defaults['_smp_mflags'] = _defaults['nil']
if _defaults['_host'] == _defaults['nil']:
raise error.general('host not set')
return _defaults
def expand(self, s, _defaults):
"""Simple basic expander of config file macros."""
mf = re.compile(r'%{[^}]+}')
expanded = True
while expanded:
expanded = False
for m in mf.findall(s):
name = m[2:-1]
if name in _defaults:
s = s.replace(m, _defaults[name])
expanded = True
else:
raise error.general('cannot process default macro: ' + m)
return s
def command(self):
return os.path.join(self.command_path, self.command_name)
def dry_run(self):
return self.opts['dry-run'] != '0'
def set_dry_run(self):
self.opts['dry-run'] = '1'
def quiet(self):
return self.opts['quiet'] != '0'
def trace(self):
return self.opts['trace'] != '0'
def warn_all(self):
return self.opts['warn-all'] != '0'
def no_clean(self):
return self.opts['no-clean'] != '0'
def no_smp(self):
return self.opts['no-smp'] != '0'
def rebuild(self):
return self.opts['rebuild'] != '0'
def params(self):
return self.opts['params']
def get_config_files(self, config):
if config.find('*') >= 0 or config.find('?'):
configdir = os.path.dirname(config)
configbase = os.path.basename(config)
if len(configbase) == 0:
configbase = '*'
if len(configdir) == 0:
configdir = self.expand(defaults['_configdir'], defaults)
if not os.path.isdir(configdir):
raise error.general('configdir is not a directory or does not exist: ' + configdir)
files = glob.glob(os.path.join(configdir, configbase))
configs = files
else:
configs = [config]
return configs
def config_files(self):
configs = []
for config in self.opts['params']:
configs.extend(self.get_config_files(config))
return configs
def logfiles(self):
if 'log' in self.opts:
return self.opts['log'].split(',')
return ['stdout']
def urls(self):
if 'url' in self.opts:
return self.opts['url'].split(',')
return None
def prefixbase(self):
if 'prefixbase' in self.opts:
return self.opts['prefixbase']
return None
def load(args):
"""
Copy the defaults, get the host specific values and merge them overriding
any matching defaults, then create an options object to handle the command
line merging in any command line overrides. Finally post process the
command line.
"""
d = defaults
overrides = None
if os.name == 'nt':
import windows
overrides = windows.load()
else:
uname = os.uname()
try:
if uname[0] == 'Darwin':
import darwin
overrides = darwin.load()
elif uname[0] == 'FreeBSD':
import freebsd
overrides = freebsd.load()
elif uname[0] == 'Linux':
import linux
overrides = linux.load()
except:
pass
if overrides is None:
raise error.general('no hosts defaults found; please add')
for k in overrides:
d[k] = overrides[k]
o = command_line(args)
for k in o.defaults:
d[k] = o.defaults[k]
d = o._post_process(d)
return o, d
if __name__ == '__main__':
import sys
try:
_opts, _defaults = load(args = sys.argv)
print _opts
pprint.pprint(_defaults)
except error.general, gerr:
print gerr
sys.exit(1)
except error.internal, ierr:
print ierr
sys.exit(1)
sys.exit(0)

54
tb/error.py Normal file
View File

@ -0,0 +1,54 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Various errors we can raise.
#
class error(Exception):
"""Base class for Builder exceptions."""
def set_output(self, msg):
self.msg = msg
def __str__(self):
return self.msg
class general(error):
"""Raise for a general error."""
def __init__(self, what):
self.set_output('error: ' + what)
class internal(error):
"""Raise for an internal error."""
def __init__(self, what):
self.set_output('internal error: ' + what)
class exit(error):
"""Raise for to exit."""
def __init__(self):
pass
if __name__ == '__main__':
try:
raise general('a general error')
except general, gerr:
print 'caught:', gerr
try:
raise internal('an internal error')
except internal, ierr:
print 'caught:', ierr

363
tb/execute.py Executable file
View File

@ -0,0 +1,363 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Execute commands or scripts.
#
# Note, the subprocess module is only in Python 2.4 or higher.
#
import os
import re
import sys
import subprocess
import threading
import error
import log
# Redefine the PIPE from subprocess
PIPE = subprocess.PIPE
# Regular expression to find quotes.
qstr = re.compile('[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"')
def check_type(command):
"""Checks the type of command we have. The types are spawn and
shell."""
if command in ['spawn', 'shell']:
return True
return False
def arg_list(args):
"""Turn a string of arguments into a list suitable for
spawning a command. If the args are already a list return
it."""
if type(args) is list:
return args
argstr = args
args = []
while len(argstr):
qs = qstr.search(argstr)
if not qs:
args.extend(argstr.split())
argstr= ''
else:
# We have a quoted string. Get the string before
# the quoted string and splt on white space then
# add the quoted string as an option then remove
# the first + quoted string and try again
front = argstr[:qs.start()]
args.extend(front.split())
args.append(argstr[qs.start() + 1:qs.end() - 1])
argstr = argstr[qs.end():]
return args
def arg_subst(command, substs):
"""Substitute the %[0-9] in the command with the subst values."""
args = arg_list(command)
if substs:
for a in range(0, len(args)):
for r in range(0, len(substs)):
args[a] = re.compile(('%%%d' % (r))).sub(substs[r], args[a])
return args
def arg_subst_str(command, subst):
cmd = arg_subst(command, subst)
def add(x, y): return x + ' ' + str(y)
return reduce(add, cmd, '')
class execute:
"""Execute commands or scripts. The 'output' is a funtion
that handles the output from the process."""
def __init__(self, output = None, error_prefix = '', verbose = False):
self.output = output
self.error_prefix = error_prefix
self.verbose = verbose
self.shell_exe = None
self.shell_commands = False
self.path = None
self.environment = None
def capture(self, proc, timeout = None):
"""Create 2 threads to read stdout and stderr and send to the
output handler. Based on the 'communicate' code in the subprocess
module."""
def _readthread(fh, out, prefix = ''):
"""Read from a file handle and write to the output handler
until the file closes."""
while True:
line = fh.readline()
if len(line) == 0:
break
if out:
out(prefix + line)
else:
log.output(prefix + line)
def _timerthread(proc, timer):
"""Timer thread calls the timer handler if one
is present once a second. The user provides a handler
and returns False to kill the process or True continue."""
while True:
time.sleep(1)
if not timer(proc):
proc.stdout.close()
proc.stderr.close()
if proc.stdout:
stdout_thread = threading.Thread(target = _readthread,
args = (proc.stdout,
self.output,
''))
stdout_thread.setDaemon(True)
stdout_thread.start()
if proc.stderr:
stderr_thread = threading.Thread(target = _readthread,
args = (proc.stderr,
self.output,
self.error_prefix))
stderr_thread.setDaemon(True)
stderr_thread.start()
if proc.stdout:
stdout_thread.join()
if proc.stderr:
stderr_thread.join()
return proc.wait()
def open(self, command, capture = True, shell = False,
cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
"""Open a command with arguments. Provide the arguments as a list or
a string."""
if self.verbose:
s = command
if type(command) is list:
def add(x, y): return x + ' ' + str(y)
s = reduce(add, command, '')[1:]
what = 'spawn'
if shell:
what = 'shell'
log.output(what + ': ' + s)
if shell and self.shell_exe:
command = arg_list(command)
command[:0] = self.shell_exe
if not stdout:
stdout = subprocess.PIPE
if not stderr:
stderr = subprocess.PIPE
proc = None
if cwd is None:
cwd = self.path
if env is None:
env = self.environment
try:
# Work around a problem on Windows with commands that
# have a '.' and no extension. Windows needs the full
# command name.
if sys.platform == "win32" and type(command) is list:
if command[0].find('.') >= 0:
r, e = os.path.splitext(command[0])
if e not in ['.exe', '.com', '.bat']:
command[0] = command[0] + '.exe'
proc = subprocess.Popen(command, shell = shell,
cwd = cwd, env = env,
stdin = stdin, stdout = stdout,
stderr = stderr)
if not capture:
return (0, proc)
exit_code = self.capture(proc)
if self.verbose:
log.output('exit: ' + str(exit_code))
except OSError, ose:
exit_code = ose.errno
if self.verbose:
log.output('exit: ' + str(ose))
return (exit_code, proc)
def spawn(self, command, capture = True, cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
"""Spawn a command with arguments. Provide the arguments as a list or
a string."""
return self.open(command, capture, False, cwd, env,
stdin, stdout, stderr)
def shell(self, command, capture = True, cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
"""Execute a command within a shell context. The command can contain
argumments. The shell is specific to the operating system. For example
it is cmd.exe on Windows XP."""
return self.open(command, capture, True, cwd, env,
stdin, stdout, stderr)
def command(self, command, args = None, capture = True, shell = False,
cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
"""Run the command with the args. The args can be a list
or a string."""
if args and not type(args) is list:
args = arg_list(args)
cmd = [command]
if args:
cmd.extend(args)
return self.open(cmd, capture = capture, shell = shell,
cwd = cwd, env = env,
stdin = stdin, stdout = stdout, stderr = stderr)
def command_subst(self, command, substs, capture = True, shell = False,
cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
"""Run the command from the config data with the
option format string subsituted with the subst variables."""
args = arg_subst(command, substs)
return self.command(args[0], args[1:], capture = capture,
shell = shell or self.shell_commands,
cwd = cwd, env = env,
stdin = stdin, stdout = stdout, stderr = stderr)
def set_shell(self, execute):
"""Set the shell to execute when issuing a shell command."""
args = arg_list(execute)
if len(args) == 0 or not os.path.isfile(args[0]):
raise error.general('could find shell: ' + execute)
self.shell_exe = args
def command_use_shell(self):
"""Force all commands to use a shell. This can be used with set_shell
to allow Unix commands be executed on Windows with a Unix shell such
as Cygwin or MSYS. This may cause piping to fail."""
self.shell_commands = True
def set_output(self, output):
"""Set the output handler. The stdout of the last process in a pipe
line is passed to this handler."""
old_output = self.output
self.output = output
return old_output
def set_path(self, path):
"""Set the path changed to before the child process is created."""
old_path = self.path
self.path = path
return old_path
def set_environ(self, environment):
"""Set the environment passed to the child process when created."""
old_environment = self.environment
self.environment = environment
return old_environment
class capture_execution(execute):
"""Capture all output as a string and return it."""
class _output_snapper:
def __init__(self, log = None, dump = False):
self.output = ''
self.log = log
self.dump = dump
def handler(self, text):
if not self.dump:
if self.log is not None:
self.log.output(text)
else:
self.output += text
def get_and_clear(self):
text = self.output
self.output = ''
return text.strip()
def __init__(self, log = None, dump = False, error_prefix = '', verbose = False):
self.snapper = capture_execution._output_snapper(log = log, dump = dump)
execute.__init__(self, output = self.snapper.handler,
error_prefix = error_prefix,
verbose = verbose)
def open(self, command, capture = True, shell = False, cwd = None, env = None,
stdin = None, stdout = None, stderr = None):
if not capture:
raise error.general('output capture must be true; leave as default')
#self.snapper.get_and_clear()
exit_code, proc = execute.open(self, command, capture = True, shell = shell,
cwd = cwd, env = env,
stdin = stdin, stdout = stdout, stderr = stderr)
return (exit_code, proc, self.snapper.get_and_clear())
def set_output(self, output):
raise error.general('output capture cannot be overrided')
if __name__ == "__main__":
def run_tests(e, commands, use_shell):
for c in commands['shell']:
e.shell(c)
for c in commands['spawn']:
e.spawn(c)
for c in commands['cmd']:
if type(c) is str:
e.command(c, shell = use_shell)
else:
e.command(c[0], c[1], shell = use_shell)
for c in commands['csubsts']:
e.command_subst(c[0], c[1], shell = use_shell)
ec, proc = e.command(commands['pipe'][0], commands['pipe'][1],
capture = False, stdin = subprocess.PIPE)
if ec == 0:
print 'piping input into ' + commands['pipe'][0] + ': ' + \
commands['pipe'][2]
proc.stdin.write(commands['pipe'][2])
proc.stdin.close()
e.capture(proc)
del proc
cmd_shell_test = 'if "%OS%" == "Windows_NT" (echo It is WinNT) else echo Is is not WinNT'
sh_shell_test = 'x="me"; if [ $x = "me" ]; then echo "It was me"; else "It was him"; fi'
commands = {}
commands['windows'] = {}
commands['unix'] = {}
commands['windows']['shell'] = ['cd', 'dir /w', '.\\xyz', cmd_shell_test]
commands['windows']['spawn'] = ['hostname', 'hostnameZZ', ['netstat', '/e']]
commands['windows']['cmd'] = [('ipconfig'), ('nslookup', 'www.python.org')]
commands['windows']['csubsts'] = [('netstat %0', ['-a']),
('netstat %0 %1', ['-a', '-n'])]
commands['windows']['pipe'] = ('ftp', None, 'help\nquit')
commands['unix']['shell'] = ['pwd', 'ls -las', './xyz', sh_shell_test]
commands['unix']['spawn'] = ['ls', 'execute.pyc', ['ls', '-i']]
commands['unix']['cmd'] = [('date'), ('date', '-R'), ('date', ['-u', '+%d %D']),
('date', '-u "+%d %D %S"')]
commands['unix']['csubsts'] = [('date %0 "+%d %D %S"', ['-u']),
('date %0 %1', ['-u', '+%d %D %S'])]
commands['unix']['pipe'] = ('grep', 'hello', 'hello world')
print arg_list('cmd a1 a2 "a3 is a string" a4')
print arg_list('cmd b1 b2 "b3 is a string a4')
print arg_subst(['nothing', 'xx-%0-yyy', '%1', '%2-something'],
['subst0', 'subst1', 'subst2'])
e = execute(error_prefix = 'ERR: ', verbose = True)
if sys.platform == "win32":
run_tests(e, commands['windows'], False)
if os.path.exists('c:\\msys\\1.0\\bin\\sh.exe'):
e.set_shell('c:\\msys\\1.0\\bin\\sh.exe --login -c')
commands['unix']['pipe'] = ('c:\\msys\\1.0\\bin\\grep',
'hello', 'hello world')
run_tests(e, commands['unix'], True)
else:
run_tests(e, commands['unix'], False)
del e

63
tb/linux.py Normal file
View File

@ -0,0 +1,63 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# This code is based on what ever doco about spec files I could find and
# RTEMS project's spec files.
#
import pprint
import os
import execute
def load():
uname = os.uname()
smp_mflags = ''
processors = '/bin/grep processor /proc/cpuinfo'
e = execute.capture_execution()
exit_code, proc, output = e.shell(processors)
if exit_code == 0:
cpus = 0
for l in output.split('\n'):
count = l.split(':')[1].strip()
if count > cpus:
cpus = int(count)
if cpus > 0:
smp_mflags = '-j%d' % (cpus)
defines = {
'_os': 'linux',
'_host': uname[4] + '-linux-gnu',
'_host_vendor': 'gnu',
'_host_os': 'linux',
'_host_cpu': uname[4],
'_host_alias': '%{nil}',
'_host_arch': uname[4],
'_usr': '/usr',
'_var': '/usr/var',
'optflags': '-O2 -fasynchronous-unwind-tables',
'_smp_mflags': smp_mflags,
'__bzip2': '/usr/bin/bzip2',
'__gzip': '/bin/gzip',
'__tar': '/bin/tar'
}
return defines
if __name__ == '__main__':
pprint.pprint(load())

111
tb/log.py Executable file
View File

@ -0,0 +1,111 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-testing'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Log output to stdout and/or a file.
#
import os
import sys
import error
#
# A global log.
#
default = None
def set_default_once(log):
if default is None:
default = log
def output(text = os.linesep, log = None):
"""Output the text to a log if provided else send it to stdout."""
if text is None:
text = os.linesep
if type(text) is list:
_text = ''
for l in text:
_text += l + os.linesep
text = _text
if log:
log.output(text)
elif default is not None:
default.output(text)
else:
for l in text.replace(chr(13), '').splitlines():
print l
def flush(log = None):
if log:
log.flush()
elif default is not None:
default.flush()
class log:
"""Log output to stdout or a file."""
def __init__(self, streams = None):
self.fhs = [None, None]
if streams:
for s in streams:
if s == 'stdout':
self.fhs[0] = sys.stdout
elif s == 'stderr':
self.fhs[1] = sys.stderr
else:
try:
self.fhs.append(file(s, 'w'))
except IOError, ioe:
raise error.general("creating log file '" + s + \
"': " + str(ioe))
def __del__(self):
for f in range(2, len(self.fhs)):
self.fhs[f].close()
def has_stdout(self):
return self.fhs[0] is not None
def has_stderr(self):
return self.fhs[1] is not None
def output(self, text):
"""Output the text message to all the logs."""
# Reformat the text to have local line types.
out = ''
for l in text.replace(chr(13), '').splitlines():
out += l + os.linesep
for f in range(0, len(self.fhs)):
if self.fhs[f] is not None:
self.fhs[f].write(out)
def flush(self):
"""Flush the output."""
for f in range(0, len(self.fhs)):
if self.fhs[f] is not None:
self.fhs[f].flush()
if __name__ == "__main__":
l = log(['stdout', 'log.txt'])
for i in range(0, 10):
l.output('hello world: %d\n' % (i))
l.output('hello world CRLF\r\n')
l.output('hello world NONE')
l.flush()
del l

65
tb/windows.py Normal file
View File

@ -0,0 +1,65 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Windows specific support and overrides.
#
import pprint
import os
import execute
def load():
uname = 'win32'
if os.environ.has_key('NUMBER_OF_PROCESSORS'):
ncpus = int(os.environ['NUMBER_OF_PROCESSORS'])
else:
ncpus = 0
if ncpus > 1:
smp_mflags = '-j' + str(ncpus)
else:
smp_mflags = ''
if os.environ.has_key('HOSTTYPE'):
hosttype = os.environ['HOSTTYPE']
else:
hosttype = 'i686'
system = 'mingw32'
defines = {
'_os': 'win32',
'_host': hosttype + '-pc-' + system,
'_host_vendor': 'microsoft',
'_host_os': 'win32',
'_host_cpu': hosttype,
'_host_alias': '%{nil}',
'_host_arch': hosttype,
'_usr': '/opt/local',
'_var': '/opt/local/var',
'optflags': '-O2 -fasynchronous-unwind-tables',
'_smp_mflags': smp_mflags,
'__sh': 'sh',
'_buildshell': '%{__sh}',
'___setup_shell': '%{__sh}',
# Build flags
'optflags': '-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions --param=ssp-buffer-size=4 -mms-bitfields'
}
Return defines
if __name__ == '__main__':
pprint.pprint(load())