#!/usr/bin/env python3 # Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. import argparse import gn_helpers import os import re import shutil import sys def ParseArgs(args): args = args[1:] parser = argparse.ArgumentParser( description='A script to copy a file tree somewhere') parser.add_argument( '--exclude_patterns', '-e', type=str, help='Patterns to exclude [passed to shutil.copytree]') parser.add_argument( '--from', '-f', dest="copy_from", type=str, help='Source directory') parser.add_argument( '--gn', '-g', dest='gn', default=False, action='store_true', help='Output for GN for multiple sources, but do not copy anything.') parser.add_argument( 'gn_paths', metavar='name path ignore_pattern', type=str, nargs='*', default=None, help='When --gn is given, the specification of source paths to list.') parser.add_argument('--to', '-t', type=str, help='Destination directory') return parser.parse_args(args) def ValidateArgs(args): if args.gn: if args.exclude_patterns or args.copy_from or args.to: print("--gn mode does not accept other switches") return False if not args.gn_paths: print("--gn mode requires a list of source specifications") return False return True if not args.copy_from or not os.path.isdir(args.copy_from): print("--from argument must refer to a directory") return False if not args.to: print("--to is required") return False return True def CopyTree(src, dst, ignore=None): # Recursive helper method to collect errors but keep processing. def copy_tree(src, dst, ignore, errors): names = os.listdir(src) if ignore is not None: ignored_names = ignore(src, names) else: ignored_names = set() if not os.path.exists(dst): os.makedirs(dst) for name in names: if name in ignored_names: continue srcname = os.path.join(src, name) dstname = os.path.join(dst, name) try: if os.path.isdir(srcname): copy_tree(srcname, dstname, ignore, errors) else: shutil.copy(srcname, dstname) except (IOError, os.error) as why: errors.append((srcname, dstname, str(why))) try: shutil.copystat(src, dst) except WindowsError: # Can't copy file access times on Windows. pass except OSError as why: errors.append((src, dst, str(why))) # Format errors from file copies. def format_error(error): if len(error) == 1: return "Error: {msg}".format(msg=str(error[0])) return "From: {src}\nTo: {dst}\n{msg}" \ .format(src=error[0], dst=error[1], msg=error[2]) errors = [] copy_tree(src, dst, ignore, errors) if errors: failures = "\n\n".join(format_error(error) for error in errors) parts = ("Some file copies failed:", "=" * 78, failures) msg = '\n'.join(parts) raise RuntimeError(msg) def ListTree(src, ignore=None): names = os.listdir(src) if ignore is not None: ignored_names = ignore(src, names) else: ignored_names = set() srcnames = [] for name in names: if name in ignored_names: continue srcname = os.path.join(src, name) if os.path.isdir(srcname): srcnames.extend(ListTree(srcname, ignore)) else: srcnames.append(srcname) return srcnames # source_dirs is organized such that sources_dirs[n] is the path for the source # directory, and source_dirs[n+1] is a list of ignore patterns. def SourcesToGN(source_dirs): if len(source_dirs) % 2 != 0: print("--gn list length should be a multiple of 2.") return False data = [] for i in range(0, len(source_dirs), 2): path = source_dirs[i] ignores = source_dirs[i + 1] if ignores in ["{}"]: sources = ListTree(path) else: patterns = ignores.split(',') sources = ListTree(path, ignore=shutil.ignore_patterns(*patterns)) data.append(sources) scope_data = {"sources": data} print(gn_helpers.ToGNString(scope_data)) return True def Main(argv): args = ParseArgs(argv) if not ValidateArgs(args): return -1 if args.gn: SourcesToGN(args.gn_paths) return 0 if args.exclude_patterns == None: CopyTree(args.copy_from, args.to) else: patterns = args.exclude_patterns.split(',') CopyTree( args.copy_from, args.to, ignore=shutil.ignore_patterns(*patterns)) return 0 if __name__ == '__main__': sys.exit(Main(sys.argv))