1# Copyright (c) 2009 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7#     * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#     * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13#     * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29from webkitpy.tool.commands.queues import AbstractReviewQueue
30from webkitpy.common.config.committers import CommitterList
31from webkitpy.common.config.ports import WebKitPort
32from webkitpy.common.system.executive import ScriptError
33from webkitpy.tool.bot.queueengine import QueueEngine
34
35
36class AbstractEarlyWarningSystem(AbstractReviewQueue):
37    _build_style = "release"
38
39    def __init__(self):
40        AbstractReviewQueue.__init__(self)
41        self.port = WebKitPort.port(self.port_name)
42
43    def should_proceed_with_work_item(self, patch):
44        return True
45
46    def _can_build(self):
47        try:
48            self.run_webkit_patch([
49                "build",
50                self.port.flag(),
51                "--build-style=%s" % self._build_style,
52                "--force-clean",
53                "--no-update"])
54            return True
55        except ScriptError, e:
56            failure_log = self._log_from_script_error_for_upload(e)
57            self._update_status("Unable to perform a build", results_file=failure_log)
58            return False
59
60    def _build(self, patch, first_run=False):
61        try:
62            args = [
63                "build-attachment",
64                self.port.flag(),
65                "--build",
66                "--build-style=%s" % self._build_style,
67                "--force-clean",
68                "--quiet",
69                "--non-interactive",
70                patch.id()]
71            if not first_run:
72                # See commit-queue for an explanation of what we're doing here.
73                args.append("--no-update")
74                args.append("--parent-command=%s" % self.name)
75            self.run_webkit_patch(args)
76            return True
77        except ScriptError, e:
78            if first_run:
79                return False
80            raise
81
82    def review_patch(self, patch):
83        if patch.is_obsolete():
84            self._did_error(patch, "%s does not process obsolete patches." % self.name)
85            return False
86
87        if patch.bug().is_closed():
88            self._did_error(patch, "%s does not process patches on closed bugs." % self.name)
89            return False
90
91        if not self._build(patch, first_run=True):
92            if not self._can_build():
93                return False
94            self._build(patch)
95        return True
96
97    @classmethod
98    def handle_script_error(cls, tool, state, script_error):
99        is_svn_apply = script_error.command_name() == "svn-apply"
100        status_id = cls._update_status_for_script_error(tool, state, script_error, is_error=is_svn_apply)
101        if is_svn_apply:
102            QueueEngine.exit_after_handled_error(script_error)
103        results_link = tool.status_server.results_url_for_status(status_id)
104        message = "Attachment %s did not build on %s:\nBuild output: %s" % (state["patch"].id(), cls.port_name, results_link)
105        tool.bugs.post_comment_to_bug(state["patch"].bug_id(), message, cc=cls.watchers)
106        exit(1)
107
108
109class GtkEWS(AbstractEarlyWarningSystem):
110    name = "gtk-ews"
111    port_name = "gtk"
112    watchers = AbstractEarlyWarningSystem.watchers + [
113        "gns@gnome.org",
114        "xan.lopez@gmail.com",
115    ]
116
117
118class EflEWS(AbstractEarlyWarningSystem):
119    name = "efl-ews"
120    port_name = "efl"
121    watchers = AbstractEarlyWarningSystem.watchers + [
122        "leandro@profusion.mobi",
123        "antognolli@profusion.mobi",
124        "lucas.demarchi@profusion.mobi",
125        "gyuyoung.kim@samsung.com",
126    ]
127
128
129class QtEWS(AbstractEarlyWarningSystem):
130    name = "qt-ews"
131    port_name = "qt"
132
133
134class WinEWS(AbstractEarlyWarningSystem):
135    name = "win-ews"
136    port_name = "win"
137    # Use debug, the Apple Win port fails to link Release on 32-bit Windows.
138    # https://bugs.webkit.org/show_bug.cgi?id=39197
139    _build_style = "debug"
140
141
142class AbstractChromiumEWS(AbstractEarlyWarningSystem):
143    port_name = "chromium"
144    watchers = AbstractEarlyWarningSystem.watchers + [
145        "dglazkov@chromium.org",
146    ]
147
148
149class ChromiumLinuxEWS(AbstractChromiumEWS):
150    # FIXME: We should rename this command to cr-linux-ews, but that requires
151    #        a database migration. :(
152    name = "chromium-ews"
153
154
155class ChromiumWindowsEWS(AbstractChromiumEWS):
156    name = "cr-win-ews"
157
158
159# For platforms that we can't run inside a VM (like Mac OS X), we require
160# patches to be uploaded by committers, who are generally trustworthy folk. :)
161class AbstractCommitterOnlyEWS(AbstractEarlyWarningSystem):
162    def __init__(self, committers=CommitterList()):
163        AbstractEarlyWarningSystem.__init__(self)
164        self._committers = committers
165
166    def process_work_item(self, patch):
167        if not self._committers.committer_by_email(patch.attacher_email()):
168            self._did_error(patch, "%s cannot process patches from non-committers :(" % self.name)
169            return False
170        return AbstractEarlyWarningSystem.process_work_item(self, patch)
171
172
173# FIXME: Inheriting from AbstractCommitterOnlyEWS is kinda a hack, but it
174# happens to work because AbstractChromiumEWS and AbstractCommitterOnlyEWS
175# provide disjoint sets of functionality, and Python is otherwise smart
176# enough to handle the diamond inheritance.
177class ChromiumMacEWS(AbstractChromiumEWS, AbstractCommitterOnlyEWS):
178    name = "cr-mac-ews"
179
180
181class MacEWS(AbstractCommitterOnlyEWS):
182    name = "mac-ews"
183    port_name = "mac"
184