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

4.104.11
Last change on this file since faa8181 was faa8181, checked in by Chris Johns <chrisj@…>, on Feb 22, 2011 at 3:46:44 AM

2011-02-22 Chris Johns <chrisj@…>

  • specbuilder/sb-versions, specbuilder/specbuilder/version.py: New. Print a suitable version message for automatic documentation updating from the spec files in CVS.
  • specbuilder/specbuilder/build.py: Add xz support. Add a function to return the name of a package.
  • specbuilder/specbuilder/crossgcc.py: Use the build name in the tmp path.
  • specbuilder/specbuilder/darwin.py: Add xz support.
  • specbuilder/specbuilder/defaults.py: Add xz support. Add Windows and Linux support.
  • specbuilder/specbuilder/setup.py: Reference the correct shell opts. Use the shell setup in the version probe command. Fix the version check. Add autotools to the list of spec files to install.
  • specbuilder/specbuilder/spec.py: Add changelog and configure tests. Manage sub-packages better.
  • specbuilder/specbuilder/version.py, specbuilder/specbuilder/windows.py: New.
  • Property mode set to 100644
File size: 14.2 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        finally:
88            if s is not None:
89                s.close()
90
91class build:
92    """Build a package given a spec file."""
93
94    def __init__(self, name, _defaults, opts):
95        self.opts = opts
96        _notice(opts, 'building: ' + name)
97        self.spec = spec.file(name, _defaults = _defaults, opts = opts)
98        self.script = script(quiet = opts.quiet())
99
100    def _output(self, text):
101        if not self.opts.quiet():
102            log.output(text)
103
104    def rmdir(self, path):
105        if not self.opts.dry_run():
106            self._output('removing: ' + path)
107            if os.path.exists(path):
108                try:
109                    shutil.rmtree(path)
110                except IOError, err:
111                    raise error.error('error removing: ' + path)
112
113    def mkdir(self, path):
114        if not self.opts.dry_run():
115            self._output('making dir: ' + path)
116            try:
117                os.makedirs(path)
118            except IOError, err:
119                raise error.general('error creating path: ' + path)
120
121    def get_file(self, url, local):
122        if not os.path.isdir(os.path.dirname(local)):
123            raise error.general('source path not found: ' + os.path.dirname(local))
124        if not os.path.exists(local):
125            #
126            # Not localy found so we need to download it. Check if a URL
127            # has been provided on the command line.
128            #
129            url_bases = self.opts.urls()
130            urls = []
131            if url_bases is not None:
132                for base in url_bases:
133                    if base[-1:] != '/':
134                        base += '/'
135                    url_path = urlparse.urlsplit(url).path
136                    slash = url_path.rfind('/')
137                    if slash < 0:
138                        url_file = url_path
139                    else:
140                        url_file = url_path[slash + 1:]
141                    urls.append(urlparse.urljoin(base, url_file))
142            urls.append(url)
143            for url in urls:
144                _notice(self.opts, 'download: ' + url + ' -> ' + local)
145                if not self.opts.dry_run():
146                    failed = False
147                    _in = None
148                    _out = None
149                    try:
150                        _in = urllib2.urlopen(url)
151                        _out = open(local, 'wb')
152                        _out.write(_in.read())
153                    except IOError, err:
154                        _notice(self.opts, 'download: ' + url + ': failed: ' + str(err))
155                        if os.path.exists(local):
156                            os.remove(local)
157                        failed = True
158                    finally:
159                        if _out is not None:
160                            _out.close()
161                    if _in is not None:
162                        del _in
163                    if not failed:
164                        if not os.path.isfile(local):
165                            raise error.general('source is not a file: ' + local)
166                        return
167            raise error.general('downloading ' + url + ': all paths have failed, giving up')
168
169    def parse_url(self, url):
170        #
171        # Split the source up into the parts we need.
172        #
173        source = {}
174        source['url'] = url
175        source['path'] = os.path.dirname(url)
176        source['file'] = os.path.basename(url)
177        source['name'], source['ext'] = os.path.splitext(source['file'])
178        #
179        # Get the file. Checks the local source directory first.
180        #
181        source['local'] = os.path.join(self.spec.abspath('_sourcedir'), 
182                                       source['file'])
183        #
184        # Is the file compressed ?
185        #
186        esl = source['ext'].split('.')
187        if esl[-1:][0] == 'gz':
188            source['compressed'] = '%{__gzip} -dc'
189        elif esl[-1:][0] == 'bz2':
190            source['compressed'] = '%{__bzip2} -dc'
191        elif esl[-1:][0] == 'bz2':
192            source['compressed'] = '%{__zip} -u'
193        elif esl[-1:][0] == 'xz':
194            source['compressed'] = '%{__xz} -dc'
195        source['script'] = ''
196        return source
197
198    def source(self, package, source_tag):
199        #
200        # Scan the sources found in the spec file for the one we are
201        # after. Infos or tags are lists.
202        #
203        sources = package.sources()
204        url = None
205        for s in sources:
206            tag = s[len('source'):]
207            if tag.isdigit():
208                if int(tag) == source_tag:
209                    url = sources[s][0]
210                    break
211        if url is None:
212            raise error.general('source tag not found: source' + str(source_tag))
213        source = self.parse_url(url)
214        self.get_file(source['url'], source['local'])
215        if 'compressed' in source:
216            source['script'] = source['compressed'] + ' ' + \
217                source['local'] + ' | %{__tar_extract} -'
218        else:
219            source['script'] = '%{__tar_extract} ' + source['local']
220        return source
221
222    def patch(self, package, args):
223        #
224        # Scan the patches found in the spec file for the one we are
225        # after. Infos or tags are lists.
226        #
227        patches = package.patches()
228        url = None
229        for p in patches:
230            if args[0][1:].lower() == p:
231                url = patches[p][0]
232                break
233        if url is None:
234            raise error.general('patch tag not found: ' + args[0])
235        patch = self.parse_url(url)
236        self.get_file(patch['url'], patch['local'])
237        if 'compressed' in patch:
238            patch['script'] = patch['compressed'] + ' ' +  patch['local']
239        else:
240            patch['script'] = '%{__cat} ' + patch['local']
241        patch['script'] += ' | %{__patch} ' + ' '.join(args[1:])
242        self.script.append(self.spec.expand(patch['script']))
243
244    def setup(self, package, args):
245        self._output('prep: ' + package.name() + ': ' + ' '.join(args))
246        opts, args = getopt.getopt(args[1:], 'qDcTn:b:a:')
247        source_tag = 0
248        quiet = False
249        unpack_default_source = True
250        delete_before_unpack = True
251        create_dir = False
252        name = None
253        unpack_before_chdir = True
254        for o in opts:
255            if o[0] == '-q':
256                quiet = True
257            elif o[0] == '-D':
258                delete_before_unpack = False
259            elif o[0] == '-c':
260                create_dir = True
261            elif o[0] == '-T':
262                unpack_default_source = False
263            elif o[0] == '-n':
264                name = o[1]
265            elif o[0] == '-b':
266                unpack_before_chdir = True
267                if not o[1].isdigit():
268                    raise error.general('setup source tag no a number: ' + o[1])
269                source_tag = int(o[1])
270            elif o[0] == '-a':
271                unpack_before_chdir = False
272                source_tag = int(o[1])
273        source0 = None
274        source = self.source(package, source_tag)
275        if name is None:
276            if source:
277                name = source['name']
278            else:
279                name = source0['name']
280        self.script.append(self.spec.expand('cd %{_builddir}'))
281        if delete_before_unpack:
282            self.script.append(self.spec.expand('%{__rm} -rf ' + name))
283        if create_dir:
284            self.script.append(self.spec.expand('%{__mkdir_p} ' + name))
285        #
286        # If -a? then change directory before unpacking.
287        #
288        if not unpack_before_chdir:
289            self.script.append(self.spec.expand('cd ' + name))
290        #
291        # Unpacking the source. Note, treated the same as -a0.
292        #
293        if unpack_default_source and source_tag != 0:
294            source0 = self.source(package, 0)
295            if source0 is None:
296                raise error.general('no setup source0 tag found')
297            self.script.append(self.spec.expand(source0['script']))
298        self.script.append(self.spec.expand(source['script']))
299        if unpack_before_chdir:
300            self.script.append(self.spec.expand('cd ' + name))
301        self.script.append(self.spec.expand('%{__setup_post}'))
302        if create_dir:
303            self.script.append(self.spec.expand('cd ..'))
304
305    def run(self, command, shell_opts = '', cwd = None):
306        e = execute.capture_execution(log = log.default, dump = self.opts.quiet())
307        cmd = self.spec.expand('%{___build_shell} -ex ' + shell_opts + ' ' + command)
308        self._output('run: ' + cmd)
309        exit_code, proc, output = e.shell(cmd, cwd = cwd)
310        if exit_code != 0:
311            raise error.general('shell cmd failed: ' + cmd)
312
313    def builddir(self):
314        builddir = self.spec.abspath('_builddir')
315        self.rmdir(builddir)
316        if not self.opts.dry_run():
317            self.mkdir(builddir)
318
319    def prep(self, package):
320        self.script.append('echo "==> %prep:"')
321        _prep = package.prep()
322        for l in _prep:
323            args = l.split()
324            if args[0] == '%setup':
325                self.setup(package, args)
326            elif args[0].startswith('%patch'):
327                self.patch(package, args)
328            else:
329                self.script.append(' '.join(args))
330
331    def build(self, package):
332        self.script.append('echo "==> %build:"')
333        _build = package.build()
334        for l in _build:
335            args = l.split()
336            self.script.append(' '.join(args))
337
338    def install(self, package):
339        self.script.append('echo "==> %install:"')
340        _install = package.install()
341        for l in _install:
342            args = l.split()
343            self.script.append(' '.join(args))
344
345    def files(self, package):
346        self.script.append('echo "==> %files:"')
347        prefixbase = self.opts.prefixbase()
348        if prefixbase is None:
349            prefixbase = ''
350        inpath = os.path.join('%{buildroot}', prefixbase)
351        self.script.append(self.spec.expand('cd ' + inpath))
352        tar = os.path.join('%{_rpmdir}', package.long_name() + '.tar.bz2')
353        cmd = self.spec.expand('%{__tar} -cf - . ' + '| %{__bzip2} > ' + tar)
354        self.script.append(cmd)
355        self.script.append(self.spec.expand('cd %{_builddir}'))
356
357    def clean(self, package):
358        self.script.append('echo "==> %clean:"')
359        _clean = package.clean()
360        for l in _clean:
361            args = l.split()
362            self.script.append(' '.join(args))
363       
364    def cleanup(self):
365        buildroot = self.spec.abspath('buildroot')
366        builddir = self.spec.abspath('_builddir')
367        self.rmdir(buildroot)
368        self.rmdir(builddir)
369
370    def make(self):
371        packages = self.spec.packages()
372        package = packages['main']
373        name = package.name() + '-' + package.version()
374        _notice(self.opts, 'package: ' + name)
375        self.script.reset()
376        self.script.append(self.spec.expand('%{___build_template}'))
377        self.script.append('echo "=> ' + name + ':"')
378        self.prep(package)
379        self.build(package)
380        self.install(package)
381        self.files(package)
382        if not self.opts.no_clean():
383            self.clean(package)
384        if not self.opts.dry_run():
385            self.builddir()
386            sn = self.spec.expand(os.path.join('%{_builddir}', 'doit'))
387            self._output('write script: ' + sn)
388            self.script.write(sn)
389            _notice(self.opts, 'building: ' + name)
390            self.run(sn)
391        if not self.opts.no_clean():
392            self.cleanup()
393
394    def name(self):
395        packages = self.spec.packages()
396        package = packages['main']
397        return package.name() + '-' + package.version()
398
399def run(args):
400    try:
401        opts, _defaults = defaults.load(args)
402        log.default = log.log(opts.logfiles())
403        _notice(opts, 'RTEMS Tools, Spec Builder, v%s' % (version))
404        for spec_file in opts.spec_files():
405            b = build(spec_file, _defaults = _defaults, opts = opts)
406            b.make()
407            del b
408    except error.general, gerr:
409        print gerr
410        sys.exit(1)
411    except error.internal, ierr:
412        print ierr
413        sys.exit(1)
414    except KeyboardInterrupt:
415        _notice(opts, 'user terminated')
416        sys.exit(1)
417    sys.exit(0)
418
419if __name__ == "__main__":
420    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.