3 # This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4 # See https://llvm.org/LICENSE.txt for license information.
5 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 """Overlays two directories into a target directory using symlinks.
8 Tries to minimize the number of symlinks created (that is, does not symlink
9 every single file). Symlinks every file in the overlay directory. Only symlinks
10 individual files in the source directory if their parent directory is also
11 contained in the overlay directory tree.
20 def _check_python_version():
21 if sys
.version_info
[0] < 3:
23 "Must be invoked with a python 3 interpreter but was %s" %
27 def _check_dir_exists(path
):
28 if not os
.path
.isdir(path
):
29 raise OSError(errno
.ENOENT
, os
.strerror(errno
.ENOENT
), path
)
32 def parse_arguments():
33 parser
= argparse
.ArgumentParser(description
="""
34 Overlays two directories into a target directory using symlinks.
36 Tries to minimize the number of symlinks created (that is, does not symlink
37 every single file). Symlinks every file in the overlay directory. Only
38 symlinks individual files in the source directory if their parent directory
39 is also contained in the overlay directory tree.
44 help="Directory that contains most of the content to symlink.")
48 help="Directory to overlay on top of the source directory.")
52 help="Directory in which to place the fused symlink directories.")
54 args
= parser
.parse_args()
56 _check_dir_exists(args
.target
)
57 _check_dir_exists(args
.overlay
)
58 _check_dir_exists(args
.src
)
63 def _symlink_abs(from_path
, to_path
):
64 os
.symlink(os
.path
.abspath(from_path
), os
.path
.abspath(to_path
))
68 for root
, dirs
, files
in os
.walk(args
.overlay
):
69 # We could do something more intelligent here and only symlink individual
70 # files if the directory is present in both overlay and src. This could also
71 # be generalized to an arbitrary number of directories without any
72 # "src/overlay" distinction. In the current use case we only have two and
73 # the overlay directory is always small, so putting that off for now.
74 rel_root
= os
.path
.relpath(root
, start
=args
.overlay
)
76 os
.mkdir(os
.path
.join(args
.target
, rel_root
))
79 relpath
= os
.path
.join(rel_root
, file)
80 _symlink_abs(os
.path
.join(args
.overlay
, relpath
),
81 os
.path
.join(args
.target
, relpath
))
83 for src_entry
in os
.listdir(os
.path
.join(args
.src
, rel_root
)):
84 if src_entry
not in dirs
:
85 relpath
= os
.path
.join(rel_root
, src_entry
)
86 _symlink_abs(os
.path
.join(args
.src
, relpath
),
87 os
.path
.join(args
.target
, relpath
))
90 if __name__
== "__main__":
91 _check_python_version()
92 main(parse_arguments())