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