[3/3] scripts/merge-abilist.py: New script to merge abilist files
Commit Message
---
.gitattributes | 1 +
scripts/merge-abilist.py | 112 +++++++++++++++++++++++++++++++++++++++
2 files changed, 113 insertions(+)
create mode 100644 scripts/merge-abilist.py
@@ -1,2 +1,3 @@
ChangeLog merge=merge-changelog
+**.abilist merge=merge-abilist
timezone/* -whitespace
new file mode 100644
@@ -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:])