2009-11-26 Adam Barth <abarth@webkit.org>
[WebKit-https.git] / WebKitTools / Scripts / modules / webkitlandingscripts.py
1 #!/usr/bin/env python
2 # Copyright (c) 2009, Google Inc. All rights reserved.
3 # Copyright (c) 2009 Apple Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8
9 #     * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 #     * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 #     * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 import os
32 import StringIO
33 import subprocess
34 import sys
35
36 from optparse import make_option
37
38 from modules.changelogs import ChangeLog
39 from modules.logging import error, log, tee
40 from modules.scm import CommitMessage, detect_scm_system, ScriptError, CheckoutNeedsUpdate
41 from modules.webkitport import WebKitPort
42
43 def commit_message_for_this_commit(scm):
44     changelog_paths = scm.modified_changelogs()
45     if not len(changelog_paths):
46         raise ScriptError(message="Found no modified ChangeLogs, cannot create a commit message.\n"
47                           "All changes require a ChangeLog.  See:\n"
48                           "http://webkit.org/coding/contributing.html")
49
50     changelog_messages = []
51     for changelog_path in changelog_paths:
52         log("Parsing ChangeLog: %s" % changelog_path)
53         changelog_entry = ChangeLog(changelog_path).latest_entry()
54         if not changelog_entry:
55             raise ScriptError(message="Failed to parse ChangeLog: " + os.path.abspath(changelog_path))
56         changelog_messages.append(changelog_entry)
57
58     # FIXME: We should sort and label the ChangeLog messages like commit-log-editor does.
59     return CommitMessage("".join(changelog_messages).splitlines())
60
61
62 class WebKitLandingScripts:
63     @staticmethod
64     def cleaning_options():
65         return [
66             make_option("--force-clean", action="store_true", dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)"),
67             make_option("--no-clean", action="store_false", dest="clean", default=True, help="Don't check if the working directory is clean before applying patches"),
68         ]
69
70     @staticmethod
71     def build_options():
72         return [
73             make_option("--ignore-builders", action="store_false", dest="check_builders", default=True, help="Don't check to see if the build.webkit.org builders are green before landing."),
74             make_option("--quiet", action="store_true", dest="quiet", default=False, help="Produce less console output."),
75             make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible."),
76         ] + WebKitPort.port_options()
77
78     @staticmethod
79     def land_options():
80         return [
81             make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory."),
82             make_option("--no-build", action="store_false", dest="build", default=True, help="Commit without building first, implies --no-test."),
83             make_option("--no-test", action="store_false", dest="test", default=True, help="Commit without running run-webkit-tests."),
84             make_option("--no-close", action="store_false", dest="close_bug", default=True, help="Leave bug open after landing."),
85         ]
86
87     @staticmethod
88     def run_command_with_teed_output(args, teed_output):
89         child_process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
90
91         # Use our own custom wait loop because Popen ignores a tee'd stderr/stdout.
92         # FIXME: This could be improved not to flatten output to stdout.
93         while True:
94             output_line = child_process.stdout.readline()
95             if output_line == "" and child_process.poll() != None:
96                 return child_process.poll()
97             teed_output.write(output_line)
98
99     @staticmethod
100     def run_and_throw_if_fail(args, quiet=False):
101         # Cache the child's output locally so it can be used for error reports.
102         child_out_file = StringIO.StringIO()
103         if quiet:
104             dev_null = open(os.devnull, "w")
105         child_stdout = tee(child_out_file, dev_null if quiet else sys.stdout)
106         exit_code = WebKitLandingScripts.run_command_with_teed_output(args, child_stdout)
107         if quiet:
108             dev_null.close()
109
110         child_output = child_out_file.getvalue()
111         child_out_file.close()
112
113         if exit_code:
114             raise ScriptError(script_args=args, exit_code=exit_code, output=child_output)
115
116     @classmethod
117     def run_webkit_script(cls, script_name, quiet=False, port=WebKitPort):
118         log("Running %s" % script_name)
119         cls.run_and_throw_if_fail(port.script_path(script_name), quiet)
120
121
122
123