diff --git a/Makefile.am b/Makefile.am index 1f74709ffc..1c2fa24c23 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1890,8 +1890,8 @@ endif libnm_docs_sources = $(src_libnm_core_impl_lib_c_settings_real) -src/libnm-client-impl/nm-property-infos-%.xml: tools/generate-docs-nm-property-infos.pl $(libnm_docs_sources) - $(AM_V_GEN) $(srcdir)/tools/generate-docs-nm-property-infos.pl $(patsubst nm-property-infos-%.xml,%,$(notdir $@)) $@ $(filter-out $<,$^) +src/libnm-client-impl/nm-property-infos-%.xml: tools/generate-docs-nm-property-infos.py $(libnm_docs_sources) + $(AM_V_GEN) "$(PYTHON)" $(srcdir)/tools/generate-docs-nm-property-infos.py $(patsubst nm-property-infos-%.xml,%,$(notdir $@)) $@ $(filter-out $<,$^) src/libnm-client-impl/nm-settings-docs-gir.xml: tools/generate-docs-nm-settings-docs-gir.py src/libnm-client-impl/NM-1.0.gir src/libnm-client-impl/NM-1.0.typelib src/libnm-client-impl/libnm.la $(libnm_docs_sources) $(AM_V_GEN) \ @@ -1918,7 +1918,7 @@ DISTCLEANFILES += $(libnm_noinst_data) endif EXTRA_DIST += \ - tools/generate-docs-nm-property-infos.pl \ + tools/generate-docs-nm-property-infos.py \ tools/generate-docs-nm-settings-docs-merge.py \ tools/generate-docs-nm-settings-docs-gir.py \ src/libnm-client-impl/meson.build \ diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build index 4682e2baf2..21a01e0b04 100644 --- a/src/libnm-client-impl/meson.build +++ b/src/libnm-client-impl/meson.build @@ -162,8 +162,8 @@ if enable_introspection input: libnm_core_settings_sources, output: 'nm-propery-infos-' + info + '.xml', command: [ - perl, - join_paths(meson.source_root(), 'tools', 'generate-docs-nm-property-infos.pl'), + python.path(), + join_paths(meson.source_root(), 'tools', 'generate-docs-nm-property-infos.py'), info, '@OUTPUT@', '@INPUT@' diff --git a/tools/generate-docs-nm-property-infos.pl b/tools/generate-docs-nm-property-infos.pl deleted file mode 100755 index 7d33d140b4..0000000000 --- a/tools/generate-docs-nm-property-infos.pl +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Copyright (C) 2014 Red Hat, Inc. -# - -# -# The script parses nm-setting-*.c files and extracts documentation related -# to setting plugins. The documentation is in a simple format of lines -# "keyword: value". The documentation is enclosed between tags -# ------ and ---end--- -# Recognized keywords are: -# "property: " - property name -# "variable: " - name of the variable used by the plugin -# "format: " - format of the value in 'keyfile' plugin -# "default: " - default value when variable is not used -# "values: " - allowed values (e.g. for enumerations) -# "example: " - example(s) -# "description: " - description text -# Value is an arbitrary string that can span over multiple lines. -# -# ifcfg-rh specifics: -# - mark NM extension variables with (+), e.g. variable: UUID(+) -# - -use strict; -use warnings; -use v5.10; - -# global variables -my @keywords = ("property", "variable", "format", "values", "default", "example", "description"); -my @data; -my $fo; - -(scalar @ARGV >= 3) or die "Usage: $0 \n"; -my ($plugin, $output, (@source_files)) = @ARGV; -my $start_tag = "---$plugin---\\s*\$"; -my $end_tag = '---end---'; - -# open output file -open $fo, '>', $output or die "Can't open $output: $!"; - -# write XML header -write_header(); - -# write generated documentation for each setting -foreach my $c_file (@source_files) { - my $setting_name = get_setting_name($c_file); - if ($setting_name) { - write_item(""); - scan_doc_comments($c_file, $start_tag, $end_tag); - write_item(""); - } -} - -# write XML footer -write_footer(); - -# close output file -close $fo; - - -### --- subroutines --- ### - -# get setting name from NM_SETTING_*_SETTING_NAME constant in C header file -sub get_setting_name { - my $path = $_[0]; - $path =~ s/\/libnm-core-impl\/nm-setting-(.*)\.c$/\/libnm-core-public\/nm-setting-$1.h/; # use header file to find out setting name - $path =~ s/\.c$/.h/; # use header file to find out setting name - open my $fh, '<', $path or die "Can't open $path: $!"; - while (my $line = <$fh>) { - if ($line =~ /NM_SETTING_.+SETTING_NAME\s+\"(\S+)\"/) { - return $1; - } - } -} - -# scan source setting file for documentation tags and write them to XML -sub scan_doc_comments { - my($setting_file, $start, $end) = @_; - open my $fi, '<', $setting_file or die "Can't open $setting_file: $!"; - while (<$fi>) { - if (/$start/ .. /$end/) { - next if /$start/; - if (/$end/) { - process_data(); - } else { - push @data, $_; - } - next; - } - # ignore text not inside marks - } - close $fi; -} - -# process plugin property documentation comments -sub process_data { - return if not @data; - my $kwd_pat = join("|", @keywords); - my %parsed_data; - my $this_key; - - foreach (@data) { - if (/^\s*\**\s+($kwd_pat):\s+(.*?)\s*$/) { - $this_key = $1; - $parsed_data{$this_key} = "$2\n"; - } elsif (/^\s*\**\s+(.*?)\s*$/) { - die "Extra mess in a comment: $_" unless $this_key; - $parsed_data{$this_key} .= "$1\n"; - } - } - - # now write a line into the XML - my $name = $parsed_data{property} // ""; - my $var = $parsed_data{variable} // $name; # fallback to "property: " - my $format = $parsed_data{format} // ""; - my $values = $parsed_data{values} // ""; - my $def = $parsed_data{default} // ""; - my $exam = $parsed_data{example} // ""; - my $desc = $parsed_data{description} // ""; - - chomp($name, $var, $format, $values, $def, $exam, $desc); - escape_xml_chars($name, $var, $format, $values, $def, $exam, $desc); - my $foo = sprintf("", - $name, $var, $format, $values, $def, $exam, $desc); - write_item($foo); - @data = (); -} - -# - XML handling - -sub write_header { - (my $header = - qq{ - }) =~ s/^ {7}//mg; - print {$fo} $header; -} - -sub write_footer { - my $footer = ""; - print {$fo} $footer; -} - -sub write_item { - my $str = join("", @_); - print {$fo} $str, "\n"; -} - -sub escape_xml_chars { - # http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Predefined%5Fentities%5Fin%5FXML - foreach my $val (@_) { - $val =~ s/&/&/sg; - $val =~ s//>/sg; - $val =~ s/"/"/sg; - $val =~ s/'/'/sg; - } -} - diff --git a/tools/generate-docs-nm-property-infos.py b/tools/generate-docs-nm-property-infos.py new file mode 100755 index 0000000000..90f757728e --- /dev/null +++ b/tools/generate-docs-nm-property-infos.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later + +import os, re, sys + + +def get_setting_name(one_file): + setting_name = "" + assert re.match(r".*/libnm-core-impl/nm-setting-.*\.c$", one_file) + header_path = one_file.replace("libnm-core-impl", "libnm-core-public") + header_path = header_path.replace(".c", ".h") + try: + header_reader = open(header_path, "r") + except OSError: + print("Can not open header file: %s" % (header_path)) + exit(1) + + line = header_reader.readline() + while line != "": + setting_name_found = re.search(r"NM_SETTING_.+SETTING_NAME\s+\"(\S+)\"", line) + if setting_name_found: + setting_name = setting_name_found.group(1) + break + line = header_reader.readline() + header_reader.close() + return setting_name + + +def scan_doc_comments(plugin, outfile, file, start_tag, end_tag): + data = [] + push_flag = 0 + try: + file_reader = open(file, "r") + except OSError: + print("Can not open file: %s" % (file)) + exit(1) + + line = file_reader.readline() + while line != "": + if start_tag in line: + push_flag = 1 + elif end_tag in line and push_flag == 1: + push_flag = 0 + parsed_data = process_data(data) + if parsed_data: + write_data(outfile, parsed_data) + data = [] + elif push_flag == 1: + data.append(line) + line = file_reader.readline() + file_reader.close() + return + + +def process_data(data): + parsed_data = {} + if not data: + return parsed_data + keywords = [ + "property", + "variable", + "format", + "values", + "default", + "example", + "description", + ] + kwd_pat = "|".join(keywords) + keyword = "" + for line in data: + kwd_first_line_found = re.search( + r"^\s*\**\s+({}):\s+(.*?)\s*$".format(kwd_pat), line + ) + kwd_more_line_found = re.search(r"^\s*\**\s+(.*?)\s*$", line) + if kwd_first_line_found: + keyword = kwd_first_line_found.group(1) + value = kwd_first_line_found.group(2) + "\n" + parsed_data[keyword] = escape_xml_char(value) + elif kwd_more_line_found: + if not keyword: + print("Extra mess in a comment: %s" % (line)) + exit(1) + else: + value = kwd_more_line_found.group(1) + "\n" + parsed_data[keyword] += escape_xml_char(value) + for keyword in keywords: + if keyword == "variable" and keyword not in parsed_data: + parsed_data[keyword] = parsed_data["property"] + elif keyword not in parsed_data: + parsed_data[keyword] = "" + for key in parsed_data.keys(): + parsed_data[key] = parsed_data[key].rstrip() + return parsed_data + + +def write_data(outfile, parsed_data): + outfile.write( + '\n'.format( + parsed_data["property"], + parsed_data["variable"], + parsed_data["format"], + parsed_data["values"], + parsed_data["default"], + parsed_data["example"], + parsed_data["description"], + ) + ) + + +def escape_xml_char(text): + text = text.replace("&", "&") + text = text.replace("<", "<") + text = text.replace(">", ">") + text = text.replace('"', """) + text = text.replace("'", "'") + + return text + + +if len(sys.argv) < 4: + print("Usage: %s [plugin] [output-xml-file] [srcfiles]" % (sys.argv[0])) + exit(1) + +argv = list(sys.argv[1:]) +plugin, output, source_files = argv[0], argv[1], argv[2:] +start_tag = "---" + plugin + "---" +end_tag = "---end---" +outfile = open(output, mode="w") + +# write XML header +outfile.write("\n") +outfile.write(" ") + +for one_file in source_files: + setting_name = get_setting_name(one_file) + if setting_name: + outfile.write('\n') + scan_doc_comments(plugin, outfile, one_file, start_tag, end_tag) + outfile.write("\n") + + +# write XML footer +outfile.write("") + +# close output file +outfile.close()