|
| 1 | +#!/usr/bin/env perl |
| 2 | +# |
| 3 | +# Copyright (c) 2014-2022 Cisco Systems, Inc. All rights reserved. |
| 4 | +# Copyright (c) 2023 Jeffrey M. Squyres. All rights reserved. |
| 5 | +# Copyright (c) 2025 Nanook Consulting All rights reserved. |
| 6 | +# $COPYRIGHT$ |
| 7 | +# |
| 8 | +# Simple script to check all the opal_show_help (and orte_show_help) |
| 9 | +# strings against what is found in help files. |
| 10 | +# |
| 11 | + |
| 12 | +use strict; |
| 13 | + |
| 14 | +use Cwd; |
| 15 | +use File::Find; |
| 16 | +use Getopt::Long; |
| 17 | +use Data::Dumper; |
| 18 | + |
| 19 | +my $num_warnings = 0; |
| 20 | +my $num_errors = 0; |
| 21 | + |
| 22 | +my $topdir_flag = "Makefile.openpmix-rules"; |
| 23 | + |
| 24 | +########################################################################### |
| 25 | + |
| 26 | +my $VERBOSE = 0; |
| 27 | +my $HELP = 0; |
| 28 | + |
| 29 | +GetOptions( |
| 30 | + "help|h" => \$HELP, |
| 31 | + "verbose|v" => \$VERBOSE, |
| 32 | +) or die "unable to parse options, aborted"; |
| 33 | + |
| 34 | +if ($HELP) { |
| 35 | + print <<EOF; |
| 36 | +%0 [options] |
| 37 | +
|
| 38 | +--help | h This help message |
| 39 | +--verbose | v Be verbose in output |
| 40 | +EOF |
| 41 | + exit(0); |
| 42 | +} |
| 43 | + |
| 44 | +########################################################################### |
| 45 | + |
| 46 | +sub verbose { |
| 47 | + print @_ |
| 48 | + if ($VERBOSE); |
| 49 | +} |
| 50 | + |
| 51 | +sub DebugDump { |
| 52 | + my $d = new Data::Dumper([@_]); |
| 53 | + $d->Purity(1)->Indent(1); |
| 54 | + my $s = $d->Dump; |
| 55 | + print $s; |
| 56 | +} |
| 57 | + |
| 58 | +sub isTopDir { |
| 59 | + my ($d) = @_; |
| 60 | + |
| 61 | + if (-f "$d/$topdir_flag") { |
| 62 | + return 1; |
| 63 | + } |
| 64 | + |
| 65 | + return 0; |
| 66 | +} |
| 67 | + |
| 68 | +########################################################################### |
| 69 | + |
| 70 | +# Find the top-level source tree dir |
| 71 | +my $start = cwd(); |
| 72 | +my $top = $start; |
| 73 | +while (!isTopDir($top)) { |
| 74 | + chdir(".."); |
| 75 | + $top = cwd(); |
| 76 | + die "Can't find top-level directory" |
| 77 | + if ($top eq "/"); |
| 78 | +} |
| 79 | +chdir($start); |
| 80 | + |
| 81 | +########################################################################### |
| 82 | + |
| 83 | +my @source_files; |
| 84 | +my @help_files; |
| 85 | + |
| 86 | +# Helper: Search for all source and help files |
| 87 | +sub match_files { |
| 88 | + # Don't process sym links |
| 89 | + return |
| 90 | + if (-l $_); |
| 91 | + |
| 92 | + # Don't recurse down "special" directories |
| 93 | + if (-d $_ && |
| 94 | + ((/^\.deps$/) || (/^\.libs$/) || (/^\.git$/))) { |
| 95 | + $File::Find::prune = 1; |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + # $File::Find::name is the path relative to the starting point. |
| 100 | + # $_ contains the file's basename. The code automatically changes |
| 101 | + # to the processed directory, so we want to open / close $_. |
| 102 | + |
| 103 | + verbose("--> $File::Find::name\n"); |
| 104 | + |
| 105 | + my $relative = $File::Find::name; |
| 106 | + $relative =~ s/^$top//; |
| 107 | + $relative =~ s/^\///; |
| 108 | + |
| 109 | + my $short = $_; |
| 110 | + if ($short =~ /^help-.*\.txt$/) { |
| 111 | + push(@help_files, { |
| 112 | + full => $File::Find::name, |
| 113 | + short => $short, |
| 114 | + relative => $relative, |
| 115 | + }); |
| 116 | + verbose(" Found help file: $short\n"); |
| 117 | + } |
| 118 | + |
| 119 | + if ($short =~ /\.c$/ || |
| 120 | + $short =~ /\.h$/ || |
| 121 | + $short =~ /\.cc$/) { |
| 122 | + push(@source_files, { |
| 123 | + full => $File::Find::name, |
| 124 | + short => $short, |
| 125 | + relative => $relative, |
| 126 | + }); |
| 127 | + verbose(" Found source file: $short\n"); |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +# Find all source and help files |
| 132 | +print "Searching for source and help files...\n"; |
| 133 | +my $startrel = $start; |
| 134 | +if ($top ne $start) { |
| 135 | + $startrel =~ s/^$top//; |
| 136 | + $startrel =~ s/^\///; |
| 137 | +} |
| 138 | +find(\&match_files, "."); |
| 139 | + |
| 140 | +########################################################################### |
| 141 | + |
| 142 | +# Index all help files |
| 143 | +my $help_topics; |
| 144 | +my $help_file_refs; |
| 145 | + |
| 146 | +print "Indexing help files (from entire source tree)...\n"; |
| 147 | + |
| 148 | +foreach my $info (@help_files) { |
| 149 | + verbose("Indexing help: $info->{full}\n"); |
| 150 | + |
| 151 | + # Check for short name collision |
| 152 | + if (exists($help_topics->{$info->{short}})) { |
| 153 | + |
| 154 | + # Found a collision! Find the original's full name. |
| 155 | + my $collide_relative = "unknown"; |
| 156 | + foreach my $i (@help_files) { |
| 157 | + if ($i->{short} eq $info->{short}) { |
| 158 | + $collide_relative = $i->{relative}; |
| 159 | + last; |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + # Print error message |
| 164 | + print "*** ERROR: Help file name collision: |
| 165 | + File 1: $info->{relative} |
| 166 | + File 2: $collide_relative\n"; |
| 167 | + ++$num_errors; |
| 168 | + } |
| 169 | + |
| 170 | + # Read in file, find all of its topics |
| 171 | + my $num_topics = 0; |
| 172 | + open(FH, $info->{full}) || die "Can't open $info->{full}"; |
| 173 | + while (<FH>) { |
| 174 | + if (m/^\s*\[(.+?)\]\s*$/) { |
| 175 | + my $topic = $1; |
| 176 | + verbose(" Topic: $topic\n"); |
| 177 | + $help_topics->{$info->{short}}->{topic}->{$topic} = 0; |
| 178 | + $help_topics->{$info->{short}}->{full} = $info->{full}; |
| 179 | + ++$num_topics; |
| 180 | + } |
| 181 | + } |
| 182 | + close(FH); |
| 183 | + |
| 184 | + if (0 == $num_topics) { |
| 185 | + print "*** WARNING: Empty help file (no topics) |
| 186 | + Help file: $info->{full}\n"; |
| 187 | + ++$num_warnings; |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +########################################################################### |
| 192 | + |
| 193 | +# Search source files for calls to opal_show_help and (o)rte_show_help |
| 194 | + |
| 195 | +if ($start eq $top) { |
| 196 | + print "Searching source files (from entire source tree)...\n"; |
| 197 | +} else { |
| 198 | + print "Searching source files (under $startrel)...\n"; |
| 199 | +} |
| 200 | + |
| 201 | +# Helper: for a given filename/topic, see if it exists |
| 202 | +sub check_file_topic { |
| 203 | + my $info = shift; |
| 204 | + my $file = shift; |
| 205 | + my $topic = shift; |
| 206 | + |
| 207 | + verbose("Found $info->{short}: $file / $topic\n"); |
| 208 | + |
| 209 | + # Do we have a help file for this? |
| 210 | + if (!exists($help_topics->{$file})) { |
| 211 | + print "*** ERROR: Source-referenced help file does not exist |
| 212 | + Source file: $info->{relative} |
| 213 | + Help file referenced: $file\n"; |
| 214 | + ++$num_errors; |
| 215 | + } |
| 216 | + |
| 217 | + # Do we have a topic in that help file for this? |
| 218 | + elsif (!exists($help_topics->{$file}->{topic}->{$topic})) { |
| 219 | + print "*** ERROR: Source-referenced help topic does not exist |
| 220 | + Source file: $info->{relative} |
| 221 | + Help file referenced: $file |
| 222 | + which is: $help_topics->{$file}->{full} |
| 223 | + Help topic referenced: $topic\n"; |
| 224 | + ++$num_errors; |
| 225 | + } |
| 226 | + |
| 227 | + # Yes, we do have a topic in that help file for this. |
| 228 | + # Increase its ref count. |
| 229 | + else { |
| 230 | + ++$help_topics->{$file}->{topic}->{$topic}; |
| 231 | + } |
| 232 | +} |
| 233 | + |
| 234 | +# Helper: search source file for a regexps matching a help filename |
| 235 | +# and topic. |
| 236 | +sub check_name { |
| 237 | + my $info = shift, |
| 238 | + my $name = shift; |
| 239 | + my $sep = shift; |
| 240 | + my $src = shift; |
| 241 | + |
| 242 | + while ($src =~ m/$name\s*$sep\s*"(.+?)"\s*,.*?"(.+?)"/) { |
| 243 | + my $file = $1; |
| 244 | + my $topic = $2; |
| 245 | + check_file_topic($info, $file, $topic); |
| 246 | + |
| 247 | + # Don't find this one again |
| 248 | + $src =~ s/$name\s*$sep\s*"(.+?)"\s*,.*?"(.+?)"/SHOW_HELP_REPLACED/; |
| 249 | + } |
| 250 | + |
| 251 | + return $src; |
| 252 | +} |
| 253 | + |
| 254 | + |
| 255 | +# Check to ensure helpfile/topic combos exist |
| 256 | +foreach my $info (@source_files) { |
| 257 | + verbose("Searching source: $info->{full}\n"); |
| 258 | + |
| 259 | + # If this source file is not in the target area, then skip it |
| 260 | + next |
| 261 | + if ($info->{relative} != /^$startrel/); |
| 262 | + |
| 263 | + my $src; |
| 264 | + open(FH, $info->{full}) || die "Can't open $info->{full}"; |
| 265 | + while (<FH>) { |
| 266 | + # Eliminate newlines, just for regexp simplicity later |
| 267 | + chomp; |
| 268 | + $src .= $_; |
| 269 | + } |
| 270 | + close(FH); |
| 271 | + |
| 272 | + # Find calls to pmix_show_help() |
| 273 | + $src = check_name($info, "pmix_show_help", "\\(", $src); |
| 274 | + # Find calls to pmix_show_help_string() |
| 275 | + $src = check_name($info, "pmix_show_help_string", "\\(", $src); |
| 276 | + # Find special tokens from comments |
| 277 | + $src = check_name($info, "SHOW_HELP", ":", $src); |
| 278 | +} |
| 279 | + |
| 280 | +########################################################################### |
| 281 | + |
| 282 | +# Check that all indexed help strings were referenced |
| 283 | + |
| 284 | +print "Checking for stale help messages / files...\n"; |
| 285 | + |
| 286 | +foreach my $file (sort(keys(%{$help_topics}))) { |
| 287 | + my $num_used = 0; |
| 288 | + foreach my $topic (sort(keys(%{$help_topics->{$file}->{topic}}))) { |
| 289 | + if (0 == $help_topics->{$file}->{topic}->{$topic}) { |
| 290 | + print "*** WARNING: Possibly unused help topic |
| 291 | + Help file: $help_topics->{$file}->{full} |
| 292 | + Help topic: $topic\n"; |
| 293 | + ++$num_warnings; |
| 294 | + } else { |
| 295 | + ++$num_used; |
| 296 | + } |
| 297 | + } |
| 298 | + |
| 299 | + # Were no topics used in this file at all? |
| 300 | + if (0 == $num_used) { |
| 301 | + print "*** WARNING: Possibly unused help file (no topics used from this file) |
| 302 | + Help file: $help_topics->{$file}->{full}\n"; |
| 303 | + ++$num_warnings; |
| 304 | + } |
| 305 | +} |
| 306 | + |
| 307 | +########################################################################### |
| 308 | + |
| 309 | +# All done |
| 310 | +if (0 == $num_errors && 0 == $num_warnings) { |
| 311 | + print "+++ All seems good!\n"; |
| 312 | + exit(0); |
| 313 | +} else { |
| 314 | + print "Total number of warnings: $num_warnings |
| 315 | +Total number of errors: $num_errors\n"; |
| 316 | + exit(1); |
| 317 | +} |
0 commit comments