OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Does one of the following depending on the --mode argument: | 6 """Does one of the following depending on the --mode argument: |
7 check Verifies all the inputs exist, touches the file specified with | 7 check Verifies all the inputs exist, touches the file specified with |
8 --result and exits. | 8 --result and exits. |
9 hashtable Puts a manifest file and hard links each of the inputs into the | 9 hashtable Puts a manifest file and hard links each of the inputs into the |
10 output directory. | 10 output directory. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 cmd = args | 67 cmd = args |
68 args = [] | 68 args = [] |
69 if files: | 69 if files: |
70 args = [ | 70 args = [ |
71 i.decode('utf-8') for i in open(files, 'rb').read().splitlines() if i | 71 i.decode('utf-8') for i in open(files, 'rb').read().splitlines() if i |
72 ] + args | 72 ] + args |
73 cwd = os.getcwd() | 73 cwd = os.getcwd() |
74 return [relpath(os.path.join(cwd, arg), root) for arg in args], cmd | 74 return [relpath(os.path.join(cwd, arg), root) for arg in args], cmd |
75 | 75 |
76 | 76 |
77 def isolate(outdir, resultfile, indir, infiles, mode, read_only, cmd): | 77 def isolate(outdir, resultfile, indir, infiles, mode, read_only, cmd, no_save): |
78 """Main function to isolate a target with its dependencies. | 78 """Main function to isolate a target with its dependencies. |
79 | 79 |
80 Arguments: | 80 Arguments: |
81 - outdir: Output directory where the result is stored. Depends on |mode|. | 81 - outdir: Output directory where the result is stored. Depends on |mode|. |
82 - resultfile: File to save the json data. | 82 - resultfile: File to save the json data. |
83 - indir: Root directory to be used as the base directory for infiles. | 83 - indir: Root directory to be used as the base directory for infiles. |
84 - infiles: List of files, with relative path, to process. | 84 - infiles: List of files, with relative path, to process. |
85 - mode: Action to do. See file level docstring. | 85 - mode: Action to do. See file level docstring. |
86 - read_only: Makes the temporary directory read only. | 86 - read_only: Makes the temporary directory read only. |
87 - cmd: Command to execute. | 87 - cmd: Command to execute. |
| 88 - no_save: If True, do not touch resultfile. |
88 | 89 |
89 Some arguments are optional, dependending on |mode|. See the corresponding | 90 Some arguments are optional, dependending on |mode|. See the corresponding |
90 MODE<mode> function for the exact behavior. | 91 MODE<mode> function for the exact behavior. |
91 """ | 92 """ |
92 mode_fn = getattr(sys.modules[__name__], 'MODE' + mode) | 93 mode_fn = getattr(sys.modules[__name__], 'MODE' + mode) |
93 assert mode_fn | 94 assert mode_fn |
| 95 assert os.path.isabs(resultfile) |
94 | 96 |
95 infiles = tree_creator.expand_directories( | 97 infiles = tree_creator.expand_directories( |
96 indir, infiles, lambda x: re.match(r'.*\.(svn|pyc)$', x)) | 98 indir, infiles, lambda x: re.match(r'.*\.(svn|pyc)$', x)) |
97 | 99 |
98 # Note the relative current directory. | 100 # Note the relative current directory. |
99 # In general, this path will be the path containing the gyp file where the | 101 # In general, this path will be the path containing the gyp file where the |
100 # target was defined. This relative directory may be created implicitely if a | 102 # target was defined. This relative directory may be created implicitely if a |
101 # file from this directory is needed to run the test. Otherwise it won't be | 103 # file from this directory is needed to run the test. Otherwise it won't be |
102 # created and the process creation will fail. It's up to the caller to create | 104 # created and the process creation will fail. It's up to the caller to create |
103 # this directory manually before starting the test. | 105 # this directory manually before starting the test. |
104 cwd = os.getcwd() | 106 cwd = os.getcwd() |
105 relative_cwd = os.path.relpath(cwd, indir) | 107 relative_cwd = os.path.relpath(cwd, indir) |
106 | 108 |
107 # Workaround make behavior of passing absolute paths. | 109 # Workaround make behavior of passing absolute paths. |
108 cmd = [to_relative(i, indir, cwd) for i in cmd] | 110 cmd = [to_relative(i, indir, cwd) for i in cmd] |
109 | 111 |
110 if not cmd: | 112 if not cmd: |
111 # Note that it is exactly the reverse of relative_cwd. | 113 # Note that it is exactly the reverse of relative_cwd. |
112 cmd = [os.path.join(os.path.relpath(indir, cwd), infiles[0])] | 114 cmd = [os.path.join(os.path.relpath(indir, cwd), infiles[0])] |
113 if cmd[0].endswith('.py'): | 115 if cmd[0].endswith('.py'): |
114 cmd.insert(0, sys.executable) | 116 cmd.insert(0, sys.executable) |
115 | 117 |
116 # Only hashtable mode really needs the sha-1. | 118 # Only hashtable mode really needs the sha-1. |
117 dictfiles = tree_creator.process_inputs( | 119 dictfiles = tree_creator.process_inputs( |
118 indir, infiles, mode == 'hashtable', read_only) | 120 indir, infiles, mode == 'hashtable', read_only) |
119 | 121 |
120 result = mode_fn( | 122 result = mode_fn( |
121 outdir, indir, dictfiles, read_only, cmd, relative_cwd, resultfile) | 123 outdir, indir, dictfiles, read_only, cmd, relative_cwd, resultfile) |
122 | 124 |
123 if result == 0: | 125 if result == 0 and not no_save: |
124 # Saves the resulting file. | 126 # Saves the resulting file. |
125 out = { | 127 out = { |
126 'command': cmd, | 128 'command': cmd, |
127 'relative_cwd': relative_cwd, | 129 'relative_cwd': relative_cwd, |
128 'files': dictfiles, | 130 'files': dictfiles, |
| 131 'read_only': read_only, |
129 } | 132 } |
130 with open(resultfile, 'wb') as f: | 133 with open(resultfile, 'wb') as f: |
131 json.dump(out, f, indent=2, sort_keys=True) | 134 json.dump(out, f, indent=2, sort_keys=True) |
132 return result | 135 return result |
133 | 136 |
134 | 137 |
135 def MODEcheck( | 138 def MODEcheck( |
136 _outdir, _indir, _dictfiles, _read_only, _cmd, _relative_cwd, _resultfile): | 139 _outdir, _indir, _dictfiles, _read_only, _cmd, _relative_cwd, _resultfile): |
137 """No-op.""" | 140 """No-op.""" |
138 return 0 | 141 return 0 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 tree_creator.rmtree(outdir) | 192 tree_creator.rmtree(outdir) |
190 | 193 |
191 | 194 |
192 def MODEtrace( | 195 def MODEtrace( |
193 _outdir, indir, _dictfiles, _read_only, cmd, relative_cwd, resultfile): | 196 _outdir, indir, _dictfiles, _read_only, cmd, relative_cwd, resultfile): |
194 """Shortcut to use trace_inputs.py properly. | 197 """Shortcut to use trace_inputs.py properly. |
195 | 198 |
196 It constructs the equivalent of dictfiles. It is hardcoded to base the | 199 It constructs the equivalent of dictfiles. It is hardcoded to base the |
197 checkout at src/. | 200 checkout at src/. |
198 """ | 201 """ |
199 gyppath = os.path.relpath(relative_cwd, indir) | 202 logging.info('Running %s, cwd=%s' % (cmd, os.path.join(indir, relative_cwd))) |
200 cwd = os.path.join(indir, relative_cwd) | |
201 logging.info('Running %s, cwd=%s' % (cmd, cwd)) | |
202 return trace_inputs.trace_inputs( | 203 return trace_inputs.trace_inputs( |
203 '%s.log' % resultfile, | 204 '%s.log' % resultfile, |
204 cmd, | 205 cmd, |
205 cwd, | |
206 gyppath, | |
207 indir, | 206 indir, |
208 True) | 207 relative_cwd, |
| 208 os.path.dirname(resultfile), # Guesswork here. |
| 209 False) |
209 | 210 |
210 | 211 |
211 def get_valid_modes(): | 212 def get_valid_modes(): |
212 """Returns the modes that can be used.""" | 213 """Returns the modes that can be used.""" |
213 return sorted( | 214 return sorted( |
214 i[4:] for i in dir(sys.modules[__name__]) if i.startswith('MODE')) | 215 i[4:] for i in dir(sys.modules[__name__]) if i.startswith('MODE')) |
215 | 216 |
216 | 217 |
217 def main(): | 218 def main(): |
218 valid_modes = get_valid_modes() | 219 valid_modes = get_valid_modes() |
219 parser = optparse.OptionParser( | 220 parser = optparse.OptionParser( |
220 usage='%prog [options] [inputs] -- [command line]', | 221 usage='%prog [options] [inputs] -- [command line]', |
221 description=sys.modules[__name__].__doc__) | 222 description=sys.modules[__name__].__doc__) |
222 parser.allow_interspersed_args = False | 223 parser.allow_interspersed_args = False |
223 parser.format_description = lambda *_: parser.description | 224 parser.format_description = lambda *_: parser.description |
224 parser.add_option( | 225 parser.add_option( |
225 '-v', '--verbose', action='count', default=0, help='Use multiple times') | 226 '-v', '--verbose', action='count', default=0, help='Use multiple times') |
226 parser.add_option( | 227 parser.add_option( |
227 '--mode', choices=valid_modes, | 228 '--mode', choices=valid_modes, |
228 help='Determines the action to be taken: %s' % ', '.join(valid_modes)) | 229 help='Determines the action to be taken: %s' % ', '.join(valid_modes)) |
229 parser.add_option( | 230 parser.add_option( |
230 '--result', metavar='FILE', | 231 '--result', metavar='FILE', |
231 help='Output file containing the json information about inputs') | 232 help='File containing the json information about inputs') |
232 parser.add_option( | 233 parser.add_option( |
233 '--root', metavar='DIR', help='Base directory to fetch files, required') | 234 '--root', metavar='DIR', help='Base directory to fetch files, required') |
234 parser.add_option( | 235 parser.add_option( |
235 '--outdir', metavar='DIR', | 236 '--outdir', metavar='DIR', |
236 help='Directory used to recreate the tree or store the hash table. ' | 237 help='Directory used to recreate the tree or store the hash table. ' |
237 'For run and remap, uses a /tmp subdirectory. For the other modes, ' | 238 'For run and remap, uses a /tmp subdirectory. For the other modes, ' |
238 'defaults to the directory containing --result') | 239 'defaults to the directory containing --result') |
239 parser.add_option( | 240 parser.add_option( |
240 '--read-only', action='store_true', | 241 '--read-only', action='store_true', default=False, |
241 help='Make the temporary tree read-only') | 242 help='Make the temporary tree read-only') |
242 parser.add_option( | 243 parser.add_option( |
| 244 '--from-results', action='store_true', |
| 245 help='Loads everything from the result file instead of generating it') |
| 246 parser.add_option( |
243 '--files', metavar='FILE', | 247 '--files', metavar='FILE', |
244 help='File to be read containing input files') | 248 help='File to be read containing input files') |
245 | 249 |
246 options, args = parser.parse_args() | 250 options, args = parser.parse_args() |
247 level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)] | 251 level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)] |
248 logging.basicConfig( | 252 logging.basicConfig( |
249 level=level, | 253 level=level, |
250 format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s') | 254 format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s') |
251 | 255 |
252 if not options.root: | 256 if not options.mode: |
253 parser.error('--root is required.') | 257 parser.error('--mode is required') |
| 258 |
254 if not options.result: | 259 if not options.result: |
255 parser.error('--result is required.') | 260 parser.error('--result is required.') |
| 261 if options.from_results: |
| 262 if not options.root: |
| 263 options.root = os.getcwd() |
| 264 if args: |
| 265 parser.error('Arguments cannot be used with --from-result') |
| 266 if options.files: |
| 267 parser.error('--files cannot be used with --from-result') |
| 268 else: |
| 269 if not options.root: |
| 270 parser.error('--root is required.') |
| 271 |
| 272 options.result = os.path.abspath(options.result) |
256 | 273 |
257 # Normalize the root input directory. | 274 # Normalize the root input directory. |
258 indir = os.path.normpath(options.root) | 275 indir = os.path.normpath(options.root) |
259 if not os.path.isdir(indir): | 276 if not os.path.isdir(indir): |
260 parser.error('%s is not a directory' % indir) | 277 parser.error('%s is not a directory' % indir) |
261 | 278 |
262 # Do not call abspath until it was verified the directory exists. | 279 # Do not call abspath until it was verified the directory exists. |
263 indir = os.path.abspath(indir) | 280 indir = os.path.abspath(indir) |
264 | 281 |
265 logging.info('sys.argv: %s' % sys.argv) | 282 logging.info('sys.argv: %s' % sys.argv) |
266 logging.info('cwd: %s' % os.getcwd()) | 283 logging.info('cwd: %s' % os.getcwd()) |
267 logging.info('Args: %s' % args) | 284 logging.info('Args: %s' % args) |
268 infiles, cmd = separate_inputs_command(args, indir, options.files) | 285 if not options.from_results: |
269 if not infiles: | 286 infiles, cmd = separate_inputs_command(args, indir, options.files) |
270 parser.error('Need at least one input file to map') | 287 if not infiles: |
| 288 parser.error('Need at least one input file to map') |
| 289 else: |
| 290 data = json.load(open(options.result)) |
| 291 cmd = data['command'] |
| 292 infiles = data['files'].keys() |
| 293 os.chdir(data['relative_cwd']) |
| 294 |
271 logging.info('infiles: %s' % infiles) | 295 logging.info('infiles: %s' % infiles) |
272 | 296 |
273 try: | 297 try: |
274 return isolate( | 298 return isolate( |
275 options.outdir, | 299 options.outdir, |
276 os.path.abspath(options.result), | 300 options.result, |
277 indir, | 301 indir, |
278 infiles, | 302 infiles, |
279 options.mode, | 303 options.mode, |
280 options.read_only, | 304 options.read_only, |
281 cmd) | 305 cmd, |
| 306 options.from_results) |
282 except tree_creator.MappingError, e: | 307 except tree_creator.MappingError, e: |
283 print >> sys.stderr, str(e) | 308 print >> sys.stderr, str(e) |
284 return 1 | 309 return 1 |
285 | 310 |
286 | 311 |
287 if __name__ == '__main__': | 312 if __name__ == '__main__': |
288 sys.exit(main()) | 313 sys.exit(main()) |
OLD | NEW |