source: rtems-tools/specbuilder/specbuilder/build.py @ 68c9b8a

4.104.115
Last change on this file since 68c9b8a was 68c9b8a, checked in by Chris Johns <chrisj@…>, on 08/04/12 at 12:29:24

Remove CVS Id and update the copyright year.

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