1#!/usr/bin/env python3 2# 3# Copyright 2014 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Copies files to a directory.""" 8 9 10import filecmp 11import itertools 12import optparse 13import os 14import shutil 15import sys 16 17from util import build_utils 18import action_helpers # build_utils adds //build to sys.path. 19 20 21def _get_all_files(base): 22 """Returns a list of all the files in |base|. Each entry is relative to the 23 last path entry of |base|.""" 24 result = [] 25 dirname = os.path.dirname(base) 26 for root, _, files in os.walk(base): 27 result.extend([os.path.join(root[len(dirname):], f) for f in files]) 28 return result 29 30def CopyFile(f, dest, deps): 31 """Copy file or directory and update deps.""" 32 if os.path.isdir(f): 33 shutil.copytree(f, os.path.join(dest, os.path.basename(f))) 34 deps.extend(_get_all_files(f)) 35 else: 36 if os.path.isfile(os.path.join(dest, os.path.basename(f))): 37 dest = os.path.join(dest, os.path.basename(f)) 38 39 deps.append(f) 40 41 if os.path.isfile(dest): 42 if filecmp.cmp(dest, f, shallow=False): 43 return 44 # The shutil.copy() below would fail if the file does not have write 45 # permissions. Deleting the file has similar costs to modifying the 46 # permissions. 47 os.unlink(dest) 48 49 shutil.copy(f, dest) 50 51def DoCopy(options, deps): 52 """Copy files or directories given in options.files and update deps.""" 53 files = list( 54 itertools.chain.from_iterable( 55 action_helpers.parse_gn_list(f) for f in options.files)) 56 57 for f in files: 58 if os.path.isdir(f) and not options.clear: 59 print('To avoid stale files you must use --clear when copying ' 60 'directories') 61 sys.exit(-1) 62 CopyFile(f, options.dest, deps) 63 64def DoRenaming(options, deps): 65 """Copy and rename files given in options.renaming_sources and update deps.""" 66 src_files = list( 67 itertools.chain.from_iterable( 68 action_helpers.parse_gn_list(f) for f in options.renaming_sources)) 69 70 dest_files = list( 71 itertools.chain.from_iterable( 72 action_helpers.parse_gn_list(f) 73 for f in options.renaming_destinations)) 74 75 if (len(src_files) != len(dest_files)): 76 print('Renaming source and destination files not match.') 77 sys.exit(-1) 78 79 for src, dest in zip(src_files, dest_files): 80 if os.path.isdir(src): 81 print('renaming diretory is not supported.') 82 sys.exit(-1) 83 else: 84 CopyFile(src, os.path.join(options.dest, dest), deps) 85 86def main(args): 87 args = build_utils.ExpandFileArgs(args) 88 89 parser = optparse.OptionParser() 90 action_helpers.add_depfile_arg(parser) 91 92 parser.add_option('--dest', help='Directory to copy files to.') 93 parser.add_option('--files', action='append', 94 help='List of files to copy.') 95 parser.add_option('--clear', action='store_true', 96 help='If set, the destination directory will be deleted ' 97 'before copying files to it. This is highly recommended to ' 98 'ensure that no stale files are left in the directory.') 99 parser.add_option('--stamp', help='Path to touch on success.') 100 parser.add_option('--renaming-sources', 101 action='append', 102 help='List of files need to be renamed while being ' 103 'copied to dest directory') 104 parser.add_option('--renaming-destinations', 105 action='append', 106 help='List of destination file name without path, the ' 107 'number of elements must match rename-sources.') 108 109 options, _ = parser.parse_args(args) 110 111 if options.clear: 112 build_utils.DeleteDirectory(options.dest) 113 build_utils.MakeDirectory(options.dest) 114 115 deps = [] 116 117 if options.files: 118 DoCopy(options, deps) 119 120 if options.renaming_sources: 121 DoRenaming(options, deps) 122 123 if options.depfile: 124 action_helpers.write_depfile(options.depfile, options.stamp, deps) 125 126 if options.stamp: 127 build_utils.Touch(options.stamp) 128 129 130if __name__ == '__main__': 131 sys.exit(main(sys.argv[1:])) 132