source: rtems-tools/specbuilder/specbuilder/build.py @ a86cd82

4.104.11
Last change on this file since a86cd82 was a86cd82, checked in by Chris Johns <chrisj@…>, on Jun 9, 2011 at 5:59:32 AM

2011-06-09 Chris Johns <chrisj@…>

  • specbuilder/specbuilder/build.py, specbuilder/specbuilder/crossgcc.py, specbuilder/specbuilder/defaults.py, specbuilder/specbuilder/linux.py, specbuilder/specbuilder/spec.py: Add CentOS support for older Pythons. Add options to build the tools with specific flags.
  • Property mode set to 100644
File size: 14.4 KB
Line 
1#
2# $Id$
3#
4# RTEMS Tools Project (http://www.rtems.org/)
5# Copyright 2010 Chris Johns (chrisj@rtems.org)
6# All rights reserved.
7#
8# This file is part of the RTEMS Tools package in 'rtems-tools'.
9#
10# RTEMS Tools is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# RTEMS Tools is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with RTEMS Tools.  If not, see <http://www.gnu.org/licenses/>.
22#
23
24#
25# This code builds a package given a spec file. It only builds to be installed
26# not to be package unless you run a packager around this.
27#
28
29import getopt
30import os
31import shutil
32import stat
33import sys
34import urllib2
35import urlparse
36
37import defaults
38import error
39import execute
40import log
41import spec
42
43#
44# Version of Spec Builder.
45#
46version = '0.1'
47
48def _notice(opts, text):
49    if not opts.quiet() and not log.default.has_stdout():
50        print text
51    log.output(text)
52    log.flush()
53
54class script:
55    """Create and manage a shell script."""
56
57    def __init__(self, quiet = True):
58        self.quiet = quiet
59        self.reset()
60
61    def reset(self):
62        self.body = []
63        self.lc = 0
64
65    def append(self, text):
66        if type(text) is str:
67            text = text.splitlines()
68        if not self.quiet:
69            i = 0
70            for l in text:
71                i += 1
72                log.output('script:%3d: ' % (self.lc + i) + l)
73        self.lc += len(text)
74        self.body.extend(text)
75
76    def write(self, name, check_for_errors = False):
77        s = None
78        try:
79            s = open(name, 'w')
80            s.write('\n'.join(self.body))
81            s.close()
82            os.chmod(name, stat.S_IRWXU | \
83                         stat.S_IRGRP | stat.S_IXGRP | \
84                         stat.S_IROTH | stat.S_IXOTH)
85        except IOError, err:
86            raise error.general('creating script: ' + name)
87        except:
88            if s is not None:
89                s.close()
90            raise
91        if s is not None:
92            s.close()
93
94class build:
95    """Build a package given a spec file."""
96
97    def __init__(self, name, _defaults, opts):
98        self.opts = opts
99        _notice(opts, 'building: ' + name)
100        self.spec = spec.file(name, _defaults = _defaults, opts = opts)
101        self.script = script(quiet = opts.quiet())
102
103    def _output(self, text):
104        if not self.opts.quiet():
105            log.output(text)
106
107    def rmdir(self, path):
108        if not self.opts.dry_run():
109            self._output('removing: ' + path)
110            if os.path.exists(path):
111                try:
112                    shutil.rmtree(path)
113                except IOError, err:
114                    raise error.error('error removing: ' + path)
115
116    def mkdir(self, path):
117        if not self.opts.dry_run():
118            self._output('making dir: ' + path)
119            try:
120                os.makedirs(path)
121            except IOError, err:
122                raise error.general('error creating path: ' + path)
123
124    def get_file(self, url, local):
125        if not os.path.isdir(os.path.dirname(local)):
126            raise error.general('source path not found: ' + os.path.dirname(local))
127        if not os.path.exists(local):
128            #
129            # Not localy found so we need to download it. Check if a URL
130            # has been provided on the command line.
131            #
132            url_bases = self.opts.urls()
133            urls = []
134            if url_bases is not None:
135                for base in url_bases:
136                    if base[-1:] != '/':
137                        base += '/'
138                    url_path = urlparse.urlsplit(url)[2]
139                    slash = url_path.rfind('/')
140                    if slash < 0:
141                        url_file = url_path
142                    else:
143                        url_file = url_path[slash + 1:]
144                    urls.append(urlparse.urljoin(base, url_file))
145            urls.append(url)
146            for url in urls:
147                _notice(self.opts, 'download: ' + url + ' -> ' + local)
148                if not self.opts.dry_run():
149                    failed = False
150                    _in = None
151                    _out = None
152                    try:
153                        _in = urllib2.urlopen(url)
154                        _out = open(local, 'wb')
155                        _out.write(_in.read())
156                    except IOError, err:
157                        _notice(self.opts, 'download: ' + url + ': failed: ' + str(err))
158                        if os.path.exists(local):
159                            os.remove(local)
160                        failed = True
161                    except:
162                        if _out is not None:
163                            _out.close()
164                        raise
165                    if _out is not None:
166                        _out.close()
167                    if _in is not None:
168                        del _in
169                    if not failed:
170                        if not os.path.isfile(local):
171                            raise error.general('source is not a file: ' + local)
172                        return
173            raise error.general('downloading ' + url + ': all paths have failed, giving up')
174
175    def parse_url(self, url):
176        #
177        # Split the source up into the parts we need.
178        #
179        source = {}
180        source['url'] = url
181        source['path'] = os.path.dirname(url)
182        source['file'] = os.path.basename(url)
183        source['name'], source['ext'] = os.path.splitext(source['file'])
184        #
185        # Get the file. Checks the local source directory first.
186        #
187        source['local'] = os.path.join(self.spec.abspath('_sourcedir'), 
188                                       source['file'])
189        #
190        # Is the file compressed ?
191        #
192        esl = source['ext'].split('.')
193        if esl[-1:][0] == 'gz':
194            source['compressed'] = '%{__gzip} -dc'
195        elif esl[-1:][0] == 'bz2':
196            source['compressed'] = '%{__bzip2} -dc'
197        elif esl[-1:][0] == 'bz2':
198            source['compressed'] = '%{__zip} -u'
199        elif esl[-1:][0] == 'xz':
200            source['compressed'] = '%{__xz} -dc'
201        source['script'] = ''
202        return source
203
204    def source(self, package, source_tag):
205        #
206        # Scan the sources found in the spec file for the one we are
207        # after. Infos or tags are lists.
208        #
209        sources = package.sources()
210        url = None
211        for s in sources:
212            tag = s[len('source'):]
213            if tag.isdigit():
214                if int(tag) == source_tag:
215                    url = sources[s][0]
216                    break
217        if url is None:
218            raise error.general('source tag not found: source' + str(source_tag))
219        source = self.parse_url(url)
220        self.get_file(source['url'], source['local'])
221        if 'compressed' in source:
222            source['script'] = source['compressed'] + ' ' + \
223                source['local'] + ' | %{__tar_extract} -'
224        else:
225            source['script'] = '%{__tar_extract} ' + source['local']
226        return source
227
228    def patch(self, package, args):
229        #
230        # Scan the patches found in the spec file for the one we are
231        # after. Infos or tags are lists.
232        #
233        patches = package.patches()
234        url = None
235        for p in patches:
236            if args[0][1:].lower() == p:
237                url = patches[p][0]
238                break
239        if url is None:
240            raise error.general('patch tag not found: ' + args[0])
241        patch = self.parse_url(url)
242        self.get_file(patch['url'], patch['local'])
243        if 'compressed' in patch:
244            patch['script'] = patch['compressed'] + ' ' +  patch['local']
245        else:
246            patch['script'] = '%{__cat} ' + patch['local']
247        patch['script'] += ' | %{__patch} ' + ' '.join(args[1:])
248        self.script.append(self.spec.expand(patch['script']))
249
250    def setup(self, package, args):
251        self._output('prep: ' + package.name() + ': ' + ' '.join(args))
252        opts, args = getopt.getopt(args[1:], 'qDcTn:b:a:')
253        source_tag = 0
254        quiet = False
255        unpack_default_source = True
256        delete_before_unpack = True
257        create_dir = False
258        name = None
259        unpack_before_chdir = True
260        for o in opts:
261            if o[0] == '-q':
262                quiet = True
263            elif o[0] == '-D':
264                delete_before_unpack = False
265            elif o[0] == '-c':
266                create_dir = True
267            elif o[0] == '-T':
268                unpack_default_source = False
269            elif o[0] == '-n':
270                name = o[1]
271            elif o[0] == '-b':
272                unpack_before_chdir = True
273                if not o[1].isdigit():
274                    raise error.general('setup source tag no a number: ' + o[1])
275                source_tag = int(o[1])
276            elif o[0] == '-a':
277                unpack_before_chdir = False
278                source_tag = int(o[1])
279        source0 = None
280        source = self.source(package, source_tag)
281        if name is None:
282            if source:
283                name = source['name']
284            else:
285                name = source0['name']
286        self.script.append(self.spec.expand('cd %{_builddir}'))
287        if delete_before_unpack:
288            self.script.append(self.spec.expand('%{__rm} -rf ' + name))
289        if create_dir:
290            self.script.append(self.spec.expand('%{__mkdir_p} ' + name))
291        #
292        # If -a? then change directory before unpacking.
293        #
294        if not unpack_before_chdir:
295            self.script.append(self.spec.expand('cd ' + name))
296        #
297        # Unpacking the source. Note, treated the same as -a0.
298        #
299        if unpack_default_source and source_tag != 0:
300            source0 = self.source(package, 0)
301            if source0 is None:
302                raise error.general('no setup source0 tag found')
303            self.script.append(self.spec.expand(source0['script']))
304        self.script.append(self.spec.expand(source['script']))
305        if unpack_before_chdir:
306            self.script.append(self.spec.expand('cd ' + name))
307        self.script.append(self.spec.expand('%{__setup_post}'))
308        if create_dir:
309            self.script.append(self.spec.expand('cd ..'))
310
311    def run(self, command, shell_opts = '', cwd = None):
312        e = execute.capture_execution(log = log.default, dump = self.opts.quiet())
313        cmd = self.spec.expand('%{___build_shell} -ex ' + shell_opts + ' ' + command)
314        self._output('run: ' + cmd)
315        exit_code, proc, output = e.shell(cmd, cwd = cwd)
316        if exit_code != 0:
317            raise error.general('shell cmd failed: ' + cmd)
318
319    def builddir(self):
320        builddir = self.spec.abspath('_builddir')
321        self.rmdir(builddir)
322        if not self.opts.dry_run():
323            self.mkdir(builddir)
324
325    def prep(self, package):
326        self.script.append('echo "==> %prep:"')
327        _prep = package.prep()
328        for l in _prep:
329            args = l.split()
330            if args[0] == '%setup':
331                self.setup(package, args)
332            elif args[0].startswith('%patch'):
333                self.patch(package, args)
334            else:
335                self.script.append(' '.join(args))
336
337    def build(self, package):
338        self.script.append('echo "==> %build:"')
339        _build = package.build()
340        for l in _build:
341            args = l.split()
342            self.script.append(' '.join(args))
343
344    def install(self, package):
345        self.script.append('echo "==> %install:"')
346        _install = package.install()
347        for l in _install:
348            args = l.split()
349            self.script.append(' '.join(args))
350
351    def files(self, package):
352        self.script.append('echo "==> %files:"')
353        prefixbase = self.opts.prefixbase()
354        if prefixbase is None:
355            prefixbase = ''
356        inpath = os.path.join('%{buildroot}', prefixbase)
357        self.script.append(self.spec.expand('cd ' + inpath))
358        tar = os.path.join('%{_rpmdir}', package.long_name() + '.tar.bz2')
359        cmd = self.spec.expand('%{__tar} -cf - . ' + '| %{__bzip2} > ' + tar)
360        self.script.append(cmd)
361        self.script.append(self.spec.expand('cd %{_builddir}'))
362
363    def clean(self, package):
364        self.script.append('echo "==> %clean:"')
365        _clean = package.clean()
366        for l in _clean:
367            args = l.split()
368            self.script.append(' '.join(args))
369       
370    def cleanup(self):
371        buildroot = self.spec.abspath('buildroot')
372        builddir = self.spec.abspath('_builddir')
373        self.rmdir(buildroot)
374        self.rmdir(builddir)
375
376    def make(self):
377        packages = self.spec.packages()
378        package = packages['main']
379        name = package.name() + '-' + package.version()
380        _notice(self.opts, 'package: ' + name)
381        self.script.reset()
382        self.script.append(self.spec.expand('%{___build_template}'))
383        self.script.append('echo "=> ' + name + ':"')
384        self.prep(package)
385        self.build(package)
386        self.install(package)
387        self.files(package)
388        if not self.opts.no_clean():
389            self.clean(package)
390        if not self.opts.dry_run():
391            self.builddir()
392            sn = self.spec.expand(os.path.join('%{_builddir}', 'doit'))
393            self._output('write script: ' + sn)
394            self.script.write(sn)
395            _notice(self.opts, 'building: ' + name)
396            self.run(sn)
397        if not self.opts.no_clean():
398            self.cleanup()
399
400    def name(self):
401        packages = self.spec.packages()
402        package = packages['main']
403        return package.name() + '-' + package.version()
404
405def run(args):
406    try:
407        opts, _defaults = defaults.load(args)
408        log.default = log.log(opts.logfiles())
409        _notice(opts, 'RTEMS Tools, Spec Builder, v%s' % (version))
410        for spec_file in opts.spec_files():
411            b = build(spec_file, _defaults = _defaults, opts = opts)
412            b.make()
413            del b
414    except error.general, gerr:
415        print gerr
416        sys.exit(1)
417    except error.internal, ierr:
418        print ierr
419        sys.exit(1)
420    except KeyboardInterrupt:
421        _notice(opts, 'user terminated')
422        sys.exit(1)
423    sys.exit(0)
424
425if __name__ == "__main__":
426    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.