| 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 |