blob: 149cceeac0f8eadd2d65c73774d3e898529ffac1 [file] [log] [blame]
Devon Loehr3b59be92025-03-10 14:16:161# Copyright 2025 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import sys
7
8###
9# The ASTRewriter plugin emits substitution directives independently for each
10# TU. This means there will be several duplicates (e.g. in headers that are
11# included in multiple source files). The format used for paths is also
12# inconsistent.
13#
14# This is a general-purpose post-processing script that can deduplicate,
15# filter edits based on path, add user headers if not already present, etc.
16# It also adds the begin/end tags when writing out the edits.
17#
18# usage: `python3 dedup.py directives.txt`
19###
20
21### Configurable options
22# List of headers to add to each modified file
23headers_to_add = ["base/strings/to_string.h"]
24# List of paths we do/don't want to replace in
25paths_to_exclude = ["third_party"]
26paths_to_include = ["/components/", "/content/", "/chrome/"]
27
28
29# Paths we don't want to process
30def filter_path(path):
31 """
32 Examine a path and return true if we want to filter it out,
33 e.g. because it's in third_party. Feel free to customize the logic.
34 """
35 if (any(exclude in path for exclude in paths_to_exclude)):
36 return True
37
38 if (not any(include in path for include in paths_to_include)):
39 return True
40
41 return False
42
43
44### Actual work
45def ProcessFile(filename, deduped_contents, unique_paths):
46 """ Read every replacement in a file, normalizing paths and removing
47 duplicates, as well as any paths we choose to filter out. Keep track
48 of all unique paths we see so we know which files to add headers to.
49
50 filename: the name of the file to be processed
51 deduped_contents: the set of replacements we've already processed
52 unique_paths: the set of unique replacement paths we've seen.
53 """
54 with open(filename) as f:
55 for line in f.readlines():
56 parts = line.split(":::")
57 if len(parts) < 2:
58 print("Skipping unexpected line: ", line)
59 continue
60 path = os.path.normpath(parts[1])
61 if filter_path(path):
62 continue
63
64 if path not in unique_paths:
65 unique_paths.add(path)
66
67 parts[1] = path
68 new_line = ":::".join(parts)
69 if new_line not in deduped_contents:
70 deduped_contents.add(new_line)
71
72
73def DedupFiles(filenames):
74 deduped_contents = set()
75 unique_paths = set()
76
77 for file in filenames:
78 ProcessFile(file, deduped_contents, unique_paths)
79
80 # This may not be necessary if the tool already emits these directives,
81 # but sometimes that may be inconvenient.
82 for path in unique_paths:
83 for header in headers_to_add:
84 deduped_contents.add(
85 f"include-user-header:::{path}:::-1:::-1:::{header}\n")
86
87 output_file = "deduped.txt"
88 WriteFile(output_file, sorted(deduped_contents))
89
90
91def WriteFile(outfile, lines):
92 with open(outfile, "w") as f:
93 f.write("==== BEGIN EDITS ====\n")
94 f.write("".join(lines))
95 f.write("==== END EDITS ====\n")
96
97
98if __name__ == "__main__":
99 DedupFiles(sys.argv[1:])