|
@@ -0,0 +1,178 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+#
|
|
|
+# Copyright OpenEmbedded Contributors
|
|
|
+#
|
|
|
+# SPDX-License-Identifier: MIT
|
|
|
+#
|
|
|
+# This script is to be called by b4:
|
|
|
+# - through the b4.prep-perpatch-check-cmd with "prep-perpatch-check-cmd" as
|
|
|
+# first argument,
|
|
|
+# - through b4.send-auto-cc-cmd with "send-auto-cc-cmd" as first argument,
|
|
|
+# - through b4.send-auto-to-cmd with "send-auto-to-cmd" as first argument,
|
|
|
+#
|
|
|
+# When prep-perpatch-check-cmd is passsed:
|
|
|
+#
|
|
|
+# This checks that a patch makes changes to at most one project in the poky
|
|
|
+# combo repo (that is, out of yocto-docs, bitbake, openembedded-core combined
|
|
|
+# into poky and the poky-specific files).
|
|
|
+#
|
|
|
+# Printing something to stdout in this file will result in b4 prep --check fail
|
|
|
+# for the currently parsed patch.
|
|
|
+#
|
|
|
+# It checks that all patches in the series make changes to at most one project.
|
|
|
+#
|
|
|
+# When send-auto-cc-cmd is passed:
|
|
|
+#
|
|
|
+# This returns the list of Cc recipients for a patch.
|
|
|
+#
|
|
|
+# When send-auto-to-cmd is passed:
|
|
|
+#
|
|
|
+# This returns the list of To recipients for a patch.
|
|
|
+#
|
|
|
+# This script takes as stdin a patch.
|
|
|
+
|
|
|
+import pathlib
|
|
|
+import re
|
|
|
+import shutil
|
|
|
+import subprocess
|
|
|
+import sys
|
|
|
+
|
|
|
+cmd = sys.argv[1]
|
|
|
+
|
|
|
+patch = sys.stdin.readlines()
|
|
|
+
|
|
|
+# Subject field is used to identify the last patch as this script is called for
|
|
|
+# each patch. We edit the same file in a series by using the References field
|
|
|
+# unique identifier to check which projects are modified by earlier patches in
|
|
|
+# the series. To avoid cluttering the disk, the last patch in the list removes
|
|
|
+# that shared file.
|
|
|
+re_subject = re.compile(r'^Subject:.*\[.*PATCH.*\s(\d+)/\1')
|
|
|
+re_ref = re.compile(r'^References: <(.*)>$')
|
|
|
+
|
|
|
+subject = None
|
|
|
+ref = None
|
|
|
+
|
|
|
+if not shutil.which("lsdiff"):
|
|
|
+ print("lsdiff missing from host, please install patchutils")
|
|
|
+ sys.exit(-1)
|
|
|
+
|
|
|
+try:
|
|
|
+ one_patch_series = False
|
|
|
+ for line in patch:
|
|
|
+ subject = re_subject.match(line)
|
|
|
+ if subject:
|
|
|
+ # Handle [PATCH 1/1]
|
|
|
+ if subject.group(1) == 1:
|
|
|
+ one_patch_series = True
|
|
|
+ break
|
|
|
+ if re.match(r'^Subject: .*\[.*PATCH[^/]*\]', line):
|
|
|
+ # Single patch is named [PATCH] but if there are prefix, it could be
|
|
|
+ # [PATCH prefix], so handle everything that doesn't have a /
|
|
|
+ # character which is used as separator between current patch number
|
|
|
+ # and total patch number
|
|
|
+ one_patch_series = True
|
|
|
+ break
|
|
|
+
|
|
|
+ if cmd == "prep-perpatch-check-cmd" and not one_patch_series:
|
|
|
+ for line in patch:
|
|
|
+ ref = re_ref.match(line)
|
|
|
+ if ref:
|
|
|
+ break
|
|
|
+
|
|
|
+ if not ref:
|
|
|
+ print("Failed to find ref to cover letter (References:)...")
|
|
|
+ sys.exit(-2)
|
|
|
+
|
|
|
+ ref = ref.group(1)
|
|
|
+ series_check = pathlib.Path(f".tmp-{ref}")
|
|
|
+
|
|
|
+ patch = "".join(patch)
|
|
|
+
|
|
|
+ if cmd == "send-auto-cc-cmd":
|
|
|
+ # Patches to BitBake documentation should also go to yocto-docs mailing list
|
|
|
+ project_paths = {
|
|
|
+ "yocto-docs": ["bitbake/doc/*"],
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ project_paths = {
|
|
|
+ "bitbake": ["bitbake/*"],
|
|
|
+ "yocto-docs": ["documentation/*"],
|
|
|
+ "poky": [
|
|
|
+ "meta-poky/*",
|
|
|
+ "meta-yocto-bsp/*",
|
|
|
+ "README.hardware.md",
|
|
|
+ "README.poky.md",
|
|
|
+ ],
|
|
|
+ }
|
|
|
+
|
|
|
+ # List of projects touched by this patch
|
|
|
+ projs = []
|
|
|
+
|
|
|
+ # Any file not matched by any path in project_paths means it is from
|
|
|
+ # OE-Core.
|
|
|
+ # When matching some path in project_paths, remove the matched files from
|
|
|
+ # that list.
|
|
|
+ files_left = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"],
|
|
|
+ input=patch, text=True)
|
|
|
+ files_left = set(files_left)
|
|
|
+
|
|
|
+ for proj, proj_paths in project_paths.items():
|
|
|
+ lsdiff_args = [f"--include={path}" for path in proj_paths]
|
|
|
+ files = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"] + lsdiff_args,
|
|
|
+ input=patch, text=True)
|
|
|
+ if len(files):
|
|
|
+ files_left = files_left - set(files)
|
|
|
+ projs.append(proj)
|
|
|
+ continue
|
|
|
+
|
|
|
+ # Handle patches made with --no-prefix
|
|
|
+ files = subprocess.check_output(["lsdiff"] + lsdiff_args,
|
|
|
+ input=patch, text=True)
|
|
|
+ if len(files):
|
|
|
+ files_left = files_left - set(files)
|
|
|
+ projs.append(proj)
|
|
|
+
|
|
|
+ # Catch-all for everything not poky-specific or in bitbake/yocto-docs
|
|
|
+ if len(files_left) and cmd != "send-auto-cc-cmd":
|
|
|
+ projs.append("openembedded-core")
|
|
|
+
|
|
|
+ if cmd == "prep-perpatch-check-cmd":
|
|
|
+ if len(projs) > 1:
|
|
|
+ print(f"Diff spans more than one project ({', '.join(sorted(projs))}), split into multiple commits...")
|
|
|
+ sys.exit(-3)
|
|
|
+
|
|
|
+ # No need to check other patches in the series as there aren't any
|
|
|
+ if one_patch_series:
|
|
|
+ sys.exit(0)
|
|
|
+
|
|
|
+ # This should be replaced once b4 supports prep-perseries-check-cmd (or something similar)
|
|
|
+
|
|
|
+ if series_check.exists():
|
|
|
+ # NOT race-free if b4 decides to parallelize prep-perpatch-check-cmd
|
|
|
+ series_projs = series_check.read_text().split('\n')
|
|
|
+ else:
|
|
|
+ series_projs = []
|
|
|
+
|
|
|
+ series_projs += projs
|
|
|
+ uniq_series_projs = set(series_projs)
|
|
|
+ # NOT race-free, if b4 decides to parallelize prep-perpatch-check-cmd
|
|
|
+ series_check.write_text('\n'.join(uniq_series_projs))
|
|
|
+
|
|
|
+ if len(uniq_series_projs) > 1:
|
|
|
+ print(f"Series spans more than one project ({', '.join(sorted(uniq_series_projs))}), split into multiple series...")
|
|
|
+ sys.exit(-4)
|
|
|
+ else: # send-auto-cc-cmd / send-auto-to-cmd
|
|
|
+ ml_projs = {
|
|
|
+ "bitbake": "bitbake-devel@lists.openembedded.org",
|
|
|
+ "yocto-docs": "docs@lists.yoctoproject.org",
|
|
|
+ "poky": "poky@lists.yoctoproject.org",
|
|
|
+ "openembedded-core": "openembedded-core@lists.openembedded.org",
|
|
|
+ }
|
|
|
+
|
|
|
+ print("\n".join([ml_projs[ml] for ml in projs]))
|
|
|
+
|
|
|
+ sys.exit(0)
|
|
|
+finally:
|
|
|
+ # Last patch in the series, cleanup tmp file
|
|
|
+ if subject and ref and series_check.exists():
|
|
|
+ series_check.unlink()
|