xref: /aosp_15_r20/build/soong/scripts/merge_directories.py (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*333d2b36SAndroid Build Coastguard Workerimport argparse
3*333d2b36SAndroid Build Coastguard Workerimport os
4*333d2b36SAndroid Build Coastguard Workerimport shutil
5*333d2b36SAndroid Build Coastguard Workerimport sys
6*333d2b36SAndroid Build Coastguard Worker
7*333d2b36SAndroid Build Coastguard Workerdef main():
8*333d2b36SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(
9*333d2b36SAndroid Build Coastguard Worker        description="Given a list of directories, this script will copy the contents of all of "
10*333d2b36SAndroid Build Coastguard Worker        "them into the first directory, erroring out if any duplicate files are found."
11*333d2b36SAndroid Build Coastguard Worker    )
12*333d2b36SAndroid Build Coastguard Worker    parser.add_argument(
13*333d2b36SAndroid Build Coastguard Worker        "--ignore-duplicates",
14*333d2b36SAndroid Build Coastguard Worker        action="store_true",
15*333d2b36SAndroid Build Coastguard Worker        help="Don't error out on duplicate files, just skip them. The file from the earliest "
16*333d2b36SAndroid Build Coastguard Worker        "directory listed on the command line will be the winner."
17*333d2b36SAndroid Build Coastguard Worker    )
18*333d2b36SAndroid Build Coastguard Worker    parser.add_argument(
19*333d2b36SAndroid Build Coastguard Worker        "--file-list",
20*333d2b36SAndroid Build Coastguard Worker        help="Path to a text file containing paths relative to in_dir. Only these paths will be "
21*333d2b36SAndroid Build Coastguard Worker        "copied out of in_dir."
22*333d2b36SAndroid Build Coastguard Worker    )
23*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("out_dir")
24*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("in_dir")
25*333d2b36SAndroid Build Coastguard Worker    args = parser.parse_args()
26*333d2b36SAndroid Build Coastguard Worker
27*333d2b36SAndroid Build Coastguard Worker    if not os.path.isdir(args.out_dir):
28*333d2b36SAndroid Build Coastguard Worker        sys.exit(f"error: {args.out_dir} must be a directory")
29*333d2b36SAndroid Build Coastguard Worker    if not os.path.isdir(args.in_dir):
30*333d2b36SAndroid Build Coastguard Worker        sys.exit(f"error: {args.in_dir} must be a directory")
31*333d2b36SAndroid Build Coastguard Worker
32*333d2b36SAndroid Build Coastguard Worker    file_list = None
33*333d2b36SAndroid Build Coastguard Worker    if args.file_list:
34*333d2b36SAndroid Build Coastguard Worker        with open(file_list_file, "r") as f:
35*333d2b36SAndroid Build Coastguard Worker            file_list = f.read().strip().splitlines()
36*333d2b36SAndroid Build Coastguard Worker
37*333d2b36SAndroid Build Coastguard Worker    in_dir = args.in_dir
38*333d2b36SAndroid Build Coastguard Worker    for root, dirs, files in os.walk(in_dir):
39*333d2b36SAndroid Build Coastguard Worker        rel_root = os.path.relpath(root, in_dir)
40*333d2b36SAndroid Build Coastguard Worker        dst_root = os.path.join(args.out_dir, rel_root)
41*333d2b36SAndroid Build Coastguard Worker        made_parent_dirs = False
42*333d2b36SAndroid Build Coastguard Worker        for f in files:
43*333d2b36SAndroid Build Coastguard Worker            src = os.path.join(root, f)
44*333d2b36SAndroid Build Coastguard Worker            dst = os.path.join(dst_root, f)
45*333d2b36SAndroid Build Coastguard Worker            p = os.path.normpath(os.path.join(rel_root, f))
46*333d2b36SAndroid Build Coastguard Worker            if file_list is not None and p not in file_list:
47*333d2b36SAndroid Build Coastguard Worker                continue
48*333d2b36SAndroid Build Coastguard Worker            if os.path.lexists(dst):
49*333d2b36SAndroid Build Coastguard Worker                if args.ignore_duplicates:
50*333d2b36SAndroid Build Coastguard Worker                    continue
51*333d2b36SAndroid Build Coastguard Worker                sys.exit(f"error: {p} exists in both {args.out_dir} and {in_dir}")
52*333d2b36SAndroid Build Coastguard Worker
53*333d2b36SAndroid Build Coastguard Worker            if not made_parent_dirs:
54*333d2b36SAndroid Build Coastguard Worker                os.makedirs(dst_root, exist_ok=True)
55*333d2b36SAndroid Build Coastguard Worker                made_parent_dirs = True
56*333d2b36SAndroid Build Coastguard Worker
57*333d2b36SAndroid Build Coastguard Worker            shutil.copy2(src, dst, follow_symlinks=False)
58*333d2b36SAndroid Build Coastguard Worker
59*333d2b36SAndroid Build Coastguard Workerif __name__ == "__main__":
60*333d2b36SAndroid Build Coastguard Worker    main()
61