blob: 3cb576191c2a4014bc6d792a2fcfb1e9390efe4e [file] [log] [blame]
Aviv Keshetb1238c32013-04-01 11:42:13 -07001#!/usr/bin/python
2
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7
8"""
9Simple script to be run inside the chroot. Used as a fast approximation of
10emerge-$board autotest-all, by simply rsync'ing changes from trunk to sysroot.
11"""
12
13import os
Aviv Keshet787ffcd2013-04-08 15:14:56 -070014import re
Aviv Keshetb1238c32013-04-01 11:42:13 -070015import sys
Aviv Keshet787ffcd2013-04-08 15:14:56 -070016from collections import namedtuple
17
Aviv Keshetb1238c32013-04-01 11:42:13 -070018
19from chromite.buildbot import constants
20from chromite.lib import cros_build_lib
21from chromite.lib import git
22
23import argparse
24
25INCLUDE_PATTERNS_FILENAME = 'autotest-quickmerge-includepatterns'
26AUTOTEST_PROJECT_NAME = 'chromiumos/third_party/autotest'
27
Aviv Keshet787ffcd2013-04-08 15:14:56 -070028
29# Data structure describing a single rsync filesystem change.
30#
31# change_description: An 11 character string, the rsync change description
32# for the particular file.
33# absolute_path: The absolute path of the created or modified file.
34ItemizedChange = namedtuple('ItemizedChange', ['change_description',
35 'absolute_path'])
36
37
38# Data structure describing the rsync new/modified files or directories.
39#
40# new_files: A list of ItemizedChange objects for new files.
41# modified_files: A list of ItemizedChange objects for modified files.
42# new_directories: A list of ItemizedChange objects for new directories.
43ItemizedChangeReport = namedtuple('ItemizedChangeReport',
44 ['new_files', 'modified_files',
45 'new_directories'])
46
47
48def ItemizeChangesFromRsyncOutput(rsync_output, destination_path):
49 """Convert the output of an rsync with `-i` to a ItemizedChangeReport object.
50
51 Arguments:
52 rsync_output: String stdout of rsync command that was run with `-i` option.
53 destination_path: String absolute path of the destination directory for the
54 rsync operations. This argument is necessary because
55 rsync's output only gives the relative path of
56 touched/added files.
57
58 Returns:
59 ItemizedChangeReport object giving the absolute paths of files that were
60 created or modified by rsync.
61 """
62 modified_matches = re.findall(r'([.>]f[^+]{9}) (.*)', rsync_output)
63 new_matches = re.findall(r'(>f\+{9}) (.*)', rsync_output)
64 new_symlink_matches = re.findall(r'(cL\+{9}) (.*) -> .*', rsync_output)
65 new_dir_matches = re.findall(r'(cd\+{9}) (.*)', rsync_output)
66
67 absolute_modified = [ItemizedChange(c, os.path.join(destination_path, f))
68 for (c, f) in modified_matches]
69
70 # Note: new symlinks are treated as new files.
71 absolute_new = [ItemizedChange(c, os.path.join(destination_path, f))
72 for (c, f) in new_matches + new_symlink_matches]
73
74 absolute_new_dir = [ItemizedChange(c, os.path.join(destination_path, f))
75 for (c, f) in new_dir_matches]
76
77 return ItemizedChangeReport(new_files=absolute_new,
78 modified_files=absolute_modified,
79 new_directories=absolute_new_dir)
80
81
Aviv Keshetb1238c32013-04-01 11:42:13 -070082def RsyncQuickmerge(source_path, sysroot_autotest_path,
83 include_pattern_file=None, pretend=False,
Aviv Keshet60968ec2013-04-11 18:44:14 -070084 overwrite=False):
Aviv Keshetb1238c32013-04-01 11:42:13 -070085 """Run rsync quickmerge command, with specified arguments.
86 Command will take form `rsync -a [options] --exclude=**.pyc
87 --exclude=**.pyo
88 [optional --include-from argument]
89 --exclude=* [source_path] [sysroot_autotest_path]`
90
91 Arguments:
92 pretend: True to use the '-n' option to rsync, to perform dry run.
93 overwrite: True to omit '-u' option, overwrite all files in sysroot,
94 not just older files.
Aviv Keshetb1238c32013-04-01 11:42:13 -070095 """
96 command = ['rsync', '-a']
97
98 if pretend:
99 command += ['-n']
100
101 if not overwrite:
102 command += ['-u']
103
Aviv Keshet60968ec2013-04-11 18:44:14 -0700104 command += ['-i']
Aviv Keshetb1238c32013-04-01 11:42:13 -0700105
106 command += ['--exclude=**.pyc']
107 command += ['--exclude=**.pyo']
108
Aviv Keshet787ffcd2013-04-08 15:14:56 -0700109 # Exclude files with a specific substring in their name, because
110 # they create an ambiguous itemized report. (see unit test file for details)
111 command += ['--exclude=** -> *']
112
Aviv Keshetb1238c32013-04-01 11:42:13 -0700113 if include_pattern_file:
114 command += ['--include-from=%s' % include_pattern_file]
115
116 command += ['--exclude=*']
117
118 command += [source_path, sysroot_autotest_path]
119
Aviv Keshet60968ec2013-04-11 18:44:14 -0700120 return cros_build_lib.SudoRunCommand(command, redirect_stdout=True)
Aviv Keshetb1238c32013-04-01 11:42:13 -0700121
122
123def ParseArguments(argv):
124 """Parse command line arguments
125
126 Returns: parsed arguments.
127 """
128 parser = argparse.ArgumentParser(description='Perform a fast approximation '
129 'to emerge-$board autotest-all, by '
130 'rsyncing source tree to sysroot.')
131
132 parser.add_argument('--board', metavar='BOARD', default=None, required=True)
133 parser.add_argument('--pretend', action='store_true',
134 help='Dry run only, do not modify sysroot autotest.')
135 parser.add_argument('--overwrite', action='store_true',
136 help='Overwrite existing files even if newer.')
137 parser.add_argument('--quiet', action='store_true',
138 help='Suppress output of list of modified files.')
139
140 return parser.parse_args(argv)
141
142
143def main(argv):
144 cros_build_lib.AssertInsideChroot()
145
146 args = ParseArguments(argv)
147
148 if not args.board:
149 print "No board specified, and no default board. Aborting."
150 return 1
151
152 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
153 source_path = manifest.GetProjectPath(AUTOTEST_PROJECT_NAME, absolute=True)
154 source_path = os.path.join(source_path, '')
155
156 script_path = os.path.dirname(__file__)
157 include_pattern_file = os.path.join(script_path, INCLUDE_PATTERNS_FILENAME)
158
159 # TODO: Determine the following string programatically.
160 sysroot_autotest_path = os.path.join('/build', args.board, 'usr', 'local',
161 'autotest', '')
162
Aviv Keshet60968ec2013-04-11 18:44:14 -0700163 rsync_output = RsyncQuickmerge(source_path, sysroot_autotest_path,
164 include_pattern_file, args.pretend, args.overwrite)
Aviv Keshetb1238c32013-04-01 11:42:13 -0700165
Aviv Keshet60968ec2013-04-11 18:44:14 -0700166 print rsync_output.output
167
168 change_report = ItemizeChangesFromRsyncOutput(rsync_output.output,
169 sysroot_autotest_path)
170
171 print change_report
Aviv Keshetb1238c32013-04-01 11:42:13 -0700172
173
174if __name__ == '__main__':
175 sys.exit(main(sys.argv))