From: Fred Morcos Date: Mon, 7 Apr 2025 12:46:46 +0000 (+0200) Subject: config: Replace settings-history Perl script with Python rewrite X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=873bcd9faac65e3ebd039a20d6d9890fa11347e4;p=thirdparty%2Fdovecot%2Fcore.git config: Replace settings-history Perl script with Python rewrite --- diff --git a/src/lib-settings/Makefile.am b/src/lib-settings/Makefile.am index 1fbd90ef6c..f11abae2c2 100644 --- a/src/lib-settings/Makefile.am +++ b/src/lib-settings/Makefile.am @@ -23,12 +23,12 @@ BUILT_SOURCES = settings-history-core.c settings-history.c: settings-history-core.c -settings-history-core.c: $(top_srcdir)/src/lib-settings/settings-history.pl $(top_srcdir)/src/lib-settings/settings-history-core.txt - $(AM_V_GEN)$(top_srcdir)/src/lib-settings/settings-history.pl $(top_srcdir)/src/lib-settings/settings-history-core.txt $(DOVECOT_PRO_BUILD) > settings-history-core.c || rm -f settings-history-core.c +settings-history-core.c: $(srcdir)/settings-history-core.txt $(srcdir)/settings-history.py + $(AM_V_GEN)$(srcdir)/settings-history.py --pro $(DOVECOT_PRO_BUILD) $< $@ EXTRA_DIST = \ settings-history-core.txt \ - settings-history.pl + settings-history.py test_programs = \ test-settings-parser \ diff --git a/src/lib-settings/settings-history.pl b/src/lib-settings/settings-history.pl deleted file mode 100755 index 11f7b54636..0000000000 --- a/src/lib-settings/settings-history.pl +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -use version; -use 5.010; - -my ($input_path, $pro_edition) = ($ARGV[0], $ARGV[1]); - -# output renames -print "static const struct setting_history_rename settings_history_core_renames[] = {\n"; -my $f; -open $f, "<:encoding(utf8)", $input_path or die "$!"; -my $prev_version = ""; -while (<$f>) { - chomp; - if (/^rename\t/) { - my ($type, $old_key, $new_key, $ce_version, $pro_version) = split("\t"); - my $version = $pro_edition ? $pro_version : $ce_version; - if ($version ne "") { - die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/; - print " { \"$old_key\", \"$new_key\", \"$version\" },\n"; - if ($prev_version ne "") { - die "Bad version sorting order" if version->parse($version) > version->parse($prev_version); - } - $prev_version = $version; - } - } elsif (! /^default\t/) { - die "Unknown line: $_"; - } -} -close $f; -print "};\n"; - -# output defaults -print "static const struct setting_history_default settings_history_core_defaults[] = {\n"; -open $f, "<:encoding(utf8)", $input_path or die "$!"; -while (<$f>) { - chomp; - if (/^default\t/) { - my ($type, $key, $old_value, $ce_version, $pro_version) = split("\t"); - my $version = $pro_edition ? $pro_version : $ce_version; - if ($version ne "") { - die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/; - print " { \"$key\", \"$old_value\", \"$version\" },\n"; - if ($prev_version ne "") { - die "Bad version sorting order" if version->parse($version) > version->parse($prev_version); - } - $prev_version = $version; - } - } -} -close $f; -print "};\n"; diff --git a/src/lib-settings/settings-history.py b/src/lib-settings/settings-history.py new file mode 100755 index 0000000000..2ddcbf6aa6 --- /dev/null +++ b/src/lib-settings/settings-history.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +"""Generate history structs for renamed settings and changed default values.""" + +import argparse +import sys + +FILE_TEMPLATE = """\ +static const struct setting_history_rename settings_history_core_renames[] = { +%s\ +}; +static const struct setting_history_default settings_history_core_defaults[] = { +%s\ +}; +""" + +STRUCT_TEMPLATE = """\ + { "%s", "%s", "%s" }, +""" + + +def die(message: str): + """Die with a message.""" + print(message, file=sys.stderr) + sys.exit(1) + + +def parse_version(line: int, version: str) -> [int]: + """Parse a string version into a list of integers.""" + values = version.split(".") + parsed = [] + for value in values: + try: + int_value = int(value) + parsed.append(int_value) + except ValueError as e: + die(f"Line {line}: Invalid version {version}: {e}") + return parsed + + +def check_version(line: int, prev_version: [int], cur_version: [int]): + """Fail if version ordering is incorrect.""" + if prev_version is not None and cur_version > prev_version: + die(f"Line {line}: Invalid version sort order") + return cur_version + + +def process(contents: str, pro: bool) -> (str, str): + """Produce the renames and defaults structs from the input data.""" + renames = "" + defaults = "" + renames_prev_version = None + defaults_prev_version = None + for line, data in enumerate(contents.splitlines()): + line = line + 1 + values = data.split("\t") + + if len(values) != 5: + die(f"Line {line}: Invalid contents `{data}`: Expecting 5 fields") + + ce_version = values[3] + pro_version = values[4] + version_text = pro_version if pro else ce_version + + if version_text == "": + continue + + version = parse_version(line, version_text) + + if values[0] == "rename": + old_key = values[1] + new_key = values[2] + renames += STRUCT_TEMPLATE % (old_key, new_key, version_text) + renames_prev_version = check_version(line, renames_prev_version, version) + elif values[0] == "default": + key = values[1] + old_value = values[2] + defaults += STRUCT_TEMPLATE % (key, old_value, version_text) + defaults_prev_version = check_version(line, defaults_prev_version, version) + else: + die(f"Line {line}: Unrecognized marker in `{data}`") + return (renames, defaults) + + +def main(): + """Entry point.""" + parser = argparse.ArgumentParser( + prog="settings-history.py", + description="Generate header file for settings migration data", + ) + parser.add_argument( + "input-file", + type=str, + help="Input data file e.g. settings-history-core.txt", + ) + parser.add_argument( + "output-file", + type=str, + help="Output header file e.g. settings-history-core.h", + ) + parser.add_argument( + "--pro", + type=int, + choices=[0, 1], + help="Whether to generate settings migration data for Pro", + ) + args = parser.parse_args() + + with open(getattr(args, "input-file"), mode="r", encoding="utf-8") as f_in: + contents = f_in.read() + (renames, defaults) = process(contents, pro=bool(args.pro)) + + with open(getattr(args, "output-file"), mode="w", encoding="utf-8") as f_out: + f_out.write(FILE_TEMPLATE % (renames, defaults)) + + +if __name__ == "__main__": + main()