xref: /aosp_15_r20/external/pdfium/testing/tools/generate_cas_paths.py (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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