1# This file is part of Buildbot.  Buildbot is free software: you can
2# redistribute it and/or modify it under the terms of the GNU General Public
3# License as published by the Free Software Foundation, version 2.
4#
5# This program is distributed in the hope that it will be useful, but WITHOUT
6# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8# details.
9#
10# You should have received a copy of the GNU General Public License along with
11# this program; if not, write to the Free Software Foundation, Inc., 51
12# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13#
14# Portions Copyright Buildbot Team Members
15# Portions Copyright Dan Radez <dradez+buildbot@redhat.com>
16# Portions Copyright Steve 'Ashcrow' Milner <smilner+buildbot@redhat.com>
17
18import os
19
20from twisted.internet import defer
21
22from buildbot import config
23from buildbot.process import buildstep
24from buildbot.process import logobserver
25
26
27class RpmBuild(buildstep.ShellMixin, buildstep.BuildStep):
28
29    """
30    RpmBuild build step.
31    """
32
33    renderables = ['dist']
34    name = "rpmbuilder"
35    haltOnFailure = 1
36    flunkOnFailure = 1
37    description = ["RPMBUILD"]
38    descriptionDone = ["RPMBUILD"]
39
40    def __init__(self,
41                 specfile=None,
42                 topdir='`pwd`',
43                 builddir='`pwd`',
44                 rpmdir='`pwd`',
45                 sourcedir='`pwd`',
46                 specdir='`pwd`',
47                 srcrpmdir='`pwd`',
48                 dist='.el6',
49                 define=None,
50                 autoRelease=False,
51                 vcsRevision=False,
52                 **kwargs):
53        kwargs = self.setupShellMixin(kwargs, prohibitArgs=['command'])
54        super().__init__(**kwargs)
55
56        self.dist = dist
57
58        self.base_rpmbuild = (
59            ('rpmbuild --define "_topdir {}" --define "_builddir {}"'
60             ' --define "_rpmdir {}" --define "_sourcedir {}"'
61             ' --define "_specdir {}" --define "_srcrpmdir {}"').format(topdir, builddir, rpmdir,
62                                                                        sourcedir, specdir,
63                                                                        srcrpmdir))
64
65        if define is None:
66            define = {}
67        for k, v in define.items():
68            self.base_rpmbuild += " --define \"{} {}\"".format(k, v)
69
70        self.specfile = specfile
71        self.autoRelease = autoRelease
72        self.vcsRevision = vcsRevision
73
74        if not self.specfile:
75            config.error("You must specify a specfile")
76
77        self.addLogObserver(
78            'stdio', logobserver.LineConsumerLogObserver(self.logConsumer))
79
80    @defer.inlineCallbacks
81    def run(self):
82
83        rpm_extras_dict = {}
84        rpm_extras_dict['dist'] = self.dist
85
86        if self.autoRelease:
87            relfile = '{}.release'.format(os.path.basename(self.specfile).split('.')[0])
88            try:
89                with open(relfile, 'r') as rfile:
90                    rel = int(rfile.readline().strip())
91            except (IOError, TypeError, ValueError):
92                rel = 0
93            rpm_extras_dict['_release'] = rel
94            with open(relfile, 'w') as rfile:
95                rfile.write(str(rel + 1))
96
97        if self.vcsRevision:
98            revision = self.getProperty('got_revision')
99            # only do this in the case where there's a single codebase
100            if revision and not isinstance(revision, dict):
101                rpm_extras_dict['_revision'] = revision
102
103        self.rpmbuild = self.base_rpmbuild
104
105        # The unit tests expect a certain order, so we sort the dict to keep
106        # format the same every time
107        for k, v in sorted(rpm_extras_dict.items()):
108            self.rpmbuild = '{0} --define "{1} {2}"'.format(
109                self.rpmbuild, k, v)
110
111        command = '{} -ba {}'.format(self.rpmbuild, self.specfile)
112
113        cmd = yield self.makeRemoteShellCommand(command=command)
114
115        yield self.runCommand(cmd)
116
117        stdio_log = yield self.getLog('stdio')
118        yield stdio_log.finish()
119
120        yield self.addCompleteLog('RPM Command Log', "\n".join(self.rpmcmdlog))
121        if self.rpmerrors:
122            yield self.addCompleteLog('RPM Errors', "\n".join(self.rpmerrors))
123
124        return cmd.results()
125
126    def logConsumer(self):
127        rpm_prefixes = ['Provides:', 'Requires(', 'Requires:',
128                        'Checking for unpackaged', 'Wrote:',
129                        'Executing(%', '+ ', 'Processing files:']
130        rpm_err_pfx = ['   ', 'RPM build errors:', 'error: ']
131        self.rpmcmdlog = []
132        self.rpmerrors = []
133
134        while True:
135            stream, line = yield
136            for pfx in rpm_prefixes:
137                if line.startswith(pfx):
138                    self.rpmcmdlog.append(line)
139                    break
140            for err in rpm_err_pfx:
141                if line.startswith(err):
142                    self.rpmerrors.append(line)
143                    break
144