| @@ -1,15 +1,15 @@ | | | @@ -1,15 +1,15 @@ |
1 | #! @PERL@ | | 1 | #! @PERL@ |
2 | # $NetBSD: pkglint.pl,v 1.779 2008/10/20 11:09:07 rillig Exp $ | | 2 | # $NetBSD: pkglint.pl,v 1.780 2008/11/04 21:45:13 rillig Exp $ |
3 | # | | 3 | # |
4 | | | 4 | |
5 | # pkglint - static analyzer and checker for pkgsrc packages | | 5 | # pkglint - static analyzer and checker for pkgsrc packages |
6 | # | | 6 | # |
7 | # Written by: | | 7 | # Written by: |
8 | # Roland Illig <rillig@NetBSD.org> | | 8 | # Roland Illig <rillig@NetBSD.org> |
9 | # | | 9 | # |
10 | # Based on work by: | | 10 | # Based on work by: |
11 | # Hubert Feyrer <hubertf@NetBSD.org> | | 11 | # Hubert Feyrer <hubertf@NetBSD.org> |
12 | # Thorsten Frueauf <frueauf@NetBSD.org> | | 12 | # Thorsten Frueauf <frueauf@NetBSD.org> |
13 | # Thomas Klausner <wiz@NetBSD.org> | | 13 | # Thomas Klausner <wiz@NetBSD.org> |
14 | # and others. | | 14 | # and others. |
15 | # | | 15 | # |
| @@ -1131,26 +1131,44 @@ use enum qw(FNAME REVISION MTIME TAG); | | | @@ -1131,26 +1131,44 @@ use enum qw(FNAME REVISION MTIME TAG); |
1131 | | | 1131 | |
1132 | sub new($$$$$) { | | 1132 | sub new($$$$$) { |
1133 | my ($class, $fname, $revision, $date, $tag) = @_; | | 1133 | my ($class, $fname, $revision, $date, $tag) = @_; |
1134 | my $self = [ $fname, $revision, $date, $tag ]; | | 1134 | my $self = [ $fname, $revision, $date, $tag ]; |
1135 | bless($self, $class); | | 1135 | bless($self, $class); |
1136 | return $self; | | 1136 | return $self; |
1137 | } | | 1137 | } |
1138 | sub fname($) { return shift()->[FNAME]; } | | 1138 | sub fname($) { return shift()->[FNAME]; } |
1139 | sub revision($) { return shift()->[REVISION]; } | | 1139 | sub revision($) { return shift()->[REVISION]; } |
1140 | sub mtime($) { return shift()->[MTIME]; } | | 1140 | sub mtime($) { return shift()->[MTIME]; } |
1141 | sub tag($) { return shift()->[TAG]; } | | 1141 | sub tag($) { return shift()->[TAG]; } |
1142 | #== End of CVS_Entry ====================================================== | | 1142 | #== End of CVS_Entry ====================================================== |
1143 | | | 1143 | |
| | | 1144 | package PkgLint::Change; |
| | | 1145 | #========================================================================== |
| | | 1146 | # A change entry from doc/CHANGES-* |
| | | 1147 | #========================================================================== |
| | | 1148 | |
| | | 1149 | sub new($$$$$$) { |
| | | 1150 | my ($class, $line, $action, $pkgpath, $version, $author, $date) = @_; |
| | | 1151 | my $self = [ $line, $action, $pkgpath, $version, $author, $date ]; |
| | | 1152 | bless($self, $class); |
| | | 1153 | return $self; |
| | | 1154 | } |
| | | 1155 | sub line($) { return shift()->[0]; } |
| | | 1156 | sub action($) { return shift()->[1]; } |
| | | 1157 | sub pkgpath($) { return shift()->[2]; } |
| | | 1158 | sub version($) { return shift()->[3]; } |
| | | 1159 | sub author($) { return shift()->[4]; } |
| | | 1160 | sub date($) { return shift()->[5]; } |
| | | 1161 | #== End of PkgLint::Change ================================================ |
1144 | | | 1162 | |
1145 | package main; | | 1163 | package main; |
1146 | #========================================================================== | | 1164 | #========================================================================== |
1147 | # This package contains the application-specific code of pkglint. | | 1165 | # This package contains the application-specific code of pkglint. |
1148 | # Most subroutines in this package follow a strict naming convention: | | 1166 | # Most subroutines in this package follow a strict naming convention: |
1149 | # | | 1167 | # |
1150 | # The get_*() functions provide easy access to important non-trivial data | | 1168 | # The get_*() functions provide easy access to important non-trivial data |
1151 | # structures that are loaded from external files and are therefore cached. | | 1169 | # structures that are loaded from external files and are therefore cached. |
1152 | # | | 1170 | # |
1153 | # The is_*() functions return a boolean value and have no side effects. | | 1171 | # The is_*() functions return a boolean value and have no side effects. |
1154 | # | | 1172 | # |
1155 | # The checkline_*() procedures check a single line for compliance with some | | 1173 | # The checkline_*() procedures check a single line for compliance with some |
1156 | # rules. | | 1174 | # rules. |
| @@ -2193,26 +2211,90 @@ sub get_doc_TODO_updates() { | | | @@ -2193,26 +2211,90 @@ sub get_doc_TODO_updates() { |
2193 | } | | 2211 | } |
2194 | return $get_doc_TODO_updates_result; | | 2212 | return $get_doc_TODO_updates_result; |
2195 | } | | 2213 | } |
2196 | | | 2214 | |
2197 | my $get_wip_TODO_updates_result = undef; | | 2215 | my $get_wip_TODO_updates_result = undef; |
2198 | sub get_wip_TODO_updates() { | | 2216 | sub get_wip_TODO_updates() { |
2199 | | | 2217 | |
2200 | if (!defined($get_wip_TODO_updates_result)) { | | 2218 | if (!defined($get_wip_TODO_updates_result)) { |
2201 | $get_wip_TODO_updates_result = load_doc_TODO_updates("${cwd_pkgsrcdir}/wip/TODO"); | | 2219 | $get_wip_TODO_updates_result = load_doc_TODO_updates("${cwd_pkgsrcdir}/wip/TODO"); |
2202 | } | | 2220 | } |
2203 | return $get_wip_TODO_updates_result; | | 2221 | return $get_wip_TODO_updates_result; |
2204 | } | | 2222 | } |
2205 | | | 2223 | |
| | | 2224 | my $get_doc_CHANGES_docs = undef; # [ $fname, undef or $lines ] |
| | | 2225 | sub get_doc_CHANGES($) { |
| | | 2226 | my ($pkgpath) = @_; |
| | | 2227 | |
| | | 2228 | $opt_debug_trace and log_debug(NO_FILE, NO_LINES, "get_doc_CHANGES(\"$pkgpath\")"); |
| | | 2229 | |
| | | 2230 | # Make a reversed list of all the CHANGES-* files, but don't load |
| | | 2231 | # them yet. |
| | | 2232 | if (!defined($get_doc_CHANGES_docs)) { |
| | | 2233 | opendir(DIR, "${cwd_pkgsrcdir}/doc") or die; |
| | | 2234 | my @files = readdir(DIR); |
| | | 2235 | closedir(DIR) or die; |
| | | 2236 | foreach my $file (reverse sort @files) { |
| | | 2237 | if ($file =~ m"^CHANGES-\d+$") { |
| | | 2238 | push(@$get_doc_CHANGES_docs, [ $file, undef ]); |
| | | 2239 | } |
| | | 2240 | } |
| | | 2241 | $opt_debug_misc and log_debug(NO_FILE, NO_LINES, "Found " . (scalar @$get_doc_CHANGES_docs) . " changes files."); |
| | | 2242 | } |
| | | 2243 | |
| | | 2244 | # Scan the *-CHANGES files in reverse order until some action |
| | | 2245 | # matches the package directory. |
| | | 2246 | my @result = (); |
| | | 2247 | foreach my $doc (@$get_doc_CHANGES_docs) { |
| | | 2248 | if (!defined($doc->[1])) { |
| | | 2249 | $opt_debug_misc and log_debug(NO_FILE, NO_LINES, "loading $doc->[0]"); |
| | | 2250 | my $lines = load_file("${cwd_pkgsrcdir}/doc/$doc->[0]") or die; |
| | | 2251 | |
| | | 2252 | my @changes = (); |
| | | 2253 | foreach my $line (@$lines) { |
| | | 2254 | my $text = $line->text; |
| | | 2255 | next unless $text =~ m"^\t[A-Z]"; |
| | | 2256 | |
| | | 2257 | if ($text =~ m"^\t(Updated) (\S+) to (\S+) \[(\S+) (\d\d\d\d-\d\d-\d\d)\]$") { |
| | | 2258 | push(@changes, PkgLint::Change->new($line, $1, $2, $3, $4, $5)); |
| | | 2259 | } elsif ($text =~ m"^\t(Added) (\S+) version (\S+) \[(\S+) (\d\d\d\d-\d\d-\d\d)\]$") { |
| | | 2260 | push(@changes, PkgLint::Change->new($line, $1, $2, $3, $4, $5)); |
| | | 2261 | } elsif ($text =~ m"^\t(Removed) (\S+) (?:successor (\S+) )?\[(\S+) (\d\d\d\d-\d\d-\d\d)\]$") { |
| | | 2262 | push(@changes, PkgLint::Change->new($line, $1, $2, undef, $3, $4)); |
| | | 2263 | } elsif ($text =~ m"^\t(Downgraded) (\S+) to (\S+) \[(\S+) (\d\d\d\d-\d\d-\d\d)\]$") { |
| | | 2264 | push(@changes, PkgLint::Change->new($line, $1, $2, $3, $4, $5)); |
| | | 2265 | } elsif ($text =~ m"^\t(Renamed|Moved) (\S+) to (\S+) \[(\S+) (\d\d\d\d-\d\d-\d\d)\]$") { |
| | | 2266 | push(@changes, PkgLint::Change->new($line, $1, $2, $3, $4, $5)); |
| | | 2267 | } else { |
| | | 2268 | $line->log_warning("Unknown doc/CHANGES line: " . $line->text); |
| | | 2269 | $line->explain_warning( |
| | | 2270 | "Maybe some developer didn't stick to the conventions that have been", |
| | | 2271 | "established by mk/misc/developer.mk?"); |
| | | 2272 | } |
| | | 2273 | } |
| | | 2274 | $doc->[1] = \@changes; |
| | | 2275 | } |
| | | 2276 | |
| | | 2277 | foreach my $change (@{$doc->[1]}) { |
| | | 2278 | next unless $pkgpath eq $change->pkgpath; |
| | | 2279 | push(@result, $change); |
| | | 2280 | } |
| | | 2281 | if (@result != 0) { |
| | | 2282 | return @result; |
| | | 2283 | } |
| | | 2284 | } |
| | | 2285 | return (); |
| | | 2286 | } |
| | | 2287 | |
2206 | sub get_suggested_package_updates() { | | 2288 | sub get_suggested_package_updates() { |
2207 | | | 2289 | |
2208 | return ($is_wip) | | 2290 | return ($is_wip) |
2209 | ? get_wip_TODO_updates() | | 2291 | ? get_wip_TODO_updates() |
2210 | : get_doc_TODO_updates(); | | 2292 | : get_doc_TODO_updates(); |
2211 | } | | 2293 | } |
2212 | | | 2294 | |
2213 | # Load all variables from mk/defaults/mk.conf. Since pkglint does not | | 2295 | # Load all variables from mk/defaults/mk.conf. Since pkglint does not |
2214 | # load the infrastructure files during normal operation, these | | 2296 | # load the infrastructure files during normal operation, these |
2215 | # definitions have to be added explicitly. | | 2297 | # definitions have to be added explicitly. |
2216 | sub load_userdefined_variables() { | | 2298 | sub load_userdefined_variables() { |
2217 | my $fname = "${cwd_pkgsrcdir}/mk/defaults/mk.conf"; | | 2299 | my $fname = "${cwd_pkgsrcdir}/mk/defaults/mk.conf"; |
2218 | my ($lines, $vars); | | 2300 | my ($lines, $vars); |
| @@ -3387,26 +3469,51 @@ sub checkword_absolute_pathname($$) { | | | @@ -3387,26 +3469,51 @@ sub checkword_absolute_pathname($$) { |
3387 | $line->explain_warning( | | 3469 | $line->explain_warning( |
3388 | "Absolute pathnames are often an indicator for unportable code. As", | | 3470 | "Absolute pathnames are often an indicator for unportable code. As", |
3389 | "pkgsrc aims to be a portable system, absolute pathnames should be", | | 3471 | "pkgsrc aims to be a portable system, absolute pathnames should be", |
3390 | "avoided whenever possible.", | | 3472 | "avoided whenever possible.", |
3391 | "", | | 3473 | "", |
3392 | "A special variable in this context is \${DESTDIR}, which is used in GNU", | | 3474 | "A special variable in this context is \${DESTDIR}, which is used in GNU", |
3393 | "projects to specify a different directory for installation than what", | | 3475 | "projects to specify a different directory for installation than what", |
3394 | "the programs see later when they are executed. Usually it is empty, so", | | 3476 | "the programs see later when they are executed. Usually it is empty, so", |
3395 | "if anything after that variable starts with a slash, it is considered", | | 3477 | "if anything after that variable starts with a slash, it is considered", |
3396 | "an absolute pathname."); | | 3478 | "an absolute pathname."); |
3397 | } | | 3479 | } |
3398 | } | | 3480 | } |
3399 | | | 3481 | |
| | | 3482 | sub checkpackage_possible_downgrade() { |
| | | 3483 | |
| | | 3484 | $opt_debug_trace and log_debug(NO_FILE, NO_LINES, "checkpackage_possible_downgrade"); |
| | | 3485 | |
| | | 3486 | return unless defined $effective_pkgname; |
| | | 3487 | return unless $effective_pkgname =~ regex_pkgname; |
| | | 3488 | my ($pkgbase, $pkgversion) = ($1, $2); |
| | | 3489 | my $line = $effective_pkgname_line; |
| | | 3490 | |
| | | 3491 | my @changes = get_doc_CHANGES($pkgpath); |
| | | 3492 | if (@changes == 0) { |
| | | 3493 | $opt_debug_misc and $line->log_debug("No changes have been recorded for package $pkgpath."); |
| | | 3494 | return; |
| | | 3495 | } |
| | | 3496 | |
| | | 3497 | my $last_change = $changes[-1]; |
| | | 3498 | return unless $last_change->action eq "Updated"; |
| | | 3499 | |
| | | 3500 | my $last_version = $last_change->version; |
| | | 3501 | |
| | | 3502 | if (dewey_cmp($pkgversion, "<", $last_version)) { |
| | | 3503 | $line->log_warning("The package is being downgraded from $last_version to $pkgversion."); |
| | | 3504 | } |
| | | 3505 | } |
| | | 3506 | |
3400 | # | | 3507 | # |
3401 | # Subroutines to check a single line. | | 3508 | # Subroutines to check a single line. |
3402 | # | | 3509 | # |
3403 | | | 3510 | |
3404 | sub checkline_length($$) { | | 3511 | sub checkline_length($$) { |
3405 | my ($line, $maxlength) = @_; | | 3512 | my ($line, $maxlength) = @_; |
3406 | | | 3513 | |
3407 | if (length($line->text) > $maxlength) { | | 3514 | if (length($line->text) > $maxlength) { |
3408 | $line->log_warning("Line too long (should be no more than $maxlength characters)."); | | 3515 | $line->log_warning("Line too long (should be no more than $maxlength characters)."); |
3409 | $line->explain_warning( | | 3516 | $line->explain_warning( |
3410 | "Back in the old time, terminals with 80x25 characters were common.", | | 3517 | "Back in the old time, terminals with 80x25 characters were common.", |
3411 | "And this is still the default size of many terminal emulators.", | | 3518 | "And this is still the default size of many terminal emulators.", |
3412 | "Moderately short lines also make reading easier."); | | 3519 | "Moderately short lines also make reading easier."); |
| @@ -6779,26 +6886,28 @@ sub checkfile_package_Makefile($$$) { | | | @@ -6779,26 +6886,28 @@ sub checkfile_package_Makefile($$$) { |
6779 | | | 6886 | |
6780 | if (!defined($pkgname) && defined($distname) && $distname !~ regex_unresolved && $distname !~ regex_pkgname) { | | 6887 | if (!defined($pkgname) && defined($distname) && $distname !~ regex_unresolved && $distname !~ regex_pkgname) { |
6781 | $distname_line->log_warning("As DISTNAME is not a valid package name, please define the PKGNAME explicitly."); | | 6888 | $distname_line->log_warning("As DISTNAME is not a valid package name, please define the PKGNAME explicitly."); |
6782 | } | | 6889 | } |
6783 | | | 6890 | |
6784 | ($effective_pkgname, $effective_pkgname_line, $effective_pkgbase, $effective_pkgversion) | | 6891 | ($effective_pkgname, $effective_pkgname_line, $effective_pkgbase, $effective_pkgversion) |
6785 | = (defined($pkgname) && $pkgname !~ regex_unresolved && $pkgname =~ regex_pkgname) ? ($pkgname, $pkgname_line, $1, $2) | | 6892 | = (defined($pkgname) && $pkgname !~ regex_unresolved && $pkgname =~ regex_pkgname) ? ($pkgname, $pkgname_line, $1, $2) |
6786 | : (defined($distname) && $distname !~ regex_unresolved && $distname =~ regex_pkgname) ? ($distname, $distname_line, $1, $2) | | 6893 | : (defined($distname) && $distname !~ regex_unresolved && $distname =~ regex_pkgname) ? ($distname, $distname_line, $1, $2) |
6787 | : (undef, undef, undef, undef); | | 6894 | : (undef, undef, undef, undef); |
6788 | if (defined($effective_pkgname_line)) { | | 6895 | if (defined($effective_pkgname_line)) { |
6789 | $opt_debug_misc and $effective_pkgname_line->log_debug("Effective name=${effective_pkgname} base=${effective_pkgbase} version=${effective_pkgversion}."); | | 6896 | $opt_debug_misc and $effective_pkgname_line->log_debug("Effective name=${effective_pkgname} base=${effective_pkgbase} version=${effective_pkgversion}."); |
6790 | } | | 6897 | } |
6791 | | | 6898 | |
| | | 6899 | checkpackage_possible_downgrade(); |
| | | 6900 | |
6792 | if (!exists($pkgctx_vardef->{"COMMENT"})) { | | 6901 | if (!exists($pkgctx_vardef->{"COMMENT"})) { |
6793 | log_warning($fname, NO_LINE_NUMBER, "No COMMENT given."); | | 6902 | log_warning($fname, NO_LINE_NUMBER, "No COMMENT given."); |
6794 | } | | 6903 | } |
6795 | | | 6904 | |
6796 | if (exists($pkgctx_vardef->{"USE_IMAKE"}) && exists($pkgctx_vardef->{"USE_X11"})) { | | 6905 | if (exists($pkgctx_vardef->{"USE_IMAKE"}) && exists($pkgctx_vardef->{"USE_X11"})) { |
6797 | $pkgctx_vardef->{"USE_IMAKE"}->log_note("USE_IMAKE makes ..."); | | 6906 | $pkgctx_vardef->{"USE_IMAKE"}->log_note("USE_IMAKE makes ..."); |
6798 | $pkgctx_vardef->{"USE_X11"}->log_note("... USE_X11 superfluous."); | | 6907 | $pkgctx_vardef->{"USE_X11"}->log_note("... USE_X11 superfluous."); |
6799 | } | | 6908 | } |
6800 | | | 6909 | |
6801 | if (defined($effective_pkgbase)) { | | 6910 | if (defined($effective_pkgbase)) { |
6802 | | | 6911 | |
6803 | foreach my $suggested_update (@{get_suggested_package_updates()}) { | | 6912 | foreach my $suggested_update (@{get_suggested_package_updates()}) { |
6804 | my ($line, $suggbase, $suggver, $suggcomm) = @{$suggested_update}; | | 6913 | my ($line, $suggbase, $suggver, $suggcomm) = @{$suggested_update}; |