abitidy: normalise anonymous type names
Currently libabigail exposes the internal names used for anonymous
types in ABI XML. The names are not stable - they are subject to
renumbering - resulting in "harmless" diffs and difficulties in
keeping ABI XML under version control.
* tools/abitidy.cc (set_attribute): Store an attribute
value.
(normalise_anonymous_type_names): Normalise anonymous type
names by stripping off numerical suffices.
(main): Add normalise-anonymous option processing and action.
Bug: 181269148
Change-Id: Ic4d29605a4d4f7f6951ff9f27b536e5f76f32c74
Signed-off-by: Giuliano Procida <gprocida@google.com>
diff --git a/tools/abitidy.cc b/tools/abitidy.cc
index fdb14c5..7c083bb 100644
--- a/tools/abitidy.cc
+++ b/tools/abitidy.cc
@@ -109,6 +109,23 @@
return result;
}
+/// Store an attribute value.
+///
+/// @param node the node
+///
+/// @param name the attribute name
+///
+/// @param value the attribute value, optionally
+static void
+set_attribute(xmlNodePtr node, const char* name,
+ const std::optional<std::string>& value)
+{
+ if (value)
+ xmlSetProp(node, to_libxml(name), to_libxml(value.value().c_str()));
+ else
+ xmlUnsetProp(node, to_libxml(name));
+}
+
/// Remove text nodes, recursively.
///
/// This simplifies subsequent analysis and manipulation. Removing and
@@ -444,6 +461,43 @@
remove_unseen(node);
}
+static const std::map<std::string, std::string> ANONYMOUS_TYPE_NAMES = {
+ {"enum-decl", "__anonymous_enum__"},
+ {"class-decl", "__anonymous_struct__"},
+ {"union-decl", "__anonymous_union__"},
+};
+
+/// Normalise anonymous type names by removing the numerical suffix.
+///
+/// Anonymous type names take the form __anonymous_foo__N where foo is
+/// one of enum, struct or union and N is an optional numerical suffix.
+/// The suffices are senstive to processing order and do not convey
+/// useful ABI information. They can cause spurious harmless diffs and
+/// make XML diffing and rebasing harder.
+///
+/// @param node the XML node to process
+static void
+normalise_anonymous_type_names(xmlNodePtr node)
+{
+ if (node->type != XML_ELEMENT_NODE)
+ return;
+
+ const auto it = ANONYMOUS_TYPE_NAMES.find(from_libxml(node->name));
+ if (it != ANONYMOUS_TYPE_NAMES.end())
+ if (const auto attribute = get_attribute(node, "name"))
+ {
+ const auto& anon = it->second;
+ const auto& name = attribute.value();
+ // __anonymous_foo__123 -> __anonymous_foo__
+ if (!name.compare(0, anon.size(), anon) &&
+ name.find_first_not_of("0123456789", anon.size()) == name.npos)
+ set_attribute(node, "name", anon);
+ }
+
+ for (auto child : get_children(node))
+ normalise_anonymous_type_names(child);
+}
+
/// Main program.
///
/// Read and write ABI XML, with optional extra processing passes.
@@ -462,6 +516,7 @@
int opt_indentation = 2;
bool opt_drop_empty = false;
bool opt_prune_unreachable = false;
+ bool opt_normalise_anonymous = false;
// Process command line.
auto usage = [&]() -> int {
@@ -472,6 +527,7 @@
<< " [-a|--all]"
<< " [-d|--[no-]drop-empty]"
<< " [-p|--[no-]prune-unreachable]"
+ << " [-n|--[no-]normalise-anonymous]"
<< '\n';
return 1;
};
@@ -496,7 +552,7 @@
exit(usage());
}
else if (!strcmp(arg, "-a") || !strcmp(arg, "--all"))
- opt_drop_empty = opt_prune_unreachable = true;
+ opt_drop_empty = opt_prune_unreachable = opt_normalise_anonymous = true;
else if (!strcmp(arg, "-d") || !strcmp(arg, "--drop-empty"))
opt_drop_empty = true;
else if (!strcmp(arg, "--no-drop-empty"))
@@ -505,6 +561,10 @@
opt_prune_unreachable = true;
else if (!strcmp(arg, "--no-prune-unreachable"))
opt_prune_unreachable = false;
+ else if (!strcmp(arg, "-n") || !strcmp(arg, "--normalise-anonymous"))
+ opt_normalise_anonymous = true;
+ else if (!strcmp(arg, "--no-normalise-anonymous"))
+ opt_normalise_anonymous = false;
else
exit(usage());
}
@@ -537,6 +597,11 @@
for (xmlNodePtr node = document->children; node; node = node->next)
strip_text(node);
+ // Normalise anonymous type names.
+ if (opt_normalise_anonymous)
+ for (xmlNodePtr node = document->children; node; node = node->next)
+ normalise_anonymous_type_names(node);
+
// Prune unreachable elements.
if (opt_prune_unreachable)
prune_unreachable(document);