| Index: tools/isolate/tree_creator.py
|
| diff --git a/tools/isolate/tree_creator.py b/tools/isolate/tree_creator.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..445daa09bf7e5acca7301dd5955487d8985724a6
|
| --- /dev/null
|
| +++ b/tools/isolate/tree_creator.py
|
| @@ -0,0 +1,171 @@
|
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""Creates a tree of hardlinks, symlinks or copy the inputs files."""
|
| +
|
| +import ctypes
|
| +import logging
|
| +import os
|
| +import shutil
|
| +import sys
|
| +
|
| +
|
| +# Types of action accepted by recreate_tree().
|
| +DRY_RUN, HARDLINK, SYMLINK, COPY = range(5)[1:]
|
| +
|
| +
|
| +class MappingError(OSError):
|
| + """Failed to recreate the tree."""
|
| + pass
|
| +
|
| +
|
| +def os_link(source, link_name):
|
| + """Add support for os.link() on Windows."""
|
| + if sys.platform == 'win32':
|
| + if not ctypes.windll.kernel32.CreateHardLinkW(
|
| + unicode(link_name), unicode(source), 0):
|
| + raise OSError()
|
| + else:
|
| + os.link(source, link_name)
|
| +
|
| +
|
| +def _process_item(outdir, indir, relfile, action, blacklist):
|
| + """Processes an input file.
|
| +
|
| + Returns True if processed.
|
| + """
|
| + logging.debug(
|
| + '_process_item(%s, %s, %s, %s, %s)' % (
|
| + outdir, indir, relfile, action, blacklist))
|
| + if blacklist and blacklist(relfile):
|
| + return False
|
| + infile = os.path.normpath(os.path.join(indir, relfile))
|
| + if not os.path.isfile(infile):
|
| + raise MappingError('%s doesn\'t exist' % infile)
|
| +
|
| + if action == DRY_RUN:
|
| + logging.info('Verified input: %s' % infile)
|
| + return True
|
| +
|
| + outfile = os.path.normpath(os.path.join(outdir, relfile))
|
| + logging.debug('Mapping %s to %s' % (infile, outfile))
|
| + outsubdir = os.path.dirname(outfile)
|
| + if not os.path.isdir(outsubdir):
|
| + os.makedirs(outsubdir)
|
| +
|
| + if os.path.isfile(outfile):
|
| + raise MappingError('%s already exist' % outfile)
|
| +
|
| + if action == COPY:
|
| + shutil.copy(infile, outfile)
|
| + elif action == SYMLINK and sys.platform != 'win32':
|
| + os.symlink(infile, outfile)
|
| + elif action == HARDLINK:
|
| + try:
|
| + os_link(infile, outfile)
|
| + except OSError:
|
| + # Probably a different file system.
|
| + logging.warn(
|
| + 'Failed to hardlink, failing back to copy %s to %s' % (
|
| + infile, outfile))
|
| + shutil.copy(infile, outfile)
|
| + else:
|
| + raise ValueError('Unknown mapping action %s' % action)
|
| + return True
|
| +
|
| +
|
| +def _recurse_dir(outdir, indir, subdir, action, blacklist):
|
| + """Processes a directory and all its decendents."""
|
| + logging.debug(
|
| + '_recurse_dir(%s, %s, %s, %s, %s)' % (
|
| + outdir, indir, subdir, action, blacklist))
|
| + root = os.path.join(indir, subdir)
|
| + for dirpath, dirnames, filenames in os.walk(root):
|
| + # Convert the absolute path to subdir + relative subdirectory.
|
| + relpath = dirpath[len(indir)+1:]
|
| +
|
| + for filename in filenames:
|
| + relfile = os.path.join(relpath, filename)
|
| + _process_item(outdir, indir, relfile, action, blacklist)
|
| +
|
| + for index, dirname in enumerate(dirnames):
|
| + # Do not process blacklisted directories.
|
| + if blacklist(os.path.normpath(os.path.join(relpath, dirname))):
|
| + del dirnames[index]
|
| +
|
| +
|
| +def recreate_tree(outdir, indir, infiles, action, blacklist):
|
| + """Creates a new tree with only the input files in it.
|
| +
|
| + Arguments:
|
| + outdir: Temporary directory to create the files in.
|
| + indir: Root directory the infiles are based in.
|
| + infiles: List of files to map from |indir| to |outdir|.
|
| + action: See assert below.
|
| + blacklist: Files to unconditionally ignore.
|
| + """
|
| + logging.debug(
|
| + 'recreate_tree(%s, %s, %s, %s, %s)' % (
|
| + outdir, indir, infiles, action, blacklist))
|
| + logging.info('Mapping from %s to %s' % (indir, outdir))
|
| +
|
| + assert action in (DRY_RUN, HARDLINK, SYMLINK, COPY)
|
| + # Both need to be a local path.
|
| + indir = os.path.normpath(indir)
|
| + if not os.path.isdir(indir):
|
| + raise MappingError('%s is not a directory' % indir)
|
| +
|
| + # Do not call abspath until it was verified the directory exists.
|
| + indir = os.path.abspath(indir)
|
| +
|
| + if action != DRY_RUN:
|
| + outdir = os.path.normpath(outdir)
|
| + if not os.path.isdir(outdir):
|
| + logging.info ('Creating %s' % outdir)
|
| + os.makedirs(outdir)
|
| + # Do not call abspath until the directory exists.
|
| + outdir = os.path.abspath(outdir)
|
| +
|
| + for relfile in infiles:
|
| + if os.path.isabs(relfile):
|
| + raise MappingError('Can\'t map absolute path %s' % relfile)
|
| + infile = os.path.normpath(os.path.join(indir, relfile))
|
| + if not infile.startswith(indir):
|
| + raise MappingError('Can\'t map file %s outside %s' % (infile, indir))
|
| +
|
| + isdir = os.path.isdir(infile)
|
| + if isdir and not relfile.endswith('/'):
|
| + raise MappingError(
|
| + 'Input directory %s must have a trailing slash' % infile)
|
| + if not isdir and relfile.endswith('/'):
|
| + raise MappingError(
|
| + 'Input file %s must not have a trailing slash' % infile)
|
| + if isdir:
|
| + _recurse_dir(outdir, indir, relfile, action, blacklist)
|
| + else:
|
| + _process_item(outdir, indir, relfile, action, blacklist)
|
| +
|
| +
|
| +def _set_write_bit(path, read_only):
|
| + mode = os.stat(path).st_mode
|
| + if read_only:
|
| + mode = mode & 0500
|
| + else:
|
| + mode = mode | 0200
|
| + if hasattr(os, 'lchmod'):
|
| + os.lchmod(path, mode) # pylint: disable=E1101
|
| + else:
|
| + # TODO(maruel): Implement proper DACL modification on Windows.
|
| + os.chmod(path, mode)
|
| +
|
| +
|
| +def make_writable(root, read_only):
|
| + """Toggle the writable bit on a directory tree."""
|
| + root = os.path.abspath(root)
|
| + for dirpath, dirnames, filenames in os.walk(root, topdown=True):
|
| + for filename in filenames:
|
| + _set_write_bit(os.path.join(dirpath, filename), read_only)
|
| +
|
| + for dirname in dirnames:
|
| + _set_write_bit(os.path.join(dirpath, dirname), read_only)
|
|
|