diff options
author | rillig <rillig@pkgsrc.org> | 2022-07-30 08:18:31 +0000 |
---|---|---|
committer | rillig <rillig@pkgsrc.org> | 2022-07-30 08:18:31 +0000 |
commit | cd6360a15cf42f01b0e4457b80f4c917e3f24fb2 (patch) | |
tree | df4fe62c461f8890d5c38d43fd44faa789a2fb82 /pkgtools | |
parent | 949a293bb1a021acecd0ea4116eeecf639c1e8d5 (diff) | |
download | pkgsrc-cd6360a15cf42f01b0e4457b80f4c917e3f24fb2.tar.gz |
lintpkgsrc: cleanup: sort subs from small to big
This way, perl properly checks the sub prototypes.
No functional change.
Diffstat (limited to 'pkgtools')
-rwxr-xr-x | pkgtools/lintpkgsrc/files/lintpkgsrc.pl | 1436 |
1 files changed, 716 insertions, 720 deletions
diff --git a/pkgtools/lintpkgsrc/files/lintpkgsrc.pl b/pkgtools/lintpkgsrc/files/lintpkgsrc.pl index ea0165c46f7..4cc07aba351 100755 --- a/pkgtools/lintpkgsrc/files/lintpkgsrc.pl +++ b/pkgtools/lintpkgsrc/files/lintpkgsrc.pl @@ -1,6 +1,6 @@ #!@PERL5@ -# $NetBSD: lintpkgsrc.pl,v 1.27 2022/07/30 07:37:03 rillig Exp $ +# $NetBSD: lintpkgsrc.pl,v 1.28 2022/07/30 08:18:31 rillig Exp $ # Written by David Brownlee <abs@netbsd.org>. # @@ -22,6 +22,207 @@ use File::Basename; use IPC::Open3; use Cwd 'realpath', 'getcwd'; +# PkgList is the master list of all packages in pkgsrc. +# +package PkgList; + +sub add($@) { + my $self = shift; + + if (!$self->pkgs($_[0])) { + $self->{_pkgs}{ $_[0] } = new Pkgs $_[0]; + } + $self->pkgs($_[0])->add(@_); +} + +sub new($) { + my $class = shift; + my $self = {}; + bless $self, $class; + return $self; +} + +sub numpkgver($) { + my $self = shift; + scalar($self->pkgver); +} + +sub pkgver($@) { + my $self = shift; + + if (@_ == 0) { + my (@list); + foreach my $pkg ($self->pkgs) { + push(@list, $pkg->pkgver); + } + return (@list); + } + + if (defined $self->{_pkgs}{$_[0]}) { + return (@_ > 1) + ? $self->{_pkgs}{$_[0]}->pkgver($_[1]) + : $self->{_pkgs}{$_[0]}->pkgver(); + } + return; +} + +sub pkgs($@) { + my $self = shift; + + if (@_) { + return $self->{_pkgs}{$_[0]}; + } else { + return (sort { $a->pkg cmp $b->pkg } values %{$self->{_pkgs}}); + } +} + +sub store($) { + my $self = shift; + my @pkgs = keys %{$self->{_pkgs}}; + my ($cnt, $subcnt) = $self->count; + + print("\$pkgcnt = $cnt;\n"); + print("\$subpkgcnt = $subcnt;\n"); + map($self->{_pkgs}{$_}->store, keys %{$self->{_pkgs}}); +} + +sub count($) { + my $self = shift; + my ($pkgcnt, $pkgsubcnt); + + map { + $pkgcnt++; + $pkgsubcnt += $self->{_pkgs}{$_}->count; + } keys %{$self->{_pkgs}}; + wantarray ? ($pkgcnt, $pkgsubcnt) : $pkgcnt; +} + +# Pkgs is all versions of a given package (eg: apache-1.x and apache-2.x) +# +package Pkgs; + +sub add($@) { + my $self = shift; + + $self->{_pkgver}{$_[1]} = new PkgVer @_; +} + +sub new($@) { + my $class = shift; + my $self = {}; + + bless $self, $class; + $self->{_pkg} = $_[0]; + return $self; +} + +sub versions($) { + my $self = shift; + + return sort { $b cmp $a } keys %{$self->{_pkgver}}; +} + +sub pkg($) { + my $self = shift; + $self->{_pkg}; +} + +sub pkgver($@) { + my $self = shift; + + if (@_) { + if ($self->{_pkgver}{$_[0]}) { + return ($self->{_pkgver}{$_[0]}); + } + return; + } + return sort { $b->ver() cmp $a->ver() } values %{$self->{_pkgver}}; +} + +sub latestver($) { + my $self = shift; + + ($self->pkgver())[0]; +} + +sub store($) { + my $self = shift; + + print("\$pkgnum++;\n"); + map($self->{_pkgver}{$_}->store, keys %{$self->{_pkgver}}); +} + +sub count($) { + my $self = shift; + + scalar(keys %{$self->{_pkgver}}); +} + +# PkgVer is a unique package+version +# +package PkgVer; + +sub new($$$) { + my $class = shift; + my $self = {}; + + bless $self, $class; + $self->{_pkg} = $_[0]; + $self->{_ver} = $_[1]; + return $self; +} + +sub pkgname($) { + my $self = shift; + + $self->pkg . '-' . $self->ver; +} + +sub pkg($) { + my $self = shift; + + $self->{_pkg}; +} + +sub var($$$) { + my $self = shift; + my ($key, $val) = @_; + + (defined $val) + ? ($self->{$key} = $val) + : $self->{$key}; +} + +sub ver($) { + my $self = shift; + + $self->{_ver}; +} + +sub vars($) { + my $self = shift; + + grep(!/^_(pkg|ver)$/, keys %{$self}); +} + +sub store($) { + my $self = shift; + my $data; + + ($data = $self->{_pkg}) =~ s/([\\\$\@\%\"])/\\$1/g; + print("\$pkgver = \$pkglist->add(\"$data\", \""); + + ($data = $self->{_ver}) =~ s/([\\\$\@\%\"])/\\$1/g; + print("$data\"); __pkgcount(1);\n"); + + foreach ($self->vars) { + ($data = $self->{$_}) =~ s/([\\\$\@\%\"])/\\$1/g; + print("\$pkgver->var(\"$_\", \"$data\");\n"); + } +} + +package main; + # Buildtime configuration my $conf_make = '@MAKE@'; my $conf_pkgsrcdir = '@PKGSRCDIR@'; @@ -38,112 +239,66 @@ my ( %prebuilt_pkgdir_cache, # To avoid symlink loops in prebuilt_pkgdirs ); -sub usage_and_exit(); -sub listdir($$); -sub get_default_makefile_vars(); -sub fail($); -sub parse_makefile_pkgsrc($); - # Horrible kludge to ensure we have a value for testing in conditionals, but # gets removed in the final evaluation my $magic_undefined = 'M_a_G_i_C_uNdEfInEd'; -sub canonicalize_pkgname($) { - my ($pkgname) = @_; +sub debug(@) { - $pkgname =~ s,^py\d+(?:pth|)-,py-,; - $pkgname =~ s,^ruby\d+-,ruby-,; - $pkgname =~ s,^php\d+-,php-,; - return $pkgname; + ($opt{D}) && print STDERR 'DEBUG: ', @_; } -# Could speed up by building a cache of package names to paths, then processing -# each package name once against the tests. -sub check_prebuilt_packages() { - - if ($_ eq 'distfiles' || $_ eq 'pkgsrc') { - # Skip these subdirs if present - $File::Find::prune = 1; - - } elsif (/(.+)-(\d.*)\.t[bg]z$/) { - my ($pkg, $ver) = ($1, $2); - - $pkg = canonicalize_pkgname($pkg); - - my ($pkgs); - if ($pkgs = $pkglist->pkgs($pkg)) { - my ($pkgver) = $pkgs->pkgver($ver); - - if (!defined $pkgver) { - if ($opt{p}) { - print "$File::Find::dir/$_\n"; - push(@matched_prebuiltpackages, "$File::Find::dir/$_"); - } - - # Pick probably the last version - $pkgver = $pkgs->latestver; - } - - if ($opt{R} && $pkgver->var('RESTRICTED')) { - print "$File::Find::dir/$_\n"; - push(@matched_prebuiltpackages, "$File::Find::dir/$_"); - } - - if ($opt{O} && $pkgver->var('OSVERSION_SPECIFIC')) { - print "$File::Find::dir/$_\n"; - push(@matched_prebuiltpackages, "$File::Find::dir/$_"); - } - } +sub verbose(@) { - } elsif (-d $_) { - if ($prebuilt_pkgdir_cache{"$File::Find::dir/$_"}) { - $File::Find::prune = 1; - return; - } + if (-t STDERR) { + print STDERR @_; + } +} - $prebuilt_pkgdir_cache{"$File::Find::dir/$_"} = 1; - if (-l $_) { - my ($dest) = readlink($_); +sub fail($) { - if (substr($dest, 0, 1) ne '/') { - $dest = "$File::Find::dir/$dest"; - } - if (!$prebuilt_pkgdir_cache{$dest}) { - push(@prebuilt_pkgdirs, $dest); - } - } - } + print STDERR shift(), "\n"; + exit(3); } -# Dewey decimal verson number matching - or thereabouts -# Also handles 'nb<N>' suffix (checked iff values otherwise identical) +# List (recursive) non directory contents of specified directory # -sub deweycmp($$$) { - my ($match, $test, $val) = @_; - my ($cmp, $match_nb, $val_nb); +#TODO this entire sub should be replaced with direct calls to +# File::Find +sub listdir($$); +sub listdir($$) { + my ($base, $dir) = @_; + my ($thisdir); + my (@list, @thislist); - $match_nb = $val_nb = 0; - if ($match =~ /(.*)nb(.*)/) { - # Handle nb<N> suffix - $match = $1; - $match_nb = $2; + $thisdir = $base; + if (defined($dir)) { + $thisdir .= "/$dir"; + $dir .= '/'; + } else { + $dir = ''; } - if ($val =~ /(.*)nb(.*)/) { - # Handle nb<N> suffix - $val = $1; - $val_nb = $2; + opendir(DIR, $thisdir) || fail("Unable to opendir($thisdir): $!"); + @thislist = grep(substr($_, 0, 1) ne '.' && $_ ne 'CVS', readdir(DIR)); + closedir(DIR); + foreach my $entry (@thislist) { + if (-d "$thisdir/$entry") { + push(@list, listdir($base, "$dir$entry")); + } else { + push(@list, "$dir$entry"); + } } + @list; +} - $cmp = deweycmp_extract($match, $val); - - if (!$cmp) { - # Iff otherwise identical, check nb suffix - $cmp = deweycmp_extract($match_nb, $val_nb); - } +sub canonicalize_pkgname($) { + my ($pkgname) = @_; - debug("eval deweycmp $cmp $test 0\n"); - eval "$cmp $test 0"; + $pkgname =~ s,^py\d+(?:pth|)-,py-,; + $pkgname =~ s,^ruby\d+-,ruby-,; + $pkgname =~ s,^php\d+-,php-,; + return $pkgname; } sub convert_to_standard_dewey(@) { @@ -203,10 +358,424 @@ sub deweycmp_extract($$) { $cmp; } -sub fail($) { +# Dewey decimal version number matching - or thereabouts +# Also handles 'nb<N>' suffix (checked iff values otherwise identical) +# +sub deweycmp($$$) { + my ($match, $test, $val) = @_; + my ($cmp, $match_nb, $val_nb); - print STDERR shift(), "\n"; - exit(3); + $match_nb = $val_nb = 0; + if ($match =~ /(.*)nb(.*)/) { + # Handle nb<N> suffix + $match = $1; + $match_nb = $2; + } + + if ($val =~ /(.*)nb(.*)/) { + # Handle nb<N> suffix + $val = $1; + $val_nb = $2; + } + + $cmp = deweycmp_extract($match, $val); + + if (!$cmp) { + # Iff otherwise identical, check nb suffix + $cmp = deweycmp_extract($match_nb, $val_nb); + } + + debug("eval deweycmp $cmp $test 0\n"); + eval "$cmp $test 0"; +} + +sub parse_expand_vars_dumb($$) { + my ($line, $vars) = @_; + + while ($line =~ /\$\{([-\w.]+)\}/) { + if (defined(${$vars}{$1})) { + $line = $` . ${$vars}{$1} . $'; + } else { + $line = $` . $magic_undefined . $'; + } + } + $line; +} + +sub parse_expand_vars($$) { + my ($line, $vars) = @_; + + while ($line =~ /\$\{([-\w.]+)\}/) { + if (defined(${$vars}{$1})) { + $line = $` . ${$vars}{$1} . $'; + } else { + $line = $` . $magic_undefined . $'; + } + } + $line; +} + +sub parse_eval_make_false($$) { + my ($line, $vars) = @_; + my ($false, $test); + + $false = 0; + $test = parse_expand_vars_dumb($line, $vars); + + # XXX This is _so_ wrong - need to parse this correctly + $test =~ s/""/\r/g; + $test =~ s/"//g; # " + $test =~ s/\r/""/g; + + debug("conditional: $test\n"); + + # XXX Could do something with target + while ($test =~ /(target|empty|make|defined|exists)\s*\(([^()]+)\)/) { + my $testname = $1; + my $varname = $2; + my $var; + + # Implement (some of) make's :M modifier + if ($varname =~ /^([^:]+):M(.+)$/) { + $varname = $1; + my $match = $2; + + $var = $${vars}{$varname}; + $var = parse_expand_vars($var, $vars) + if defined $var; + + $match =~ s/([{.+])/\\$1/g; + $match =~ s/\*/.*/g; + $match =~ s/\?/./g; + $match = '^' . $match . '$'; + $var = ($var =~ /$match/) + if defined $var; + } else { + $var = $${vars}{$varname}; + $var = parse_expand_vars($var, $vars) + if defined $var; + } + + if (defined $var && $var eq $magic_undefined) { + $var = undef; + } + + if ($testname eq 'exists') { + $_ = (-e $varname) ? 1 : 0; + + } elsif ($testname eq 'defined') { + $_ = defined($var) ? 1 : 0; + + } elsif ($testname eq 'empty') { + $_ = ((not defined($var) or (length($var) == 0)) ? 1 : 0); + + } else { + $_ = 0; + } + + $test =~ s/$testname\s*\([^()]+\)/$_/; + debug("conditional: update to $test\n"); + } + + while ($test =~ /([^\s()\|\&]+)\s+(!=|==)\s+([^\s()]+)/) { + if ($2 eq '==') { + $_ = ($1 eq $3) ? 1 : 0; + } else { + $_ = ($1 ne $3) ? 1 : 0; + } + $test =~ s/[^\s()\|\&]+\s+(!=|==)\s+[^\s()]+/$_/; + } + + if ($test !~ /[^<>\d()\s&|.!]/) { + debug("eval test $test\n"); + $false = eval "($test)?0:1"; + if (!defined $false) { + fail("Eval failed $line - $test"); + } + debug("conditional: evaluated to " . ($false ? 0 : 1) . "\n"); + + } else { + $false = 0; + debug("conditional: defaulting to 0\n"); + } + $false; +} + +# Extract variable assignments from Makefile +# Much unpalatable magic to avoid having to use make (all for speed) +# +sub parse_makefile_vars($$) { + my ($file, $cwd) = @_; + my ( + $pkgname, %vars, $plus, $value, @data, + %incfiles, # Cache of previously included fils + %incdirs, # Directories in which to check for includes + @if_false + ); # 0:true 1:false 2:nested-false&nomore-elsif + + if (!open(FILE, $file)) { + return (undef); + } + @data = map { chomp; + $_; } <FILE>; + close(FILE); + + $incdirs{"."} = 1; + $incdirs{dirname($file)} = 1; + + # Some Makefiles depend on these being set + if ($file eq '/etc/mk.conf') { + $vars{LINTPKGSRC} = 'YES'; + } else { + %vars = %{$default_vars}; + } + $vars{BSD_PKG_MK} = 'YES'; + + if ($cwd) { + $vars{'.CURDIR'} = $cwd; + } elsif ($file =~ m#(.*)/#) { + $vars{'.CURDIR'} = $1; + } else { + $vars{'.CURDIR'} = getcwd; + } + + $incdirs{$vars{'.CURDIR'}} = 1; + if ($opt{L}) { + print "$file\n"; + } + + while (defined($_ = shift(@data))) { + s/\s*[^\\]#.*//; + + # Continuation lines + # + while (substr($_, -1) eq "\\") { + substr($_, -2) = shift @data; + } + + # Conditionals + # + if (m#^\.\s*if(|def|ndef)\s+(.*)#) { + my ($type, $false); + + $type = $1; + if ($if_false[$#if_false]) { + push(@if_false, 2); + + } elsif ($type eq '') { + # Straight if + push(@if_false, parse_eval_make_false($2, \%vars)); + + } else { + $false = !defined($vars{ parse_expand_vars($2, \%vars) }); + if ($type eq 'ndef') { + $false = !$false; + } + push(@if_false, $false ? 1 : 0); + } + debug("$file: .if$type (! @if_false)\n"); + next; + } + + if (m#^\.\s*elif\s+(.*)# && @if_false) { + if ($if_false[$#if_false] == 0) { + $if_false[$#if_false] = 2; + } elsif ($if_false[$#if_false] == 1 + && !parse_eval_make_false($1, \%vars)) { + $if_false[$#if_false] = 0; + } + debug("$file: .elif (! @if_false)\n"); + next; + } + + if (m#^\.\s*else\b# && @if_false) { + $if_false[$#if_false] = $if_false[$#if_false] == 1 ? 0 : 1; + debug("$file: .else (! @if_false)\n"); + next; + } + + if (m#^\.\s*endif\b#) { + pop(@if_false); + debug("$file: .endif (! @if_false)\n"); + next; + } + + $if_false[$#if_false] && next; + + # Included files (just unshift onto @data) + # + if (m#^\.\s*include\s+"([^"]+)"#) { + my ($incfile) = parse_expand_vars($1, \%vars); + + # At this point just skip any includes which we were not able to + # fully expand + if ($incfile =~ m#/mk/bsd# + || $incfile =~ /$magic_undefined/ + || $incfile =~ /\$\{/ + || (!$opt{d} && $incfile =~ m#/(buildlink[^/]*\.mk)#)) { + debug("$file: .include \"$incfile\" skipped\n"); + + } else { + debug("$file: .include \"$incfile\"\n"); + + # Expand any simple vars in $incfile + # + + if (substr($incfile, 0, 1) ne '/') { + foreach my $dir (keys %incdirs) { + if (-f "$dir/$incfile") { + $incfile = "$dir/$incfile"; + last; + } + } + } + + # perl 5.6.1 realpath() cannot handle files, only directories + # If the last component is a symlink this will give a false + # negative, but that is not a problem as the duplicate check + # is for performance + $incfile =~ m#^(.+)(/[^/]+)$#; + + if (!-f $incfile) { + if (!$opt{L}) { + verbose("\n"); + } + + verbose("$file: Cannot locate $incfile in " + . join(" ", sort keys %incdirs) + . "\n"); + + } else { + $incfile = realpath($1) . $2; + + if (!$incfiles{$incfile}) { + if ($opt{L}) { + print "inc $incfile\n"; + } + $incfiles{$incfile} = 1; + + if (!open(FILE, $incfile)) { + verbose("Cannot open '$incfile' (from $file): $_ $!\n"); + } else { + my $NEWCURDIR = $incfile; + $NEWCURDIR =~ s#/[^/]*$##; + $incdirs{$NEWCURDIR} = 1; + unshift(@data, ".CURDIR=$vars{'.CURDIR'}"); + unshift(@data, map { chomp; + $_ } <FILE>); + unshift(@data, ".CURDIR=$NEWCURDIR"); + close(FILE); + } + } + } + } + next; + } + + if (/^ *([-\w\.]+)\s*([:+?]?)=\s*(.*)/) { + my ($key); + + $key = $1; + $plus = $2; + $value = $3; + + if ($plus eq ':') { + $vars{$key} = parse_expand_vars($value, \%vars); + } elsif ($plus eq '+' && defined $vars{$key}) { + $vars{$key} .= " $value"; + } elsif ($plus ne '?' || !defined $vars{$key}) { + $vars{$key} = $value; + } + debug("assignment: $key$plus=[$value] ($vars{$key})\n"); + + # Give python a little hand (XXX - do we wanna consider actually + # implementing make .for loops, etc? + # + if ($key eq "PYTHON_VERSIONS_ACCEPTED") { + my ($pv); + + foreach $pv (split(/\s+/, $vars{PYTHON_VERSIONS_ACCEPTED})) { + $vars{"_PYTHON_VERSION_FIRSTACCEPTED"} ||= $pv; + $vars{"_PYTHON_VERSION_${pv}_OK"} = "yes"; + } + } + } + } + + debug("$file: expand\n"); + + # Handle variable substitutions FRED = a-${JIM:S/-/-b-/} + # + my ($loop); + + for ($loop = 1; $loop;) { + $loop = 0; + foreach my $key (keys %vars) { + if (index($vars{$key}, '$') == -1) { + next; + } + + $_ = parse_expand_vars($vars{$key}, \%vars); + if ($_ ne $vars{$key}) { + $vars{$key} = $_; + $loop = 1; + + } elsif ($vars{$key} =~ m#\$\{([\w.]+):([CS]([^{}])[^{}\3]+\3[^{}\3]*\3[g1]*(|:[^{}]+)|U[^{}]+)\}#) { + my ($left, $subvar, $right) = ($`, $1, $'); + my (@patterns) = split(':', $2); + my ($result); + + $result = $vars{$subvar}; + $result ||= ''; + + # If $vars{$subvar} contains a $ skip it on this pass. + # Hopefully it will get substituted and we can catch it + # next time around. + if (index($result, '${') != -1) { + next; + } + + debug("$file: substitutelist $key ($result) $subvar (@patterns)\n"); + foreach (@patterns) { + if (m#(U)(.*)#) { + $result ||= $2; + } elsif (m#([CS])(.)([^/@]+)\2([^/@]*)\2([1g]*)#) { + my ($how, $from, $to, $global) = ($1, $3, $4, $5); + + debug("$file: substituteglob $subvar, $how, $from, $to, $global\n"); + if ($how eq 'S') { + # Limited substitution - keep ^ and $ + $from =~ s/([?.{}\]\[*+])/\\$1/g; + } + $to =~ s/\\(\d)/\$$1/g; # Change \1 etc to $1 + $to =~ s/\&/\$&/g; # Change & to $1 + + my ($notfirst); + if ($global =~ s/1//) { + ($from, $notfirst) = split('\s', $from, 2); + } + + debug("$file: substituteperl $subvar, $how, $from, $to\n"); + debug("eval substitute <$from> <$to> <$global>\n"); + eval "\$result =~ s/$from/$to/$global"; + if (defined $notfirst) { + $result .= " $notfirst"; + } + } else { + next; + } + } + + $vars{$key} = $left . $result . $right; + $loop = 1; + } + } + } + + foreach my $key (keys %vars) { + $vars{$key} =~ s/$magic_undefined//; + } + \%vars; } sub get_default_makefile_vars() { @@ -333,36 +902,6 @@ sub invalid_version($) { $fail; } -# List (recursive) non directory contents of specified directory -# -#TODO this entire sub should be replaced with direct calls to -# File::Find -sub listdir($$) { - my ($base, $dir) = @_; - my ($thisdir); - my (@list, @thislist); - - $thisdir = $base; - if (defined($dir)) { - $thisdir .= "/$dir"; - $dir .= '/'; - } else { - $dir = ''; - } - - opendir(DIR, $thisdir) || fail("Unable to opendir($thisdir): $!"); - @thislist = grep(substr($_, 0, 1) ne '.' && $_ ne 'CVS', readdir(DIR)); - closedir(DIR); - foreach my $entry (@thislist) { - if (-d "$thisdir/$entry") { - push(@list, listdir($base, "$dir$entry")); - } else { - push(@list, "$dir$entry"); - } - } - @list; -} - # Use pkg_info to list installed packages # sub list_installed_packages() { @@ -657,394 +1196,6 @@ sub parse_makefile_pkgsrc($) { } } -# Extract variable assignments from Makefile -# Much unpalatable magic to avoid having to use make (all for speed) -# -sub parse_makefile_vars($$) { - my ($file, $cwd) = @_; - my ( - $pkgname, %vars, $plus, $value, @data, - %incfiles, # Cache of previously included fils - %incdirs, # Directories in which to check for includes - @if_false - ); # 0:true 1:false 2:nested-false&nomore-elsif - - if (!open(FILE, $file)) { - return (undef); - } - @data = map { chomp; - $_; } <FILE>; - close(FILE); - - $incdirs{"."} = 1; - $incdirs{ dirname($file) } = 1; - - # Some Makefiles depend on these being set - if ($file eq '/etc/mk.conf') { - $vars{LINTPKGSRC} = 'YES'; - } else { - %vars = %{$default_vars}; - } - $vars{BSD_PKG_MK} = 'YES'; - - if ($cwd) { - $vars{'.CURDIR'} = $cwd; - } elsif ($file =~ m#(.*)/#) { - $vars{'.CURDIR'} = $1; - } else { - $vars{'.CURDIR'} = getcwd; - } - - $incdirs{ $vars{'.CURDIR'} } = 1; - if ($opt{L}) { - print "$file\n"; - } - - while (defined($_ = shift(@data))) { - s/\s*[^\\]#.*//; - - # Continuation lines - # - while (substr($_, -1) eq "\\") { - substr($_, -2) = shift @data; - } - - # Conditionals - # - if (m#^\.\s*if(|def|ndef)\s+(.*)#) { - my ($type, $false); - - $type = $1; - if ($if_false[$#if_false]) { - push(@if_false, 2); - - } elsif ($type eq '') { - # Straight if - push(@if_false, parse_eval_make_false($2, \%vars)); - - } else { - $false = !defined($vars{ parse_expand_vars($2, \%vars) }); - if ($type eq 'ndef') { - $false = !$false; - } - push(@if_false, $false ? 1 : 0); - } - debug("$file: .if$type (! @if_false)\n"); - next; - } - - if (m#^\.\s*elif\s+(.*)# && @if_false) { - if ($if_false[$#if_false] == 0) { - $if_false[$#if_false] = 2; - } elsif ($if_false[$#if_false] == 1 - && !parse_eval_make_false($1, \%vars)) { - $if_false[$#if_false] = 0; - } - debug("$file: .elif (! @if_false)\n"); - next; - } - - if (m#^\.\s*else\b# && @if_false) { - $if_false[$#if_false] = $if_false[$#if_false] == 1 ? 0 : 1; - debug("$file: .else (! @if_false)\n"); - next; - } - - if (m#^\.\s*endif\b#) { - pop(@if_false); - debug("$file: .endif (! @if_false)\n"); - next; - } - - $if_false[$#if_false] && next; - - # Included files (just unshift onto @data) - # - if (m#^\.\s*include\s+"([^"]+)"#) { - my ($incfile) = parse_expand_vars($1, \%vars); - - # At this point just skip any includes which we were not able to - # fully expand - if ($incfile =~ m#/mk/bsd# - || $incfile =~ /$magic_undefined/ - || $incfile =~ /\$\{/ - || (!$opt{d} && $incfile =~ m#/(buildlink[^/]*\.mk)#)) { - debug("$file: .include \"$incfile\" skipped\n"); - - } else { - debug("$file: .include \"$incfile\"\n"); - - # Expand any simple vars in $incfile - # - - if (substr($incfile, 0, 1) ne '/') { - foreach my $dir (keys %incdirs) { - if (-f "$dir/$incfile") { - $incfile = "$dir/$incfile"; - last; - } - } - } - - # perl 5.6.1 realpath() cannot handle files, only directories - # If the last component is a symlink this will give a false - # negative, but that is not a problem as the duplicate check - # is for performance - $incfile =~ m#^(.+)(/[^/]+)$#; - - if (!-f $incfile) { - if (!$opt{L}) { - verbose("\n"); - } - - verbose("$file: Cannot locate $incfile in " - . join(" ", sort keys %incdirs) - . "\n"); - - } else { - $incfile = realpath($1) . $2; - - if (!$incfiles{$incfile}) { - if ($opt{L}) { - print "inc $incfile\n"; - } - $incfiles{$incfile} = 1; - - if (!open(FILE, $incfile)) { - verbose("Cannot open '$incfile' (from $file): $_ $!\n"); - } else { - my $NEWCURDIR = $incfile; - $NEWCURDIR =~ s#/[^/]*$##; - $incdirs{$NEWCURDIR} = 1; - unshift(@data, ".CURDIR=$vars{'.CURDIR'}"); - unshift(@data, map { chomp; - $_ } <FILE>); - unshift(@data, ".CURDIR=$NEWCURDIR"); - close(FILE); - } - } - } - } - next; - } - - if (/^ *([-\w\.]+)\s*([:+?]?)=\s*(.*)/) { - my ($key); - - $key = $1; - $plus = $2; - $value = $3; - - if ($plus eq ':') { - $vars{$key} = parse_expand_vars($value, \%vars); - } elsif ($plus eq '+' && defined $vars{$key}) { - $vars{$key} .= " $value"; - } elsif ($plus ne '?' || !defined $vars{$key}) { - $vars{$key} = $value; - } - debug("assignment: $key$plus=[$value] ($vars{$key})\n"); - - # Give python a little hand (XXX - do we wanna consider actually - # implementing make .for loops, etc? - # - if ($key eq "PYTHON_VERSIONS_ACCEPTED") { - my ($pv); - - foreach $pv (split(/\s+/, $vars{PYTHON_VERSIONS_ACCEPTED})) { - $vars{"_PYTHON_VERSION_FIRSTACCEPTED"} ||= $pv; - $vars{"_PYTHON_VERSION_${pv}_OK"} = "yes"; - } - } - } - } - - debug("$file: expand\n"); - - # Handle variable substitutions FRED = a-${JIM:S/-/-b-/} - # - my ($loop); - - for ($loop = 1; $loop;) { - $loop = 0; - foreach my $key (keys %vars) { - if (index($vars{$key}, '$') == -1) { - next; - } - - $_ = parse_expand_vars($vars{$key}, \%vars); - if ($_ ne $vars{$key}) { - $vars{$key} = $_; - $loop = 1; - - } elsif ($vars{$key} =~ m#\$\{([\w.]+):([CS]([^{}])[^{}\3]+\3[^{}\3]*\3[g1]*(|:[^{}]+)|U[^{}]+)\}#) { - my ($left, $subvar, $right) = ($`, $1, $'); - my (@patterns) = split(':', $2); - my ($result); - - $result = $vars{$subvar}; - $result ||= ''; - - # If $vars{$subvar} contains a $ skip it on this pass. - # Hopefully it will get substituted and we can catch it - # next time around. - if (index($result, '${') != -1) { - next; - } - - debug("$file: substitutelist $key ($result) $subvar (@patterns)\n"); - foreach (@patterns) { - if (m#(U)(.*)#) { - $result ||= $2; - } elsif (m#([CS])(.)([^/@]+)\2([^/@]*)\2([1g]*)#) { - my ($how, $from, $to, $global) = ($1, $3, $4, $5); - - debug("$file: substituteglob $subvar, $how, $from, $to, $global\n"); - if ($how eq 'S') { - # Limited substitution - keep ^ and $ - $from =~ s/([?.{}\]\[*+])/\\$1/g; - } - $to =~ s/\\(\d)/\$$1/g; # Change \1 etc to $1 - $to =~ s/\&/\$&/g; # Change & to $1 - - my ($notfirst); - if ($global =~ s/1//) { - ($from, $notfirst) = split('\s', $from, 2); - } - - debug("$file: substituteperl $subvar, $how, $from, $to\n"); - debug("eval substitute <$from> <$to> <$global>\n"); - eval "\$result =~ s/$from/$to/$global"; - if (defined $notfirst) { - $result .= " $notfirst"; - } - } else { - next; - } - } - - $vars{$key} = $left . $result . $right; - $loop = 1; - } - } - } - - foreach my $key (keys %vars) { - $vars{$key} =~ s/$magic_undefined//; - } - \%vars; -} - -sub parse_expand_vars($$) { - my ($line, $vars) = @_; - - while ($line =~ /\$\{([-\w.]+)\}/) { - if (defined(${$vars}{$1})) { - $line = $` . ${$vars}{$1} . $'; - } else { - $line = $` . $magic_undefined . $'; - } - } - $line; -} - -sub parse_expand_vars_dumb($$) { - my ($line, $vars) = @_; - - while ($line =~ /\$\{([-\w.]+)\}/) { - if (defined(${$vars}{$1})) { - $line = $` . ${$vars}{$1} . $'; - } else { - $line = $` . $magic_undefined . $'; - } - } - $line; -} - -sub parse_eval_make_false($$) { - my ($line, $vars) = @_; - my ($false, $test); - - $false = 0; - $test = parse_expand_vars_dumb($line, $vars); - - # XXX This is _so_ wrong - need to parse this correctly - $test =~ s/""/\r/g; - $test =~ s/"//g; # " - $test =~ s/\r/""/g; - - debug("conditional: $test\n"); - - # XXX Could do something with target - while ($test =~ /(target|empty|make|defined|exists)\s*\(([^()]+)\)/) { - my $testname = $1; - my $varname = $2; - my $var; - - # Implement (some of) make's :M modifier - if ($varname =~ /^([^:]+):M(.+)$/) { - $varname = $1; - my $match = $2; - - $var = $${vars}{$varname}; - $var = parse_expand_vars($var, $vars) - if defined $var; - - $match =~ s/([{.+])/\\$1/g; - $match =~ s/\*/.*/g; - $match =~ s/\?/./g; - $match = '^' . $match . '$'; - $var = ($var =~ /$match/) - if defined $var; - } else { - $var = $${vars}{$varname}; - $var = parse_expand_vars($var, $vars) - if defined $var; - } - - if (defined $var && $var eq $magic_undefined) { - $var = undef; - } - - if ($testname eq 'exists') { - $_ = (-e $varname) ? 1 : 0; - - } elsif ($testname eq 'defined') { - $_ = defined($var) ? 1 : 0; - - } elsif ($testname eq 'empty') { - $_ = ((not defined($var) or (length($var) == 0)) ? 1 : 0); - - } else { - $_ = 0; - } - - $test =~ s/$testname\s*\([^()]+\)/$_/; - debug("conditional: update to $test\n"); - } - - while ($test =~ /([^\s()\|\&]+)\s+(!=|==)\s+([^\s()]+)/) { - if ($2 eq '==') { - $_ = ($1 eq $3) ? 1 : 0; - } else { - $_ = ($1 ne $3) ? 1 : 0; - } - $test =~ s/[^\s()\|\&]+\s+(!=|==)\s+[^\s()]+/$_/; - } - - if ($test !~ /[^<>\d()\s&|.!]/) { - debug("eval test $test\n"); - $false = eval "($test)?0:1"; - if (!defined $false) { - fail("Eval failed $line - $test"); - } - debug("conditional: evaluated to " . ($false ? 0 : 1) . "\n"); - - } else { - $false = 0; - debug("conditional: defaulting to 0\n"); - } - $false; -} # chdir() || fail() # @@ -1057,6 +1208,19 @@ sub safe_chdir($) { } } +sub load_pkgsrc_makefiles($) { + + open(STORE, "<$_[0]") || die("Cannot read pkgsrc store from $_[0]: $!\n"); + my ($pkgver); + our ($pkgcnt, $pkgnum, $subpkgcnt, $subpkgnum); + $pkglist = new PkgList; + while (<STORE>) { + debug("eval store $_"); + eval $_; + } + close(STORE); +} + # Generate pkgname->category/pkg mapping, optionally check DEPENDS # sub scan_pkgsrc_makefiles($) { @@ -1192,7 +1356,7 @@ sub scan_pkgsrc_distfiles_vs_distinfo($$$$) { push(@{$sumfiles{ $dist->{sumtype} }}, $distn); } } - } }, + } }, ($pkgdistdir)); if ($check_unref && %bad_distfiles) { @@ -1246,19 +1410,6 @@ sub scan_pkgsrc_distfiles_vs_distinfo($$$$) { (sort keys %bad_distfiles); } -sub load_pkgsrc_makefiles() { - - open(STORE, "<$_[0]") || die("Cannot read pkgsrc store from $_[0]: $!\n"); - my ($pkgver); - our ($pkgcnt, $pkgnum, $subpkgcnt, $subpkgnum); - $pkglist = new PkgList; - while (<STORE>) { - debug("eval store $_"); - eval $_; - } - close(STORE); -} - sub store_pkgsrc_makefiles($) { open(STORE, ">$_[0]") || die("Cannot save pkgsrc store to $_[0]: $!\n"); my $was = select(STORE); @@ -1308,219 +1459,64 @@ Modifiers: exit; } -sub verbose(@) { - - if (-t STDERR) { - print STDERR @_; - } -} - -sub debug(@) { +# Could speed up by building a cache of package names to paths, then processing +# each package name once against the tests. +sub check_prebuilt_packages() { - ($opt{D}) && print STDERR 'DEBUG: ', @_; -} + if ($_ eq 'distfiles' || $_ eq 'pkgsrc') { + # Skip these subdirs if present + $File::Find::prune = 1; -# PkgList is the master list of all packages in pkgsrc. -# -package PkgList; + } elsif (/(.+)-(\d.*)\.t[bg]z$/) { + my ($pkg, $ver) = ($1, $2); -sub add($@) { - my $self = shift; + $pkg = canonicalize_pkgname($pkg); - if (!$self->pkgs($_[0])) { - $self->{_pkgs}{ $_[0] } = new Pkgs $_[0]; - } - $self->pkgs($_[0])->add(@_); -} + my ($pkgs); + if ($pkgs = $pkglist->pkgs($pkg)) { + my ($pkgver) = $pkgs->pkgver($ver); -sub new($) { - my $class = shift; - my $self = {}; - bless $self, $class; - return $self; -} + if (!defined $pkgver) { + if ($opt{p}) { + print "$File::Find::dir/$_\n"; + push(@matched_prebuiltpackages, "$File::Find::dir/$_"); + } -sub numpkgver($) { - my $self = shift; - scalar($self->pkgver); -} + # Pick probably the last version + $pkgver = $pkgs->latestver; + } -sub pkgver($@) { - my $self = shift; + if ($opt{R} && $pkgver->var('RESTRICTED')) { + print "$File::Find::dir/$_\n"; + push(@matched_prebuiltpackages, "$File::Find::dir/$_"); + } - if (@_ == 0) { - my (@list); - foreach my $pkg ($self->pkgs) { - push(@list, $pkg->pkgver); + if ($opt{O} && $pkgver->var('OSVERSION_SPECIFIC')) { + print "$File::Find::dir/$_\n"; + push(@matched_prebuiltpackages, "$File::Find::dir/$_"); + } } - return (@list); - } - - if (defined $self->{_pkgs}{$_[0]}) { - return (@_ > 1) - ? $self->{_pkgs}{$_[0]}->pkgver($_[1]) - : $self->{_pkgs}{$_[0]}->pkgver(); - } - return; -} -sub pkgs($@) { - my $self = shift; - - if (@_) { - return $self->{_pkgs}{$_[0]}; - } else { - return (sort { $a->pkg cmp $b->pkg } values %{$self->{_pkgs}}); - } -} - -sub store($) { - my $self = shift; - my @pkgs = keys %{$self->{_pkgs}}; - my ($cnt, $subcnt) = $self->count; - - print("\$pkgcnt = $cnt;\n"); - print("\$subpkgcnt = $subcnt;\n"); - map($self->{_pkgs}{$_}->store, keys %{$self->{_pkgs}}); -} - -sub count($) { - my $self = shift; - my ($pkgcnt, $pkgsubcnt); - - map { - $pkgcnt++; - $pkgsubcnt += $self->{_pkgs}{$_}->count; - } keys %{$self->{_pkgs}}; - wantarray ? ($pkgcnt, $pkgsubcnt) : $pkgcnt; -} - -# Pkgs is all versions of a given package (eg: apache-1.x and apache-2.x) -# -package Pkgs; - -sub add($@) { - my $self = shift; - - $self->{_pkgver}{$_[1]} = new PkgVer @_; -} - -sub new($@) { - my $class = shift; - my $self = {}; - - bless $self, $class; - $self->{_pkg} = $_[0]; - return $self; -} - -sub versions($) { - my $self = shift; - - return sort { $b cmp $a } keys %{$self->{_pkgver}}; -} - -sub pkg($) { - my $self = shift; - $self->{_pkg}; -} - -sub pkgver($@) { - my $self = shift; - - if (@_) { - if ($self->{_pkgver}{$_[0]}) { - return ($self->{_pkgver}{$_[0]}); + } elsif (-d $_) { + if ($prebuilt_pkgdir_cache{"$File::Find::dir/$_"}) { + $File::Find::prune = 1; + return; } - return; - } - return sort { $b->ver() cmp $a->ver() } values %{$self->{_pkgver}}; -} -sub latestver($) { - my $self = shift; - - ($self->pkgver())[0]; -} - -sub store($) { - my $self = shift; - - print("\$pkgnum++;\n"); - map($self->{_pkgver}{$_}->store, keys %{$self->{_pkgver}}); -} - -sub count($) { - my $self = shift; - - scalar(keys %{$self->{_pkgver}}); -} - -# PkgVer is a unique package+version -# -package PkgVer; - -sub new($$$) { - my $class = shift; - my $self = {}; - - bless $self, $class; - $self->{_pkg} = $_[0]; - $self->{_ver} = $_[1]; - return $self; -} - -sub pkgname($) { - my $self = shift; - - $self->pkg . '-' . $self->ver; -} - -sub pkg($) { - my $self = shift; - - $self->{_pkg}; -} - -sub var($$$) { - my $self = shift; - my ($key, $val) = @_; - - (defined $val) - ? ($self->{$key} = $val) - : $self->{$key}; -} - -sub ver($) { - my $self = shift; - - $self->{_ver}; -} - -sub vars($) { - my $self = shift; - - grep(!/^_(pkg|ver)$/, keys %{$self}); -} - -sub store($) { - my $self = shift; - my $data; - - ($data = $self->{_pkg}) =~ s/([\\\$\@\%\"])/\\$1/g; - print("\$pkgver = \$pkglist->add(\"$data\", \""); - - ($data = $self->{_ver}) =~ s/([\\\$\@\%\"])/\\$1/g; - print("$data\"); __pkgcount(1);\n"); + $prebuilt_pkgdir_cache{"$File::Find::dir/$_"} = 1; + if (-l $_) { + my ($dest) = readlink($_); - foreach ($self->vars) { - ($data = $self->{$_}) =~ s/([\\\$\@\%\"])/\\$1/g; - print("\$pkgver->var(\"$_\", \"$data\");\n"); + if (substr($dest, 0, 1) ne '/') { + $dest = "$File::Find::dir/$dest"; + } + if (!$prebuilt_pkgdir_cache{$dest}) { + push(@prebuilt_pkgdirs, $dest); + } + } } } -package main; - sub main() { $ENV{PATH} .= |