| #!/usr/bin/env perl | |
| # SPDX-License-Identifier: GPL-2.0 | |
| use warnings; | |
| use strict; | |
| my $srctree = shift @ARGV; | |
| unless (defined $srctree) { | |
| $srctree = `git rev-parse --show-toplevel 2>/dev/null`; | |
| chomp $srctree; | |
| my $msg = "Usage: $0 <srctree> [excludes file]\n"; | |
| $msg .= "Please provide <srctree>."; | |
| $msg .= " Is it '$srctree'?" if $srctree; | |
| $msg .= "\n"; | |
| die $msg; | |
| } | |
| my $kconfig_sym_check_excludes = defined $ARGV[0] ? $ARGV[0] : undef; | |
| sub indent_depth { | |
| my ($ws) = @_; | |
| my $col = 0; | |
| for my $c (split //, $ws) { | |
| $col = $c eq "\t" ? int($col / 8) * 8 + 8 : $col + 1; | |
| } | |
| return $col; | |
| } | |
| my @files = `git -C \Q$srctree\E ls-files '*Kconfig*' 2>/dev/null`; | |
| if (@files) { | |
| chomp @files; | |
| @files = map { "$srctree/$_" } @files; | |
| } else { | |
| @files = `find \Q$srctree\E -name '*Kconfig*'`; | |
| chomp @files; | |
| } | |
| @files = grep { !m{/scripts/kconfig/tests/} } @files; | |
| my %configs = (); | |
| my %refs = (); | |
| foreach my $file (@files) { | |
| open F, $file or die "Cannot open $file: $!"; | |
| my $help = 0; | |
| my $help_level; | |
| my $level; | |
| while (<F>) { | |
| chomp; | |
| while (/\\\s*$/) { | |
| s/\\\s*$/ /; | |
| my $cont = <F> // last; | |
| chomp $cont; | |
| $_ .= $cont; | |
| } | |
| next if /^\s*$/; | |
| next if /^\s*#/; | |
| /^(\s*)/; | |
| $level = indent_depth($1); | |
| if ($help && $level < $help_level) { | |
| $help = 0; | |
| } | |
| next if ($help); | |
| if (/^\s*(help|\-\-\-help\-\-\-)$/) { | |
| $help = 1; | |
| my $next; | |
| while (defined($next = <F>)) { | |
| last unless $next =~ /^\s*(?:#.*)?$/; | |
| } | |
| last unless defined $next; | |
| $next =~ /^(\s*)/; | |
| if (indent_depth($1) >= $level) { | |
| $help_level = indent_depth($1); | |
| } else { | |
| $help = 0; | |
| } | |
| $_ = $next; | |
| redo; | |
| } | |
| if (/^\s*(config|menuconfig)\s+([a-zA-Z0-9_]+)\s*(#.*)?$/) { | |
| $configs{$2}++; | |
| next; | |
| } | |
| if (/^\s*(default|def_bool|def_tristate|select|depends\s+on|imply|visible\s+if|range|if|bool|tristate|int|hex|string|prompt)\s+(.+)\s*$/) { | |
| my $s = $2; | |
| $s =~ s/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'//g; | |
| $s =~ s/#.*//; | |
| $s =~ s/\$\((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*\)//g; | |
| $s =~ s/%%[^%]*%%//g; | |
| my @syms = split /[^a-zA-Z0-9_]+/, $s; | |
| map { | |
| $refs{$_}++ if (/[a-zA-Z]/ && $_ ne "if" && $_ ne "y" && $_ ne "n" && $_ ne "m" && !/^0[xX][0-9a-fA-F]+$/); | |
| } @syms | |
| } | |
| } | |
| close F; | |
| } | |
| my %known_syms = (); | |
| if (defined $kconfig_sym_check_excludes) { | |
| my $file = $kconfig_sym_check_excludes; | |
| open(F, "<", $file) or die "Cannot open $file: $!"; | |
| while (<F>) { | |
| chomp; | |
| next if /^\s*$/; | |
| next if /^\s*#/; | |
| $known_syms{$1}++ if (/^\s*([a-zA-Z0-9_]+)\s*(#.*)?$/); | |
| } | |
| } | |
| my $ret = 0; | |
| foreach my $k (sort keys %refs) { | |
| next if (exists $configs{$k} || exists $known_syms{$k}); | |
| print "$k"; | |
| print " - warning: '$k' is probably not what you want; Kconfig tristate literals are always lowercase ('n', 'y', 'm')" if ($k eq "N" || $k eq "Y" || $k eq "M"); | |
| print "\n"; | |
| $ret = 1; | |
| } | |
| exit $ret; |