| @@ -1,1683 +1,1684 @@ | | | @@ -1,1683 +1,1684 @@ |
1 | #!@PERL5@ | | 1 | #!@PERL5@ |
2 | # $NetBSD: lintpkgsrc.pl,v 1.90 2022/08/14 03:12:02 rillig Exp $ | | 2 | # $NetBSD: lintpkgsrc.pl,v 1.91 2022/08/14 03:18:36 rillig Exp $ |
3 | | | 3 | |
4 | # Written by David Brownlee <abs@netbsd.org>. | | 4 | # Written by David Brownlee <abs@netbsd.org>. |
5 | # | | 5 | # |
6 | # Caveats: | | 6 | # Caveats: |
7 | # The 'Makefile parsing' algorithm used to obtain package versions and | | 7 | # The 'Makefile parsing' algorithm used to obtain package versions and |
8 | # DEPENDS information is geared towards speed rather than perfection, | | 8 | # DEPENDS information is geared towards speed rather than perfection, |
9 | # though it has gotten somewhat better over time, it only parses the | | 9 | # though it has gotten somewhat better over time, it only parses the |
10 | # simpler Makefile conditionals. | | 10 | # simpler Makefile conditionals. |
11 | # | | 11 | # |
12 | # TODO: Handle fun DEPENDS like avifile-devel with | | 12 | # TODO: Handle fun DEPENDS like avifile-devel with |
13 | # {qt2-designer>=2.2.4,qt2-designer-kde>=2.3.1nb1} | | 13 | # {qt2-designer>=2.2.4,qt2-designer-kde>=2.3.1nb1} |
14 | | | 14 | |
15 | use v5.36; | | 15 | use v5.36; |
16 | use locale; | | 16 | use locale; |
17 | use strict; | | 17 | use strict; |
18 | use warnings; | | 18 | use warnings; |
19 | use Cwd 'realpath', 'getcwd'; | | 19 | use Cwd 'realpath', 'getcwd'; |
20 | use File::Basename; | | 20 | use File::Basename; |
21 | use File::Find; | | 21 | use File::Find; |
22 | use Getopt::Std; | | 22 | use Getopt::Std; |
23 | use IPC::Open3; | | 23 | use IPC::Open3; |
24 | | | 24 | |
25 | # PkgVer is a PKGBASE + PKGVERSION, including some of the variables that | | 25 | # PkgVer is a PKGBASE + PKGVERSION, including some of the variables that |
26 | # have been extracted from the package Makefile. | | 26 | # have been extracted from the package Makefile. |
27 | package PkgVer; | | 27 | package PkgVer; |
28 | | | 28 | |
29 | sub new($class, $pkgbase, $pkgversion) { | | 29 | sub new($class, $pkgbase, $pkgversion) { |
30 | my $self = { | | 30 | my $self = { |
31 | pkgbase => $pkgbase, | | 31 | pkgbase => $pkgbase, |
32 | pkgversion => $pkgversion, | | 32 | pkgversion => $pkgversion, |
33 | vars => {}, | | 33 | vars => {}, |
34 | }; | | 34 | }; |
35 | bless $self, $class; | | 35 | bless $self, $class; |
36 | return $self; | | 36 | return $self; |
37 | } | | 37 | } |
38 | | | 38 | |
39 | sub pkgbase($self) { | | 39 | sub pkgbase($self) { |
40 | $self->{pkgbase}; | | 40 | $self->{pkgbase}; |
41 | } | | 41 | } |
42 | | | 42 | |
43 | sub pkgversion($self) { | | 43 | sub pkgversion($self) { |
44 | $self->{pkgversion}; | | 44 | $self->{pkgversion}; |
45 | } | | 45 | } |
46 | | | 46 | |
47 | sub pkgname($self) { | | 47 | sub pkgname($self) { |
48 | $self->pkgbase . '-' . $self->pkgversion; | | 48 | $self->pkgbase . '-' . $self->pkgversion; |
49 | } | | 49 | } |
50 | | | 50 | |
51 | sub var($self, $name, $value = undef) { | | 51 | sub var($self, $name, $value = undef) { |
52 | defined $value | | 52 | defined $value |
53 | ? ($self->{vars}->{$name} = $value) | | 53 | ? ($self->{vars}->{$name} = $value) |
54 | : $self->{vars}->{$name}; | | 54 | : $self->{vars}->{$name}; |
55 | } | | 55 | } |
56 | | | 56 | |
57 | sub vars($self) { | | 57 | sub vars($self) { |
58 | keys $self->{vars}->%*; | | 58 | keys $self->{vars}->%*; |
59 | } | | 59 | } |
60 | | | 60 | |
61 | # All versions of a given PKGBASE, e.g. apache-1.3.27 and apache-2.0.46. | | 61 | # All versions of a given PKGBASE, e.g. apache-1.3.27 and apache-2.0.46. |
62 | # Multi-prefix packages like py39-* are stored as simply py-*. | | 62 | # Multi-prefix packages like py39-* are stored as simply py-*. |
63 | package Pkgs; | | 63 | package Pkgs; |
64 | | | 64 | |
65 | sub new($class, $pkgbase) { | | 65 | sub new($class, $pkgbase) { |
66 | my $self = { | | 66 | my $self = { |
67 | pkgbase => $pkgbase, | | 67 | pkgbase => $pkgbase, |
68 | pkgvers => {}, | | 68 | pkgvers => {}, |
69 | }; | | 69 | }; |
70 | bless $self, $class; | | 70 | bless $self, $class; |
71 | return $self; | | 71 | return $self; |
72 | } | | 72 | } |
73 | | | 73 | |
74 | sub pkgbase($self) { | | 74 | sub pkgbase($self) { |
75 | $self->{pkgbase}; | | 75 | $self->{pkgbase}; |
76 | } | | 76 | } |
77 | | | 77 | |
78 | # All available versions of the package, in decreasing alphabetical(!) order. | | 78 | # All available versions of the package, in decreasing alphabetical(!) order. |
79 | sub versions($self) { | | 79 | sub versions($self) { |
80 | reverse sort keys $self->{pkgvers}->%*; | | 80 | reverse sort keys $self->{pkgvers}->%*; |
81 | } | | 81 | } |
82 | | | 82 | |
83 | sub add($self, $pkgbase, $pkgversion) { | | 83 | sub add($self, $pkgbase, $pkgversion) { |
84 | $self->{pkgvers}->{$pkgversion} = PkgVer->new($pkgbase, $pkgversion); | | 84 | $self->{pkgvers}->{$pkgversion} = PkgVer->new($pkgbase, $pkgversion); |
85 | } | | 85 | } |
86 | | | 86 | |
87 | # All PkgVers of this pkgbase, in decreasing alphabetical(!) version order. | | 87 | # All PkgVers of this pkgbase, in decreasing alphabetical(!) version order. |
88 | sub pkgvers_all($self) { | | 88 | sub pkgvers_all($self) { |
89 | my $pkgvers = $self->{pkgvers}; | | 89 | my $pkgvers = $self->{pkgvers}; |
90 | sort { $b->pkgversion cmp $a->pkgversion } values %$pkgvers; | | 90 | sort { $b->pkgversion cmp $a->pkgversion } values %$pkgvers; |
91 | } | | 91 | } |
92 | | | 92 | |
93 | sub pkgver($self, $pkgversion) { | | 93 | sub pkgver($self, $pkgversion) { |
94 | $self->{pkgvers}->{$pkgversion} | | 94 | $self->{pkgvers}->{$pkgversion} |
95 | } | | 95 | } |
96 | | | 96 | |
97 | # PkgDb is a small database of all packages in pkgsrc. | | 97 | # PkgDb is a small database of all packages in pkgsrc. |
98 | package PkgDb; | | 98 | package PkgDb; |
99 | | | 99 | |
100 | sub new($class) { | | 100 | sub new($class) { |
101 | my $self = {}; # pkgbase => Pkgs | | 101 | my $self = {}; # pkgbase => Pkgs |
102 | bless $self, $class; | | 102 | bless $self, $class; |
103 | return $self; | | 103 | return $self; |
104 | } | | 104 | } |
105 | | | 105 | |
106 | sub add($self, $pkgbase, $pkgversion) { | | 106 | sub add($self, $pkgbase, $pkgversion) { |
107 | my $pkgs = ($self->{$pkgbase} ||= Pkgs->new($pkgbase)); | | 107 | my $pkgs = ($self->{$pkgbase} ||= Pkgs->new($pkgbase)); |
108 | $pkgs->add($pkgbase, $pkgversion); | | 108 | $pkgs->add($pkgbase, $pkgversion); |
109 | } | | 109 | } |
110 | | | 110 | |
111 | # All PkgVers, sorted by pkgbase, then by version in decreasing | | 111 | # All PkgVers, sorted by pkgbase, then by version in decreasing |
112 | # alphabetical(!) order. | | 112 | # alphabetical(!) order. |
113 | sub pkgvers_all($self) { | | 113 | sub pkgvers_all($self) { |
114 | map { $_->pkgvers_all } $self->pkgs; | | 114 | map { $_->pkgvers_all } $self->pkgs; |
115 | } | | 115 | } |
116 | | | 116 | |
117 | # All PkgVers of the given pkgbase, sorted by version in decreasing | | 117 | # All PkgVers of the given pkgbase, sorted by version in decreasing |
118 | # alphabetical(!) order. | | 118 | # alphabetical(!) order. |
119 | sub pkgvers_by_pkgbase($self, $pkgbase) { | | 119 | sub pkgvers_by_pkgbase($self, $pkgbase) { |
120 | my $pkgs = $self->{$pkgbase}; | | 120 | my $pkgs = $self->{$pkgbase}; |
121 | defined $pkgs ? $pkgs->pkgvers_all : (); | | 121 | defined $pkgs ? $pkgs->pkgvers_all : (); |
122 | } | | 122 | } |
123 | | | 123 | |
124 | sub pkgver($self, $pkgbase, $pkgversion) { | | 124 | sub pkgver($self, $pkgbase, $pkgversion) { |
125 | my $pkgs = $self->{$pkgbase}; | | 125 | my $pkgs = $self->{$pkgbase}; |
126 | defined $pkgs ? $pkgs->pkgver($pkgversion) : undef; | | 126 | defined $pkgs ? $pkgs->pkgver($pkgversion) : undef; |
127 | } | | 127 | } |
128 | | | 128 | |
129 | # pkgs() returns all Pkgs, sorted by pkgbase. | | 129 | # pkgs() returns all Pkgs, sorted by pkgbase. |
130 | # | | 130 | # |
131 | # pkgs($pkgbase) returns the Pkgs, or undef. | | 131 | # pkgs($pkgbase) returns the Pkgs, or undef. |
132 | sub pkgs($self, $pkgbase = undef) { | | 132 | sub pkgs($self, $pkgbase = undef) { |
133 | defined $pkgbase | | 133 | defined $pkgbase |
134 | ? $self->{$pkgbase} | | 134 | ? $self->{$pkgbase} |
135 | : sort { $a->pkgbase cmp $b->pkgbase } values %$self; | | 135 | : sort { $a->pkgbase cmp $b->pkgbase } values %$self; |
136 | } | | 136 | } |
137 | | | 137 | |
138 | package main; | | 138 | package main; |
139 | | | 139 | |
140 | # Buildtime configuration | | 140 | # Buildtime configuration |
141 | my $conf_make = '@MAKE@'; | | 141 | my $conf_make = '@MAKE@'; |
142 | my $conf_makeconf = '@MAKECONF@'; | | 142 | my $conf_makeconf = '@MAKECONF@'; |
143 | my $conf_pkg_info = '@PKG_INFO@'; | | 143 | my $conf_pkg_info = '@PKG_INFO@'; |
144 | my $conf_pkgsrcdir = '@PKGSRCDIR@'; | | 144 | my $conf_pkgsrcdir = '@PKGSRCDIR@'; |
145 | my $conf_prefix = '@PREFIX@'; | | 145 | my $conf_prefix = '@PREFIX@'; |
146 | my $conf_x11base = '@X11BASE@'; | | 146 | my $conf_x11base = '@X11BASE@'; |
147 | | | 147 | |
148 | my ( | | 148 | my ( |
149 | $pkgdb, # Database of pkgsrc packages | | 149 | $pkgdb, # Database of pkgsrc packages |
150 | $default_vars, # Set for Makefiles, inc PACKAGES & PKGSRCDIR | | 150 | $default_vars, # Set for Makefiles, inc PACKAGES & PKGSRCDIR |
151 | %opt, # Command line options | | 151 | %opt, # Command line options |
152 | @matched_prebuiltpackages, # List of obsolete prebuilt package paths | | 152 | @matched_prebuiltpackages, # List of obsolete prebuilt package paths |
153 | @prebuilt_pkgdirs, # Use to follow symlinks in prebuilt pkgdirs | | 153 | @prebuilt_pkgdirs, # Use to follow symlinks in prebuilt pkgdirs |
154 | %prebuilt_pkgdir_cache, # To avoid symlink loops in prebuilt_pkgdirs | | 154 | %prebuilt_pkgdir_cache, # To avoid symlink loops in prebuilt_pkgdirs |
155 | ); | | 155 | ); |
156 | | | 156 | |
157 | # Horrible kludge to ensure we have a value for testing in conditionals, but | | 157 | # Horrible kludge to ensure we have a value for testing in conditionals, but |
158 | # gets removed in the final evaluation | | 158 | # gets removed in the final evaluation |
159 | my $magic_undefined = 'M_a_G_i_C_uNdEfInEd'; | | 159 | my $magic_undefined = 'M_a_G_i_C_uNdEfInEd'; |
160 | | | 160 | |
161 | sub debug(@args) { | | 161 | sub debug(@args) { |
162 | $opt{D} and print STDERR 'DEBUG: ', @args, "\n"; | | 162 | $opt{D} and print STDERR 'DEBUG: ', @args, "\n"; |
163 | } | | 163 | } |
164 | | | 164 | |
165 | sub verbose(@args) { | | 165 | sub verbose(@args) { |
166 | -t STDERR and print STDERR @args; | | 166 | -t STDERR and print STDERR @args; |
167 | } | | 167 | } |
168 | | | 168 | |
169 | sub fail($msg) { | | 169 | sub fail($msg) { |
170 | print STDERR $msg, "\n"; | | 170 | print STDERR $msg, "\n"; |
171 | exit(3); | | 171 | exit(3); |
172 | } | | 172 | } |
173 | | | 173 | |
174 | # List (recursive) non directory contents of specified directory | | 174 | # List (recursive) non directory contents of specified directory |
175 | # | | 175 | # |
176 | #TODO this entire sub should be replaced with direct calls to | | 176 | #TODO this entire sub should be replaced with direct calls to |
177 | # File::Find | | 177 | # File::Find |
178 | sub listdir($base, $dir = undef) { | | 178 | sub listdir($base, $dir = undef) { |
179 | my ($thisdir); | | 179 | my ($thisdir); |
180 | my (@list, @thislist); | | 180 | my (@list, @thislist); |
181 | | | 181 | |
182 | $thisdir = $base; | | 182 | $thisdir = $base; |
183 | if (defined $dir) { | | 183 | if (defined $dir) { |
184 | $thisdir .= "/$dir"; | | 184 | $thisdir .= "/$dir"; |
185 | $dir .= '/'; | | 185 | $dir .= '/'; |
186 | } else { | | 186 | } else { |
187 | $dir = ''; | | 187 | $dir = ''; |
188 | } | | 188 | } |
189 | | | 189 | |
190 | opendir(DIR, $thisdir) || fail("Unable to opendir($thisdir): $!"); | | 190 | opendir(DIR, $thisdir) || fail("Unable to opendir($thisdir): $!"); |
191 | @thislist = grep { substr($_, 0, 1) ne '.' && $_ ne 'CVS' } readdir(DIR); | | 191 | @thislist = grep { substr($_, 0, 1) ne '.' && $_ ne 'CVS' } readdir(DIR); |
192 | closedir(DIR); | | 192 | closedir(DIR); |
193 | foreach my $entry (@thislist) { | | 193 | foreach my $entry (@thislist) { |
194 | if (-d "$thisdir/$entry") { | | 194 | if (-d "$thisdir/$entry") { |
195 | push @list, listdir($base, "$dir$entry"); | | 195 | push @list, listdir($base, "$dir$entry"); |
196 | } else { | | 196 | } else { |
197 | push @list, "$dir$entry"; | | 197 | push @list, "$dir$entry"; |
198 | } | | 198 | } |
199 | } | | 199 | } |
200 | @list; | | 200 | @list; |
201 | } | | 201 | } |
202 | | | 202 | |
203 | sub canonicalize_pkgname($pkgname) { | | 203 | sub canonicalize_pkgname($pkgname) { |
204 | $pkgname =~ s,^py\d+(?:pth|)-,py-,; | | 204 | $pkgname =~ s,^py\d+(?:pth|)-,py-,; |
205 | $pkgname =~ s,^ruby\d+-,ruby-,; | | 205 | $pkgname =~ s,^ruby\d+-,ruby-,; |
206 | $pkgname =~ s,^php\d+-,php-,; | | 206 | $pkgname =~ s,^php\d+-,php-,; |
207 | return $pkgname; | | 207 | return $pkgname; |
208 | } | | 208 | } |
209 | | | 209 | |
210 | sub split_pkgversion($pkgversion) { | | 210 | sub split_pkgversion($pkgversion) { |
211 | $pkgversion = lc($pkgversion); | | 211 | $pkgversion = lc($pkgversion); |
212 | | | 212 | |
213 | # See pkgtools/pkg_install/files/lib/dewey.c. | | 213 | # See pkgtools/pkg_install/files/lib/dewey.c. |
214 | my @temp = ($pkgversion =~ s/nb(\d+)//) ? +$1 : 0; | | 214 | my @temp = ($pkgversion =~ s/nb(\d+)//) ? +$1 : 0; |
215 | foreach my $elem (split(/(pl|pre|rc|beta|alpha|\D)/, $pkgversion)) { | | 215 | foreach my $elem (split(/(pl|pre|rc|beta|alpha|\D)/, $pkgversion)) { |
216 | if ($elem =~ /\d/) { | | 216 | if ($elem =~ /\d/) { |
217 | push @temp, +$elem; | | 217 | push @temp, +$elem; |
218 | } elsif ($elem eq 'pl' || $elem eq '.' || $elem eq '_') { | | 218 | } elsif ($elem eq 'pl' || $elem eq '.' || $elem eq '_') { |
219 | push @temp, 0; | | 219 | push @temp, 0; |
220 | } elsif ($elem eq 'pre' || $elem eq 'rc') { | | 220 | } elsif ($elem eq 'pre' || $elem eq 'rc') { |
221 | push @temp, -1; | | 221 | push @temp, -1; |
222 | } elsif ($elem eq 'beta') { | | 222 | } elsif ($elem eq 'beta') { |
223 | push @temp, -2; | | 223 | push @temp, -2; |
224 | } elsif ($elem eq 'alpha') { | | 224 | } elsif ($elem eq 'alpha') { |
225 | push @temp, -3; | | 225 | push @temp, -3; |
226 | } elsif ('a' le $elem && $elem le 'z') { | | 226 | } elsif ('a' le $elem && $elem le 'z') { |
227 | push @temp, 0, ord($elem) - ord('a') + 1; | | 227 | push @temp, 0, ord($elem) - ord('a') + 1; |
228 | } | | 228 | } |
229 | } | | 229 | } |
230 | @temp; | | 230 | @temp; |
231 | } | | 231 | } |
232 | | | 232 | |
233 | sub pkgversion_cmp($va, $op, $vb) { | | 233 | sub pkgversion_cmp($va, $op, $vb) { |
234 | my ($nb_a, @a) = split_pkgversion($va); | | 234 | my ($nb_a, @a) = split_pkgversion($va); |
235 | my ($nb_b, @b) = split_pkgversion($vb); | | 235 | my ($nb_b, @b) = split_pkgversion($vb); |
236 | | | 236 | |
237 | my $cmp = 0; | | 237 | my $cmp = 0; |
238 | while ($cmp == 0 && (@a || @b)) { | | 238 | while ($cmp == 0 && (@a || @b)) { |
239 | $cmp = (shift @a || 0) <=> (shift @b || 0); | | 239 | $cmp = (shift @a || 0) <=> (shift @b || 0); |
240 | } | | 240 | } |
241 | $cmp ||= $nb_a <=> $nb_b; | | 241 | $cmp ||= $nb_a <=> $nb_b; |
242 | | | 242 | |
243 | $op eq '<' ? $cmp < 0 | | 243 | $op eq '<' ? $cmp < 0 |
244 | : $op eq '<=' ? $cmp <= 0 | | 244 | : $op eq '<=' ? $cmp <= 0 |
245 | : $op eq '>' ? $cmp > 0 | | 245 | : $op eq '>' ? $cmp > 0 |
246 | : $cmp >= 0; | | 246 | : $cmp >= 0; |
247 | } | | 247 | } |
248 | | | 248 | |
249 | # Return a copy of $value in which trivial variable expressions are replaced | | 249 | # Return a copy of $value in which trivial variable expressions are replaced |
250 | # with their variable values. | | 250 | # with their variable values. |
251 | sub expand_exprs($value, $vars) { | | 251 | sub expand_exprs($value, $vars) { |
252 | while ($value =~ /\$\{([-\w.]+)\}/) { | | 252 | while ($value =~ /\$\{([-\w.]+)\}/) { |
253 | $value = defined $vars->{$1} | | 253 | $value = defined $vars->{$1} |
254 | ? "$`$vars->{$1}$'" | | 254 | ? "$`$vars->{$1}$'" |
255 | : "$`$magic_undefined$'"; | | 255 | : "$`$magic_undefined$'"; |
256 | } | | 256 | } |
257 | $value; | | 257 | $value; |
258 | } | | 258 | } |
259 | | | 259 | |
260 | sub eval_mk_cond_func($func, $arg, $vars) { | | 260 | sub eval_mk_cond_func($func, $arg, $vars) { |
261 | if ($func eq 'defined') { | | 261 | if ($func eq 'defined') { |
262 | my $varname = expand_exprs($arg, $vars); | | 262 | my $varname = expand_exprs($arg, $vars); |
263 | defined $vars->{$varname} ? 1 : 0; | | 263 | defined $vars->{$varname} ? 1 : 0; |
264 | | | 264 | |
265 | } elsif ($func eq 'empty') { | | 265 | } elsif ($func eq 'empty') { |
266 | | | 266 | |
267 | # Implement (some of) make's :M modifier | | 267 | # Implement (some of) make's :M modifier |
268 | if ($arg =~ /^ ([^:]+) :M ([^:]+) $/x) { | | 268 | if ($arg =~ /^ ([^:]+) :M ([^:]+) $/x) { |
269 | my ($varname, $pattern) = ($1, $2); | | 269 | my ($varname, $pattern) = ($1, $2); |
270 | $varname = expand_exprs($varname, $vars); | | 270 | $varname = expand_exprs($varname, $vars); |
271 | $pattern = expand_exprs($pattern, $vars); | | 271 | $pattern = expand_exprs($pattern, $vars); |
272 | | | 272 | |
273 | my $value = $vars->{$varname}; | | 273 | my $value = $vars->{$varname}; |
274 | return 1 unless defined $value; | | 274 | return 1 unless defined $value; |
275 | | | 275 | |
276 | $value = expand_exprs($value, $vars); | | 276 | $value = expand_exprs($value, $vars); |
277 | | | 277 | |
278 | $pattern =~ s/([{.+])/\\$1/g; | | 278 | $pattern =~ s/([{.+])/\\$1/g; |
279 | $pattern =~ s/\*/.*/g; | | 279 | $pattern =~ s/\*/.*/g; |
280 | $pattern =~ s/\?/./g; | | 280 | $pattern =~ s/\?/./g; |
281 | $pattern = '^' . $pattern . '$'; | | 281 | $pattern = '^' . $pattern . '$'; |
282 | | | 282 | |
283 | # XXX: Splitting by whitespace is not correct, but | | 283 | # XXX: Splitting by whitespace is not correct, but |
284 | # it's good enough for lists with only unquoted | | 284 | # it's good enough for lists with only unquoted |
285 | # words. See devel/bmake/files/str.c:brk_string. | | 285 | # words. See devel/bmake/files/str.c:brk_string. |
286 | foreach my $word (split(/\s+/, $value)) { | | 286 | foreach my $word (split(/\s+/, $value)) { |
287 | return 0 if $word =~ /$pattern/; | | 287 | return 0 if $word =~ /$pattern/; |
288 | } | | 288 | } |
289 | return 1; | | 289 | return 1; |
290 | } elsif ($arg =~ /:M/) { | | 290 | } elsif ($arg =~ /:M/) { |
291 | debug("Unsupported ':M' modifier in '$arg'"); | | 291 | debug("Unsupported ':M' modifier in '$arg'"); |
292 | } | | 292 | } |
293 | | | 293 | |
294 | my $value = expand_exprs("\${$arg}", $vars); | | 294 | my $value = expand_exprs("\${$arg}", $vars); |
295 | defined $value && $value =~ /\S/ ? 0 : 1; | | 295 | defined $value && $value =~ /\S/ ? 0 : 1; |
296 | | | 296 | |
297 | } elsif ($func eq 'exists') { | | 297 | } elsif ($func eq 'exists') { |
298 | my $fname = expand_exprs($arg, $vars); | | 298 | my $fname = expand_exprs($arg, $vars); |
299 | -e $fname ? 1 : 0; | | 299 | -e $fname ? 1 : 0; |
300 | | | 300 | |
301 | } elsif ($func eq 'make') { | | 301 | } elsif ($func eq 'make') { |
302 | 0; | | 302 | 0; |
303 | | | 303 | |
304 | } else { # $func eq 'target' | | 304 | } else { # $func eq 'target' |
305 | 0; | | 305 | 0; |
306 | } | | 306 | } |
307 | } | | 307 | } |
308 | | | 308 | |
309 | sub eval_mk_cond($line, $vars) { | | 309 | sub eval_mk_cond($line, $vars) { |
310 | my $cond = expand_exprs($line, $vars); | | 310 | my $cond = expand_exprs($line, $vars); |
311 | | | 311 | |
312 | # XXX This is _so_ wrong - need to parse this correctly | | 312 | # XXX This is _so_ wrong - need to parse this correctly |
313 | $cond =~ s/""/\r/g; | | 313 | $cond =~ s/""/\r/g; |
314 | $cond =~ s/"//g; | | 314 | $cond =~ s/"//g; |
315 | $cond =~ s/\r/""/g; | | 315 | $cond =~ s/\r/""/g; |
316 | | | 316 | |
317 | debug("conditional: $cond"); | | 317 | debug("conditional: $cond"); |
318 | | | 318 | |
319 | while ($cond =~ /(target|empty|make|defined|exists)\s*\(([^()]+)\)/) { | | 319 | while ($cond =~ /(target|empty|make|defined|exists)\s*\(([^()]+)\)/) { |
320 | my ($func, $arg) = ($1, $2); | | 320 | my ($func, $arg) = ($1, $2); |
321 | my $result = eval_mk_cond_func($func, $arg, $vars); | | 321 | my $result = eval_mk_cond_func($func, $arg, $vars); |
322 | $cond =~ s/$func\s*\([^()]+\)/$result/; | | 322 | $cond =~ s/$func\s*\([^()]+\)/$result/; |
323 | debug("conditional: update to $cond"); | | 323 | debug("conditional: update to $cond"); |
324 | } | | 324 | } |
325 | | | 325 | |
326 | while ($cond =~ /([^\s()\|\&]+) \s+ (!=|==) \s+ ([^\s()]+)/x) { | | 326 | while ($cond =~ /([^\s()\|\&]+) \s+ (!=|==) \s+ ([^\s()]+)/x) { |
327 | my $result = 0 + (($2 eq '==') ? ($1 eq $3) : ($1 ne $3)); | | 327 | my $result = 0 + (($2 eq '==') ? ($1 eq $3) : ($1 ne $3)); |
328 | $cond =~ s/[^\s()\|\&]+ \s+ (!=|==) \s+ [^\s()]+/$result/x; | | 328 | $cond =~ s/[^\s()\|\&]+ \s+ (!=|==) \s+ [^\s()]+/$result/x; |
329 | } | | 329 | } |
330 | | | 330 | |
331 | if ($cond =~ /^[ <> \d () \s & | . ! ]+$/xx) { | | 331 | if ($cond =~ /^[ <> \d () \s & | . ! ]+$/xx) { |
332 | my $result = eval "($cond) ? 1 : 0"; | | 332 | my $result = eval "($cond) ? 1 : 0"; |
333 | defined $result or fail("Eval '$cond' failed in '$line': $@"); | | 333 | defined $result or fail("Eval '$cond' failed in '$line': $@"); |
334 | debug("conditional: evaluated to " . ($result ? 'true' : 'false')); | | 334 | debug("conditional: evaluated to " . ($result ? 'true' : 'false')); |
335 | $result; | | 335 | $result; |
336 | | | 336 | |
337 | } else { | | 337 | } else { |
338 | debug("conditional: defaulting '$cond' to true"); | | 338 | debug("conditional: defaulting '$cond' to true"); |
339 | 1; | | 339 | 1; |
340 | } | | 340 | } |
341 | } | | 341 | } |
342 | | | 342 | |
343 | sub parse_makefile_line_include($file, $incfile, | | 343 | sub parse_makefile_line_include($file, $incfile, |
344 | $incdirs, $included, $lines, $vars) { | | 344 | $incdirs, $included, $lines, $vars) { |
345 | | | 345 | |
346 | # At this point just skip any includes which we were | | 346 | # At this point just skip any includes which we were |
347 | # not able to fully expand. | | 347 | # not able to fully expand. |
348 | if ($incfile =~ m#/mk/bsd# | | 348 | if ($incfile =~ m#/mk/bsd# |
349 | || $incfile =~ /$magic_undefined/ | | 349 | || $incfile =~ /$magic_undefined/ |
350 | || $incfile =~ /\$\{/ | | 350 | || $incfile =~ /\$\{/ |
351 | || (!$opt{d} && $incfile =~ m#/(buildlink[^/]*\.mk)#)) { | | 351 | || (!$opt{d} && $incfile =~ m#/(buildlink[^/]*\.mk)#)) { |
352 | debug("$file: .include \"$incfile\" skipped"); | | 352 | debug("$file: .include \"$incfile\" skipped"); |
353 | return; | | 353 | return; |
354 | } | | 354 | } |
355 | | | 355 | |
356 | debug("$file: .include \"$incfile\""); | | 356 | debug("$file: .include \"$incfile\""); |
357 | | | 357 | |
358 | if (substr($incfile, 0, 1) ne '/') { | | 358 | if (substr($incfile, 0, 1) ne '/') { |
359 | foreach my $dir (reverse @$incdirs) { | | 359 | foreach my $dir (reverse @$incdirs) { |
360 | if (-f "$dir/$incfile") { | | 360 | if (-f "$dir/$incfile") { |
361 | $incfile = "$dir/$incfile"; | | 361 | $incfile = "$dir/$incfile"; |
362 | last; | | 362 | last; |
363 | } | | 363 | } |
364 | } | | 364 | } |
365 | } | | 365 | } |
366 | | | 366 | |
367 | # perl 5.6.1 realpath() cannot handle files, only directories. | | 367 | # perl 5.6.1 realpath() cannot handle files, only directories. |
368 | # If the last component is a symlink, this will give a false | | 368 | # If the last component is a symlink, this will give a false |
369 | # negative, but that is not a problem, as the duplicate check | | 369 | # negative, but that is not a problem, as the duplicate check |
370 | # is for performance. | | 370 | # is for performance. |
371 | $incfile =~ m#^(.+)(/[^/]+)$#; | | 371 | $incfile =~ m#^(.+)(/[^/]+)$#; |
372 | | | 372 | |
373 | if (!-f $incfile) { | | 373 | if (!-f $incfile) { |
374 | $opt{L} or verbose("\n"); | | 374 | $opt{L} or verbose("\n"); |
375 | | | 375 | |
376 | my $dirs = join(' ', @$incdirs); | | 376 | my $dirs = join(' ', @$incdirs); |
377 | verbose("$file: Cannot locate $incfile in $dirs\n"); | | 377 | verbose("$file: Cannot locate $incfile in $dirs\n"); |
378 | return; | | 378 | return; |
379 | } | | 379 | } |
380 | | | 380 | |
381 | $incfile = realpath($1) . $2; | | 381 | $incfile = realpath($1) . $2; |
382 | return if $included->{$incfile}; | | 382 | return if $included->{$incfile}; |
383 | | | 383 | |
384 | $opt{L} and print "inc $incfile\n"; | | 384 | $opt{L} and print "inc $incfile\n"; |
385 | $included->{$incfile} = 1; | | 385 | $included->{$incfile} = 1; |
386 | | | 386 | |
387 | if (!open(FILE, $incfile)) { | | 387 | if (!open(FILE, $incfile)) { |
388 | verbose("Cannot open '$incfile' (from $file): $_ $!\n"); | | 388 | verbose("Cannot open '$incfile' (from $file): $_ $!\n"); |
389 | return; | | 389 | return; |
390 | } | | 390 | } |
391 | | | 391 | |
392 | # FIXME: .CURDIR doesn't change, but .PARSEDIR does. | | 392 | # FIXME: .CURDIR doesn't change, but .PARSEDIR does. |
393 | my $NEWCURDIR = $incfile; | | 393 | my $NEWCURDIR = $incfile; |
394 | $NEWCURDIR =~ s#/[^/]*$##; | | 394 | $NEWCURDIR =~ s#/[^/]*$##; |
395 | push @$incdirs, $NEWCURDIR | | 395 | push @$incdirs, $NEWCURDIR |
396 | unless grep { $_ eq $NEWCURDIR } @$incdirs; | | 396 | unless grep { $_ eq $NEWCURDIR } @$incdirs; |
397 | unshift @$lines, ".CURDIR=" . $vars->{'.CURDIR'}; | | 397 | unshift @$lines, ".CURDIR=" . $vars->{'.CURDIR'}; |
398 | chomp(my @inc_lines = <FILE>); | | 398 | chomp(my @inc_lines = <FILE>); |
399 | unshift @$lines, @inc_lines; | | 399 | unshift @$lines, @inc_lines; |
400 | unshift @$lines, ".CURDIR=$NEWCURDIR"; | | 400 | unshift @$lines, ".CURDIR=$NEWCURDIR"; |
401 | close(FILE); | | 401 | close(FILE); |
402 | } | | 402 | } |
403 | | | 403 | |
404 | sub parse_makefile_line_var($varname, $op, $value, $vars) { | | 404 | sub parse_makefile_line_var($varname, $op, $value, $vars) { |
405 | if ($op eq ':=') { | | 405 | if ($op eq ':=') { |
406 | $vars->{$varname} = expand_exprs($value, $vars); | | 406 | $vars->{$varname} = expand_exprs($value, $vars); |
407 | } elsif ($op eq '+=' && defined $vars->{$varname}) { | | 407 | } elsif ($op eq '+=' && defined $vars->{$varname}) { |
408 | $vars->{$varname} .= " $value"; | | 408 | $vars->{$varname} .= " $value"; |
409 | } elsif ($op eq '?=' && defined $vars->{$varname}) { | | 409 | } elsif ($op eq '?=' && defined $vars->{$varname}) { |
410 | # Do nothing. | | 410 | # Do nothing. |
411 | } else { | | 411 | } else { |
412 | $vars->{$varname} = $value; | | 412 | $vars->{$varname} = $value; |
413 | } | | 413 | } |
414 | debug($op eq '=' | | 414 | debug($op eq '=' |
415 | ? "assignment: $varname $op $value" | | 415 | ? "assignment: $varname $op $value" |
416 | : "assignment: $varname $op $value => $vars->{$varname}"); | | 416 | : "assignment: $varname $op $value => $vars->{$varname}"); |
417 | | | 417 | |
418 | # Give python a little hand (XXX - do we wanna consider actually | | 418 | # Give python a little hand (XXX - do we wanna consider actually |
419 | # implementing make .for loops, etc? | | 419 | # implementing make .for loops, etc? |
420 | # | | 420 | # |
421 | if ($varname eq 'PYTHON_VERSIONS_ACCEPTED') { | | 421 | if ($varname eq 'PYTHON_VERSIONS_ACCEPTED') { |
422 | foreach my $pv (split(/\s+/, $vars->{PYTHON_VERSIONS_ACCEPTED})) { | | 422 | foreach my $pv (split(/\s+/, $vars->{PYTHON_VERSIONS_ACCEPTED})) { |
423 | $vars->{'_PYTHON_VERSION_FIRSTACCEPTED'} ||= $pv; | | 423 | $vars->{'_PYTHON_VERSION_FIRSTACCEPTED'} ||= $pv; |
424 | $vars->{"_PYTHON_VERSION_${pv}_OK"} = 'yes'; | | 424 | $vars->{"_PYTHON_VERSION_${pv}_OK"} = 'yes'; |
425 | } | | 425 | } |
426 | } | | 426 | } |
427 | } | | 427 | } |
428 | | | 428 | |
429 | sub expand_modifiers($file, $varname, $left, $subvar, $mods, $right, $vars) { | | 429 | sub expand_modifiers($file, $varname, $left, $subvar, $mods, $right, $vars) { |
430 | my @mods = split(':', $mods); | | 430 | my @mods = split(':', $mods); |
431 | my $result = $vars->{$subvar}; | | 431 | my $result = $vars->{$subvar}; |
432 | $result = '' unless defined $result; | | 432 | $result = '' unless defined $result; |
433 | | | 433 | |
434 | # If the value of $subvar contains a '$', skip it on this pass. | | 434 | # If the value of $subvar contains a '$', skip it on this pass. |
435 | # Hopefully it will get substituted and we can catch it | | 435 | # Hopefully it will get substituted and we can catch it |
436 | # next time around. | | 436 | # next time around. |
437 | return 0 if index($result, '${') != -1; | | 437 | return 0 if index($result, '${') != -1; |
438 | | | 438 | |
439 | debug("$file: substitutelist $varname ($result) $subvar (@mods)"); | | 439 | debug("$file: substitutelist $varname ($result) $subvar (@mods)"); |
440 | foreach (@mods) { | | 440 | foreach (@mods) { |
441 | debug("expanding modifier '$_'"); | | 441 | debug("expanding modifier '$_'"); |
442 | | | 442 | |
443 | if (m#^ (U) (.*) #x) { | | 443 | if (m#^ (U) (.*) #x) { |
444 | $result = $2 unless defined $vars->{$subvar}; | | 444 | $result = $2 unless defined $vars->{$subvar}; |
445 | | | 445 | |
446 | } elsif (m#^ ([CS]) (.) ([^/\@]+) \2 ([^/\@]*) \2 ([1g]*) #x) { | | 446 | } elsif (m#^ ([CS]) (.) ([^/\@]+) \2 ([^/\@]*) \2 ([1g]*) #x) { |
447 | # TODO: Use non-greedy repetitions above. | | 447 | # TODO: Use non-greedy repetitions above. |
448 | # TODO: Properly handle separators other than '/' and '@'. | | 448 | # TODO: Properly handle separators other than '/' and '@'. |
449 | my ($how, $from, $to, $global) = ($1, $3, $4, $5); | | 449 | my ($how, $from, $to, $global) = ($1, $3, $4, $5); |
450 | | | 450 | |
451 | debug("$file: ':S' $subvar, $how, $from, $to, $global"); | | 451 | debug("$file: ':S' $subvar, $how, $from, $to, $global"); |
452 | if ($how eq 'S') { | | 452 | if ($how eq 'S') { |
453 | # Limited substitution - keep ^ and $ | | 453 | # Limited substitution - keep ^ and $ |
454 | $from =~ s/([?.{}\]\[*+])/\\$1/g; | | 454 | $from =~ s/([?.{}\]\[*+])/\\$1/g; |
455 | } | | 455 | } |
456 | $to =~ s/\\(\d)/\$$1/g; # Change \1 etc to $1 | | 456 | $to =~ s/\\(\d)/\$$1/g; # Change \1 etc to $1 |
457 | $to =~ s/\&/\$&/g; | | 457 | $to =~ s/\&/\$&/g; |
458 | | | 458 | |
459 | my ($notfirst); | | 459 | my ($notfirst); |
460 | if ($global =~ s/1//) { | | 460 | if ($global =~ s/1//) { |
461 | # FIXME: The modifier '1' applies to the first | | 461 | # FIXME: The modifier '1' applies to the first |
462 | # occurrence in any word, it doesn't have to | | 462 | # occurrence in any word, it doesn't have to |
463 | # be in the first word. | | 463 | # be in the first word. |
464 | ($from, $notfirst) = split('\s', $from, 2); | | 464 | ($from, $notfirst) = split('\s', $from, 2); |
465 | } | | 465 | } |
466 | | | 466 | |
467 | debug("$file: substituteperl $subvar, $how, $from, $to"); | | 467 | debug("$file: substituteperl $subvar, $how, $from, $to"); |
468 | debug("eval substitute <$from> <$to> <$global>"); | | 468 | debug("eval substitute <$from> <$to> <$global>"); |
469 | eval "\$result =~ s/$from/$to/$global"; | | 469 | eval "\$result =~ s/$from/$to/$global"; |
470 | if (defined $notfirst) { | | 470 | if (defined $notfirst) { |
471 | $result .= " $notfirst"; | | 471 | $result .= " $notfirst"; |
472 | } | | 472 | } |
473 | | | 473 | |
474 | } else { | | 474 | } else { |
475 | debug("$file: variable '$varname' has unknown modifier '$_'"); | | 475 | debug("$file: variable '$varname' has unknown modifier '$_'"); |
476 | } | | 476 | } |
477 | } | | 477 | } |
478 | | | 478 | |
479 | $result = '' if !defined $result; | | 479 | $result = '' if !defined $result; |
480 | $vars->{$varname} = "$left$result$right"; | | 480 | $vars->{$varname} = "$left$result$right"; |
481 | return 1; | | 481 | return 1; |
482 | } | | 482 | } |
483 | | | 483 | |
484 | # Extract variable assignments from Makefile | | 484 | # Extract variable assignments from Makefile |
485 | # Much unpalatable magic to avoid having to use make (all for speed) | | 485 | # Much unpalatable magic to avoid having to use make (all for speed) |
486 | # | | 486 | # |
487 | sub parse_makefile_vars($file, $cwd = undef) { | | 487 | sub parse_makefile_vars($file, $cwd = undef) { |
488 | my %vars; | | 488 | my %vars; |
489 | my %incfiles; # Cache of previously included files | | 489 | my %incfiles; # Cache of previously included files |
490 | my @incdirs; # Directories in which to check for includes | | 490 | my @incdirs; # Directories in which to check for includes |
491 | my @lines; | | 491 | my @lines; |
492 | | | 492 | |
493 | open(FILE, $file) or return undef; | | 493 | open(FILE, $file) or return undef; |
494 | chomp(@lines = <FILE>); | | 494 | chomp(@lines = <FILE>); |
495 | close(FILE); | | 495 | close(FILE); |
496 | | | 496 | |
497 | push @incdirs, '.'; | | 497 | push @incdirs, '.'; |
498 | push @incdirs, dirname($file); | | 498 | push @incdirs, dirname($file); |
499 | | | 499 | |
500 | # Some Makefiles depend on these being set | | 500 | # Some Makefiles depend on these being set |
501 | if ($file eq $conf_makeconf) { | | 501 | if ($file eq $conf_makeconf) { |
502 | $vars{LINTPKGSRC} = 'YES'; | | 502 | $vars{LINTPKGSRC} = 'YES'; |
503 | } else { | | 503 | } else { |
504 | %vars = %{$default_vars}; | | 504 | %vars = %{$default_vars}; |
505 | } | | 505 | } |
506 | $vars{BSD_PKG_MK} = 'YES'; | | 506 | $vars{BSD_PKG_MK} = 'YES'; |
507 | | | 507 | |
508 | my $curdir = $cwd || ($file =~ m#(.*)/# ? $1 : getcwd); | | 508 | my $curdir = $cwd || ($file =~ m#(.*)/# ? $1 : getcwd); |
509 | $vars{'.CURDIR'} = $curdir; | | 509 | $vars{'.CURDIR'} = $curdir; |
510 | push @incdirs, $curdir; | | 510 | push @incdirs, $curdir; |
511 | if ($opt{L}) { | | 511 | if ($opt{L}) { |
512 | print "$file\n"; | | 512 | print "$file\n"; |
513 | } | | 513 | } |
514 | | | 514 | |
515 | my @if_state; # 'not_yet', 'active', 'done' | | 515 | my @if_state; # 'not_yet', 'active', 'done' |
516 | while (defined($_ = shift @lines)) { | | 516 | while (defined($_ = shift @lines)) { |
517 | s/(*negative_lookbehind:\\)#.*//; | | 517 | s/(*negative_lookbehind:\\)#.*//; |
518 | s/\s+$//; | | 518 | s/\s+$//; |
519 | | | 519 | |
520 | # Join continuation lines. | | 520 | # Join continuation lines. |
521 | # See devel/bmake/files/parse.c, 'replace following'. | | 521 | # See devel/bmake/files/parse.c, 'replace following'. |
522 | while (substr($_, -1) eq "\\" && @lines > 0) { | | 522 | while (substr($_, -1) eq "\\" && @lines > 0) { |
523 | my $cont = shift @lines; | | 523 | my $cont = shift @lines; |
524 | $cont =~ s,^\s*, ,; | | 524 | $cont =~ s,^\s*, ,; |
525 | $cont =~ s/(*negative_lookbehind:\\)#.*//; | | 525 | $cont =~ s/(*negative_lookbehind:\\)#.*//; |
526 | $cont =~ s/\s+$//; | | 526 | $cont =~ s/\s+$//; |
527 | substr($_, -1) = $cont; | | 527 | substr($_, -1) = $cont; |
528 | } | | 528 | } |
529 | | | 529 | |
530 | # Conditionals | | 530 | # Conditionals |
531 | # | | 531 | # |
532 | if (m#^ \. \s* (if|ifdef|ifndef) \s+ (.*) #x) { | | 532 | if (m#^ \. \s* (if|ifdef|ifndef) \s+ (.*) #x) { |
533 | my ($kind, $cond) = ($1, $2); | | 533 | my ($kind, $cond) = ($1, $2); |
534 | | | 534 | |
535 | if (@if_state > 0 && $if_state[-1] ne 'active') { | | 535 | if (@if_state > 0 && $if_state[-1] ne 'active') { |
536 | push @if_state, 'done'; | | 536 | push @if_state, 'done'; |
537 | | | 537 | |
538 | } elsif ($kind eq 'if') { | | 538 | } elsif ($kind eq 'if') { |
539 | my $result = eval_mk_cond($cond, \%vars); | | 539 | my $result = eval_mk_cond($cond, \%vars); |
540 | push @if_state, $result ? 'active' : 'not_yet'; | | 540 | push @if_state, $result ? 'active' : 'not_yet'; |
541 | | | 541 | |
542 | } else { | | 542 | } else { |
543 | my $varname = expand_exprs($cond, \%vars); | | 543 | my $varname = expand_exprs($cond, \%vars); |
544 | | | 544 | |
545 | # bmake also allows '.ifdef A && B'. | | 545 | # bmake also allows '.ifdef A && B'. |
546 | debug("not implemented: .ifdef $varname") | | 546 | debug("not implemented: .ifdef $varname") |
547 | if $cond =~ /\s/; | | 547 | if $cond =~ /\s/; |
548 | | | 548 | |
549 | my $result = $kind eq 'ifdef' | | 549 | my $result = $kind eq 'ifdef' |
550 | ? defined($vars{$varname}) | | 550 | ? defined($vars{$varname}) |
551 | : !defined($vars{$varname}); | | 551 | : !defined($vars{$varname}); |
552 | push @if_state, $result ? 'active' : 'not_yet'; | | 552 | push @if_state, $result ? 'active' : 'not_yet'; |
553 | } | | 553 | } |
554 | | | 554 | |
555 | debug("$file: .$kind @if_state"); | | 555 | debug("$file: .$kind @if_state"); |
556 | | | 556 | |
557 | } elsif ( # XXX: bmake also knows '.elifdef' and '.elifnmake'. | | 557 | } elsif ( # XXX: bmake also knows '.elifdef' and '.elifnmake'. |
558 | m#^ \. \s* elif \s+ (.*)#x && @if_state > 0) { | | 558 | m#^ \. \s* elif \s+ (.*)#x && @if_state > 0) { |
559 | my ($cond) = ($1); | | 559 | my ($cond) = ($1); |
560 | | | 560 | |
561 | if ($if_state[-1] eq 'active') { | | 561 | if ($if_state[-1] eq 'active') { |
562 | $if_state[-1] = 'done'; | | 562 | $if_state[-1] = 'done'; |
563 | } elsif ($if_state[-1] eq 'not_yet' | | 563 | } elsif ($if_state[-1] eq 'not_yet' |
564 | && eval_mk_cond($cond, \%vars)) { | | 564 | && eval_mk_cond($cond, \%vars)) { |
565 | $if_state[-1] = 'active'; | | 565 | $if_state[-1] = 'active'; |
566 | } | | 566 | } |
567 | debug("$file: .elif @if_state"); | | 567 | debug("$file: .elif @if_state"); |
568 | | | 568 | |
569 | } elsif (m#^ \. \s* else \b #x && @if_state > 0) { | | 569 | } elsif (m#^ \. \s* else \b #x && @if_state > 0) { |
570 | $if_state[-1] = | | 570 | $if_state[-1] = |
571 | $if_state[-1] eq 'not_yet' ? 'active' : 'done'; | | 571 | $if_state[-1] eq 'not_yet' ? 'active' : 'done'; |
572 | debug("$file: .else @if_state"); | | 572 | debug("$file: .else @if_state"); |
573 | | | 573 | |
574 | } elsif (m#^\. \s* endif \b #x) { | | 574 | } elsif (m#^\. \s* endif \b #x) { |
575 | pop @if_state; | | 575 | pop @if_state; |
576 | debug("$file: .endif @if_state"); | | 576 | debug("$file: .endif @if_state"); |
577 | | | 577 | |
578 | } elsif (@if_state > 0 && $if_state[-1] ne 'active') { | | 578 | } elsif (@if_state > 0 && $if_state[-1] ne 'active') { |
579 | # Skip branches whose condition evaluated to false. | | 579 | # Skip branches whose condition evaluated to false. |
580 | | | 580 | |
581 | } elsif (m#^\. \s* include \s+ "([^"]+)" #x) { | | 581 | } elsif (m#^\. \s* include \s+ "([^"]+)" #x) { |
582 | my $incfile = expand_exprs($1, \%vars); | | 582 | my $incfile = expand_exprs($1, \%vars); |
583 | | | 583 | |
584 | parse_makefile_line_include($file, $incfile, | | 584 | parse_makefile_line_include($file, $incfile, |
585 | \@incdirs, \%incfiles, \@lines, \%vars); | | 585 | \@incdirs, \%incfiles, \@lines, \%vars); |
586 | | | 586 | |
587 | } elsif (m#^[ ]* ([-\w\.]+) \s* ([:+?]?=) \s* (.*)#x) { | | 587 | } elsif (m#^[ ]* ([-\w\.]+) \s* ([:+?]?=) \s* (.*)#x) { |
588 | parse_makefile_line_var($1, $2, $3, \%vars); | | 588 | parse_makefile_line_var($1, $2, $3, \%vars); |
589 | | | 589 | |
590 | } elsif ($_ eq '' || m#^\s*\## || m#^\t#) { | | 590 | } elsif ($_ eq '' || m#^\s*\## || m#^\t#) { |
591 | # Skip comment lines and shell commands. | | 591 | # Skip comment lines and shell commands. |
592 | | | 592 | |
593 | } else { | | 593 | } else { |
594 | debug("$file: unknown line '$_'"); | | 594 | debug("$file: unknown line '$_'"); |
595 | } | | 595 | } |
596 | } | | 596 | } |
597 | | | 597 | |
598 | debug("$file: expand"); | | 598 | debug("$file: expand"); |
599 | | | 599 | |
600 | # Handle variable substitutions FRED = a-${JIM:S/-/-b-/} | | 600 | # Handle variable substitutions FRED = a-${JIM:S/-/-b-/} |
601 | | | 601 | |
602 | for (my $loop = 1; $loop != 0;) { | | 602 | for (my $loop = 1; $loop != 0;) { |
603 | $loop = 0; | | 603 | $loop = 0; |
604 | foreach my $key (keys %vars) { | | 604 | foreach my $key (keys %vars) { |
605 | next if index($vars{$key}, '$') == -1; | | 605 | next if index($vars{$key}, '$') == -1; |
606 | | | 606 | |
607 | $_ = expand_exprs($vars{$key}, \%vars); | | 607 | $_ = expand_exprs($vars{$key}, \%vars); |
608 | if ($_ ne $vars{$key}) { | | 608 | if ($_ ne $vars{$key}) { |
609 | $vars{$key} = $_; | | 609 | $vars{$key} = $_; |
610 | $loop = 1; | | 610 | $loop = 1; |
611 | | | 611 | |
612 | } elsif ($vars{$key} =~ m#\$\{([\w.]+):([CS]([^{}])[^{}\3]+\3[^{}\3]*\3[g1]*(|:[^{}]+)|U[^{}]+)\}#) { | | 612 | } elsif ($vars{$key} =~ m#\$\{([\w.]+):([CS]([^{}])[^{}\3]+\3[^{}\3]*\3[g1]*(|:[^{}]+)|U[^{}]+)\}#) { |
613 | $loop ||= expand_modifiers($file, $key, $`, $1, $2, $', \%vars); | | 613 | $loop ||= expand_modifiers($file, $key, $`, $1, $2, $', \%vars); |
614 | } | | 614 | } |
615 | } | | 615 | } |
616 | } | | 616 | } |
617 | | | 617 | |
618 | foreach my $key (keys %vars) { | | 618 | foreach my $key (keys %vars) { |
619 | $vars{$key} =~ s/$magic_undefined//; | | 619 | $vars{$key} =~ s/$magic_undefined//; |
620 | } | | 620 | } |
621 | \%vars; | | 621 | \%vars; |
622 | } | | 622 | } |
623 | | | 623 | |
624 | sub get_default_makefile_vars() { | | 624 | sub get_default_makefile_vars() { |
625 | | | 625 | |
626 | chomp($_ = `uname -srm`); | | 626 | chomp($_ = `uname -srm`); |
627 | ( | | 627 | ( |
628 | $default_vars->{OPSYS}, | | 628 | $default_vars->{OPSYS}, |
629 | $default_vars->{OS_VERSION}, | | 629 | $default_vars->{OS_VERSION}, |
630 | $default_vars->{MACHINE} | | 630 | $default_vars->{MACHINE} |
631 | ) = split; | | 631 | ) = split; |
632 | $default_vars->{MACHINE} | | 632 | $default_vars->{MACHINE} |
633 | or die('Unable to extract machine from uname'); | | 633 | or die('Unable to extract machine from uname'); |
634 | | | 634 | |
635 | # Handle systems without uname -p (NetBSD pre 1.4) | | 635 | # Handle systems without uname -p (NetBSD pre 1.4) |
636 | chomp($default_vars->{MACHINE_ARCH} = `uname -p 2>/dev/null`); | | 636 | chomp($default_vars->{MACHINE_ARCH} = `uname -p 2>/dev/null`); |
637 | | | 637 | |
638 | if (!$default_vars->{MACHINE_ARCH} | | 638 | if (!$default_vars->{MACHINE_ARCH} |
639 | && $default_vars->{OS_VERSION} eq 'NetBSD') { | | 639 | && $default_vars->{OS_VERSION} eq 'NetBSD') { |
640 | chomp($default_vars->{MACHINE_ARCH} = `sysctl -n hw.machine_arch`); | | 640 | chomp($default_vars->{MACHINE_ARCH} = `sysctl -n hw.machine_arch`); |
641 | } | | 641 | } |
642 | | | 642 | |
643 | $default_vars->{MACHINE_ARCH} ||= $default_vars->{MACHINE}; | | 643 | $default_vars->{MACHINE_ARCH} ||= $default_vars->{MACHINE}; |
644 | | | 644 | |
645 | $default_vars->{OBJECT_FMT} = 'x'; | | 645 | $default_vars->{OBJECT_FMT} = 'x'; |
646 | $default_vars->{LOWER_OPSYS} = lc($default_vars->{OPSYS}); | | 646 | $default_vars->{LOWER_OPSYS} = lc($default_vars->{OPSYS}); |
647 | | | 647 | |
648 | $default_vars->{PKGSRCDIR} = $opt{P} | | 648 | $default_vars->{PKGSRCDIR} = $opt{P} |
649 | ? realpath($opt{P}) | | 649 | ? realpath($opt{P}) |
650 | : $conf_pkgsrcdir; | | 650 | : $conf_pkgsrcdir; |
651 | | | 651 | |
652 | $default_vars->{DESTDIR} = ''; | | 652 | $default_vars->{DESTDIR} = ''; |
653 | $default_vars->{LOCALBASE} = $conf_pkgsrcdir; | | 653 | $default_vars->{LOCALBASE} = $conf_pkgsrcdir; |
654 | $default_vars->{X11BASE} = $conf_x11base; | | 654 | $default_vars->{X11BASE} = $conf_x11base; |
655 | | | 655 | |
656 | my ($vars); | | 656 | my ($vars); |
657 | if (-f $conf_makeconf && | | 657 | if (-f $conf_makeconf && |
658 | ($vars = parse_makefile_vars($conf_makeconf, undef))) { | | 658 | ($vars = parse_makefile_vars($conf_makeconf, undef))) { |
659 | foreach my $var (keys %{$vars}) { | | 659 | foreach my $var (keys %{$vars}) { |
660 | $default_vars->{$var} = $vars->{$var}; | | 660 | $default_vars->{$var} = $vars->{$var}; |
661 | } | | 661 | } |
662 | } | | 662 | } |
663 | | | 663 | |
664 | # XXX: repeated from above? | | 664 | # XXX: repeated from above? |
665 | if ($opt{P}) { | | 665 | if ($opt{P}) { |
666 | $default_vars->{PKGSRCDIR} = realpath($opt{P}); | | 666 | $default_vars->{PKGSRCDIR} = realpath($opt{P}); |
667 | } | | 667 | } |
668 | | | 668 | |
669 | if ($opt{M}) { | | 669 | if ($opt{M}) { |
670 | $default_vars->{DISTDIR} = realpath($opt{M}); | | 670 | $default_vars->{DISTDIR} = realpath($opt{M}); |
671 | } else { | | 671 | } else { |
672 | $default_vars->{DISTDIR} ||= $default_vars->{PKGSRCDIR} . '/distfiles'; | | 672 | $default_vars->{DISTDIR} ||= $default_vars->{PKGSRCDIR} . '/distfiles'; |
673 | } | | 673 | } |
674 | | | 674 | |
675 | if ($opt{K}) { | | 675 | if ($opt{K}) { |
676 | $default_vars->{PACKAGES} = realpath($opt{K}); | | 676 | $default_vars->{PACKAGES} = realpath($opt{K}); |
677 | } | | 677 | } |
678 | | | 678 | |
679 | # Extract some variables from bsd.pkg.mk | | 679 | # Extract some variables from bsd.pkg.mk |
680 | my ($mkvars); | | 680 | my ($mkvars); |
681 | $mkvars = parse_makefile_vars( | | 681 | $mkvars = parse_makefile_vars( |
682 | "$default_vars->{PKGSRCDIR}/mk/bsd.pkg.mk", | | 682 | "$default_vars->{PKGSRCDIR}/mk/bsd.pkg.mk", |
683 | "$default_vars->{PKGSRCDIR}/mk/scripts" | | 683 | "$default_vars->{PKGSRCDIR}/mk/scripts" |
684 | ); | | 684 | ); |
685 | foreach my $varname (keys %{$mkvars}) { | | 685 | foreach my $varname (keys %{$mkvars}) { |
686 | if ($varname =~ /_REQD$/ || $varname eq 'EXTRACT_SUFX') { | | 686 | if ($varname =~ /_REQD$/ || $varname eq 'EXTRACT_SUFX') { |
687 | $default_vars->{$varname} = $mkvars->{$varname}; | | 687 | $default_vars->{$varname} = $mkvars->{$varname}; |
688 | } | | 688 | } |
689 | } | | 689 | } |
690 | | | 690 | |
691 | $default_vars->{PACKAGES} ||= $default_vars->{PKGSRCDIR} . '/packages'; | | 691 | $default_vars->{PACKAGES} ||= $default_vars->{PKGSRCDIR} . '/packages'; |
692 | } | | 692 | } |
693 | | | 693 | |
694 | # Determine if a package version is current. If not, report correct version | | 694 | # Determine if a package version is current. If not, report correct version |
695 | # if found | | 695 | # if found |
696 | # | | 696 | # |
697 | sub invalid_version($pkgmatch) { | | 697 | sub invalid_version($pkgmatch) { |
698 | my ($fail, $ok); | | 698 | my ($fail, $ok); |
699 | my (@pkgmatches); | | 699 | my (@pkgmatches); |
700 | | | 700 | |
701 | my @todo = ($pkgmatch); | | 701 | my @todo = ($pkgmatch); |
702 | | | 702 | |
703 | # We handle {} here, everything else in package_globmatch | | 703 | # We handle {} here, everything else in package_globmatch |
704 | while ($pkgmatch = shift @todo) { | | 704 | while ($pkgmatch = shift @todo) { |
705 | if ($pkgmatch =~ /(.*)\{([^{}]+)}(.*)/) { | | 705 | if ($pkgmatch =~ /(.*)\{([^{}]+)}(.*)/) { |
706 | foreach (split(',', $2)) { | | 706 | foreach (split(',', $2)) { |
707 | push @todo, "$1$_$3"; | | 707 | push @todo, "$1$_$3"; |
708 | } | | 708 | } |
709 | } else { | | 709 | } else { |
710 | push @pkgmatches, $pkgmatch; | | 710 | push @pkgmatches, $pkgmatch; |
711 | } | | 711 | } |
712 | } | | 712 | } |
713 | | | 713 | |
714 | foreach $pkgmatch (@pkgmatches) { | | 714 | foreach $pkgmatch (@pkgmatches) { |
715 | my ($pkg, $badver) = package_globmatch($pkgmatch); | | 715 | my ($pkg, $badver) = package_globmatch($pkgmatch); |
716 | | | 716 | |
717 | if (defined $badver) { | | 717 | if (defined $badver) { |
718 | if (my $pkgs = $pkgdb->pkgs($pkg)) { | | 718 | if (my $pkgs = $pkgdb->pkgs($pkg)) { |
719 | $fail .= | | 719 | $fail .= |
720 | "Version mismatch: '$pkg' $badver vs " | | 720 | "Version mismatch: '$pkg' $badver vs " |
721 | . join(',', $pkgs->versions) . "\n"; | | 721 | . join(',', $pkgs->versions) . "\n"; |
722 | } else { | | 722 | } else { |
723 | $fail .= "Unknown package: '$pkg' version $badver\n"; | | 723 | $fail .= "Unknown package: '$pkg' version $badver\n"; |
724 | } | | 724 | } |
725 | } else { | | 725 | } else { |
726 | # If we find one match, don't bitch about others | | 726 | # If we find one match, don't bitch about others |
727 | $ok = 1; | | 727 | $ok = 1; |
728 | } | | 728 | } |
729 | } | | 729 | } |
730 | $ok ? undef : $fail; | | 730 | $ok ? undef : $fail; |
731 | } | | 731 | } |
732 | | | 732 | |
733 | sub list_installed_packages() { | | 733 | sub list_installed_packages() { |
734 | open(PKG_INFO, "$conf_pkg_info -e '*' |") | | 734 | open(PKG_INFO, "$conf_pkg_info -e '*' |") |
735 | or fail("Unable to run $conf_pkg_info: $!"); | | 735 | or fail("Unable to run $conf_pkg_info: $!"); |
736 | chomp(my @pkgs = <PKG_INFO>); | | 736 | chomp(my @pkgs = <PKG_INFO>); |
737 | close(PKG_INFO); | | 737 | close(PKG_INFO); |
738 | map { $_ = canonicalize_pkgname($_) } @pkgs; | | 738 | map { $_ = canonicalize_pkgname($_) } @pkgs; |
739 | } | | 739 | } |
740 | | | 740 | |
741 | # List top level pkgsrc categories | | 741 | # List top level pkgsrc categories |
742 | # | | 742 | # |
743 | sub list_pkgsrc_categories($pkgsrcdir) { | | 743 | sub list_pkgsrc_categories($pkgsrcdir) { |
744 | my (@categories); | | 744 | my (@categories); |
745 | | | 745 | |
746 | opendir(BASE, $pkgsrcdir) || die("Unable to opendir($pkgsrcdir): $!"); | | 746 | opendir(BASE, $pkgsrcdir) || die("Unable to opendir($pkgsrcdir): $!"); |
747 | @categories = | | 747 | @categories = |
748 | grep(substr($_, 0, 1) ne '.' | | 748 | grep(substr($_, 0, 1) ne '.' |
749 | && $_ ne 'CVS' | | 749 | && $_ ne 'CVS' |
750 | && -f "$pkgsrcdir/$_/Makefile", | | 750 | && -f "$pkgsrcdir/$_/Makefile", |
751 | readdir(BASE)); | | 751 | readdir(BASE)); |
752 | closedir(BASE); | | 752 | closedir(BASE); |
753 | @categories; | | 753 | @categories; |
754 | } | | 754 | } |
755 | | | 755 | |
756 | # For a given category, list potentially valid pkgdirs | | 756 | # For a given category, list potentially valid pkgdirs |
757 | # | | 757 | # |
758 | sub list_pkgsrc_pkgdirs($pkgsrcdir, $cat) { | | 758 | sub list_pkgsrc_pkgdirs($pkgsrcdir, $cat) { |
759 | opendir(CAT, "$pkgsrcdir/$cat") | | 759 | opendir(CAT, "$pkgsrcdir/$cat") |
760 | or die("Unable to opendir($pkgsrcdir/$cat): $!"); | | 760 | or die("Unable to opendir($pkgsrcdir/$cat): $!"); |
761 | my @pkgdirs = sort grep { | | 761 | my @pkgdirs = sort grep { |
762 | $_ ne 'Makefile' | | 762 | $_ ne 'Makefile' |
763 | && $_ ne 'pkg' | | 763 | && $_ ne 'pkg' |
764 | && $_ ne 'CVS' | | 764 | && $_ ne 'CVS' |
765 | && substr($_, 0, 1) ne '.' | | 765 | && substr($_, 0, 1) ne '.' |
766 | } readdir(CAT); | | 766 | } readdir(CAT); |
767 | closedir(CAT) or die; | | 767 | closedir(CAT) or die; |
768 | @pkgdirs; | | 768 | @pkgdirs; |
769 | } | | 769 | } |
770 | | | 770 | |
771 | # Convert the glob pattern to a regular expression. | | 771 | # Convert the glob pattern to a regular expression. |
772 | # Return '' if the regular expression equals the glob expression. | | 772 | # Return '' if the regular expression equals the glob expression. |
773 | # Return undef on error. | | 773 | # Return undef on error. |
774 | sub glob2regex($glob) { | | 774 | sub glob2regex($glob) { |
775 | my @chars = split(//, $glob); | | 775 | my @chars = split(//, $glob); |
776 | my $alternative_depth = 0; | | 776 | my $alternative_depth = 0; |
777 | my $regex = ''; | | 777 | my $regex = ''; |
778 | while (defined($_ = shift @chars)) { | | 778 | while (defined($_ = shift @chars)) { |
779 | if ($_ eq '*') { | | 779 | if ($_ eq '*') { |
780 | $regex .= '.*'; | | 780 | $regex .= '.*'; |
781 | } elsif ($_ eq '?') { | | 781 | } elsif ($_ eq '?') { |
782 | $regex .= '.'; | | 782 | $regex .= '.'; |
783 | } elsif ($_ eq '+') { | | 783 | } elsif ($_ eq '+') { |
784 | $regex .= '\\+'; | | 784 | $regex .= '\\+'; |
785 | } elsif ($_ eq '\\' && @chars > 0) { | | 785 | } elsif ($_ eq '\\' && @chars > 0) { |
786 | my $next = shift @chars; | | 786 | my $next = shift @chars; |
787 | $regex .= $next =~ /\w/ ? $next : "\\$next"; | | 787 | $regex .= $next =~ /\w/ ? $next : "\\$next"; |
788 | } elsif ($_ eq '.' || $_ eq '|') { | | 788 | } elsif ($_ eq '.' || $_ eq '|') { |
789 | $regex .= "\\$_"; | | 789 | $regex .= "\\$_"; |
790 | } elsif ($_ eq '{') { | | 790 | } elsif ($_ eq '{') { |
791 | $regex .= '('; | | 791 | $regex .= '('; |
792 | ++$alternative_depth; | | 792 | ++$alternative_depth; |
793 | } elsif ($_ eq '}') { | | 793 | } elsif ($_ eq '}') { |
794 | return undef if $alternative_depth == 0; | | 794 | return undef if $alternative_depth == 0; |
795 | $regex .= ')'; | | 795 | $regex .= ')'; |
796 | --$alternative_depth; | | 796 | --$alternative_depth; |
797 | } elsif ($_ eq ',' && $alternative_depth > 0) { | | 797 | } elsif ($_ eq ',' && $alternative_depth > 0) { |
798 | $regex .= '|'; | | 798 | $regex .= '|'; |
799 | } elsif ($_ eq '[') { | | 799 | } elsif ($_ eq '[') { |
800 | $regex .= '['; | | 800 | $regex .= '['; |
801 | while (defined($_ = shift @chars)) { | | 801 | while (defined($_ = shift @chars)) { |
802 | $regex .= $_; | | 802 | $regex .= $_; |
803 | if ($_ eq ']') { | | 803 | if ($_ eq ']') { |
804 | last; | | 804 | last; |
805 | } elsif ($_ eq '\\' && @chars > 0) { | | 805 | } elsif ($_ eq '\\' && @chars > 0) { |
806 | $regex .= shift @chars; | | 806 | $regex .= shift @chars; |
807 | } | | 807 | } |
808 | } | | 808 | } |
809 | return undef if $_ ne ']'; | | 809 | return undef if $_ ne ']'; |
810 | } else { | | 810 | } else { |
811 | $regex .= $_; | | 811 | $regex .= $_; |
812 | } | | 812 | } |
813 | } | | 813 | } |
814 | | | 814 | |
815 | return undef if $alternative_depth > 0; | | 815 | return undef if $alternative_depth > 0; |
816 | return '' if $regex eq $glob; # XXX: why? | | 816 | return '' if $regex eq $glob; # XXX: why? |
817 | | | 817 | |
818 | $opt{D} and print "glob2regex: $glob -> $regex\n"; | | 818 | $opt{D} and print "glob2regex: $glob -> $regex\n"; |
819 | '^' . $regex . '$'; | | 819 | '^' . $regex . '$'; |
820 | } | | 820 | } |
821 | | | 821 | |
822 | # Perform some (reasonable) subset of 'pkg_info -e' / glob(3) | | 822 | # Perform some (reasonable) subset of 'pkg_info -e' / glob(3) |
823 | # Returns (sometimes best guess at) package name, | | 823 | # Returns (sometimes best guess at) package name, |
824 | # and either 'problem version' or undef if all OK | | 824 | # and either 'problem version' or undef if all OK |
825 | # | | 825 | # |
826 | sub package_globmatch($pkgmatch) { | | 826 | sub package_globmatch($pkgmatch) { |
827 | my ($matchpkgname, $matchver, $regex); | | 827 | my ($matchpkgname, $matchver, $regex); |
828 | | | 828 | |
829 | if ($pkgmatch =~ /^([^*?[]+)(<|>|<=|>=|-)(\d[^*?[{]*)$/) { | | 829 | if ($pkgmatch =~ /^([^*?[]+)(<|>|<=|>=|-)(\d[^*?[{]*)$/) { |
830 | | | 830 | |
831 | # (package)(cmp)(pkgversion) | | 831 | # (package)(cmp)(pkgversion) |
832 | my ($test, @pkgvers); | | 832 | my ($test, @pkgvers); |
833 | | | 833 | |
834 | # TODO: rename $matchpkgname to be more accurate. | | 834 | # TODO: rename $matchpkgname to be more accurate. |
835 | ($matchpkgname, $test, $matchver) = ($1, $2, $3); | | 835 | ($matchpkgname, $test, $matchver) = ($1, $2, $3); |
836 | if (@pkgvers = $pkgdb->pkgvers_by_pkgbase($matchpkgname)) { | | 836 | if (@pkgvers = $pkgdb->pkgvers_by_pkgbase($matchpkgname)) { |
837 | foreach my $pkgver (@pkgvers) { | | 837 | foreach my $pkgver (@pkgvers) { |
838 | if ($test eq '-') { | | 838 | if ($test eq '-') { |
839 | if ($pkgver->pkgversion eq $matchver) { | | 839 | if ($pkgver->pkgversion eq $matchver) { |
840 | $matchver = undef; | | 840 | $matchver = undef; |
841 | last; | | 841 | last; |
842 | } | | 842 | } |
843 | } else { | | 843 | } else { |
844 | if (pkgversion_cmp($pkgver->pkgversion, $test, $matchver)) { | | 844 | if (pkgversion_cmp($pkgver->pkgversion, $test, $matchver)) { |
845 | $matchver = undef; | | 845 | $matchver = undef; |
846 | last; | | 846 | last; |
847 | } | | 847 | } |
848 | } | | 848 | } |
849 | } | | 849 | } |
850 | | | 850 | |
851 | if ($matchver && $test ne '-') { | | 851 | if ($matchver && $test ne '-') { |
852 | $matchver = "$test$matchver"; | | 852 | $matchver = "$test$matchver"; |
853 | } | | 853 | } |
854 | } | | 854 | } |
855 | | | 855 | |
856 | } elsif ($pkgmatch =~ /^([^[]+)-([\d*?{[].*)$/) { | | 856 | } elsif ($pkgmatch =~ /^([^[]+)-([\d*?{[].*)$/) { |
857 | | | 857 | |
858 | # (package)-(globver) | | 858 | # (package)-(globver) |
859 | my (@pkgnames); | | 859 | my (@pkgnames); |
860 | | | 860 | |
861 | ($matchpkgname, $matchver) = ($1, $2); | | 861 | ($matchpkgname, $matchver) = ($1, $2); |
862 | | | 862 | |
863 | if (defined $pkgdb->pkgs($matchpkgname)) { | | 863 | if (defined $pkgdb->pkgs($matchpkgname)) { |
864 | push @pkgnames, $matchpkgname; | | 864 | push @pkgnames, $matchpkgname; |
865 | | | 865 | |
866 | } elsif ($regex = glob2regex($matchpkgname)) { | | 866 | } elsif ($regex = glob2regex($matchpkgname)) { |
867 | foreach my $pkg ($pkgdb->pkgs) { | | 867 | foreach my $pkg ($pkgdb->pkgs) { |
868 | if ($pkg->pkgbase =~ /$regex/) { | | 868 | if ($pkg->pkgbase =~ /$regex/) { |
869 | push @pkgnames, $pkg->pkgbase; | | 869 | push @pkgnames, $pkg->pkgbase; |
870 | } | | 870 | } |
871 | } | | 871 | } |
872 | } | | 872 | } |
873 | | | 873 | |
874 | # Try to convert $matchver into regex version | | 874 | # Try to convert $matchver into regex version |
875 | # | | 875 | # |
876 | $regex = glob2regex($matchver); | | 876 | $regex = glob2regex($matchver); |
877 | | | 877 | |
878 | foreach my $pkg (@pkgnames) { | | 878 | foreach my $pkg (@pkgnames) { |
879 | if (defined $pkgdb->pkgver($pkg, $matchver)) { | | 879 | if (defined $pkgdb->pkgver($pkg, $matchver)) { |
880 | return ($matchver); | | 880 | return ($matchver); |
881 | } | | 881 | } |
882 | | | 882 | |
883 | if ($regex) { | | 883 | if ($regex) { |
884 | foreach my $ver ($pkgdb->pkgs($pkg)->versions) { | | 884 | foreach my $ver ($pkgdb->pkgs($pkg)->versions) { |
885 | if ($ver =~ /$regex/) { | | 885 | if ($ver =~ /$regex/) { |
886 | $matchver = undef; | | 886 | $matchver = undef; |
887 | last; | | 887 | last; |
888 | } | | 888 | } |
889 | } | | 889 | } |
890 | } | | 890 | } |
891 | | | 891 | |
892 | $matchver || last; | | 892 | $matchver || last; |
893 | } | | 893 | } |
894 | | | 894 | |
895 | # last ditch attempt to handle the whole DEPENDS as a glob | | 895 | # last ditch attempt to handle the whole DEPENDS as a glob |
896 | # | | 896 | # |
897 | if ($matchver && ($regex = glob2regex($pkgmatch))) { | | 897 | if ($matchver && ($regex = glob2regex($pkgmatch))) { |
898 | | | 898 | |
899 | # (large-glob) | | 899 | # (large-glob) |
900 | foreach my $pkgver ($pkgdb->pkgvers_all) { | | 900 | foreach my $pkgver ($pkgdb->pkgvers_all) { |
901 | if ($pkgver->pkgname =~ /$regex/) { | | 901 | if ($pkgver->pkgname =~ /$regex/) { |
902 | $matchver = undef; | | 902 | $matchver = undef; |
903 | last; | | 903 | last; |
904 | } | | 904 | } |
905 | } | | 905 | } |
906 | } | | 906 | } |
907 | | | 907 | |
908 | } else { | | 908 | } else { |
909 | ($matchpkgname, $matchver) = ($pkgmatch, 'missing'); | | 909 | ($matchpkgname, $matchver) = ($pkgmatch, 'missing'); |
910 | } | | 910 | } |
911 | | | 911 | |
912 | ($matchpkgname, $matchver); | | 912 | ($matchpkgname, $matchver); |
913 | } | | 913 | } |
914 | | | 914 | |
915 | # Parse a pkgsrc package makefile and return the pkgname and set variables | | 915 | # Parse a pkgsrc package makefile and return the pkgname and set variables |
916 | # | | 916 | # |
917 | sub parse_makefile_pkgsrc($file) { | | 917 | sub parse_makefile_pkgsrc($file) { |
918 | my ($pkgname, $vars); | | 918 | my ($pkgname, $vars); |
919 | | | 919 | |
920 | $vars = parse_makefile_vars($file, undef); | | 920 | $vars = parse_makefile_vars($file, undef); |
921 | | | 921 | |
922 | if (!$vars) { | | 922 | if (!$vars) { |
923 | | | 923 | |
924 | # Missing Makefile | | 924 | # Missing Makefile |
925 | return undef; | | 925 | return undef; |
926 | } | | 926 | } |
927 | | | 927 | |
928 | if (defined $vars->{PKGNAME}) { | | 928 | if (defined $vars->{PKGNAME}) { |
929 | $pkgname = $vars->{PKGNAME}; | | 929 | $pkgname = $vars->{PKGNAME}; |
930 | } elsif (defined $vars->{DISTNAME}) { | | 930 | } elsif (defined $vars->{DISTNAME}) { |
931 | $pkgname = $vars->{DISTNAME}; | | 931 | $pkgname = $vars->{DISTNAME}; |
932 | } | | 932 | } |
933 | | | 933 | |
934 | if (defined $vars->{PKGNAME}) { | | 934 | if (defined $vars->{PKGNAME}) { |
935 | debug("$file: PKGNAME=$vars->{PKGNAME}"); | | 935 | debug("$file: PKGNAME=$vars->{PKGNAME}"); |
936 | } | | 936 | } |
937 | if (defined $vars->{DISTNAME}) { | | 937 | if (defined $vars->{DISTNAME}) { |
938 | debug("$file: DISTNAME=$vars->{DISTNAME}"); | | 938 | debug("$file: DISTNAME=$vars->{DISTNAME}"); |
939 | } | | 939 | } |
940 | | | 940 | |
941 | if (!defined $pkgname || $pkgname =~ /\$/ || $pkgname !~ /(.*)-(\d.*)/) { | | 941 | if (!defined $pkgname || $pkgname =~ /\$/ || $pkgname !~ /(.*)-(\d.*)/) { |
942 | | | 942 | |
943 | # invoke make here as a last resort | | 943 | # invoke make here as a last resort |
944 | my $pkgdir = dirname $file; | | 944 | my $pkgdir = dirname $file; |
945 | debug("Running '$conf_make' in '$pkgdir'"); | | 945 | debug("Running '$conf_make' in '$pkgdir'"); |
946 | my $pid = open3(\*WTR, \*RDR, \*ERR, | | 946 | my $pid = open3(\*WTR, \*RDR, \*ERR, |
947 | "cd $pkgdir || exit 1; $conf_make show-vars VARNAMES=PKGNAME"); | | 947 | "cd $pkgdir || exit 1; $conf_make show-vars VARNAMES=PKGNAME"); |
948 | if (!$pid) { | | 948 | if (!$pid) { |
949 | warn "$file: Unable to run make: $!"; | | 949 | warn "$file: Unable to run make: $!"; |
950 | } else { | | 950 | } else { |
951 | close(WTR); | | 951 | close(WTR); |
952 | my @errors = <ERR>; | | 952 | my @errors = <ERR>; |
953 | close(ERR); | | 953 | close(ERR); |
954 | my ($makepkgname) = <RDR>; | | 954 | my ($makepkgname) = <RDR>; |
955 | close(RDR); | | 955 | close(RDR); |
956 | wait; | | 956 | wait; |
957 | chomp @errors; | | 957 | chomp @errors; |
958 | if (@errors) { warn "\n$file: @errors\n"; } | | 958 | if (@errors) { warn "\n$file: @errors\n"; } |
959 | | | 959 | |
960 | if ($makepkgname =~ /(.*)-(\d.*)/) { | | 960 | if ($makepkgname =~ /(.*)-(\d.*)/) { |
961 | $pkgname = $makepkgname; | | 961 | $pkgname = $makepkgname; |
962 | } | | 962 | } |
963 | } | | 963 | } |
964 | } | | 964 | } |
965 | | | 965 | |
966 | if (defined $pkgname) { | | 966 | if (defined $pkgname) { |
967 | $pkgname = canonicalize_pkgname($pkgname); | | 967 | $pkgname = canonicalize_pkgname($pkgname); |
968 | | | 968 | |
969 | if (defined $vars->{PKGREVISION} | | 969 | if (defined $vars->{PKGREVISION} |
970 | and not $vars->{PKGREVISION} =~ /^\s*$/) { | | 970 | and not $vars->{PKGREVISION} =~ /^\s*$/) { |
971 | if ($vars->{PKGREVISION} =~ /^\$\{(_(CVS|GIT|HG|SVN)_PKGVERSION):.*\}$/) { | | 971 | if ($vars->{PKGREVISION} =~ /^\$\{(_(CVS|GIT|HG|SVN)_PKGVERSION):.*\}$/) { |
972 | # See wip/mk/*-package.mk. | | 972 | # See wip/mk/*-package.mk. |
973 | } elsif ($vars->{PKGREVISION} =~ /\D/) { | | 973 | } elsif ($vars->{PKGREVISION} =~ /\D/) { |
974 | print "\nBogus: PKGREVISION $vars->{PKGREVISION} (from $file)\n"; | | 974 | print "\nBogus: PKGREVISION $vars->{PKGREVISION} (from $file)\n"; |
975 | | | 975 | |
976 | } elsif ($vars->{PKGREVISION}) { | | 976 | } elsif ($vars->{PKGREVISION}) { |
977 | $pkgname .= 'nb'; | | 977 | $pkgname .= 'nb'; |
978 | $pkgname .= $vars->{PKGREVISION}; | | 978 | $pkgname .= $vars->{PKGREVISION}; |
979 | } | | 979 | } |
980 | } | | 980 | } |
981 | | | 981 | |
982 | if ($pkgname =~ /\$/) { | | 982 | if ($pkgname =~ /\$/) { |
983 | print "\nBogus: $pkgname (from $file)\n"; | | 983 | print "\nBogus: $pkgname (from $file)\n"; |
984 | | | 984 | |
985 | } elsif ($pkgname =~ /(.*)-(\d.*)/) { | | 985 | } elsif ($pkgname =~ /(.*)-(\d.*)/) { |
986 | if ($pkgdb) { | | 986 | if ($pkgdb) { |
987 | my ($pkgver) = $pkgdb->add($1, $2); | | 987 | my ($pkgver) = $pkgdb->add($1, $2); |
988 | | | 988 | |
989 | debug("add $1 $2"); | | 989 | debug("add $1 $2"); |
990 | | | 990 | |
991 | foreach my $var (qw(DEPENDS RESTRICTED OSVERSION_SPECIFIC BROKEN)) { | | 991 | foreach my $var (qw(DEPENDS RESTRICTED OSVERSION_SPECIFIC BROKEN)) { |
992 | $pkgver->var($var, $vars->{$var}); | | 992 | $pkgver->var($var, $vars->{$var}); |
993 | } | | 993 | } |
994 | | | 994 | |
995 | if (defined $vars->{NO_BIN_ON_FTP}) { | | 995 | if (defined $vars->{NO_BIN_ON_FTP}) { |
996 | $pkgver->var('RESTRICTED', 'NO_BIN_ON_FTP'); | | 996 | $pkgver->var('RESTRICTED', 'NO_BIN_ON_FTP'); |
997 | } | | 997 | } |
998 | | | 998 | |
999 | if ($file =~ m:([^/]+/[^/]+)/Makefile$:) { | | 999 | if ($file =~ m:([^/]+/[^/]+)/Makefile$:) { |
1000 | $pkgver->var('dir', $1); | | 1000 | $pkgver->var('dir', $1); |
1001 | } else { | | 1001 | } else { |
1002 | $pkgver->var('dir', 'unknown'); | | 1002 | $pkgver->var('dir', 'unknown'); |
1003 | } | | 1003 | } |
1004 | } | | 1004 | } |
1005 | } else { | | 1005 | } else { |
1006 | print "Cannot extract $pkgname version ($file)\n"; | | 1006 | print "Cannot extract $pkgname version ($file)\n"; |
1007 | } | | 1007 | } |
1008 | | | 1008 | |
1009 | return ($pkgname, $vars); | | 1009 | return ($pkgname, $vars); |
1010 | | | 1010 | |
1011 | } else { | | 1011 | } else { |
1012 | return (undef); | | 1012 | return (undef); |
1013 | } | | 1013 | } |
1014 | } | | 1014 | } |
1015 | | | 1015 | |
1016 | | | 1016 | |
1017 | sub chdir_or_fail($dir) { | | 1017 | sub chdir_or_fail($dir) { |
1018 | debug("chdir: $dir"); | | 1018 | debug("chdir: $dir"); |
1019 | chdir($dir) or fail("Cannot chdir($dir): $!\n"); | | 1019 | chdir($dir) or fail("Cannot chdir($dir): $!\n"); |
1020 | } | | 1020 | } |
1021 | | | 1021 | |
1022 | sub load_pkgdb_from_cache($fname) { | | 1022 | sub load_pkgdb_from_cache($fname) { |
1023 | open(STORE, '<', $fname) | | 1023 | open(STORE, '<', $fname) |
1024 | or die("Cannot read pkgsrc store from $fname: $!\n"); | | 1024 | or die("Cannot read pkgsrc store from $fname: $!\n"); |
1025 | my ($pkgver); | | 1025 | my ($pkgver); |
1026 | $pkgdb = PkgDb->new(); | | 1026 | $pkgdb = PkgDb->new(); |
1027 | while (defined(my $line = <STORE>)) { | | 1027 | while (defined(my $line = <STORE>)) { |
1028 | chomp($line); | | 1028 | chomp($line); |
1029 | if ($line =~ qr"^package\t([^\t]+)\t([^\t]+$)$") { | | 1029 | if ($line =~ qr"^package\t([^\t]+)\t([^\t]+$)$") { |
1030 | $pkgver = $pkgdb->add($1, $2); | | 1030 | $pkgver = $pkgdb->add($1, $2); |
1031 | } elsif ($line =~ qr"^var\t([^\t]+)\t(.*)$") { | | 1031 | } elsif ($line =~ qr"^var\t([^\t]+)\t(.*)$") { |
1032 | $pkgver->var($1, $2); | | 1032 | $pkgver->var($1, $2); |
1033 | } elsif ($line =~ qr"^sub ") { | | 1033 | } elsif ($line =~ qr"^sub ") { |
1034 | die "Outdated cache format in '$fname'\n"; | | 1034 | die "Outdated cache format in '$fname'\n"; |
1035 | } else { | | 1035 | } else { |
1036 | die "Invalid line '$line' in cache '$fname'\n"; | | 1036 | die "Invalid line '$line' in cache '$fname'\n"; |
1037 | } | | 1037 | } |
1038 | } | | 1038 | } |
1039 | close(STORE); | | 1039 | close(STORE); |
1040 | } | | 1040 | } |
1041 | | | 1041 | |
1042 | # Generate pkgname->category/pkg mapping, optionally check DEPENDS | | 1042 | # Generate pkgname->category/pkg mapping, optionally check DEPENDS |
1043 | # | | 1043 | # |
1044 | sub scan_pkgsrc_makefiles($pkgsrcdir) { | | 1044 | sub scan_pkgsrc_makefiles($pkgsrcdir) { |
1045 | | | 1045 | |
1046 | return if defined $pkgdb; # Already done. | | 1046 | return if defined $pkgdb; # Already done. |
1047 | | | 1047 | |
1048 | if ($opt{I}) { | | 1048 | if ($opt{I}) { |
1049 | load_pkgdb_from_cache($opt{I}); | | 1049 | load_pkgdb_from_cache($opt{I}); |
1050 | return; | | 1050 | return; |
1051 | } | | 1051 | } |
1052 | | | 1052 | |
1053 | $pkgdb = PkgDb->new(); | | 1053 | $pkgdb = PkgDb->new(); |
1054 | my @categories = list_pkgsrc_categories($pkgsrcdir); | | 1054 | my @categories = list_pkgsrc_categories($pkgsrcdir); |
1055 | verbose('Scan Makefiles: '); | | 1055 | verbose('Scan Makefiles: '); |
1056 | | | 1056 | |
1057 | if (!$opt{L}) { | | 1057 | if (!$opt{L}) { |
1058 | verbose('_' x @categories . "\b" x @categories); | | 1058 | verbose('_' x @categories . "\b" x @categories); |
1059 | } else { | | 1059 | } else { |
1060 | verbose("\n"); | | 1060 | verbose("\n"); |
1061 | } | | 1061 | } |
1062 | | | 1062 | |
1063 | foreach my $cat (sort @categories) { | | 1063 | foreach my $cat (sort @categories) { |
1064 | foreach my $pkgdir (list_pkgsrc_pkgdirs($pkgsrcdir, $cat)) { | | 1064 | foreach my $pkgdir (list_pkgsrc_pkgdirs($pkgsrcdir, $cat)) { |
1065 | my ($pkg, $vars) = parse_makefile_pkgsrc("$pkgsrcdir/$cat/$pkgdir/Makefile"); | | 1065 | my ($pkg, $vars) = parse_makefile_pkgsrc("$pkgsrcdir/$cat/$pkgdir/Makefile"); |
1066 | } | | 1066 | } |
1067 | | | 1067 | |
1068 | if (!$opt{L}) { | | 1068 | if (!$opt{L}) { |
1069 | verbose('.'); | | 1069 | verbose('.'); |
1070 | } | | 1070 | } |
1071 | } | | 1071 | } |
1072 | | | 1072 | |
1073 | if (!$opt{L}) { | | 1073 | if (!$opt{L}) { |
1074 | my ($len); | | 1074 | my ($len); |
1075 | | | 1075 | |
1076 | $_ = scalar $pkgdb->pkgvers_all . ' packages'; | | 1076 | $_ = scalar $pkgdb->pkgvers_all . ' packages'; |
1077 | $len = @categories - length($_); | | 1077 | $len = @categories - length($_); |
1078 | verbose("\b" x @categories, $_, ' ' x $len, "\b" x $len, "\n"); | | 1078 | verbose("\b" x @categories, $_, ' ' x $len, "\b" x $len, "\n"); |
1079 | } | | 1079 | } |
1080 | } | | 1080 | } |
1081 | | | 1081 | |
1082 | # Cross reference all depends | | 1082 | # Cross reference all depends |
1083 | # | | 1083 | # |
1084 | sub pkgsrc_check_depends() { | | 1084 | sub pkgsrc_check_depends() { |
1085 | foreach my $pkgver ($pkgdb->pkgvers_all) { | | 1085 | foreach my $pkgver ($pkgdb->pkgvers_all) { |
1086 | my ($err, $msg); | | 1086 | my ($err, $msg); |
1087 | | | 1087 | |
1088 | defined $pkgver->var('DEPENDS') || next; | | 1088 | defined $pkgver->var('DEPENDS') || next; |
1089 | foreach my $depend (split(' ', $pkgver->var('DEPENDS'))) { | | 1089 | foreach my $depend (split(' ', $pkgver->var('DEPENDS'))) { |
1090 | | | 1090 | |
1091 | $depend =~ s/:.*// || next; | | 1091 | $depend =~ s/:.*// || next; |
1092 | | | 1092 | |
1093 | $depend = canonicalize_pkgname($depend); | | 1093 | $depend = canonicalize_pkgname($depend); |
1094 | if (($msg = invalid_version($depend))) { | | 1094 | if (($msg = invalid_version($depend))) { |
1095 | if (!defined $err) { | | 1095 | if (!defined $err) { |
1096 | print $pkgver->pkgname . " DEPENDS errors:\n"; | | 1096 | print $pkgver->pkgname . " DEPENDS errors:\n"; |
1097 | } | | 1097 | } |
1098 | $err = 1; | | 1098 | $err = 1; |
1099 | $msg =~ s/(\n)(.)/$1\t$2/g; | | 1099 | $msg =~ s/(\n)(.)/$1\t$2/g; |
1100 | print "\t$msg"; | | 1100 | print "\t$msg"; |
1101 | } | | 1101 | } |
1102 | } | | 1102 | } |
1103 | } | | 1103 | } |
1104 | } | | 1104 | } |
1105 | | | 1105 | |
1106 | sub load_distinfo($pkgsrcdir, $cat, $pkgdir, $distfiles, $warnings) { | | 1106 | sub load_distinfo($pkgsrcdir, $cat, $pkgdir, $distfiles, $warnings) { |
1107 | my $fname = "$pkgsrcdir/$cat/$pkgdir/distinfo"; | | 1107 | my $fname = "$pkgsrcdir/$cat/$pkgdir/distinfo"; |
1108 | open(DISTINFO, '<', $fname) or return; | | 1108 | open(DISTINFO, '<', $fname) or return; |
1109 | | | 1109 | |
1110 | while (defined(my $line = <DISTINFO>)) { | | 1110 | while (defined(my $line = <DISTINFO>)) { |
1111 | chomp($line); | | 1111 | chomp($line); |
1112 | | | 1112 | |
1113 | next if $line eq '' || $line =~ /^\$NetBSD/; | | 1113 | next if $line eq '' || $line =~ /^\$NetBSD/; |
1114 | | | 1114 | |
1115 | if ($line !~ m/^ (\w+) \s \( ([^)]+) \) \s=\s (\S+)/x) { | | 1115 | if ($line !~ m/^ (\w+) \s \( ([^)]+) \) \s=\s (\S+)/x) { |
1116 | warn "Invalid line in $fname:$.: $line\n"; | | 1116 | warn "Invalid line in $fname:$.: $line\n"; |
1117 | next; | | 1117 | next; |
1118 | } | | 1118 | } |
1119 | my ($alg, $distfile, $hash) = ($1, $2, $3); | | 1119 | my ($alg, $distfile, $hash) = ($1, $2, $3); |
1120 | | | 1120 | |
1121 | next if $distfile =~ /^patch-[\w.+\-]+$/; | | 1121 | next if $distfile =~ /^patch-[\w.+\-]+$/; |
1122 | | | 1122 | |
1123 | # Only store and check the first algorithm listed in distinfo. | | 1123 | # Only store and check the first algorithm listed in distinfo. |
1124 | if (!defined $distfiles->{$distfile}) { | | 1124 | if (!defined $distfiles->{$distfile}) { |
1125 | $distfiles->{$distfile} = { | | 1125 | $distfiles->{$distfile} = { |
1126 | sumtype => $alg, | | 1126 | sumtype => $alg, |
1127 | sum => $hash, | | 1127 | sum => $hash, |
1128 | path => "$cat/$pkgdir", | | 1128 | path => "$cat/$pkgdir", |
1129 | }; | | 1129 | }; |
1130 | | | 1130 | |
1131 | } elsif ($distfiles->{$distfile}->{sumtype} eq $alg | | 1131 | } elsif ($distfiles->{$distfile}->{sumtype} eq $alg |
1132 | && $distfiles->{$distfile}->{sum} ne $hash) { | | 1132 | && $distfiles->{$distfile}->{sum} ne $hash) { |
1133 | push @$warnings, | | 1133 | push @$warnings, |
1134 | "checksum mismatch between '$alg' for '$distfile' " | | 1134 | "checksum mismatch between '$alg' for '$distfile' " |
1135 | . "in $cat/$pkgdir " | | 1135 | . "in $cat/$pkgdir " |
1136 | . "and $distfiles->{$distfile}{path}\n"; | | 1136 | . "and $distfiles->{$distfile}{path}\n"; |
1137 | } | | 1137 | } |
1138 | } | | 1138 | } |
1139 | close(DISTINFO) or die; | | 1139 | close(DISTINFO) or die; |
1140 | } | | 1140 | } |
1141 | | | 1141 | |
1142 | # Extract all distinfo entries, then verify contents of distfiles | | 1142 | # Extract all distinfo entries, then verify contents of distfiles |
1143 | # | | 1143 | # |
1144 | sub scan_pkgsrc_distfiles_vs_distinfo($pkgsrcdir, $pkgdistdir, $check_unref, | | 1144 | sub scan_pkgsrc_distfiles_vs_distinfo($pkgsrcdir, $pkgdistdir, $check_unref, |
1145 | $check_distinfo) { | | 1145 | $check_distinfo) { |
1146 | my (@categories); | | 1146 | my (@categories); |
1147 | my (%distfiles, %sumfiles, @distwarn, $numpkg); | | 1147 | my (%distfiles, %sumfiles, @distwarn, $numpkg); |
1148 | my (%unref_distfiles); | | 1148 | my (%unref_distfiles); |
1149 | | | 1149 | |
1150 | @categories = list_pkgsrc_categories($pkgsrcdir); | | 1150 | @categories = list_pkgsrc_categories($pkgsrcdir); |
1151 | | | 1151 | |
1152 | verbose('Scan distinfo: ' . '_' x @categories . "\b" x @categories); | | 1152 | verbose('Scan distinfo: ' . '_' x @categories . "\b" x @categories); |
1153 | $numpkg = 0; | | 1153 | $numpkg = 0; |
1154 | foreach my $cat (sort @categories) { | | 1154 | foreach my $cat (sort @categories) { |
1155 | foreach my $pkgdir (list_pkgsrc_pkgdirs($pkgsrcdir, $cat)) { | | 1155 | foreach my $pkgdir (list_pkgsrc_pkgdirs($pkgsrcdir, $cat)) { |
1156 | ++$numpkg; | | 1156 | ++$numpkg; |
1157 | load_distinfo($pkgsrcdir, $cat, $pkgdir, | | 1157 | load_distinfo($pkgsrcdir, $cat, $pkgdir, |
1158 | \%distfiles, \@distwarn); | | 1158 | \%distfiles, \@distwarn); |
1159 | } | | 1159 | } |
1160 | verbose('.'); | | 1160 | verbose('.'); |
1161 | } | | 1161 | } |
1162 | verbose(" ($numpkg packages)\n"); | | 1162 | verbose(" ($numpkg packages)\n"); |
1163 | | | 1163 | |
1164 | # check each file in $pkgdistdir | | 1164 | # check each file in $pkgdistdir |
1165 | find({ wanted => sub { | | 1165 | find({ wanted => sub { |
1166 | my ($dist); | | 1166 | my ($dist); |
1167 | if (-f $File::Find::name) { | | 1167 | if (-f $File::Find::name) { |
1168 | my $distn = $File::Find::name; | | 1168 | my $distn = $File::Find::name; |
1169 | $distn =~ s/$pkgdistdir\/?//g; | | 1169 | $distn =~ s/$pkgdistdir\/?//g; |
1170 | #pkg/47516 ignore cvs dirs | | 1170 | #pkg/47516 ignore cvs dirs |
1171 | return if $distn =~ m/^\.cvsignore/; | | 1171 | return if $distn =~ m/^\.cvsignore/; |
1172 | return if $distn =~ m/^CVS\//; | | 1172 | return if $distn =~ m/^CVS\//; |
1173 | if (!defined($dist = $distfiles{$distn})) { | | 1173 | if (!defined($dist = $distfiles{$distn})) { |
1174 | $unref_distfiles{$distn} = 1; | | 1174 | $unref_distfiles{$distn} = 1; |
1175 | } elsif ($dist->{sum} ne 'IGNORE') { | | 1175 | } elsif ($dist->{sum} ne 'IGNORE') { |
1176 | push @{$sumfiles{ $dist->{sumtype} }}, $distn; | | 1176 | push @{$sumfiles{ $dist->{sumtype} }}, $distn; |
1177 | } | | 1177 | } |
1178 | } | | 1178 | } |
1179 | } }, | | 1179 | } }, |
1180 | ($pkgdistdir)); | | 1180 | ($pkgdistdir)); |
1181 | | | 1181 | |
1182 | if ($check_unref && %unref_distfiles) { | | 1182 | if ($check_unref && %unref_distfiles) { |
1183 | verbose(scalar(keys %unref_distfiles), | | 1183 | verbose(scalar(keys %unref_distfiles), |
1184 | " unreferenced file(s) in '$pkgdistdir':\n"); | | 1184 | " unreferenced file(s) in '$pkgdistdir':\n"); |
1185 | print join("\n", sort keys %unref_distfiles), "\n"; | | 1185 | print join("\n", sort keys %unref_distfiles), "\n"; |
1186 | } | | 1186 | } |
1187 | | | 1187 | |
1188 | if ($check_distinfo) { | | 1188 | if ($check_distinfo) { |
1189 | if (@distwarn) { | | 1189 | if (@distwarn) { |
1190 | verbose(@distwarn); | | 1190 | verbose(@distwarn); |
1191 | } | | 1191 | } |
1192 | | | 1192 | |
1193 | verbose("checksum mismatches\n"); | | 1193 | verbose("checksum mismatches\n"); |
| | | 1194 | my $prev_cwd = getcwd() or die; |
1194 | chdir_or_fail($pkgdistdir); | | 1195 | chdir_or_fail($pkgdistdir); |
1195 | foreach my $sum (keys %sumfiles) { | | 1196 | foreach my $sum (keys %sumfiles) { |
1196 | if ($sum eq 'Size') { | | 1197 | if ($sum eq 'Size') { |
1197 | foreach my $file (@{$sumfiles{$sum}}) { | | 1198 | foreach my $file (@{$sumfiles{$sum}}) { |
1198 | if (!-f $file || -S $file != $distfiles{$file}{sum}) { | | 1199 | if (!-f $file || -S $file != $distfiles{$file}{sum}) { |
1199 | print $file, " (Size)\n"; | | 1200 | print $file, " (Size)\n"; |
1200 | $unref_distfiles{$file} = 1; | | 1201 | $unref_distfiles{$file} = 1; |
1201 | } | | 1202 | } |
1202 | } | | 1203 | } |
1203 | next; | | 1204 | next; |
1204 | } | | 1205 | } |
1205 | | | 1206 | |
1206 | my $pid = open3(my $in, my $out, undef, 'xargs', 'digest', $sum); | | 1207 | my $pid = open3(my $in, my $out, undef, 'xargs', 'digest', $sum); |
1207 | defined $pid || fail 'fork'; | | 1208 | defined $pid || fail 'fork'; |
1208 | my $pid2 = fork(); | | 1209 | my $pid2 = fork(); |
1209 | defined $pid2 || fail 'fork'; | | 1210 | defined $pid2 || fail 'fork'; |
1210 | if ($pid2) { | | 1211 | if ($pid2) { |
1211 | close($in); | | 1212 | close($in); |
1212 | } else { | | 1213 | } else { |
1213 | print $in "@{$sumfiles{$sum}}"; | | 1214 | print $in "@{$sumfiles{$sum}}"; |
1214 | exit 0; | | 1215 | exit 0; |
1215 | } | | 1216 | } |
1216 | while (<$out>) { | | 1217 | while (<$out>) { |
1217 | if (m/^$sum ?\(([^\)]+)\) = (\S+)/) { | | 1218 | if (m/^$sum ?\(([^\)]+)\) = (\S+)/) { |
1218 | if ($distfiles{$1}{sum} ne $2) { | | 1219 | if ($distfiles{$1}{sum} ne $2) { |
1219 | print $1, " ($sum)\n"; | | 1220 | print $1, " ($sum)\n"; |
1220 | $unref_distfiles{$1} = 1; | | 1221 | $unref_distfiles{$1} = 1; |
1221 | } | | 1222 | } |
1222 | } | | 1223 | } |
1223 | } | | 1224 | } |
1224 | close($out); | | 1225 | close($out); |
1225 | waitpid($pid, 0) || fail "xargs digest $sum"; | | 1226 | waitpid($pid, 0) || fail "xargs digest $sum"; |
1226 | waitpid($pid2, 0) || fail 'pipe write to xargs'; | | 1227 | waitpid($pid2, 0) || fail 'pipe write to xargs'; |
1227 | } | | 1228 | } |
1228 | chdir_or_fail('/'); # Do not want to stay in $pkgdistdir | | 1229 | chdir_or_fail($prev_cwd); |
1229 | } | | 1230 | } |
1230 | | | 1231 | |
1231 | sort keys %unref_distfiles; | | 1232 | sort keys %unref_distfiles; |
1232 | } | | 1233 | } |
1233 | | | 1234 | |
1234 | sub store_pkgdb_in_cache($db, $fname) { | | 1235 | sub store_pkgdb_in_cache($db, $fname) { |
1235 | open(STORE, '>', $fname) | | 1236 | open(STORE, '>', $fname) |
1236 | or die("Cannot save package data to $fname: $!\n"); | | 1237 | or die("Cannot save package data to $fname: $!\n"); |
1237 | foreach my $pkgver ($db->pkgvers_all) { | | 1238 | foreach my $pkgver ($db->pkgvers_all) { |
1238 | my $pkgbase = $pkgver->pkgbase; | | 1239 | my $pkgbase = $pkgver->pkgbase; |
1239 | my $pkgversion = $pkgver->pkgversion; | | 1240 | my $pkgversion = $pkgver->pkgversion; |
1240 | | | 1241 | |
1241 | $pkgbase =~ /^\S+$/ | | 1242 | $pkgbase =~ /^\S+$/ |
1242 | or die "cannot store package name '$pkgbase'\n"; | | 1243 | or die "cannot store package name '$pkgbase'\n"; |
1243 | $pkgversion =~ /^\S+$/ | | 1244 | $pkgversion =~ /^\S+$/ |
1244 | or die "cannot store package version '$pkgversion'\n"; | | 1245 | or die "cannot store package version '$pkgversion'\n"; |
1245 | print STORE "package\t$pkgbase\t$pkgversion\n"; | | 1246 | print STORE "package\t$pkgbase\t$pkgversion\n"; |
1246 | | | 1247 | |
1247 | foreach my $varname (sort $pkgver->vars) { | | 1248 | foreach my $varname (sort $pkgver->vars) { |
1248 | my $value = $pkgver->var($varname); | | 1249 | my $value = $pkgver->var($varname); |
1249 | $varname =~ /^\S+$/ | | 1250 | $varname =~ /^\S+$/ |
1250 | or die "cannot store variable name '$varname'\n"; | | 1251 | or die "cannot store variable name '$varname'\n"; |
1251 | $value =~ /^.*$/ | | 1252 | $value =~ /^.*$/ |
1252 | or die "cannot store variable value '$value'\n"; | | 1253 | or die "cannot store variable value '$value'\n"; |
1253 | print STORE "var\t$varname\t$value\n"; | | 1254 | print STORE "var\t$varname\t$value\n"; |
1254 | } | | 1255 | } |
1255 | } | | 1256 | } |
1256 | close(STORE) | | 1257 | close(STORE) |
1257 | or die("Cannot save package data to $fname: $!\n"); | | 1258 | or die("Cannot save package data to $fname: $!\n"); |
1258 | } | | 1259 | } |
1259 | | | 1260 | |
1260 | # Remember to update manual page when modifying option list | | 1261 | # Remember to update manual page when modifying option list |
1261 | # | | 1262 | # |
1262 | sub usage_and_exit($status) { | | 1263 | sub usage_and_exit($status) { |
1263 | print "Usage: lintpkgsrc [opts] [makefiles] | | 1264 | print "Usage: lintpkgsrc [opts] [makefiles] |
1264 | opts: | | 1265 | opts: |
1265 | -h : This help. [see lintpkgsrc(1) for more information] | | 1266 | -h : This help. [see lintpkgsrc(1) for more information] |
1266 | | | 1267 | |
1267 | Installed package options: Distfile options: | | 1268 | Installed package options: Distfile options: |
1268 | -i : Check version against pkgsrc -m : List distinfo mismatches | | 1269 | -i : Check version against pkgsrc -m : List distinfo mismatches |
1269 | -u : As -i + fetch dist (may change) -o : List obsolete (no distinfo) | | 1270 | -u : As -i + fetch dist (may change) -o : List obsolete (no distinfo) |
1270 | -y : Remove orphan distfiles | | 1271 | -y : Remove orphan distfiles |
1271 | -z : Remove installed distfiles | | 1272 | -z : Remove installed distfiles |
1272 | | | 1273 | |
1273 | Prebuilt package options: Makefile options: | | 1274 | Prebuilt package options: Makefile options: |
1274 | -p : List old/obsolete -B : List packages marked as 'BROKEN' | | 1275 | -p : List old/obsolete -B : List packages marked as 'BROKEN' |
1275 | -O : List OSVERSION_SPECIFIC -d : Check 'DEPENDS' up to date | | 1276 | -O : List OSVERSION_SPECIFIC -d : Check 'DEPENDS' up to date |
1276 | -R : List NO_BIN_ON_FTP/RESTRICTED -S : List packages not in 'SUBDIRS' | | 1277 | -R : List NO_BIN_ON_FTP/RESTRICTED -S : List packages not in 'SUBDIRS' |
1277 | | | 1278 | |
1278 | Misc: | | 1279 | Misc: |
1279 | -E file : Export the internal pkgsrc database to file | | 1280 | -E file : Export the internal pkgsrc database to file |
1280 | -I file : Import the internal pkgsrc database to file (for use with -i) | | 1281 | -I file : Import the internal pkgsrc database to file (for use with -i) |
1281 | -g file : Generate 'pkgname pkgdir pkgver' map in file | | 1282 | -g file : Generate 'pkgname pkgdir pkgver' map in file |
1282 | -r : Remove bad files (Without -m -o -p or -V implies all, can use -R) | | 1283 | -r : Remove bad files (Without -m -o -p or -V implies all, can use -R) |
1283 | | | 1284 | |
1284 | Modifiers: | | 1285 | Modifiers: |
1285 | -K path : Set PACKAGES basedir (default PKGSRCDIR/packages) | | 1286 | -K path : Set PACKAGES basedir (default PKGSRCDIR/packages) |
1286 | -M path : Set DISTDIR (default PKGSRCDIR/distfiles) | | 1287 | -M path : Set DISTDIR (default PKGSRCDIR/distfiles) |
1287 | -P path : Set PKGSRCDIR (default $conf_pkgsrcdir) | | 1288 | -P path : Set PKGSRCDIR (default $conf_pkgsrcdir) |
1288 | -D : Debug makefile and glob parsing | | 1289 | -D : Debug makefile and glob parsing |
1289 | -L : List each Makefile when scanned | | 1290 | -L : List each Makefile when scanned |
1290 | "; | | 1291 | "; |
1291 | exit $status; | | 1292 | exit $status; |
1292 | } | | 1293 | } |
1293 | | | 1294 | |
1294 | # Could speed up by building a cache of package names to paths, then processing | | 1295 | # Could speed up by building a cache of package names to paths, then processing |
1295 | # each package name once against the tests. | | 1296 | # each package name once against the tests. |
1296 | sub check_prebuilt_packages() { | | 1297 | sub check_prebuilt_packages() { |
1297 | | | 1298 | |
1298 | if ($_ eq 'distfiles' || $_ eq 'pkgsrc') { | | 1299 | if ($_ eq 'distfiles' || $_ eq 'pkgsrc') { |
1299 | # Skip these subdirs if present | | 1300 | # Skip these subdirs if present |
1300 | $File::Find::prune = 1; | | 1301 | $File::Find::prune = 1; |
1301 | | | 1302 | |
1302 | } elsif (/(.+)-(\d.*)\.t[bg]z$/) { | | 1303 | } elsif (/(.+)-(\d.*)\.t[bg]z$/) { |
1303 | my ($pkg, $ver) = ($1, $2); | | 1304 | my ($pkg, $ver) = ($1, $2); |
1304 | | | 1305 | |
1305 | $pkg = canonicalize_pkgname($pkg); | | 1306 | $pkg = canonicalize_pkgname($pkg); |
1306 | | | 1307 | |
1307 | my ($pkgs); | | 1308 | my ($pkgs); |
1308 | if ($pkgs = $pkgdb->pkgs($pkg)) { | | 1309 | if ($pkgs = $pkgdb->pkgs($pkg)) { |
1309 | my ($pkgver) = $pkgs->pkgver($ver); | | 1310 | my ($pkgver) = $pkgs->pkgver($ver); |
1310 | | | 1311 | |
1311 | if (!defined $pkgver) { | | 1312 | if (!defined $pkgver) { |
1312 | if ($opt{p}) { | | 1313 | if ($opt{p}) { |
1313 | print "$File::Find::dir/$_\n"; | | 1314 | print "$File::Find::dir/$_\n"; |
1314 | push @matched_prebuiltpackages, "$File::Find::dir/$_"; | | 1315 | push @matched_prebuiltpackages, "$File::Find::dir/$_"; |
1315 | } | | 1316 | } |
1316 | | | 1317 | |
1317 | # Pick probably the last version | | 1318 | # Pick probably the last version |
1318 | $pkgver = ($pkgs->pkgvers_all)[0]; | | 1319 | $pkgver = ($pkgs->pkgvers_all)[0]; |
1319 | } | | 1320 | } |
1320 | | | 1321 | |
1321 | if ($opt{R} && $pkgver->var('RESTRICTED')) { | | 1322 | if ($opt{R} && $pkgver->var('RESTRICTED')) { |
1322 | print "$File::Find::dir/$_\n"; | | 1323 | print "$File::Find::dir/$_\n"; |
1323 | push @matched_prebuiltpackages, "$File::Find::dir/$_"; | | 1324 | push @matched_prebuiltpackages, "$File::Find::dir/$_"; |
1324 | } | | 1325 | } |
1325 | | | 1326 | |
1326 | if ($opt{O} && $pkgver->var('OSVERSION_SPECIFIC')) { | | 1327 | if ($opt{O} && $pkgver->var('OSVERSION_SPECIFIC')) { |
1327 | print "$File::Find::dir/$_\n"; | | 1328 | print "$File::Find::dir/$_\n"; |
1328 | push @matched_prebuiltpackages, "$File::Find::dir/$_"; | | 1329 | push @matched_prebuiltpackages, "$File::Find::dir/$_"; |
1329 | } | | 1330 | } |
1330 | } | | 1331 | } |
1331 | | | 1332 | |
1332 | } elsif (-d $_) { | | 1333 | } elsif (-d $_) { |
1333 | if ($prebuilt_pkgdir_cache{"$File::Find::dir/$_"}) { | | 1334 | if ($prebuilt_pkgdir_cache{"$File::Find::dir/$_"}) { |
1334 | $File::Find::prune = 1; | | 1335 | $File::Find::prune = 1; |
1335 | return; | | 1336 | return; |
1336 | } | | 1337 | } |
1337 | | | 1338 | |
1338 | $prebuilt_pkgdir_cache{"$File::Find::dir/$_"} = 1; | | 1339 | $prebuilt_pkgdir_cache{"$File::Find::dir/$_"} = 1; |
1339 | if (-l $_) { | | 1340 | if (-l $_) { |
1340 | my ($dest) = readlink($_); | | 1341 | my ($dest) = readlink($_); |
1341 | | | 1342 | |
1342 | if (substr($dest, 0, 1) ne '/') { | | 1343 | if (substr($dest, 0, 1) ne '/') { |
1343 | $dest = "$File::Find::dir/$dest"; | | 1344 | $dest = "$File::Find::dir/$dest"; |
1344 | } | | 1345 | } |
1345 | if (!$prebuilt_pkgdir_cache{$dest}) { | | 1346 | if (!$prebuilt_pkgdir_cache{$dest}) { |
1346 | push @prebuilt_pkgdirs, $dest; | | 1347 | push @prebuilt_pkgdirs, $dest; |
1347 | } | | 1348 | } |
1348 | } | | 1349 | } |
1349 | } | | 1350 | } |
1350 | } | | 1351 | } |
1351 | | | 1352 | |
1352 | sub debug_parse_makefiles(@args) { | | 1353 | sub debug_parse_makefiles(@args) { |
1353 | foreach my $file (@args) { | | 1354 | foreach my $file (@args) { |
1354 | -d $file and $file .= '/Makefile'; | | 1355 | -d $file and $file .= '/Makefile'; |
1355 | -f $file or fail("No such file: $file"); | | 1356 | -f $file or fail("No such file: $file"); |
1356 | | | 1357 | |
1357 | my ($pkgname, $vars) = parse_makefile_pkgsrc($file); | | 1358 | my ($pkgname, $vars) = parse_makefile_pkgsrc($file); |
1358 | $pkgname ||= 'uNDEFINEd'; | | 1359 | $pkgname ||= 'uNDEFINEd'; |
1359 | | | 1360 | |
1360 | print "$file -> $pkgname\n"; | | 1361 | print "$file -> $pkgname\n"; |
1361 | foreach my $varname (sort keys %$vars) { | | 1362 | foreach my $varname (sort keys %$vars) { |
1362 | print "\t$varname = $vars->{$varname}\n"; | | 1363 | print "\t$varname = $vars->{$varname}\n"; |
1363 | } | | 1364 | } |
1364 | | | 1365 | |
1365 | #if ($opt{d}) { | | 1366 | #if ($opt{d}) { |
1366 | # pkgsrc_check_depends(); | | 1367 | # pkgsrc_check_depends(); |
1367 | #} | | 1368 | #} |
1368 | } | | 1369 | } |
1369 | } | | 1370 | } |
1370 | | | 1371 | |
1371 | sub check_distfiles($pkgsrcdir, $pkgdistdir) { | | 1372 | sub check_distfiles($pkgsrcdir, $pkgdistdir) { |
1372 | my @unref_distfiles = scan_pkgsrc_distfiles_vs_distinfo( | | 1373 | my @unref_distfiles = scan_pkgsrc_distfiles_vs_distinfo( |
1373 | $pkgsrcdir, $pkgdistdir, $opt{o}, $opt{m}); | | 1374 | $pkgsrcdir, $pkgdistdir, $opt{o}, $opt{m}); |
1374 | | | 1375 | |
1375 | return unless $opt{r}; | | 1376 | return unless $opt{r}; |
1376 | verbose("Unlinking unreferenced distfiles\n"); | | 1377 | verbose("Unlinking unreferenced distfiles\n"); |
1377 | foreach my $distfile (@unref_distfiles) { | | 1378 | foreach my $distfile (@unref_distfiles) { |
1378 | unlink("$pkgdistdir/$distfile"); | | 1379 | unlink("$pkgdistdir/$distfile"); |
1379 | } | | 1380 | } |
1380 | } | | 1381 | } |
1381 | | | 1382 | |
1382 | sub remove_distfiles($pkgsrcdir, $pkgdistdir) { | | 1383 | sub remove_distfiles($pkgsrcdir, $pkgdistdir) { |
1383 | my @pkgs = list_installed_packages(); | | 1384 | my @pkgs = list_installed_packages(); |
1384 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1385 | scan_pkgsrc_makefiles($pkgsrcdir); |
1385 | | | 1386 | |
1386 | # list the installed packages and the directory they live in | | 1387 | # list the installed packages and the directory they live in |
1387 | my @installed; | | 1388 | my @installed; |
1388 | foreach my $pkgname (sort @pkgs) { | | 1389 | foreach my $pkgname (sort @pkgs) { |
1389 | if ($pkgname =~ /^([^*?[]+)-([\d*?[].*)/) { | | 1390 | if ($pkgname =~ /^([^*?[]+)-([\d*?[].*)/) { |
1390 | foreach my $pkgver ($pkgdb->pkgvers_by_pkgbase($1)) { | | 1391 | foreach my $pkgver ($pkgdb->pkgvers_by_pkgbase($1)) { |
1391 | next if $pkgver->var('dir') =~ /-current/; | | 1392 | next if $pkgver->var('dir') =~ /-current/; |
1392 | push @installed, $pkgver; | | 1393 | push @installed, $pkgver; |
1393 | last; | | 1394 | last; |
1394 | } | | 1395 | } |
1395 | } | | 1396 | } |
1396 | } | | 1397 | } |
1397 | | | 1398 | |
1398 | # distfiles belonging to the currently installed packages | | 1399 | # distfiles belonging to the currently installed packages |
1399 | my (%distfiles, @pkgdistfiles); | | 1400 | my (%distfiles, @pkgdistfiles); |
1400 | foreach my $pkgver (sort @installed) { | | 1401 | foreach my $pkgver (sort @installed) { |
1401 | my $pkgpath = $pkgver->var('dir'); | | 1402 | my $pkgpath = $pkgver->var('dir'); |
1402 | next unless open(DISTINFO, "$pkgsrcdir/$pkgpath/distinfo"); | | 1403 | next unless open(DISTINFO, "$pkgsrcdir/$pkgpath/distinfo"); |
1403 | while (<DISTINFO>) { | | 1404 | while (<DISTINFO>) { |
1404 | next unless m/^(\w+) ?\(([^\)]+)\) = (\S+)/; | | 1405 | next unless m/^(\w+) ?\(([^\)]+)\) = (\S+)/; |
1405 | my $dn = $2; | | 1406 | my $dn = $2; |
1406 | next if $dn =~ /^patch-[\w.+\-]+$/; | | 1407 | next if $dn =~ /^patch-[\w.+\-]+$/; |
1407 | # Strip leading ./ which sometimes gets added | | 1408 | # Strip leading ./ which sometimes gets added |
1408 | # because of DISTSUBDIR=. | | 1409 | # because of DISTSUBDIR=. |
1409 | $dn =~ s/^(\.\/)*//; | | 1410 | $dn =~ s/^(\.\/)*//; |
1410 | if (!defined $distfiles{$dn}) { | | 1411 | if (!defined $distfiles{$dn}) { |
1411 | $distfiles{$dn}{name} = $dn; | | 1412 | $distfiles{$dn}{name} = $dn; |
1412 | push @pkgdistfiles, $dn; | | 1413 | push @pkgdistfiles, $dn; |
1413 | } | | 1414 | } |
1414 | } | | 1415 | } |
1415 | close(DISTINFO); | | 1416 | close(DISTINFO); |
1416 | } | | 1417 | } |
1417 | | | 1418 | |
1418 | # distfiles downloaded on the current system | | 1419 | # distfiles downloaded on the current system |
1419 | my @tmpdistfiles = listdir("$pkgdistdir", undef); | | 1420 | my @tmpdistfiles = listdir("$pkgdistdir", undef); |
1420 | my @dldistfiles = grep { $_ ne 'pkg-vulnerabilities' } @tmpdistfiles; | | 1421 | my @dldistfiles = grep { $_ ne 'pkg-vulnerabilities' } @tmpdistfiles; |
1421 | | | 1422 | |
1422 | # sort the two arrays to make searching a bit faster | | 1423 | # sort the two arrays to make searching a bit faster |
1423 | @dldistfiles = sort { $a cmp $b } @dldistfiles; | | 1424 | @dldistfiles = sort { $a cmp $b } @dldistfiles; |
1424 | @pkgdistfiles = sort { $a cmp $b } @pkgdistfiles; | | 1425 | @pkgdistfiles = sort { $a cmp $b } @pkgdistfiles; |
1425 | | | 1426 | |
1426 | if ($opt{y}) { | | 1427 | if ($opt{y}) { |
1427 | # looking for files that are downloaded on the current system | | 1428 | # looking for files that are downloaded on the current system |
1428 | # but do not belong to any currently installed package i.e. orphaned | | 1429 | # but do not belong to any currently installed package i.e. orphaned |
1429 | my $found = 0; | | 1430 | my $found = 0; |
1430 | my @orphan; | | 1431 | my @orphan; |
1431 | foreach my $dldf (@dldistfiles) { | | 1432 | foreach my $dldf (@dldistfiles) { |
1432 | foreach my $pkgdf (@pkgdistfiles) { | | 1433 | foreach my $pkgdf (@pkgdistfiles) { |
1433 | if ($dldf eq $pkgdf) { | | 1434 | if ($dldf eq $pkgdf) { |
1434 | $found = 1; | | 1435 | $found = 1; |
1435 | } | | 1436 | } |
1436 | } | | 1437 | } |
1437 | if ($found != 1) { | | 1438 | if ($found != 1) { |
1438 | push @orphan, $dldf; | | 1439 | push @orphan, $dldf; |
1439 | print "Orphaned file: $dldf\n"; | | 1440 | print "Orphaned file: $dldf\n"; |
1440 | } | | 1441 | } |
1441 | $found = 0; | | 1442 | $found = 0; |
1442 | } | | 1443 | } |
1443 | | | 1444 | |
1444 | if ($opt{r}) { | | 1445 | if ($opt{r}) { |
1445 | chdir_or_fail("$pkgdistdir"); | | 1446 | chdir_or_fail("$pkgdistdir"); |
1446 | verbose("Unlinking 'orphaned' distfiles\n"); | | 1447 | verbose("Unlinking 'orphaned' distfiles\n"); |
1447 | foreach my $distfile (@orphan) { | | 1448 | foreach my $distfile (@orphan) { |
1448 | unlink($distfile) | | 1449 | unlink($distfile) |
1449 | } | | 1450 | } |
1450 | } | | 1451 | } |
1451 | } | | 1452 | } |
1452 | | | 1453 | |
1453 | if ($opt{z}) { | | 1454 | if ($opt{z}) { |
1454 | # looking for files that are downloaded on the current system | | 1455 | # looking for files that are downloaded on the current system |
1455 | # but belong to a currently installed package i.e. parented | | 1456 | # but belong to a currently installed package i.e. parented |
1456 | my $found = 0; | | 1457 | my $found = 0; |
1457 | my @parent; | | 1458 | my @parent; |
1458 | foreach my $pkgdf (@pkgdistfiles) { | | 1459 | foreach my $pkgdf (@pkgdistfiles) { |
1459 | foreach my $dldf (@dldistfiles) { | | 1460 | foreach my $dldf (@dldistfiles) { |
1460 | if ($pkgdf eq $dldf) { | | 1461 | if ($pkgdf eq $dldf) { |
1461 | $found = 1; | | 1462 | $found = 1; |
1462 | } | | 1463 | } |
1463 | } | | 1464 | } |
1464 | if ($found == 1) { | | 1465 | if ($found == 1) { |
1465 | push @parent, $pkgdf; | | 1466 | push @parent, $pkgdf; |
1466 | print "Parented file: $pkgdf\n"; | | 1467 | print "Parented file: $pkgdf\n"; |
1467 | } | | 1468 | } |
1468 | $found = 0; | | 1469 | $found = 0; |
1469 | } | | 1470 | } |
1470 | | | 1471 | |
1471 | if ($opt{r}) { | | 1472 | if ($opt{r}) { |
1472 | chdir_or_fail("$pkgdistdir"); | | 1473 | chdir_or_fail("$pkgdistdir"); |
1473 | verbose("Unlinking 'parented' distfiles\n"); | | 1474 | verbose("Unlinking 'parented' distfiles\n"); |
1474 | foreach my $distfile (@parent) { | | 1475 | foreach my $distfile (@parent) { |
1475 | unlink($distfile); | | 1476 | unlink($distfile); |
1476 | } | | 1477 | } |
1477 | } | | 1478 | } |
1478 | } | | 1479 | } |
1479 | } | | 1480 | } |
1480 | | | 1481 | |
1481 | sub list_broken_packages($pkgsrcdir) { | | 1482 | sub list_broken_packages($pkgsrcdir) { |
1482 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1483 | scan_pkgsrc_makefiles($pkgsrcdir); |
1483 | foreach my $pkgver ($pkgdb->pkgvers_all) { | | 1484 | foreach my $pkgver ($pkgdb->pkgvers_all) { |
1484 | my $broken = $pkgver->var('BROKEN'); | | 1485 | my $broken = $pkgver->var('BROKEN'); |
1485 | next unless $broken; | | 1486 | next unless $broken; |
1486 | print $pkgver->pkgname . ": $broken\n"; | | 1487 | print $pkgver->pkgname . ": $broken\n"; |
1487 | } | | 1488 | } |
1488 | } | | 1489 | } |
1489 | | | 1490 | |
1490 | # List obsolete or NO_BIN_ON_FTP/RESTRICTED prebuilt packages | | 1491 | # List obsolete or NO_BIN_ON_FTP/RESTRICTED prebuilt packages |
1491 | # | | 1492 | # |
1492 | sub list_prebuilt_packages($pkgsrcdir) { | | 1493 | sub list_prebuilt_packages($pkgsrcdir) { |
1493 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1494 | scan_pkgsrc_makefiles($pkgsrcdir); |
1494 | | | 1495 | |
1495 | @prebuilt_pkgdirs = ($default_vars->{PACKAGES}); | | 1496 | @prebuilt_pkgdirs = ($default_vars->{PACKAGES}); |
1496 | %prebuilt_pkgdir_cache = (); | | 1497 | %prebuilt_pkgdir_cache = (); |
1497 | | | 1498 | |
1498 | while (@prebuilt_pkgdirs) { | | 1499 | while (@prebuilt_pkgdirs) { |
1499 | find(\&check_prebuilt_packages, shift @prebuilt_pkgdirs); | | 1500 | find(\&check_prebuilt_packages, shift @prebuilt_pkgdirs); |
1500 | } | | 1501 | } |
1501 | | | 1502 | |
1502 | if ($opt{r}) { | | 1503 | if ($opt{r}) { |
1503 | verbose("Unlinking listed prebuilt packages\n"); | | 1504 | verbose("Unlinking listed prebuilt packages\n"); |
1504 | foreach my $pkgfile (@matched_prebuiltpackages) { | | 1505 | foreach my $pkgfile (@matched_prebuiltpackages) { |
1505 | unlink($pkgfile); | | 1506 | unlink($pkgfile); |
1506 | } | | 1507 | } |
1507 | } | | 1508 | } |
1508 | } | | 1509 | } |
1509 | | | 1510 | |
1510 | sub list_packages_not_in_SUBDIR($pkgsrcdir) { | | 1511 | sub list_packages_not_in_SUBDIR($pkgsrcdir) { |
1511 | my (%in_subdir); | | 1512 | my (%in_subdir); |
1512 | foreach my $cat (list_pkgsrc_categories($pkgsrcdir)) { | | 1513 | foreach my $cat (list_pkgsrc_categories($pkgsrcdir)) { |
1513 | my $vars = parse_makefile_vars("$pkgsrcdir/$cat/Makefile", undef); | | 1514 | my $vars = parse_makefile_vars("$pkgsrcdir/$cat/Makefile", undef); |
1514 | | | 1515 | |
1515 | if (!$vars->{SUBDIR}) { | | 1516 | if (!$vars->{SUBDIR}) { |
1516 | print "Warning - no SUBDIR for $cat\n"; | | 1517 | print "Warning - no SUBDIR for $cat\n"; |
1517 | next; | | 1518 | next; |
1518 | } | | 1519 | } |
1519 | foreach my $pkgdir (split(/\s+/, $vars->{SUBDIR})) { | | 1520 | foreach my $pkgdir (split(/\s+/, $vars->{SUBDIR})) { |
1520 | $in_subdir{"$cat/$pkgdir"} = 1; | | 1521 | $in_subdir{"$cat/$pkgdir"} = 1; |
1521 | } | | 1522 | } |
1522 | } | | 1523 | } |
1523 | | | 1524 | |
1524 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1525 | scan_pkgsrc_makefiles($pkgsrcdir); |
1525 | foreach my $pkgver ($pkgdb->pkgvers_all) { | | 1526 | foreach my $pkgver ($pkgdb->pkgvers_all) { |
1526 | my $pkgpath = $pkgver->var('dir'); | | 1527 | my $pkgpath = $pkgver->var('dir'); |
1527 | if (!defined $in_subdir{$pkgpath}) { | | 1528 | if (!defined $in_subdir{$pkgpath}) { |
1528 | print "$pkgpath: Not in SUBDIR\n"; | | 1529 | print "$pkgpath: Not in SUBDIR\n"; |
1529 | } | | 1530 | } |
1530 | } | | 1531 | } |
1531 | } | | 1532 | } |
1532 | | | 1533 | |
1533 | sub generate_map_file($pkgsrcdir, $fname) { | | 1534 | sub generate_map_file($pkgsrcdir, $fname) { |
1534 | my $tmpfile = "$fname.tmp.$$"; | | 1535 | my $tmpfile = "$fname.tmp.$$"; |
1535 | | | 1536 | |
1536 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1537 | scan_pkgsrc_makefiles($pkgsrcdir); |
1537 | open(TABLE, '>', $tmpfile) or fail("Cannot write '$tmpfile': $!"); | | 1538 | open(TABLE, '>', $tmpfile) or fail("Cannot write '$tmpfile': $!"); |
1538 | foreach my $pkgver ($pkgdb->pkgvers_all) { | | 1539 | foreach my $pkgver ($pkgdb->pkgvers_all) { |
1539 | print TABLE $pkgver->pkgbase . "\t" | | 1540 | print TABLE $pkgver->pkgbase . "\t" |
1540 | . $pkgver->var('dir') . "\t" | | 1541 | . $pkgver->var('dir') . "\t" |
1541 | . $pkgver->pkgversion . "\n"; | | 1542 | . $pkgver->pkgversion . "\n"; |
1542 | } | | 1543 | } |
1543 | close(TABLE) or fail("close('$tmpfile'): $!"); | | 1544 | close(TABLE) or fail("close('$tmpfile'): $!"); |
1544 | rename($tmpfile, $fname) | | 1545 | rename($tmpfile, $fname) |
1545 | or fail("rename('$tmpfile', '$fname'): $!"); | | 1546 | or fail("rename('$tmpfile', '$fname'): $!"); |
1546 | } | | 1547 | } |
1547 | | | 1548 | |
1548 | sub check_outdated_installed_packages($pkgsrcdir) { | | 1549 | sub check_outdated_installed_packages($pkgsrcdir) { |
1549 | my @pkgs = list_installed_packages(); | | 1550 | my @pkgs = list_installed_packages(); |
1550 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1551 | scan_pkgsrc_makefiles($pkgsrcdir); |
1551 | | | 1552 | |
1552 | my @update; | | 1553 | my @update; |
1553 | foreach my $pkgname (sort @pkgs) { | | 1554 | foreach my $pkgname (sort @pkgs) { |
1554 | next unless $_ = invalid_version($pkgname); | | 1555 | next unless $_ = invalid_version($pkgname); |
1555 | | | 1556 | |
1556 | print $_; | | 1557 | print $_; |
1557 | next unless $pkgname =~ /^([^*?[]+)-([\d*?[].*)/; | | 1558 | next unless $pkgname =~ /^([^*?[]+)-([\d*?[].*)/; |
1558 | | | 1559 | |
1559 | foreach my $pkgver ($pkgdb->pkgvers_by_pkgbase($1)) { | | 1560 | foreach my $pkgver ($pkgdb->pkgvers_by_pkgbase($1)) { |
1560 | next if $pkgver->var('dir') =~ /-current/; | | 1561 | next if $pkgver->var('dir') =~ /-current/; |
1561 | push @update, $pkgver; | | 1562 | push @update, $pkgver; |
1562 | last; | | 1563 | last; |
1563 | } | | 1564 | } |
1564 | } | | 1565 | } |
1565 | | | 1566 | |
1566 | return unless $opt{u}; | | 1567 | return unless $opt{u}; |
1567 | | | 1568 | |
1568 | print "\nREQUIRED details for packages that could be updated:\n"; | | 1569 | print "\nREQUIRED details for packages that could be updated:\n"; |
1569 | | | 1570 | |
1570 | foreach my $pkgver (@update) { | | 1571 | foreach my $pkgver (@update) { |
1571 | my $pkgbase = $pkgver->pkgbase; | | 1572 | my $pkgbase = $pkgver->pkgbase; |
1572 | print "$pkgbase:"; | | 1573 | print "$pkgbase:"; |
1573 | if (open(PKGINFO, "$conf_pkg_info -R $pkgbase |")) { | | 1574 | if (open(PKGINFO, "$conf_pkg_info -R $pkgbase |")) { |
1574 | my ($list); | | 1575 | my ($list); |
1575 | | | 1576 | |
1576 | while (<PKGINFO>) { | | 1577 | while (<PKGINFO>) { |
1577 | if (/Required by:/) { | | 1578 | if (/Required by:/) { |
1578 | $list = 1; | | 1579 | $list = 1; |
1579 | } elsif ($list) { | | 1580 | } elsif ($list) { |
1580 | chomp; | | 1581 | chomp; |
1581 | s/-\d.*//; | | 1582 | s/-\d.*//; |
1582 | print " $_"; | | 1583 | print " $_"; |
1583 | } | | 1584 | } |
1584 | } | | 1585 | } |
1585 | close(PKGINFO); | | 1586 | close(PKGINFO); |
1586 | } | | 1587 | } |
1587 | print "\n"; | | 1588 | print "\n"; |
1588 | } | | 1589 | } |
1589 | | | 1590 | |
1590 | print "\nRunning '$conf_make fetch-list | sh' for each package:\n"; | | 1591 | print "\nRunning '$conf_make fetch-list | sh' for each package:\n"; |
1591 | foreach my $pkgver (@update) { | | 1592 | foreach my $pkgver (@update) { |
1592 | my $pkgpath = $pkgver->var('dir'); | | 1593 | my $pkgpath = $pkgver->var('dir'); |
1593 | defined $pkgpath | | 1594 | defined $pkgpath |
1594 | or fail('Cannot determine ' . $pkgver->pkgbase . ' directory'); | | 1595 | or fail('Cannot determine ' . $pkgver->pkgbase . ' directory'); |
1595 | | | 1596 | |
1596 | print "$pkgsrcdir/$pkgpath\n"; | | 1597 | print "$pkgsrcdir/$pkgpath\n"; |
1597 | chdir_or_fail("$pkgsrcdir/$pkgpath"); | | 1598 | chdir_or_fail("$pkgsrcdir/$pkgpath"); |
1598 | system("$conf_make fetch-list | sh"); | | 1599 | system("$conf_make fetch-list | sh"); |
1599 | } | | 1600 | } |
1600 | } | | 1601 | } |
1601 | | | 1602 | |
1602 | sub main() { | | 1603 | sub main() { |
1603 | | | 1604 | |
1604 | $ENV{PATH} .= | | 1605 | $ENV{PATH} .= |
1605 | ":/bin:/usr/bin:/sbin:/usr/sbin:$conf_prefix/sbin:$conf_prefix/bin"; | | 1606 | ":/bin:/usr/bin:/sbin:/usr/sbin:$conf_prefix/sbin:$conf_prefix/bin"; |
1606 | | | 1607 | |
1607 | if ( | | 1608 | if ( |
1608 | !getopts('-BDE:I:K:LM:OP:RSVdg:himopruyz', \%opt) | | 1609 | !getopts('-BDE:I:K:LM:OP:RSVdg:himopruyz', \%opt) |
1609 | || $opt{h} | | 1610 | || $opt{h} |
1610 | || !grep(/[BDEORSdgimopruyz]/, keys %opt)) { | | 1611 | || !grep(/[BDEORSdgimopruyz]/, keys %opt)) { |
1611 | usage_and_exit($opt{h} ? 0 : 1); | | 1612 | usage_and_exit($opt{h} ? 0 : 1); |
1612 | } | | 1613 | } |
1613 | $| = 1; | | 1614 | $| = 1; |
1614 | | | 1615 | |
1615 | get_default_makefile_vars(); # $default_vars | | 1616 | get_default_makefile_vars(); # $default_vars |
1616 | | | 1617 | |
1617 | if ($opt{D} && @ARGV) { | | 1618 | if ($opt{D} && @ARGV) { |
1618 | debug_parse_makefiles(@ARGV); | | 1619 | debug_parse_makefiles(@ARGV); |
1619 | exit; | | 1620 | exit; |
1620 | } | | 1621 | } |
1621 | | | 1622 | |
1622 | my $pkgsrcdir = $default_vars->{PKGSRCDIR}; | | 1623 | my $pkgsrcdir = $default_vars->{PKGSRCDIR}; |
1623 | my $pkgdistdir = $default_vars->{DISTDIR}; | | 1624 | my $pkgdistdir = $default_vars->{DISTDIR}; |
1624 | | | 1625 | |
1625 | if ($opt{r} && !$opt{o} && !$opt{m} && !$opt{p}) { | | 1626 | if ($opt{r} && !$opt{o} && !$opt{m} && !$opt{p}) { |
1626 | $opt{o} = $opt{m} = $opt{p} = 1; | | 1627 | $opt{o} = $opt{m} = $opt{p} = 1; |
1627 | } | | 1628 | } |
1628 | if ($opt{o} || $opt{m}) { | | 1629 | if ($opt{o} || $opt{m}) { |
1629 | check_distfiles($pkgsrcdir, $pkgdistdir); | | 1630 | check_distfiles($pkgsrcdir, $pkgdistdir); |
1630 | } | | 1631 | } |
1631 | | | 1632 | |
1632 | # Remove all distfiles that are / are not part of an installed package | | 1633 | # Remove all distfiles that are / are not part of an installed package |
1633 | if ($opt{y} || $opt{z}) { | | 1634 | if ($opt{y} || $opt{z}) { |
1634 | remove_distfiles($pkgsrcdir, $pkgdistdir); | | 1635 | remove_distfiles($pkgsrcdir, $pkgdistdir); |
1635 | } | | 1636 | } |
1636 | | | 1637 | |
1637 | if ($opt{B}) { | | 1638 | if ($opt{B}) { |
1638 | list_broken_packages($pkgsrcdir); | | 1639 | list_broken_packages($pkgsrcdir); |
1639 | } | | 1640 | } |
1640 | | | 1641 | |
1641 | if ($opt{p} || $opt{O} || $opt{R}) { | | 1642 | if ($opt{p} || $opt{O} || $opt{R}) { |
1642 | list_prebuilt_packages($pkgsrcdir); | | 1643 | list_prebuilt_packages($pkgsrcdir); |
1643 | } | | 1644 | } |
1644 | | | 1645 | |
1645 | if ($opt{S}) { | | 1646 | if ($opt{S}) { |
1646 | list_packages_not_in_SUBDIR($pkgsrcdir); | | 1647 | list_packages_not_in_SUBDIR($pkgsrcdir); |
1647 | } | | 1648 | } |
1648 | | | 1649 | |
1649 | if ($opt{g}) { | | 1650 | if ($opt{g}) { |
1650 | generate_map_file($pkgsrcdir, $opt{g}); | | 1651 | generate_map_file($pkgsrcdir, $opt{g}); |
1651 | } | | 1652 | } |
1652 | | | 1653 | |
1653 | if ($opt{d}) { | | 1654 | if ($opt{d}) { |
1654 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1655 | scan_pkgsrc_makefiles($pkgsrcdir); |
1655 | pkgsrc_check_depends(); | | 1656 | pkgsrc_check_depends(); |
1656 | } | | 1657 | } |
1657 | | | 1658 | |
1658 | if ($opt{i} || $opt{u}) { | | 1659 | if ($opt{i} || $opt{u}) { |
1659 | check_outdated_installed_packages($pkgsrcdir); | | 1660 | check_outdated_installed_packages($pkgsrcdir); |
1660 | } | | 1661 | } |
1661 | | | 1662 | |
1662 | if ($opt{E}) { | | 1663 | if ($opt{E}) { |
1663 | scan_pkgsrc_makefiles($pkgsrcdir); | | 1664 | scan_pkgsrc_makefiles($pkgsrcdir); |
1664 | store_pkgdb_in_cache($pkgdb, $opt{E}); | | 1665 | store_pkgdb_in_cache($pkgdb, $opt{E}); |
1665 | } | | 1666 | } |
1666 | } | | 1667 | } |
1667 | | | 1668 | |
1668 | sub export_for_test() { | | 1669 | sub export_for_test() { |
1669 | ({ | | 1670 | ({ |
1670 | 'opt' => \%opt, | | 1671 | 'opt' => \%opt, |
1671 | 'default_vars' => $default_vars, | | 1672 | 'default_vars' => $default_vars, |
1672 | 'pkgdb' => $pkgdb, | | 1673 | 'pkgdb' => $pkgdb, |
1673 | }); | | 1674 | }); |
1674 | } | | 1675 | } |
1675 | | | 1676 | |
1676 | if (caller()) { | | 1677 | if (caller()) { |
1677 | # To allow easy testing of the code. | | 1678 | # To allow easy testing of the code. |
1678 | # TODO: reduce the use of global variables, or make them accessible | | 1679 | # TODO: reduce the use of global variables, or make them accessible |
1679 | # to the tests. | | 1680 | # to the tests. |
1680 | $default_vars = {}; | | 1681 | $default_vars = {}; |
1681 | } | | 1682 | } |
1682 | | | 1683 | |
1683 | main() unless caller(); | | 1684 | main() unless caller(); |