1*3ac0a46fSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*3ac0a46fSAndroid Build Coastguard Worker# Copyright 2023 The PDFium Authors 3*3ac0a46fSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*3ac0a46fSAndroid Build Coastguard Worker# found in the LICENSE file. 5*3ac0a46fSAndroid Build Coastguard Worker"""Tool for converting GN runtime_deps to CAS archive paths.""" 6*3ac0a46fSAndroid Build Coastguard Worker 7*3ac0a46fSAndroid Build Coastguard Workerimport argparse 8*3ac0a46fSAndroid Build Coastguard Workerfrom collections import deque 9*3ac0a46fSAndroid Build Coastguard Workerimport filecmp 10*3ac0a46fSAndroid Build Coastguard Workerimport json 11*3ac0a46fSAndroid Build Coastguard Workerimport logging 12*3ac0a46fSAndroid Build Coastguard Workerfrom pathlib import Path 13*3ac0a46fSAndroid Build Coastguard Workerimport os 14*3ac0a46fSAndroid Build Coastguard Worker 15*3ac0a46fSAndroid Build Coastguard WorkerEXCLUDE_DIRS = { 16*3ac0a46fSAndroid Build Coastguard Worker '.git', 17*3ac0a46fSAndroid Build Coastguard Worker '__pycache__', 18*3ac0a46fSAndroid Build Coastguard Worker} 19*3ac0a46fSAndroid Build Coastguard Worker 20*3ac0a46fSAndroid Build Coastguard Worker 21*3ac0a46fSAndroid Build Coastguard Workerdef parse_runtime_deps(runtime_deps): 22*3ac0a46fSAndroid Build Coastguard Worker """Parses GN's `runtime_deps` format.""" 23*3ac0a46fSAndroid Build Coastguard Worker with runtime_deps: 24*3ac0a46fSAndroid Build Coastguard Worker return [line.rstrip() for line in runtime_deps] 25*3ac0a46fSAndroid Build Coastguard Worker 26*3ac0a46fSAndroid Build Coastguard Worker 27*3ac0a46fSAndroid Build Coastguard Workerdef resolve_paths(root, initial_paths): 28*3ac0a46fSAndroid Build Coastguard Worker """Converts paths to CAS archive paths format.""" 29*3ac0a46fSAndroid Build Coastguard Worker absolute_root = os.path.abspath(root) 30*3ac0a46fSAndroid Build Coastguard Worker 31*3ac0a46fSAndroid Build Coastguard Worker resolved_paths = [] 32*3ac0a46fSAndroid Build Coastguard Worker unvisited_paths = deque(map(Path, initial_paths)) 33*3ac0a46fSAndroid Build Coastguard Worker while unvisited_paths: 34*3ac0a46fSAndroid Build Coastguard Worker path = unvisited_paths.popleft() 35*3ac0a46fSAndroid Build Coastguard Worker 36*3ac0a46fSAndroid Build Coastguard Worker if not path.exists(): 37*3ac0a46fSAndroid Build Coastguard Worker logging.warning('"%(path)s" does not exist', {'path': path}) 38*3ac0a46fSAndroid Build Coastguard Worker continue 39*3ac0a46fSAndroid Build Coastguard Worker 40*3ac0a46fSAndroid Build Coastguard Worker if path.is_dir(): 41*3ac0a46fSAndroid Build Coastguard Worker # Expand specific children if any are excluded. 42*3ac0a46fSAndroid Build Coastguard Worker child_paths = expand_dir(path) 43*3ac0a46fSAndroid Build Coastguard Worker if child_paths: 44*3ac0a46fSAndroid Build Coastguard Worker unvisited_paths.extendleft(child_paths) 45*3ac0a46fSAndroid Build Coastguard Worker continue 46*3ac0a46fSAndroid Build Coastguard Worker 47*3ac0a46fSAndroid Build Coastguard Worker resolved_paths.append(os.path.relpath(path, start=absolute_root)) 48*3ac0a46fSAndroid Build Coastguard Worker 49*3ac0a46fSAndroid Build Coastguard Worker resolved_paths.sort() 50*3ac0a46fSAndroid Build Coastguard Worker return [[absolute_root, path] for path in resolved_paths] 51*3ac0a46fSAndroid Build Coastguard Worker 52*3ac0a46fSAndroid Build Coastguard Worker 53*3ac0a46fSAndroid Build Coastguard Workerdef expand_dir(path): 54*3ac0a46fSAndroid Build Coastguard Worker """Explicitly expands directory if any children are excluded.""" 55*3ac0a46fSAndroid Build Coastguard Worker expand = False 56*3ac0a46fSAndroid Build Coastguard Worker expanded_paths = [] 57*3ac0a46fSAndroid Build Coastguard Worker 58*3ac0a46fSAndroid Build Coastguard Worker for child_path in path.iterdir(): 59*3ac0a46fSAndroid Build Coastguard Worker if child_path.name in EXCLUDE_DIRS and path.is_dir(): 60*3ac0a46fSAndroid Build Coastguard Worker expand = True 61*3ac0a46fSAndroid Build Coastguard Worker continue 62*3ac0a46fSAndroid Build Coastguard Worker expanded_paths.append(child_path) 63*3ac0a46fSAndroid Build Coastguard Worker 64*3ac0a46fSAndroid Build Coastguard Worker return expanded_paths if expand else [] 65*3ac0a46fSAndroid Build Coastguard Worker 66*3ac0a46fSAndroid Build Coastguard Worker 67*3ac0a46fSAndroid Build Coastguard Workerdef replace_output(resolved, output_path): 68*3ac0a46fSAndroid Build Coastguard Worker """Atomically replaces the output with the resolved JSON if changed.""" 69*3ac0a46fSAndroid Build Coastguard Worker new_output_path = output_path + '.new' 70*3ac0a46fSAndroid Build Coastguard Worker try: 71*3ac0a46fSAndroid Build Coastguard Worker with open(new_output_path, 'w', encoding='ascii') as new_output: 72*3ac0a46fSAndroid Build Coastguard Worker json.dump(resolved, new_output) 73*3ac0a46fSAndroid Build Coastguard Worker 74*3ac0a46fSAndroid Build Coastguard Worker if (os.path.exists(output_path) and 75*3ac0a46fSAndroid Build Coastguard Worker filecmp.cmp(new_output_path, output_path, shallow=False)): 76*3ac0a46fSAndroid Build Coastguard Worker return 77*3ac0a46fSAndroid Build Coastguard Worker 78*3ac0a46fSAndroid Build Coastguard Worker os.replace(new_output_path, output_path) 79*3ac0a46fSAndroid Build Coastguard Worker new_output_path = None 80*3ac0a46fSAndroid Build Coastguard Worker finally: 81*3ac0a46fSAndroid Build Coastguard Worker if new_output_path: 82*3ac0a46fSAndroid Build Coastguard Worker os.remove(new_output_path) 83*3ac0a46fSAndroid Build Coastguard Worker 84*3ac0a46fSAndroid Build Coastguard Worker 85*3ac0a46fSAndroid Build Coastguard Workerdef main(): 86*3ac0a46fSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description=__doc__) 87*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument('--root') 88*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 89*3ac0a46fSAndroid Build Coastguard Worker 'runtime_deps', 90*3ac0a46fSAndroid Build Coastguard Worker help='runtime_deps written by GN', 91*3ac0a46fSAndroid Build Coastguard Worker type=argparse.FileType('r', encoding='utf_8'), 92*3ac0a46fSAndroid Build Coastguard Worker metavar='input.runtime_deps') 93*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 94*3ac0a46fSAndroid Build Coastguard Worker 'output_json', 95*3ac0a46fSAndroid Build Coastguard Worker help='CAS archive paths in JSON format', 96*3ac0a46fSAndroid Build Coastguard Worker metavar='output.json') 97*3ac0a46fSAndroid Build Coastguard Worker args = parser.parse_args() 98*3ac0a46fSAndroid Build Coastguard Worker 99*3ac0a46fSAndroid Build Coastguard Worker runtime_deps = parse_runtime_deps(args.runtime_deps) 100*3ac0a46fSAndroid Build Coastguard Worker resolved_paths = resolve_paths(args.root, runtime_deps) 101*3ac0a46fSAndroid Build Coastguard Worker replace_output(resolved_paths, args.output_json) 102*3ac0a46fSAndroid Build Coastguard Worker 103*3ac0a46fSAndroid Build Coastguard Worker 104*3ac0a46fSAndroid Build Coastguard Workerif __name__ == '__main__': 105*3ac0a46fSAndroid Build Coastguard Worker main() 106