15f9996aaSopenharmony_ci#!/usr/bin/env python
25f9996aaSopenharmony_ci# -*- coding: utf-8 -*-
35f9996aaSopenharmony_ci# Copyright 2014 The Chromium Authors. All rights reserved.
45f9996aaSopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
55f9996aaSopenharmony_ci# found in the LICENSE file.
65f9996aaSopenharmony_ci"""Copies files to a directory."""
75f9996aaSopenharmony_ci
85f9996aaSopenharmony_ciimport filecmp
95f9996aaSopenharmony_ciimport itertools
105f9996aaSopenharmony_ciimport optparse
115f9996aaSopenharmony_ciimport os
125f9996aaSopenharmony_ciimport shutil
135f9996aaSopenharmony_ciimport sys
145f9996aaSopenharmony_ci
155f9996aaSopenharmony_cifrom util import build_utils
165f9996aaSopenharmony_ci
175f9996aaSopenharmony_ci
185f9996aaSopenharmony_cidef copy_tree(src: str,
195f9996aaSopenharmony_ci              dest: str,
205f9996aaSopenharmony_ci              follow_all_symlinks=False,
215f9996aaSopenharmony_ci              follow_outside_symlinks=False):
225f9996aaSopenharmony_ci    """copy src/* to dest/
235f9996aaSopenharmony_ci    I. If follow_outside_symlinks is true,
245f9996aaSopenharmony_ci        1. If src item is a symlink, and points to some item inside src,
255f9996aaSopenharmony_ci            then copy the symlink to dest.
265f9996aaSopenharmony_ci        2. If src item points to items outside src, then follow links to copy
275f9996aaSopenharmony_ci            the original file to dest.
285f9996aaSopenharmony_ci        3. Else copy src item to dest.
295f9996aaSopenharmony_ci    II. If follow_all_symlinks is true,
305f9996aaSopenharmony_ci        1. If src item is a symlink, then follow links to copy the original file
315f9996aaSopenharmony_ci            to dest.
325f9996aaSopenharmony_ci        2. Else copy src item to dest.
335f9996aaSopenharmony_ci    follow_outside_symlinks is true when follow_all_symlinks is true.
345f9996aaSopenharmony_ci    """
355f9996aaSopenharmony_ci    with os.scandir(src) as itr:
365f9996aaSopenharmony_ci        items = list(itr)
375f9996aaSopenharmony_ci    return _do_copy_tree(items,
385f9996aaSopenharmony_ci                         src,
395f9996aaSopenharmony_ci                         dest,
405f9996aaSopenharmony_ci                         follow_all_symlinks=follow_all_symlinks,
415f9996aaSopenharmony_ci                         follow_outside_symlinks=follow_outside_symlinks)
425f9996aaSopenharmony_ci
435f9996aaSopenharmony_ci
445f9996aaSopenharmony_cidef _do_copy_tree(items: list,
455f9996aaSopenharmony_ci                  src: str,
465f9996aaSopenharmony_ci                  dest: str,
475f9996aaSopenharmony_ci                  follow_all_symlinks: bool = False,
485f9996aaSopenharmony_ci                  follow_outside_symlinks: bool = False) -> str:
495f9996aaSopenharmony_ci    os.makedirs(dest, exist_ok=True)
505f9996aaSopenharmony_ci    for item in items:
515f9996aaSopenharmony_ci        srcname = os.path.join(src, item.name)
525f9996aaSopenharmony_ci        destname = os.path.join(dest, item.name)
535f9996aaSopenharmony_ci        is_symlink = item.is_symlink()
545f9996aaSopenharmony_ci        if is_symlink:
555f9996aaSopenharmony_ci            org_linkto = os.readlink(srcname)
565f9996aaSopenharmony_ci            linkto = org_linkto
575f9996aaSopenharmony_ci            if not os.path.isabs(org_linkto):
585f9996aaSopenharmony_ci                linkto = os.path.join(os.path.dirname(item), org_linkto)
595f9996aaSopenharmony_ci
605f9996aaSopenharmony_ci            if not os.path.exists(linkto):
615f9996aaSopenharmony_ci                os.symlink(org_linkto, destname)
625f9996aaSopenharmony_ci                shutil.copymode(srcname, destname, follow_symlinks=False)
635f9996aaSopenharmony_ci                continue
645f9996aaSopenharmony_ci
655f9996aaSopenharmony_ci            if follow_all_symlinks:
665f9996aaSopenharmony_ci                if item.is_dir():
675f9996aaSopenharmony_ci                    copy_tree(item,
685f9996aaSopenharmony_ci                              destname,
695f9996aaSopenharmony_ci                              follow_all_symlinks=follow_all_symlinks,
705f9996aaSopenharmony_ci                              follow_outside_symlinks=follow_outside_symlinks)
715f9996aaSopenharmony_ci                else:
725f9996aaSopenharmony_ci                    shutil.copy(item, destname)
735f9996aaSopenharmony_ci                    shutil.copymode(item, destname)
745f9996aaSopenharmony_ci
755f9996aaSopenharmony_ci            elif follow_outside_symlinks:
765f9996aaSopenharmony_ci                if os.path.abspath(src) in os.path.abspath(
775f9996aaSopenharmony_ci                        linkto) and not os.path.isabs(org_linkto):
785f9996aaSopenharmony_ci                    os.symlink(org_linkto, destname)
795f9996aaSopenharmony_ci                    shutil.copymode(srcname, destname, follow_symlinks=False)
805f9996aaSopenharmony_ci                else:
815f9996aaSopenharmony_ci                    if item.is_dir():
825f9996aaSopenharmony_ci                        copy_tree(
835f9996aaSopenharmony_ci                            item,
845f9996aaSopenharmony_ci                            destname,
855f9996aaSopenharmony_ci                            follow_all_symlinks=follow_all_symlinks,
865f9996aaSopenharmony_ci                            follow_outside_symlinks=follow_outside_symlinks)
875f9996aaSopenharmony_ci                    else:
885f9996aaSopenharmony_ci                        shutil.copy(item, destname)
895f9996aaSopenharmony_ci                        shutil.copymode(item, destname)
905f9996aaSopenharmony_ci            else:
915f9996aaSopenharmony_ci                os.symlink(org_linkto, destname)
925f9996aaSopenharmony_ci                shutil.copymode(srcname, destname, follow_symlinks=False)
935f9996aaSopenharmony_ci        elif item.is_dir():
945f9996aaSopenharmony_ci            copy_tree(item,
955f9996aaSopenharmony_ci                      destname,
965f9996aaSopenharmony_ci                      follow_all_symlinks=follow_all_symlinks,
975f9996aaSopenharmony_ci                      follow_outside_symlinks=follow_outside_symlinks)
985f9996aaSopenharmony_ci        else:
995f9996aaSopenharmony_ci            shutil.copy(item, destname)
1005f9996aaSopenharmony_ci            shutil.copymode(item, destname)
1015f9996aaSopenharmony_ci    shutil.copystat(src, dest)
1025f9996aaSopenharmony_ci    return dest
1035f9996aaSopenharmony_ci
1045f9996aaSopenharmony_ci
1055f9996aaSopenharmony_cidef copy_file(f: str,
1065f9996aaSopenharmony_ci              dest: str,
1075f9996aaSopenharmony_ci              deps: list,
1085f9996aaSopenharmony_ci              follow_all_symlinks: bool = False,
1095f9996aaSopenharmony_ci              follow_outside_symlinks: bool = False):
1105f9996aaSopenharmony_ci    """Copy file or directory and update deps."""
1115f9996aaSopenharmony_ci    if os.path.isdir(f):
1125f9996aaSopenharmony_ci        copy_tree(f,
1135f9996aaSopenharmony_ci                  os.path.join(dest, os.path.basename(f)),
1145f9996aaSopenharmony_ci                  follow_all_symlinks=follow_all_symlinks,
1155f9996aaSopenharmony_ci                  follow_outside_symlinks=follow_outside_symlinks)
1165f9996aaSopenharmony_ci        deps.extend(build_utils.get_all_files(f))
1175f9996aaSopenharmony_ci    else:
1185f9996aaSopenharmony_ci        if os.path.isfile(os.path.join(dest, os.path.basename(f))):
1195f9996aaSopenharmony_ci            dest = os.path.join(dest, os.path.basename(f))
1205f9996aaSopenharmony_ci
1215f9996aaSopenharmony_ci        deps.append(f)
1225f9996aaSopenharmony_ci
1235f9996aaSopenharmony_ci        if os.path.isfile(dest):
1245f9996aaSopenharmony_ci            if filecmp.cmp(dest, f, shallow=False):
1255f9996aaSopenharmony_ci                return
1265f9996aaSopenharmony_ci            # The shutil.copy() below would fail if the file does not
1275f9996aaSopenharmony_ci            # have write permissions. Deleting the file
1285f9996aaSopenharmony_ci            # has similar costs to modifying the permissions.
1295f9996aaSopenharmony_ci            os.unlink(dest)
1305f9996aaSopenharmony_ci
1315f9996aaSopenharmony_ci        shutil.copy(f, dest)
1325f9996aaSopenharmony_ci
1335f9996aaSopenharmony_ci
1345f9996aaSopenharmony_cidef do_copy(options, deps: list):
1355f9996aaSopenharmony_ci    """Copy files or directories given in options.files and update deps."""
1365f9996aaSopenharmony_ci    files = list(
1375f9996aaSopenharmony_ci        itertools.chain.from_iterable(
1385f9996aaSopenharmony_ci            build_utils.parse_gn_list(f) for f in options.files))
1395f9996aaSopenharmony_ci
1405f9996aaSopenharmony_ci    for f in files:
1415f9996aaSopenharmony_ci        if not options.ignore_stale:
1425f9996aaSopenharmony_ci            if os.path.isdir(f) and not options.clear:
1435f9996aaSopenharmony_ci                print('To avoid stale files you must use --clear when copying '
1445f9996aaSopenharmony_ci                      'directories')
1455f9996aaSopenharmony_ci                sys.exit(-1)
1465f9996aaSopenharmony_ci        copy_file(f,
1475f9996aaSopenharmony_ci                  options.dest,
1485f9996aaSopenharmony_ci                  deps,
1495f9996aaSopenharmony_ci                  follow_all_symlinks=options.follow_all_symlinks,
1505f9996aaSopenharmony_ci                  follow_outside_symlinks=options.follow_outside_symlinks)
1515f9996aaSopenharmony_ci
1525f9996aaSopenharmony_ci
1535f9996aaSopenharmony_cidef do_renaming(options, deps: list):
1545f9996aaSopenharmony_ci    """Copy and rename files given in options.renaming_sources
1555f9996aaSopenharmony_ci    and update deps.
1565f9996aaSopenharmony_ci    """
1575f9996aaSopenharmony_ci    src_files = list(
1585f9996aaSopenharmony_ci        itertools.chain.from_iterable(
1595f9996aaSopenharmony_ci            build_utils.parse_gn_list(f) for f in options.renaming_sources))
1605f9996aaSopenharmony_ci
1615f9996aaSopenharmony_ci    dest_files = list(
1625f9996aaSopenharmony_ci        itertools.chain.from_iterable(
1635f9996aaSopenharmony_ci            build_utils.parse_gn_list(f)
1645f9996aaSopenharmony_ci            for f in options.renaming_destinations))
1655f9996aaSopenharmony_ci
1665f9996aaSopenharmony_ci    if (len(src_files) != len(dest_files)):
1675f9996aaSopenharmony_ci        print('Renaming source and destination files not match.')
1685f9996aaSopenharmony_ci        sys.exit(-1)
1695f9996aaSopenharmony_ci
1705f9996aaSopenharmony_ci    for src, dest in zip(src_files, dest_files):
1715f9996aaSopenharmony_ci        if os.path.isdir(src):
1725f9996aaSopenharmony_ci            print('renaming directory is not supported.')
1735f9996aaSopenharmony_ci            sys.exit(-1)
1745f9996aaSopenharmony_ci        else:
1755f9996aaSopenharmony_ci            copy_file(src, os.path.join(options.dest, dest), deps)
1765f9996aaSopenharmony_ci
1775f9996aaSopenharmony_ci
1785f9996aaSopenharmony_cidef main(args):
1795f9996aaSopenharmony_ci    args = build_utils.expand_file_args(args)
1805f9996aaSopenharmony_ci
1815f9996aaSopenharmony_ci    parser = optparse.OptionParser()
1825f9996aaSopenharmony_ci    build_utils.add_depfile_option(parser)
1835f9996aaSopenharmony_ci
1845f9996aaSopenharmony_ci    parser.add_option('--dest', help='Directory to copy files to.')
1855f9996aaSopenharmony_ci    parser.add_option('--files',
1865f9996aaSopenharmony_ci                      action='append',
1875f9996aaSopenharmony_ci                      help='List of files to copy.')
1885f9996aaSopenharmony_ci    parser.add_option(
1895f9996aaSopenharmony_ci        '--clear',
1905f9996aaSopenharmony_ci        action='store_true',
1915f9996aaSopenharmony_ci        help='If set, the destination directory will be deleted '
1925f9996aaSopenharmony_ci        'before copying files to it. This is highly recommended to '
1935f9996aaSopenharmony_ci        'ensure that no stale files are left in the directory.')
1945f9996aaSopenharmony_ci    parser.add_option('--ignore-stale',
1955f9996aaSopenharmony_ci                      action='store_true',
1965f9996aaSopenharmony_ci                      help='Do copy even there may be stale files. '
1975f9996aaSopenharmony_ci                      'If set, overrule options.clear')
1985f9996aaSopenharmony_ci    parser.add_option('--stamp', help='Path to touch on success.')
1995f9996aaSopenharmony_ci    parser.add_option('--follow-all-symlinks',
2005f9996aaSopenharmony_ci                      action='store_true',
2015f9996aaSopenharmony_ci                      help='whether to follow symlinks')
2025f9996aaSopenharmony_ci    parser.add_option(
2035f9996aaSopenharmony_ci        '--follow-outside-symlinks',
2045f9996aaSopenharmony_ci        action='store_true',
2055f9996aaSopenharmony_ci        help='whether to follow symlinks which points to targets outside of'
2065f9996aaSopenharmony_ci        'source directory')
2075f9996aaSopenharmony_ci    parser.add_option('--renaming-sources',
2085f9996aaSopenharmony_ci                      action='append',
2095f9996aaSopenharmony_ci                      help='List of files need to be renamed while being '
2105f9996aaSopenharmony_ci                      'copied to dest directory')
2115f9996aaSopenharmony_ci    parser.add_option('--renaming-destinations',
2125f9996aaSopenharmony_ci                      action='append',
2135f9996aaSopenharmony_ci                      help='List of destination file name without path, the '
2145f9996aaSopenharmony_ci                      'number of elements must match rename-sources.')
2155f9996aaSopenharmony_ci
2165f9996aaSopenharmony_ci    options, _ = parser.parse_args(args)
2175f9996aaSopenharmony_ci    if options.follow_all_symlinks:
2185f9996aaSopenharmony_ci        options.follow_outside_symlinks = True
2195f9996aaSopenharmony_ci
2205f9996aaSopenharmony_ci    if options.clear and not options.ignore_stale:
2215f9996aaSopenharmony_ci        build_utils.delete_directory(options.dest)
2225f9996aaSopenharmony_ci        build_utils.make_directory(options.dest)
2235f9996aaSopenharmony_ci
2245f9996aaSopenharmony_ci    deps = []
2255f9996aaSopenharmony_ci
2265f9996aaSopenharmony_ci    if options.files:
2275f9996aaSopenharmony_ci        do_copy(options, deps)
2285f9996aaSopenharmony_ci
2295f9996aaSopenharmony_ci    if options.renaming_sources:
2305f9996aaSopenharmony_ci        do_renaming(options, deps)
2315f9996aaSopenharmony_ci
2325f9996aaSopenharmony_ci    if options.depfile:
2335f9996aaSopenharmony_ci        build_utils.write_depfile(options.depfile,
2345f9996aaSopenharmony_ci                                  options.stamp,
2355f9996aaSopenharmony_ci                                  deps,
2365f9996aaSopenharmony_ci                                  add_pydeps=False)
2375f9996aaSopenharmony_ci
2385f9996aaSopenharmony_ci    if options.stamp:
2395f9996aaSopenharmony_ci        build_utils.touch(options.stamp)
2405f9996aaSopenharmony_ci
2415f9996aaSopenharmony_ci
2425f9996aaSopenharmony_ciif __name__ == '__main__':
2435f9996aaSopenharmony_ci    sys.exit(main(sys.argv[1:]))
244