1#!/usr/bin/env python 2# 3# Copyright 2014 The Chromium Authors. All rights reserved. 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 9from __future__ import print_function 10 11import filecmp 12import itertools 13import optparse 14import os 15import shutil 16import sys 17 18from util import build_utils 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(itertools.chain.from_iterable(build_utils.ParseGnList(f) 54 for f in options.files)) 55 56 for f in files: 57 if os.path.isdir(f) and not options.clear: 58 print('To avoid stale files you must use --clear when copying ' 59 'directories') 60 sys.exit(-1) 61 CopyFile(f, options.dest, deps) 62 63def DoRenaming(options, deps): 64 """Copy and rename files given in options.renaming_sources and update deps.""" 65 src_files = list(itertools.chain.from_iterable( 66 build_utils.ParseGnList(f) 67 for f in options.renaming_sources)) 68 69 dest_files = list(itertools.chain.from_iterable( 70 build_utils.ParseGnList(f) 71 for f in options.renaming_destinations)) 72 73 if (len(src_files) != len(dest_files)): 74 print('Renaming source and destination files not match.') 75 sys.exit(-1) 76 77 for src, dest in itertools.izip(src_files, dest_files): 78 if os.path.isdir(src): 79 print('renaming diretory is not supported.') 80 sys.exit(-1) 81 else: 82 CopyFile(src, os.path.join(options.dest, dest), deps) 83 84def main(args): 85 args = build_utils.ExpandFileArgs(args) 86 87 parser = optparse.OptionParser() 88 build_utils.AddDepfileOption(parser) 89 90 parser.add_option('--dest', help='Directory to copy files to.') 91 parser.add_option('--files', action='append', 92 help='List of files to copy.') 93 parser.add_option('--clear', action='store_true', 94 help='If set, the destination directory will be deleted ' 95 'before copying files to it. This is highly recommended to ' 96 'ensure that no stale files are left in the directory.') 97 parser.add_option('--stamp', help='Path to touch on success.') 98 parser.add_option('--renaming-sources', 99 action='append', 100 help='List of files need to be renamed while being ' 101 'copied to dest directory') 102 parser.add_option('--renaming-destinations', 103 action='append', 104 help='List of destination file name without path, the ' 105 'number of elements must match rename-sources.') 106 107 options, _ = parser.parse_args(args) 108 109 if options.clear: 110 build_utils.DeleteDirectory(options.dest) 111 build_utils.MakeDirectory(options.dest) 112 113 deps = [] 114 115 if options.files: 116 DoCopy(options, deps) 117 118 if options.renaming_sources: 119 DoRenaming(options, deps) 120 121 if options.depfile: 122 build_utils.WriteDepfile(options.depfile, options.stamp, deps) 123 124 if options.stamp: 125 build_utils.Touch(options.stamp) 126 127 128if __name__ == '__main__': 129 sys.exit(main(sys.argv[1:])) 130