3
# xref-quadlet-docs - cross-validate quadlet man page vs actual source
5
# $Id: .perl-template,v 1.2 2020/03/03 20:08:31 esm Exp esm $
7
package Podman::CrossrefQuadletDocs;
15
(our $ME = $0) =~ s|.*/||;
18
###############################################################################
19
# BEGIN user-customizable section
21
our $Go = 'pkg/systemd/quadlet/quadlet.go';
22
our $Doc = 'docs/source/markdown/podman-systemd.unit.5.md';
24
# END user-customizable section
25
###############################################################################
27
###############################################################################
28
# BEGIN boilerplate args checking, usage messages
34
$ME cross-checks quadlet documentation between the Go source[Go]
42
* all keys in [Go] are documented in [MD]
43
* all keys in [MD] exist in [Go]
44
* any keys listed in [MD] tables also have a description block
46
* all keys everywhere are in sorted order
50
--help display this message
51
--version display program name and version
57
# Command-line options. Note that this operates directly on @ARGV !
66
version => sub { print "$ME version $VERSION\n"; exit 0 },
67
) or die "Try `$ME --help' for help\n";
70
# END boilerplate args checking, usage messages
71
###############################################################################
73
############################## CODE BEGINS HERE ###############################
75
# The term is "modulino".
76
__PACKAGE__->main() unless caller();
80
# Note that we operate directly on @ARGV, not on function parameters.
81
# This is deliberate: it's because Getopt::Long only operates on @ARGV
82
# and there's no clean way to make it use @_.
83
handle_opts(); # will set package globals
85
# No command-line args
86
die "$ME: Too many arguments; try $ME --help\n" if @ARGV;
89
$SIG{__WARN__} = sub {
94
# Assume that Go source file has Truth
95
my $true_keys = read_go($Go);
97
# Read md file, compare against Truth
98
crossref_doc($Doc, $true_keys);
105
# read_go # Returns list of key strings found in quadlet.go
109
open my $fh, '<', $path
110
or die "$ME: Cannot read $path: $!\n";;
112
my @found; # List of key strings
113
my $last_constname; # Most recently seen const name
115
while (my $line = <$fh>) {
116
# Only interested in lines of the form KeyFoo = "Foo"
117
if ($line =~ /^\s+Key(\S+)\s+=\s+"(\S+)"/) {
118
my ($constname, $keystring) = ($1, $2);
120
my $deprecated = ($line =~ m!\s//\s+deprecated!i);
122
# const name must be the same as the string
123
$constname eq $keystring
124
or warn "$ME: $path:$.: mismatched strings: Key$constname = \"$keystring\"\n";
127
if ($last_constname) {
128
if (lc($constname) lt lc($last_constname)) {
129
warn "$ME: $path:$.: out-of-order variable name 'Key$constname' should precede 'Key$last_constname'\n";
132
$last_constname = $constname;
134
push @found, $keystring
144
# crossref_doc # Read the markdown page, cross-check against Truth
147
my $path = shift; # in: path to .md file
148
my $true_keys = shift; # in: AREF, list of keys from .go
150
open my $fh, '<', $path
151
or die "$ME: Cannot read $path: $!\n";;
158
# Helper function: when done reading description blocks,
159
# make sure that there's one block for each key listed
160
# in the table. Defined as a local function because we
161
# need to call it from two different places.
162
my $crossref_against_table = sub {
163
for my $k (@found_in_table) {
164
grep { $_ eq $k } @described
165
or warn "$ME: key not documented: '$k' listed in table for unit '$unit' but not actually documented\n";
169
# Main loop: read the docs line by line
170
while (my $line = <$fh>) {
173
# New section, with its own '| table |' and '### Keyword blocks'
174
if ($line =~ /^##\s+(\S+)\s+units\s+\[(\S+)\]/) {
177
or warn "$ME: $path:$.: inconsistent block names in '$line'\n";
179
$crossref_against_table->();
183
# Reset, because each section has its own table & blocks
184
@found_in_table = ();
190
if ($line =~ s/^\|\s+//) {
191
next if $line =~ /^\*\*/; # title
192
next if $line =~ /^-----/; # divider
194
if ($line =~ /^([A-Z][A-Za-z6]+)=/) {
197
grep { $_ eq $key } @$true_keys
198
or warn "$ME: $path:$.: unknown key '$key' (not present in $Go)\n";
201
if (@found_in_table) {
202
if (lc($key) lt lc($found_in_table[-1])) {
203
warn "$ME: $path:$.: out-of-order key '$key' in table\n";
207
push @found_in_table, $key;
211
warn "$ME: $path:$.: cannot grok table line '$line'\n";
216
elsif ($line =~ /^###\s+`(\S+)=`/) {
219
# Check for dups and for out-of-order
221
if (lc($key) lt lc($described[-1])) {
222
warn "$ME: $path:$.: out-of-order key '$key'\n";
224
if (grep { lc($_) eq lc($key) } @described) {
225
warn "$ME: $path:$.: duplicate key '$key'\n";
229
grep { $_ eq $key } @found_in_table
230
or warn "$ME: $path:$.: key '$key' is not listed in table for unit '$unit'\n";
232
push @described, $key;
239
# Final cross-check between table and description blocks
240
$crossref_against_table->();
242
# Check that no Go keys are missing
244
(my $md_basename = $path) =~ s|^.*/||;
245
for my $k (@$true_keys) {
247
or warn "$ME: undocumented key: '$k' not found anywhere in $md_basename\n";