diff options
Diffstat (limited to 'scripts/b4-wrapper-poky.py')
-rwxr-xr-x | scripts/b4-wrapper-poky.py | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/scripts/b4-wrapper-poky.py b/scripts/b4-wrapper-poky.py new file mode 100755 index 0000000000..f1170db06b --- /dev/null +++ b/scripts/b4-wrapper-poky.py | |||
@@ -0,0 +1,185 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | # | ||
3 | # Copyright OpenEmbedded Contributors | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | # | ||
7 | # This script is to be called by b4: | ||
8 | # - through the b4.prep-perpatch-check-cmd with "prep-perpatch-check-cmd" as | ||
9 | # first argument, | ||
10 | # - through b4.send-auto-cc-cmd with "send-auto-cc-cmd" as first argument, | ||
11 | # - through b4.send-auto-to-cmd with "send-auto-to-cmd" as first argument, | ||
12 | # | ||
13 | # When prep-perpatch-check-cmd is passsed: | ||
14 | # | ||
15 | # This checks that a patch makes changes to at most one project in the poky | ||
16 | # combo repo (that is, out of yocto-docs, bitbake, openembedded-core combined | ||
17 | # into poky and the poky-specific files). | ||
18 | # | ||
19 | # Printing something to stdout in this file will result in b4 prep --check fail | ||
20 | # for the currently parsed patch. | ||
21 | # | ||
22 | # It checks that all patches in the series make changes to at most one project. | ||
23 | # | ||
24 | # When send-auto-cc-cmd is passed: | ||
25 | # | ||
26 | # This returns the list of Cc recipients for a patch. | ||
27 | # | ||
28 | # When send-auto-to-cmd is passed: | ||
29 | # | ||
30 | # This returns the list of To recipients for a patch. | ||
31 | # | ||
32 | # This script takes as stdin a patch. | ||
33 | |||
34 | import pathlib | ||
35 | import re | ||
36 | import shutil | ||
37 | import subprocess | ||
38 | import sys | ||
39 | |||
40 | cmd = sys.argv[1] | ||
41 | |||
42 | patch = sys.stdin.readlines() | ||
43 | |||
44 | # Subject field is used to identify the last patch as this script is called for | ||
45 | # each patch. We edit the same file in a series by using the References field | ||
46 | # unique identifier to check which projects are modified by earlier patches in | ||
47 | # the series. To avoid cluttering the disk, the last patch in the list removes | ||
48 | # that shared file. | ||
49 | re_subject = re.compile(r'^Subject:.*\[.*PATCH.*\s(\d+)/\1') | ||
50 | re_ref = re.compile(r'^References: <(.*)>$') | ||
51 | |||
52 | subject = None | ||
53 | ref = None | ||
54 | |||
55 | if not shutil.which("lsdiff"): | ||
56 | print("lsdiff missing from host, please install patchutils", file=sys.stderr) | ||
57 | sys.exit(-1) | ||
58 | |||
59 | try: | ||
60 | one_patch_series = False | ||
61 | for line in patch: | ||
62 | subject = re_subject.match(line) | ||
63 | if subject: | ||
64 | # Handle [PATCH 1/1] | ||
65 | if subject.group(1) == 1: | ||
66 | one_patch_series = True | ||
67 | break | ||
68 | if re.match(r'^Subject: .*\[.*PATCH[^/]*\]', line): | ||
69 | # Single patch is named [PATCH] but if there are prefix, it could be | ||
70 | # [PATCH prefix], so handle everything that doesn't have a / | ||
71 | # character which is used as separator between current patch number | ||
72 | # and total patch number | ||
73 | one_patch_series = True | ||
74 | break | ||
75 | |||
76 | if cmd == "prep-perpatch-check-cmd" and not one_patch_series: | ||
77 | for line in patch: | ||
78 | ref = re_ref.match(line) | ||
79 | if ref: | ||
80 | break | ||
81 | |||
82 | if not ref: | ||
83 | print("Failed to find ref to cover letter (References:)...", file=sys.stderr) | ||
84 | sys.exit(-2) | ||
85 | |||
86 | ref = ref.group(1) | ||
87 | series_check = pathlib.Path(f".tmp-{ref}") | ||
88 | |||
89 | patch = "".join(patch) | ||
90 | |||
91 | if cmd == "send-auto-cc-cmd": | ||
92 | # Patches to BitBake documentation should also go to yocto-docs mailing list | ||
93 | project_paths = { | ||
94 | "yocto-docs": ["bitbake/doc/*"], | ||
95 | } | ||
96 | else: | ||
97 | project_paths = { | ||
98 | "bitbake": ["bitbake/*"], | ||
99 | "yocto-docs": ["documentation/*"], | ||
100 | "poky": [ | ||
101 | "meta-poky/*", | ||
102 | "meta-yocto-bsp/*", | ||
103 | "README.hardware.md", | ||
104 | "README.poky.md", | ||
105 | # scripts/b4-wrapper-poky.py is only run by b4 when in poky | ||
106 | # git repo. With that limitation, changes made to .b4-config | ||
107 | # can only be for poky's and not OE-Core's as only poky's is | ||
108 | # stored in poky git repo. | ||
109 | ".b4-config", | ||
110 | ], | ||
111 | } | ||
112 | |||
113 | # List of projects touched by this patch | ||
114 | projs = [] | ||
115 | |||
116 | # Any file not matched by any path in project_paths means it is from | ||
117 | # OE-Core. | ||
118 | # When matching some path in project_paths, remove the matched files from | ||
119 | # that list. | ||
120 | files_left = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"], | ||
121 | input=patch, text=True) | ||
122 | files_left = set(files_left) | ||
123 | |||
124 | for proj, proj_paths in project_paths.items(): | ||
125 | lsdiff_args = [f"--include={path}" for path in proj_paths] | ||
126 | files = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"] + lsdiff_args, | ||
127 | input=patch, text=True) | ||
128 | if len(files): | ||
129 | files_left = files_left - set(files) | ||
130 | projs.append(proj) | ||
131 | continue | ||
132 | |||
133 | # Handle patches made with --no-prefix | ||
134 | files = subprocess.check_output(["lsdiff"] + lsdiff_args, | ||
135 | input=patch, text=True) | ||
136 | if len(files): | ||
137 | files_left = files_left - set(files) | ||
138 | projs.append(proj) | ||
139 | |||
140 | # Catch-all for everything not poky-specific or in bitbake/yocto-docs | ||
141 | if len(files_left) and cmd != "send-auto-cc-cmd": | ||
142 | projs.append("openembedded-core") | ||
143 | |||
144 | if cmd == "prep-perpatch-check-cmd": | ||
145 | if len(projs) > 1: | ||
146 | print(f"Diff spans more than one project ({', '.join(sorted(projs))}), split into multiple commits...", | ||
147 | file=sys.stderr) | ||
148 | sys.exit(-3) | ||
149 | |||
150 | # No need to check other patches in the series as there aren't any | ||
151 | if one_patch_series: | ||
152 | sys.exit(0) | ||
153 | |||
154 | # This should be replaced once b4 supports prep-perseries-check-cmd (or something similar) | ||
155 | |||
156 | if series_check.exists(): | ||
157 | # NOT race-free if b4 decides to parallelize prep-perpatch-check-cmd | ||
158 | series_projs = series_check.read_text().split('\n') | ||
159 | else: | ||
160 | series_projs = [] | ||
161 | |||
162 | series_projs += projs | ||
163 | uniq_series_projs = set(series_projs) | ||
164 | # NOT race-free, if b4 decides to parallelize prep-perpatch-check-cmd | ||
165 | series_check.write_text('\n'.join(uniq_series_projs)) | ||
166 | |||
167 | if len(uniq_series_projs) > 1: | ||
168 | print(f"Series spans more than one project ({', '.join(sorted(uniq_series_projs))}), split into multiple series...", | ||
169 | file=sys.stderr) | ||
170 | sys.exit(-4) | ||
171 | else: # send-auto-cc-cmd / send-auto-to-cmd | ||
172 | ml_projs = { | ||
173 | "bitbake": "bitbake-devel@lists.openembedded.org", | ||
174 | "yocto-docs": "docs@lists.yoctoproject.org", | ||
175 | "poky": "poky@lists.yoctoproject.org", | ||
176 | "openembedded-core": "openembedded-core@lists.openembedded.org", | ||
177 | } | ||
178 | |||
179 | print("\n".join([ml_projs[ml] for ml in projs])) | ||
180 | |||
181 | sys.exit(0) | ||
182 | finally: | ||
183 | # Last patch in the series, cleanup tmp file | ||
184 | if subject and ref and series_check.exists(): | ||
185 | series_check.unlink() | ||