xref: /aosp_15_r20/external/cronet/build/config/ios/hardlink.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2017 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Recursively create hardlinks to targets at output."""
6
7
8import argparse
9import os
10import shutil
11import sys
12
13
14def CreateHardlinkHelper(target, output):
15  """
16  Creates hardlink to `target` at `output`.
17
18  If `target` is a directory, the directory structure will be copied and
19  each file will be hardlinked independently. If `target` is a symlink,
20  a new symlink will be created.
21
22  The parent directory of `output` must exists or the function will fail.
23  """
24  if os.path.islink(target):
25    os.symlink(os.readlink(target), output)
26  elif os.path.isfile(target):
27    try:
28      os.link(target, output)
29    except:
30      shutil.copy(target, output)
31  else:
32    os.mkdir(output)
33    for name in os.listdir(target):
34      CreateHardlinkHelper(
35          os.path.join(target, name),
36          os.path.join(output, name))
37
38
39def CreateHardlink(target, output):
40  """
41  Creates hardlink to `target` at `output`.
42
43  If `target` is a directory, the directory structure will be copied and
44  each file will be hardlinked independently. If `target` is a symlink,
45  a new symlink will be created.
46
47  If `output` already exists, it is first deleted. The parent directory
48  of `output` is created if it does not exists.
49  """
50  if os.path.exists(output):
51    if os.path.isdir(output):
52      shutil.rmtree(output)
53    else:
54      os.unlink(output)
55  dirname = os.path.dirname(output)
56  if not os.path.isdir(dirname):
57    os.makedirs(dirname)
58  CreateHardlinkHelper(target, output)
59
60
61def CreateHardlinks(output_dir, relative_to, targets):
62  """
63  Creates hardlinks to `targets` in `output_dir`.
64
65  The `targets` should starts with `relative_to` and the hardlink will
66  be created at `{output_dir}/{os.path.relpath(sources, relative_to)}`.
67
68  Fails with an error if any file in `targets` not located inside the
69  `relative_to` directory or if creating any of the hardlinks fails.
70  """
71  for target in targets:
72    if not target.startswith(relative_to):
73      print(f'error: "{target}" not relative to "{relative_to}',
74            file=sys.stderr)
75      sys.exit(1)
76
77  for target in targets:
78    output = os.path.join(output_dir, os.path.relpath(target, relative_to))
79    CreateHardlink(target, output)
80
81
82def main(args):
83  parser = argparse.ArgumentParser()
84
85  parser.add_argument('--output-dir',
86                      required=True,
87                      help='directory where the hardlinks should be created')
88
89  parser.add_argument('--relative-to',
90                      required=True,
91                      help='sources file will be rebased to this directory')
92
93  parser.add_argument(
94      'sources',
95      nargs='+',
96      help='files that should be hardlinked, must be below RELATIVE_TO')
97
98  parsed = parser.parse_args(args)
99  CreateHardlinks(os.path.normpath(parsed.output_dir),
100                  os.path.normpath(parsed.relative_to) + os.sep,
101                  [os.path.normpath(source) for source in parsed.sources])
102
103
104if __name__ == '__main__':
105  main(sys.argv[1:])
106