blob: 43690eb940bd342114a9322cbd354a60989b3fa5 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2023 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tool for converting GN runtime_deps to CAS archive paths."""
import argparse
from collections import deque
import filecmp
import json
import logging
from pathlib import Path
import os
EXCLUDE_DIRS = {
'.git',
'__pycache__',
}
def parse_runtime_deps(runtime_deps):
"""Parses GN's `runtime_deps` format."""
with runtime_deps:
return [line.rstrip() for line in runtime_deps]
def resolve_paths(root, initial_paths):
"""Converts paths to CAS archive paths format."""
absolute_root = os.path.abspath(root)
resolved_paths = []
unvisited_paths = deque(map(Path, initial_paths))
while unvisited_paths:
path = unvisited_paths.popleft()
if not path.exists():
logging.warning('"%(path)s" does not exist', {'path': path})
continue
if path.is_dir():
# Expand specific children if any are excluded.
child_paths = expand_dir(path)
if child_paths:
unvisited_paths.extendleft(child_paths)
continue
resolved_paths.append(os.path.relpath(path, start=absolute_root))
resolved_paths.sort()
return [[absolute_root, path] for path in resolved_paths]
def expand_dir(path):
"""Explicitly expands directory if any children are excluded."""
expand = False
expanded_paths = []
for child_path in path.iterdir():
if child_path.name in EXCLUDE_DIRS and path.is_dir():
expand = True
continue
expanded_paths.append(child_path)
return expanded_paths if expand else []
def replace_output(resolved, output_path):
"""Atomically replaces the output with the resolved JSON if changed."""
new_output_path = output_path + '.new'
try:
with open(new_output_path, 'w', encoding='ascii') as new_output:
json.dump(resolved, new_output)
if (os.path.exists(output_path) and
filecmp.cmp(new_output_path, output_path, shallow=False)):
return
os.replace(new_output_path, output_path)
new_output_path = None
finally:
if new_output_path:
os.remove(new_output_path)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--root')
parser.add_argument(
'runtime_deps',
help='runtime_deps written by GN',
type=argparse.FileType('r', encoding='utf_8'),
metavar='input.runtime_deps')
parser.add_argument(
'output_json',
help='CAS archive paths in JSON format',
metavar='output.json')
args = parser.parse_args()
runtime_deps = parse_runtime_deps(args.runtime_deps)
resolved_paths = resolve_paths(args.root, runtime_deps)
replace_output(resolved_paths, args.output_json)
if __name__ == '__main__':
main()