diff mbox series

[3/3] scripts/merge-abilist.py: New script to merge abilist files

Message ID a00595a82538890033a91dce12c15238ba4afc1c.1615537262.git.fweimer@redhat.com
State New
Headers show
Series Merge helper for abilist files | expand

Commit Message

Florian Weimer March 12, 2021, 8:24 a.m. UTC
---
 .gitattributes           |   1 +
 scripts/merge-abilist.py | 112 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+)
 create mode 100644 scripts/merge-abilist.py
diff mbox series

Patch

diff --git a/.gitattributes b/.gitattributes
index 06b553db80..859638588b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,3 @@ 
 ChangeLog    merge=merge-changelog
+**.abilist   merge=merge-abilist
 timezone/* -whitespace
diff --git a/scripts/merge-abilist.py b/scripts/merge-abilist.py
new file mode 100644
index 0000000000..ad2bae2b8a
--- /dev/null
+++ b/scripts/merge-abilist.py
@@ -0,0 +1,112 @@ 
+#!/usr/bin/python3
+# Automatic merging of abilist updates for Git.
+# Copyright (C) 2021 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+"""Merge helper for consolidating changes in abilist files.
+
+To active, copy glibcsymbols.py and this file to a separate directory,
+make merge-abilist.py executable, and add this to .git/config:
+
+[merge "merge-abilist"]
+	name = Merger for abilists
+	driver = /path/to/merge-abilist.py %O %A %B %P
+"""
+
+import argparse
+import os
+import sys
+
+# Make available glibc Python modules.
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+
+import glibcsymbols
+
+def get_parser():
+    """Return an argument parser for this module."""
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('ancestor',
+                        help='path to the file with the merge ancestor')
+    parser.add_argument('current',
+                        help='path to the file with the current version')
+    parser.add_argument('other',
+                        help='path to the file with the other branch version')
+    parser.add_argument('path', help='path name in the source tree')
+    return parser
+
+def main(argv):
+    parser = get_parser()
+    opts = parser.parse_args(argv)
+
+    if not opts.path.endswith(".abilist"):
+        # Not an abilist file.
+        sys.exit(3)
+
+    ancestor = glibcsymbols.read_abilist(opts.ancestor)
+    current = glibcsymbols.read_abilist(opts.current)
+    other = glibcsymbols.read_abilist(opts.other)
+
+    added_symbols_current = current.keys() - ancestor.keys()
+    deleted_symbols_current = ancestor.keys() - current.keys()
+    added_symbols_other = other.keys() - ancestor.keys()
+    deleted_symbols_other = ancestor.keys() - other.keys()
+
+    # Check that the symbols have the same value if present in both inputs.
+    ok = True
+    for common_symbol in current.keys() & other.keys():
+        if current[common_symbol] != other[common_symbol]:
+            print('{}: mismatch for {}: {!r} != {!r}'.format(
+                opts.path, common_symbol,
+                current[common_symbol], other[common_symbol]))
+            ok = False
+    # Check that there are no add/delete conflicts.
+    for common_symbol in added_symbols_current & deleted_symbols_other:
+        print('{}: added in current branch, deleted in other'.format(
+            common_symbol))
+        ok = False
+    for common_symbol in added_symbols_other & deleted_symbols_current:
+        print('{}: added in other other branch, deleted in current'.format(
+            common_symbol))
+        ok = False
+    if not ok:
+        sys.exit(2)
+
+    # Apply the updates from the other branch.
+    result = current.copy()
+    for symbol in added_symbols_other:
+        result[symbol] = other[symbol]
+    for symbol in deleted_symbols_other:
+        del result[symbol]
+
+    # Introduce placeholder symbols if a symbol set was completely
+    # deleted.
+    placeholder = "__{}_version_placeholder".format(
+        os.path.basename(opts.path)[:-len(".abilist")])
+    old_versions = set(versym.version for versym in current.keys())
+    new_versions = set(versym.version for versym in result.keys())
+    for missing_version in old_versions - new_versions:
+        result[glibcsymbols.VersionedSymbol(
+            placeholder, missing_version)] = 'F'
+
+    # Write out the merged abilist.  Do not use replace_file because
+    # of the file system race due to the ...T name.
+    with open(opts.current, 'w') as out:
+        for line in glibcsymbols.abilist_lines(result):
+            out.write(line)
+
+if __name__ == '__main__':
+    main(sys.argv[1:])