Back to home page

OSCL-LXR

 
 

    


0001 #!/usr/bin/env perl
0002 # SPDX-License-Identifier: GPL-2.0
0003 #
0004 # Treewide grep for references to files under Documentation, and report
0005 # non-existing files in stderr.
0006 
0007 use warnings;
0008 use strict;
0009 use Getopt::Long qw(:config no_auto_abbrev);
0010 
0011 # NOTE: only add things here when the file was gone, but the text wants
0012 # to mention a past documentation file, for example, to give credits for
0013 # the original work.
0014 my %false_positives = (
0015         "Documentation/scsi/scsi_mid_low_api.rst" => "Documentation/Configure.help",
0016         "drivers/vhost/vhost.c" => "Documentation/virtual/lguest/lguest.c",
0017 );
0018 
0019 my $scriptname = $0;
0020 $scriptname =~ s,.*/([^/]+/),$1,;
0021 
0022 # Parse arguments
0023 my $help = 0;
0024 my $fix = 0;
0025 my $warn = 0;
0026 
0027 if (! -e ".git") {
0028         printf "Warning: can't check if file exists, as this is not a git tree\n";
0029         exit 0;
0030 }
0031 
0032 GetOptions(
0033         'fix' => \$fix,
0034         'warn' => \$warn,
0035         'h|help|usage' => \$help,
0036 );
0037 
0038 if ($help != 0) {
0039     print "$scriptname [--help] [--fix]\n";
0040     exit -1;
0041 }
0042 
0043 # Step 1: find broken references
0044 print "Finding broken references. This may take a while...  " if ($fix);
0045 
0046 my %broken_ref;
0047 
0048 my $doc_fix = 0;
0049 
0050 open IN, "git grep ':doc:\`' Documentation/|"
0051      or die "Failed to run git grep";
0052 while (<IN>) {
0053         next if (!m,^([^:]+):.*\:doc\:\`([^\`]+)\`,);
0054         next if (m,sphinx/,);
0055 
0056         my $file = $1;
0057         my $d = $1;
0058         my $doc_ref = $2;
0059 
0060         my $f = $doc_ref;
0061 
0062         $d =~ s,(.*/).*,$1,;
0063         $f =~ s,.*\<([^\>]+)\>,$1,;
0064 
0065         if ($f =~ m,^/,) {
0066                 $f = "$f.rst";
0067                 $f =~ s,^/,Documentation/,;
0068         } else {
0069                 $f = "$d$f.rst";
0070         }
0071 
0072         next if (grep -e, glob("$f"));
0073 
0074         if ($fix && !$doc_fix) {
0075                 print STDERR "\nWARNING: Currently, can't fix broken :doc:`` fields\n";
0076         }
0077         $doc_fix++;
0078 
0079         print STDERR "$file: :doc:`$doc_ref`\n";
0080 }
0081 close IN;
0082 
0083 open IN, "git grep 'Documentation/'|"
0084      or die "Failed to run git grep";
0085 while (<IN>) {
0086         next if (!m/^([^:]+):(.*)/);
0087 
0088         my $f = $1;
0089         my $ln = $2;
0090 
0091         # On linux-next, discard the Next/ directory
0092         next if ($f =~ m,^Next/,);
0093 
0094         # Makefiles and scripts contain nasty expressions to parse docs
0095         next if ($f =~ m/Makefile/ || $f =~ m/\.sh$/);
0096 
0097         # It doesn't make sense to parse hidden files
0098         next if ($f =~ m#/\.#);
0099 
0100         # Skip this script
0101         next if ($f eq $scriptname);
0102 
0103         # Ignore the dir where documentation will be built
0104         next if ($ln =~ m,\b(\S*)Documentation/output,);
0105 
0106         if ($ln =~ m,\b(\S*)(Documentation/[A-Za-z0-9\_\.\,\~/\*\[\]\?+-]*)(.*),) {
0107                 my $prefix = $1;
0108                 my $ref = $2;
0109                 my $base = $2;
0110                 my $extra = $3;
0111 
0112                 # some file references are like:
0113                 # /usr/src/linux/Documentation/DMA-{API,mapping}.txt
0114                 # For now, ignore them
0115                 next if ($extra =~ m/^{/);
0116 
0117                 # Remove footnotes at the end like:
0118                 # Documentation/devicetree/dt-object-internal.txt[1]
0119                 $ref =~ s/(txt|rst)\[\d+]$/$1/;
0120 
0121                 # Remove ending ']' without any '['
0122                 $ref =~ s/\].*// if (!($ref =~ m/\[/));
0123 
0124                 # Remove puntuation marks at the end
0125                 $ref =~ s/[\,\.]+$//;
0126 
0127                 my $fulref = "$prefix$ref";
0128 
0129                 $fulref =~ s/^(\<file|ref)://;
0130                 $fulref =~ s/^[\'\`]+//;
0131                 $fulref =~ s,^\$\(.*\)/,,;
0132                 $base =~ s,.*/,,;
0133 
0134                 # Remove URL false-positives
0135                 next if ($fulref =~ m/^http/);
0136 
0137                 # Remove sched-pelt false-positive
0138                 next if ($fulref =~ m,^Documentation/scheduler/sched-pelt$,);
0139 
0140                 # Discard some build examples from Documentation/target/tcm_mod_builder.rst
0141                 next if ($fulref =~ m,mnt/sdb/lio-core-2.6.git/Documentation/target,);
0142 
0143                 # Check if exists, evaluating wildcards
0144                 next if (grep -e, glob("$ref $fulref"));
0145 
0146                 # Accept relative Documentation patches for tools/
0147                 if ($f =~ m/tools/) {
0148                         my $path = $f;
0149                         $path =~ s,(.*)/.*,$1,;
0150                         $path =~ s,testing/selftests/bpf,bpf/bpftool,;
0151                         next if (grep -e, glob("$path/$ref $path/../$ref $path/$fulref"));
0152                 }
0153 
0154                 # Discard known false-positives
0155                 if (defined($false_positives{$f})) {
0156                         next if ($false_positives{$f} eq $fulref);
0157                 }
0158 
0159                 if ($fix) {
0160                         if (!($ref =~ m/(scripts|Kconfig|Kbuild)/)) {
0161                                 $broken_ref{$ref}++;
0162                         }
0163                 } elsif ($warn) {
0164                         print STDERR "Warning: $f references a file that doesn't exist: $fulref\n";
0165                 } else {
0166                         print STDERR "$f: $fulref\n";
0167                 }
0168         }
0169 }
0170 close IN;
0171 
0172 exit 0 if (!$fix);
0173 
0174 # Step 2: Seek for file name alternatives
0175 print "Auto-fixing broken references. Please double-check the results\n";
0176 
0177 foreach my $ref (keys %broken_ref) {
0178         my $new =$ref;
0179 
0180         my $basedir = ".";
0181         # On translations, only seek inside the translations directory
0182         $basedir  = $1 if ($ref =~ m,(Documentation/translations/[^/]+),);
0183 
0184         # get just the basename
0185         $new =~ s,.*/,,;
0186 
0187         my $f="";
0188 
0189         # usual reason for breakage: DT file moved around
0190         if ($ref =~ /devicetree/) {
0191                 # usual reason for breakage: DT file renamed to .yaml
0192                 if (!$f) {
0193                         my $new_ref = $ref;
0194                         $new_ref =~ s/\.txt$/.yaml/;
0195                         $f=$new_ref if (-f $new_ref);
0196                 }
0197 
0198                 if (!$f) {
0199                         my $search = $new;
0200                         $search =~ s,^.*/,,;
0201                         $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
0202                         if (!$f) {
0203                                 # Manufacturer name may have changed
0204                                 $search =~ s/^.*,//;
0205                                 $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
0206                         }
0207                 }
0208         }
0209 
0210         # usual reason for breakage: file renamed to .rst
0211         if (!$f) {
0212                 $new =~ s/\.txt$/.rst/;
0213                 $f=qx(find $basedir -iname $new) if ($new);
0214         }
0215 
0216         # usual reason for breakage: use dash or underline
0217         if (!$f) {
0218                 $new =~ s/[-_]/[-_]/g;
0219                 $f=qx(find $basedir -iname $new) if ($new);
0220         }
0221 
0222         # Wild guess: seek for the same name on another place
0223         if (!$f) {
0224                 $f = qx(find $basedir -iname $new) if ($new);
0225         }
0226 
0227         my @find = split /\s+/, $f;
0228 
0229         if (!$f) {
0230                 print STDERR "ERROR: Didn't find a replacement for $ref\n";
0231         } elsif (scalar(@find) > 1) {
0232                 print STDERR "WARNING: Won't auto-replace, as found multiple files close to $ref:\n";
0233                 foreach my $j (@find) {
0234                         $j =~ s,^./,,;
0235                         print STDERR "    $j\n";
0236                 }
0237         } else {
0238                 $f = $find[0];
0239                 $f =~ s,^./,,;
0240                 print "INFO: Replacing $ref to $f\n";
0241                 foreach my $j (qx(git grep -l $ref)) {
0242                         qx(sed "s\@$ref\@$f\@g" -i $j);
0243                 }
0244         }
0245 }