Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Unified Diff: tools/isolate/isolate.py

Issue 10080013: Makes the code simpler by automatically deducing the root directory to use. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Now completely working on windows Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/cacheinvalidation/cacheinvalidation.gyp ('k') | tools/isolate/isolate_smoke_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/isolate/isolate.py
diff --git a/tools/isolate/isolate.py b/tools/isolate/isolate.py
index 72fa311e773335f4dae58e31252b0f5834df3045..dcf361143f5d8f5db88e0c3500d086833490cf0f 100755
--- a/tools/isolate/isolate.py
+++ b/tools/isolate/isolate.py
@@ -229,76 +229,31 @@ def recreate_tree(outdir, indir, infiles, action):
run_test_from_archive.link_file(outfile, infile, action)
-def isolate(
- outdir, indir, infiles, mode, read_only, cmd, relative_cwd, resultfile):
- """Main function to isolate a target with its dependencies.
-
- Arguments:
- - outdir: Output directory where the result is stored. Depends on |mode|.
- - indir: Root directory to be used as the base directory for infiles.
- - infiles: List of files, with relative path, to process.
- - mode: Action to do. See file level docstring.
- - read_only: Makes the temporary directory read only.
- - cmd: Command to execute.
- - relative_cwd: Directory relative to the base directory where to start the
- command from. In general, this path will be the path
- containing the gyp file where the target was defined. This
- relative directory may be created implicitely if a file from
- this directory is needed to run the test. Otherwise it won't
- be created and the process creation will fail. It's up to the
- caller to create this directory manually before starting the
- test.
- - resultfile: Path where to read and write the metadata.
-
- Some arguments are optional, dependending on |mode|. See the corresponding
- MODE<mode> function for the exact behavior.
- """
- mode_fn = getattr(sys.modules[__name__], 'MODE' + mode)
- assert mode_fn
-
- # Load the previous results as an optimization.
- prevdict = {}
+def load_results(resultfile):
+ """Loads the previous results as an optimization."""
+ data = {}
if resultfile and os.path.isfile(resultfile):
resultfile = os.path.abspath(resultfile)
- with open(resultfile, 'rb') as f:
- prevdict = json.load(f)
+ with open(resultfile, 'r') as f:
+ data = json.load(f)
else:
resultfile = os.path.abspath(resultfile)
+
# Works with native os.path.sep but stores as '/'.
- if 'files' in prevdict and os.path.sep != '/':
- prevdict['files'] = dict(
+ if 'files' in data and os.path.sep != '/':
+ data['files'] = dict(
(k.replace('/', os.path.sep), v)
- for k, v in prevdict['files'].iteritems())
+ for k, v in data['files'].iteritems())
+ return data
- infiles = expand_directories(
- indir, infiles, lambda x: re.match(r'.*\.(svn|pyc)$', x))
-
- # Only hashtable mode really needs the sha-1.
- level = {
- 'check': NO_INFO,
- 'hashtable': WITH_HASH,
- 'remap': STATS_ONLY,
- 'run': STATS_ONLY,
- 'trace': STATS_ONLY,
- }
- dictfiles = process_inputs(
- prevdict.get('files', {}), indir, infiles, level[mode], read_only)
-
- result = mode_fn(
- outdir, indir, dictfiles, read_only, cmd, relative_cwd, resultfile)
- out = {
- 'command': cmd,
- 'relative_cwd': relative_cwd,
- 'files': dictfiles,
- # Makes the directories read-only in addition to the files.
- 'read_only': read_only,
- }
+def save_results(resultfile, data):
+ data = data.copy()
# Works with native os.path.sep but stores as '/'.
if os.path.sep != '/':
- out['files'] = dict(
- (k.replace(os.path.sep, '/'), v) for k, v in out['files'].iteritems())
+ data['files'] = dict(
+ (k.replace(os.path.sep, '/'), v) for k, v in data['files'].iteritems())
f = None
try:
@@ -306,37 +261,78 @@ def isolate(
f = open(resultfile, 'wb')
else:
f = sys.stdout
- json.dump(out, f, indent=2, sort_keys=True)
+ json.dump(data, f, indent=2, sort_keys=True)
f.write('\n')
finally:
if resultfile and f:
f.close()
- total_bytes = sum(i.get('size', 0) for i in out['files'].itervalues())
+ total_bytes = sum(i.get('size', 0) for i in data['files'].itervalues())
if total_bytes:
logging.debug('Total size: %d bytes' % total_bytes)
- return result
-def MODEcheck(
- _outdir, _indir, _dictfiles, _read_only, _cmd, _relative_cwd, _resultfile):
+def isolate(outdir, mode, indir, infiles, data):
+ """Main function to isolate a target with its dependencies.
+
+ Arguments:
+ - outdir: Output directory where the result is stored. Depends on |mode|.
+ - indir: Root directory to be used as the base directory for infiles.
+ - infiles: List of files, with relative path, to process.
+ - mode: Action to do. See file level docstring.
+ - data: Contains all the command specific meta-data.
+
+ Some arguments are optional, dependending on |mode|. See the corresponding
+ MODE<mode> function for the exact behavior.
+ """
+ modes = {
+ 'check': MODEcheck,
+ 'hashtable': MODEhashtable,
+ 'remap': MODEremap,
+ 'run': MODErun,
+ 'trace': MODEtrace,
+ }
+ mode_fn = modes[mode]
+
+ infiles = expand_directories(
+ indir, infiles, lambda x: re.match(r'.*\.(git|svn|pyc)$', x))
+
+ # Only hashtable mode really needs the sha-1.
+ level = {
+ 'check': NO_INFO,
+ 'hashtable': WITH_HASH,
+ 'remap': STATS_ONLY,
+ 'run': STATS_ONLY,
+ 'trace': STATS_ONLY,
+ }
+ # Regenerate data['files'] from infiles.
+ data['files'] = process_inputs(
+ data.get('files', {}), indir, infiles, level[mode], data.get('read_only'))
+
+ result = mode_fn(outdir, indir, data)
+ return result, data
+
+
+def MODEcheck(_outdir, _indir, _data):
"""No-op."""
return 0
-def MODEhashtable(
- outdir, indir, dictfiles, _read_only, _cmd, _relative_cwd, resultfile):
- outdir = outdir or os.path.join(os.path.dirname(resultfile), 'hashtable')
+def MODEhashtable(outdir, indir, data):
+ outdir = (
+ outdir or os.path.join(os.path.dirname(data['resultdir']), 'hashtable'))
if not os.path.isdir(outdir):
os.makedirs(outdir)
- for relfile, properties in dictfiles.iteritems():
+ for relfile, properties in data['files'].iteritems():
infile = os.path.join(indir, relfile)
outfile = os.path.join(outdir, properties['sha-1'])
if os.path.isfile(outfile):
# Just do a quick check that the file size matches. No need to stat()
# again the input file, grab the value from the dict.
out_size = os.stat(outfile).st_size
- in_size = dictfiles.get(infile, {}).get('size') or os.stat(infile).st_size
+ in_size = (
+ data.get('files', {}).get(infile, {}).get('size') or
+ os.stat(infile).st_size)
if in_size == out_size:
continue
# Otherwise, an exception will be raised.
@@ -345,8 +341,7 @@ def MODEhashtable(
return 0
-def MODEremap(
- outdir, indir, dictfiles, read_only, _cmd, _relative_cwd, _resultfile):
+def MODEremap(outdir, indir, data):
if not outdir:
outdir = tempfile.mkdtemp(prefix='isolate')
else:
@@ -356,57 +351,55 @@ def MODEremap(
if len(os.listdir(outdir)):
print 'Can\'t remap in a non-empty directory'
return 1
- recreate_tree(outdir, indir, dictfiles.keys(), run_test_from_archive.HARDLINK)
- if read_only:
+ recreate_tree(
+ outdir, indir, data['files'].keys(), run_test_from_archive.HARDLINK)
+ if data['read_only']:
run_test_from_archive.make_writable(outdir, True)
return 0
-def MODErun(
- _outdir, indir, dictfiles, read_only, cmd, relative_cwd, _resultfile):
+def MODErun(_outdir, indir, data):
"""Always uses a temporary directory."""
try:
outdir = tempfile.mkdtemp(prefix='isolate')
recreate_tree(
- outdir, indir, dictfiles.keys(), run_test_from_archive.HARDLINK)
- cwd = os.path.join(outdir, relative_cwd)
+ outdir, indir, data['files'].keys(), run_test_from_archive.HARDLINK)
+ cwd = os.path.join(outdir, data['relative_cwd'])
if not os.path.isdir(cwd):
os.makedirs(cwd)
- if read_only:
+ if data['read_only']:
run_test_from_archive.make_writable(outdir, True)
- if not cmd:
+ if not data['command']:
print 'No command to run'
return 1
- cmd = trace_inputs.fix_python_path(cmd)
+ cmd = trace_inputs.fix_python_path(data['command'])
logging.info('Running %s, cwd=%s' % (cmd, cwd))
return subprocess.call(cmd, cwd=cwd)
finally:
run_test_from_archive.rmtree(outdir)
-def MODEtrace(
- _outdir, indir, _dictfiles, _read_only, cmd, relative_cwd, resultfile):
+def MODEtrace(_outdir, indir, data):
"""Shortcut to use trace_inputs.py properly.
It constructs the equivalent of dictfiles. It is hardcoded to base the
checkout at src/.
"""
- logging.info('Running %s, cwd=%s' % (cmd, os.path.join(indir, relative_cwd)))
- if resultfile:
- # Guesswork here.
- product_dir = os.path.dirname(resultfile)
- if product_dir and indir:
- product_dir = os.path.relpath(product_dir, indir)
- else:
- product_dir = None
- if not cmd:
+ logging.info(
+ 'Running %s, cwd=%s' % (
+ data['command'], os.path.join(indir, data['relative_cwd'])))
+ product_dir = None
+ if data['resultdir'] and indir:
+ # Defaults to none if both are the same directory.
+ product_dir = os.path.relpath(data['resultdir'], indir) or None
+ if not data['command']:
print 'No command to run'
return 1
return trace_inputs.trace_inputs(
- '%s.log' % resultfile,
- cmd,
+ data['resultfile'],
+ data['command'],
indir,
- relative_cwd,
+ data['relative_cwd'],
product_dir,
False)
@@ -417,6 +410,78 @@ def get_valid_modes():
i[4:] for i in dir(sys.modules[__name__]) if i.startswith('MODE'))
+def process_options(variables, resultfile, input_file, error):
+ """Processes the options and loads the input file. Returns the processed
+ values.
+ """
+ input_file = os.path.abspath(input_file)
+ isolate_dir = os.path.dirname(input_file)
+ resultfile = os.path.abspath(resultfile)
+ logging.info(
+ 'process_options(%s, %s, %s, ...)' % (variables, resultfile, input_file))
+
+ # Process path variables as a special case. First normalize it, verifies it
+ # exists, convert it to an absolute path, then set it as relative to
+ # isolate_dir.
+ for i in ('DEPTH', 'PRODUCT_DIR'):
+ if i not in variables:
+ continue
+ variable = os.path.normpath(variables[i])
+ if not os.path.isdir(variable):
+ error('%s=%s is not a directory' % (i, variable))
+ variable = os.path.abspath(variable)
+ # All variables are relative to the input file.
+ variables[i] = os.path.relpath(variable, isolate_dir)
+
+ command, infiles, read_only = load_isolate(
+ open(input_file, 'r').read(), variables, error)
+
+ # The trick used to determine the root directory is to look at "how far" back
+ # up it is looking up.
+ # TODO(maruel): Stop the msbuild generator from generating a mix of / and \\.
+ root_dir = isolate_dir.replace(os.path.sep, '/')
+ for i in infiles:
+ i = i.replace(os.path.sep, '/')
+ x = isolate_dir.replace(os.path.sep, '/')
+ while i.startswith('../'):
+ i = i[3:]
+ assert not i.startswith('/')
+ x = posixpath.dirname(x)
+ if root_dir.startswith(x):
+ root_dir = x
+ root_dir = root_dir.replace('/', os.path.sep)
+ # The relative directory is automatically determined by the relative path
+ # between root_dir and the directory containing the .isolate file.
+ relative_dir = os.path.relpath(isolate_dir, root_dir)
+ logging.debug('relative_dir: %s' % relative_dir)
+
+ logging.debug(
+ 'variables: %s' % ', '.join(
+ '%s=%s' % (k, v) for k, v in variables.iteritems()))
+
+ data = load_results(resultfile)
+
+ command, infiles, read_only = load_isolate(
+ open(input_file, 'r').read(), variables, error)
+
+ # Update data with the up to date information:
+ data['command'] = command
+ data['read_only'] = read_only
+ data['relative_cwd'] = relative_dir
+ data['resultfile'] = resultfile
+ data['resultdir'] = os.path.dirname(resultfile)
+
+ # Keep the old variables.
+ data.setdefault('variables', {}).update(variables)
+
+ logging.debug('command: %s' % command)
+ logging.debug('infiles: %s' % infiles)
+ logging.debug('read_only: %s' % read_only)
+ infiles = [normpath(os.path.join(relative_dir, f)) for f in infiles]
+ logging.debug('processed infiles: %s' % infiles)
+ return root_dir, infiles, data
+
+
def main():
default_variables = [('OS', trace_inputs.get_flavor())]
if sys.platform in ('win32', 'cygwin'):
@@ -465,69 +530,24 @@ def main():
if not options.mode:
parser.error('--mode is required')
if len(args) != 1:
+ logging.debug('%s' % sys.argv)
parser.error('Use only one argument which should be a .isolate file')
- input_file = os.path.abspath(args[0])
- isolate_dir = os.path.dirname(input_file)
-
- # Extract the variables.
- variables = dict(options.variables)
- # Process path variables as a special case. First normalize it, verifies it
- # exists, convert it to an absolute path, then set it as relative to
- # isolate_dir.
- for i in ('PRODUCT_DIR',):
- if i not in variables:
- continue
- variable = os.path.normpath(variables[i])
- if not os.path.isdir(variable):
- parser.error('%s=%s is not a directory' % (i, variable))
- variable = os.path.abspath(variable)
- # All variables are relative to the input file.
- variables[i] = os.path.relpath(variable, isolate_dir)
-
- command, infiles, read_only = load_isolate(
- open(input_file, 'r').read(), variables, parser.error)
-
- # The trick used to determine the root directory is to look at "how far" back
- # up it is looking up.
- root_dir = isolate_dir.replace(os.path.sep, '/')
- for i in infiles:
- i = i.replace(os.path.sep, '/')
- x = isolate_dir.replace(os.path.sep, '/')
- while i.startswith('../'):
- i = i[3:]
- assert not i.startswith('/')
- x = posixpath.dirname(x)
- if root_dir.startswith(x):
- root_dir = x
- root_dir = root_dir.replace('/', os.path.sep)
- # The relative directory is automatically determined by the relative path
- # between root_dir and the directory containing the .isolate file.
- relative_dir = os.path.relpath(isolate_dir, root_dir)
- logging.debug('relative_dir: %s' % relative_dir)
-
- logging.debug(
- 'variables: %s' % ', '.join(
- '%s=%s' % (k, v) for k, v in variables.iteritems()))
- logging.debug('command: %s' % command)
- logging.debug('infiles: %s' % infiles)
- logging.debug('read_only: %s' % read_only)
- infiles = [normpath(os.path.join(relative_dir, f)) for f in infiles]
- logging.debug('processed infiles: %s' % infiles)
+ root_dir, infiles, data = process_options(
+ dict(options.variables), options.result, args[0], parser.error)
try:
- return isolate(
+ resultcode, data = isolate(
options.outdir,
+ options.mode,
root_dir,
infiles,
- options.mode,
- read_only,
- command,
- relative_dir,
- options.result)
+ data)
except run_test_from_archive.MappingError, e:
print >> sys.stderr, str(e)
return 1
+ save_results(options.result, data)
+ return resultcode
if __name__ == '__main__':
« no previous file with comments | « third_party/cacheinvalidation/cacheinvalidation.gyp ('k') | tools/isolate/isolate_smoke_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698