blob: fd6f9fd348bedea100c3a5374d40d0d6462b8744 [file] [log] [blame] [edit]
#!/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('--depfile',
'-d',
type=str,
help='Path to a depfile to write when copying.')
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(
'--stamp',
'-s',
type=str,
help='The path to a stamp file to output when finished.')
parser.add_argument('--to', '-t', type=str, help='Destination directory')
return parser.parse_args(args)
def ValidateArgs(args):
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
# Returns a list of the files under 'src' that were copied.
def CopyTree(src, dst, ignore=None):
copied_files = []
# 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:
copied_files.append(srcname)
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)
return copied_files
def WriteDepfile(depfile, stamp, dep_list):
os.makedirs(os.path.dirname(depfile), exist_ok=True)
# Paths in the depfile must be relative to the root build output directory,
# which is the cwd that ninja invokes the script from.
cwd = os.getcwd()
relstamp = os.path.relpath(stamp, cwd)
reldep_list = [os.path.relpath(d, cwd) for d in dep_list]
# Depfile paths must use an escape sequence for space characters.
reldep_list = [path.replace(" ", r"\ ") for path in reldep_list]
with open(depfile, 'w') as f:
print("{0}: {1}".format(relstamp, " ".join(reldep_list)), file=f)
def Main(argv):
args = ParseArgs(argv)
if not ValidateArgs(args):
return -1
if args.exclude_patterns == None:
copied_files = CopyTree(args.copy_from, args.to)
else:
patterns = args.exclude_patterns.split(',')
copied_files = CopyTree(args.copy_from,
args.to,
ignore=shutil.ignore_patterns(*patterns))
if args.depfile and args.stamp:
WriteDepfile(args.depfile, args.stamp, copied_files)
if args.stamp:
open(args.stamp, 'w').close()
return 0
if __name__ == '__main__':
sys.exit(Main(sys.argv))