libnm-util: generate-plugin-docs.pl script for extracting plugin docs
The scripts extracts plugin description from document comments for particular properties and builds a XML file out of the data. The XML file can be used later for generating manual pages or other documentation. Unfortunately, gtk-doc won't allow descriptions that would be separated from the main gtk-doc stuff. But it is still useful to have plugin description bits co-located with property definitions. We use our home-grown comments and parse them ourself. Afterall it's not that bad, and in addition it brings us a freedom in shaping the comments to our needs.
This commit is contained in:
25
configure.ac
25
configure.ac
@@ -773,6 +773,8 @@ install_pregen_manpages=no
|
|||||||
if test "$enable_gtk_doc" != "yes" \
|
if test "$enable_gtk_doc" != "yes" \
|
||||||
-a -f man/NetworkManager.conf.5 \
|
-a -f man/NetworkManager.conf.5 \
|
||||||
-a -f man/nm-settings.5 \
|
-a -f man/nm-settings.5 \
|
||||||
|
-a -f man/nm-settings-keyfile.5 \
|
||||||
|
-a -f man/nm-settings-ifcfg-rh.5 \
|
||||||
-a -f man/nmcli-examples.5 \
|
-a -f man/nmcli-examples.5 \
|
||||||
-a -f man/NetworkManager.8; then
|
-a -f man/NetworkManager.8; then
|
||||||
install_pregen_manpages=yes
|
install_pregen_manpages=yes
|
||||||
@@ -783,7 +785,26 @@ AM_CONDITIONAL(INSTALL_PREGEN_MANPAGES, test "x${install_pregen_manpages}" = "xy
|
|||||||
if test -n "$INTROSPECTION_MAKEFILE" -a "$enable_gtk_doc" = "yes"; then
|
if test -n "$INTROSPECTION_MAKEFILE" -a "$enable_gtk_doc" = "yes"; then
|
||||||
# If g-i is installed we know we have python, but we might not have pygobject
|
# If g-i is installed we know we have python, but we might not have pygobject
|
||||||
if python -c 'from gi.repository import GObject' >& /dev/null; then
|
if python -c 'from gi.repository import GObject' >& /dev/null; then
|
||||||
AC_DEFINE(BUILD_SETTING_DOCS, [1], [Define if you we can build nm-setting-docs.xml])
|
have_pyobject=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# gtk-doc depends on perl, but we can check for it anyway
|
||||||
|
AC_PATH_PROG(PERL, perl, no)
|
||||||
|
# check for needed perl modules (use YAML; YAML::XS is better, but may not be so widespread)
|
||||||
|
required_perl_modules="YAML"
|
||||||
|
for module in $required_perl_modules; do
|
||||||
|
AC_MSG_CHECKING([checking for perl module '$module'])
|
||||||
|
if ${PERL} -e 'use '$module 2>/dev/null ; then
|
||||||
|
AC_MSG_RESULT([Ok])
|
||||||
|
have_perl_modules=yes
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([Failed])
|
||||||
|
AC_MSG_ERROR([You must have Perl modules to build settings plugin docs: $required_perl_modules.])
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test "$have_pyobject" = "yes" -a "$have_perl_modules" = "yes"; then
|
||||||
|
AC_DEFINE(BUILD_SETTING_DOCS, [1], [Define if you we can build nm-setting-docs.xml, nm-keyfile-docs.xml and nm-ifcfg-rh-docs.xml])
|
||||||
build_setting_docs=yes
|
build_setting_docs=yes
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -791,6 +812,8 @@ fi
|
|||||||
# check for pre-built setting docs
|
# check for pre-built setting docs
|
||||||
if test "$build_setting_docs" != "yes" \
|
if test "$build_setting_docs" != "yes" \
|
||||||
-a -f man/nm-settings.xml \
|
-a -f man/nm-settings.xml \
|
||||||
|
-a -f man/nm-settings-keyfile.xml \
|
||||||
|
-a -f man/nm-settings-ifcfg-rh.xml \
|
||||||
-a -f docs/api/settings-spec.xml \
|
-a -f docs/api/settings-spec.xml \
|
||||||
-a -f cli/src/settings-docs.c; then
|
-a -f cli/src/settings-docs.c; then
|
||||||
AC_DEFINE(HAVE_SETTING_DOCS, [1], [Define if you have pre-built settings docs])
|
AC_DEFINE(HAVE_SETTING_DOCS, [1], [Define if you have pre-built settings docs])
|
||||||
|
@@ -188,7 +188,7 @@ endif
|
|||||||
|
|
||||||
if BUILD_SETTING_DOCS
|
if BUILD_SETTING_DOCS
|
||||||
|
|
||||||
noinst_DATA = nm-setting-docs.xml
|
noinst_DATA = nm-setting-docs.xml nm-keyfile-docs.xml nm-ifcfg-rh-docs.xml
|
||||||
|
|
||||||
nm-setting-docs.xml: generate-setting-docs.py NetworkManager-1.0.gir NetworkManager-1.0.typelib libnm-util.la
|
nm-setting-docs.xml: generate-setting-docs.py NetworkManager-1.0.gir NetworkManager-1.0.typelib libnm-util.la
|
||||||
export GI_TYPELIB_PATH=$(abs_builddir)$${GI_TYPELIB_PATH:+:$$GI_TYPELIB_PATH}; \
|
export GI_TYPELIB_PATH=$(abs_builddir)$${GI_TYPELIB_PATH:+:$$GI_TYPELIB_PATH}; \
|
||||||
@@ -197,11 +197,16 @@ nm-setting-docs.xml: generate-setting-docs.py NetworkManager-1.0.gir NetworkMana
|
|||||||
--gir $(builddir)/NetworkManager-1.0.gir \
|
--gir $(builddir)/NetworkManager-1.0.gir \
|
||||||
--output $@
|
--output $@
|
||||||
|
|
||||||
|
nm-keyfile-docs.xml: generate-plugin-docs.pl $(libnm_util_la_csources)
|
||||||
|
$(srcdir)/generate-plugin-docs.pl keyfile $@
|
||||||
|
nm-ifcfg-rh-docs.xml: generate-plugin-docs.pl $(libnm_util_la_csources)
|
||||||
|
$(srcdir)/generate-plugin-docs.pl ifcfg-rh $@
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
DISTCLEANFILES += nm-setting-docs.xml
|
DISTCLEANFILES += nm-setting-docs.xml nm-keyfile-docs.xml nm-ifcfg-rh-docs.xml
|
||||||
|
|
||||||
EXTRA_DIST += generate-setting-docs.py
|
EXTRA_DIST += generate-setting-docs.py generate-plugin-docs.pl
|
||||||
|
|
||||||
if ENABLE_TESTS
|
if ENABLE_TESTS
|
||||||
|
|
||||||
|
198
libnm-util/generate-plugin-docs.pl
Executable file
198
libnm-util/generate-plugin-docs.pl
Executable file
@@ -0,0 +1,198 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
# vim: ft=perl ts=2 sts=2 sw=2 et ai
|
||||||
|
# -*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Copyright 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
|
||||||
|
# ---<plugin-name>--- 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;
|
||||||
|
#YAML:XS is based on libyaml C library and it is a good and fast YAML implementation.
|
||||||
|
#However it may not be present everywhere. So use YAML instead.
|
||||||
|
#use YAML::XS qw(Load);
|
||||||
|
use YAML qw(Load);
|
||||||
|
|
||||||
|
# global variables
|
||||||
|
my @keywords = ("property", "variable", "format", "values", "default", "example", "description");
|
||||||
|
my @source_files;
|
||||||
|
my @data;
|
||||||
|
my $fo;
|
||||||
|
|
||||||
|
(scalar @ARGV == 2) or die "Usage: $0 <plugin> <output-xml-file>\n";
|
||||||
|
($ARGV[0] eq "keyfile" || $ARGV[0] eq "ifcfg-rh") or die "Allowed <plugin> values: keyfile, ifcfg-rh\n";
|
||||||
|
my ($plugin, $output) = @ARGV;
|
||||||
|
my $start_tag = "---$plugin---\\s*\$";
|
||||||
|
my $end_tag = '---end---';
|
||||||
|
|
||||||
|
# get source files to scan for documentation comments (nm-setting-<something>.c)
|
||||||
|
my $file = 'Makefile.am';
|
||||||
|
open my $fh, '<', $file or die "Can't open $file: $!";
|
||||||
|
while (my $line = <$fh>) {
|
||||||
|
chomp $line;
|
||||||
|
my @strings = $line =~ /(?:^|\s)(nm-setting-[^.]*\.c)(?:\s|$)/g;
|
||||||
|
push @source_files, @strings
|
||||||
|
}
|
||||||
|
close $fh;
|
||||||
|
|
||||||
|
# open output file
|
||||||
|
open $fo, '>', $output or die "Can't open $output: $!";
|
||||||
|
|
||||||
|
# write XML header
|
||||||
|
write_header();
|
||||||
|
|
||||||
|
# write generated documenation for each setting
|
||||||
|
foreach my $c_file (@source_files) {
|
||||||
|
my $path = "$c_file";
|
||||||
|
my $setting_name = get_setting_name($path);
|
||||||
|
write_item("<setting name=\"$setting_name\">");
|
||||||
|
scan_doc_comments($path, $start_tag, $end_tag);
|
||||||
|
write_item("</setting>");
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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/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 (as a YAML document)
|
||||||
|
sub process_data {
|
||||||
|
return if not @data;
|
||||||
|
my $kwd_pat = join("|", @keywords);
|
||||||
|
my $yaml_literal_seq = "|\n";
|
||||||
|
|
||||||
|
foreach (@data) {
|
||||||
|
# make a proper YAML document from @data
|
||||||
|
$_ =~ s/^\s*\**\s+|\s+$//; # remove leading spaces and *, and traling spaces
|
||||||
|
# Properly indent the text so that it is a valid YAML, and insert | (for literal text)
|
||||||
|
if ($_ =~ /^($kwd_pat):\s+/) {
|
||||||
|
# add | after "keyword:" that allows using literal text (YAML won't break on special character)
|
||||||
|
# http://learnxinyminutes.com/docs/yaml/ and http://www.yaml.org/spec/1.2/spec.html#id2795688
|
||||||
|
$_ =~ s/(^($kwd_pat):)/$1 $yaml_literal_seq/;
|
||||||
|
} else {
|
||||||
|
$_ = " " . $_; # indent the text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
my $str = join ("", @data);
|
||||||
|
my $yaml_data = Load($str);
|
||||||
|
|
||||||
|
# now write ia line into the XML
|
||||||
|
my $name = $yaml_data->{property} // "";
|
||||||
|
my $var = $yaml_data->{variable} // $name; # fallback to "property: "
|
||||||
|
my $format = $yaml_data->{format} // "";
|
||||||
|
my $values = $yaml_data->{values} // "";
|
||||||
|
my $def = $yaml_data->{default} // "";
|
||||||
|
my $exam = $yaml_data->{example} // "";
|
||||||
|
my $desc = $yaml_data->{description} // "";
|
||||||
|
|
||||||
|
escape_xml_chars($name, $var, $format, $values, $def, $exam, $desc);
|
||||||
|
my $foo = sprintf("<property name=\"%s\" variable=\"%s\" format=\"%s\" values=\"%s\" ".
|
||||||
|
"default=\"%s\" example=\"%s\" description=\"%s\"/>",
|
||||||
|
$name, $var, $format, $values, $def, $exam, $desc);
|
||||||
|
write_item($foo);
|
||||||
|
@data = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
# - XML handling -
|
||||||
|
sub write_header {
|
||||||
|
(my $header =
|
||||||
|
qq{<?xml version=\"1.0\"?>
|
||||||
|
<!DOCTYPE nm-$plugin-docs [
|
||||||
|
]>
|
||||||
|
|
||||||
|
<nm-$plugin-docs>
|
||||||
|
}) =~ s/^ {7}//mg;
|
||||||
|
print {$fo} $header;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub write_footer {
|
||||||
|
my $footer = "</nm-$plugin-docs>";
|
||||||
|
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;
|
||||||
|
$val =~ s/'/'/sg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user