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