opkg: fix the -force_space option
[openwrt.git] / scripts / feeds
1 #!/usr/bin/perl
2 use Getopt::Std;
3 use FindBin;
4 use Cwd;
5 use lib "$FindBin::Bin";
6 use metadata;
7 use warnings;
8 use strict;
9 use Cwd 'abs_path';
10
11 chdir "$FindBin::Bin/..";
12 $ENV{TOPDIR}=getcwd();
13
14 my $mk=`which gmake`; # select the right 'make' program
15 chomp($mk); # trim trailing newline
16 $mk or $mk = "make"; # default to 'make'
17
18 # check version of make
19 my @mkver = split /\s+/, `$mk -v`, 4;
20 my $valid_mk = 1;
21 $mkver[0] =~ /^GNU/ or $valid_mk = 0;
22 $mkver[1] =~ /^Make/ or $valid_mk = 0;
23 $mkver[2] >= "3.81" or $valid_mk = 0;
24 $valid_mk or die "Unsupported version of make found: $mk\n";
25
26 my @feeds;
27 my %build_packages;
28 my %installed;
29
30 sub parse_config() {
31 my $line = 0;
32 my %name;
33
34 open FEEDS, "feeds.conf" or
35 open FEEDS, "feeds.conf.default" or
36 die "Unable to open feeds configuration";
37 while (<FEEDS>) {
38 chomp;
39 s/#.+$//;
40 next unless /\S/;
41 my @line = split /\s+/, $_, 3;
42 my @src;
43 $line++;
44
45 my $valid = 1;
46 $line[0] =~ /^src-\w+$/ or $valid = 0;
47 $line[1] =~ /^\w+$/ or $valid = 0;
48 @src = split /\s+/, $line[2];
49 $valid or die "Syntax error in feeds.conf, line: $line\n";
50
51 $name{$line[1]} and die "Duplicate feed name '$line[1]', line: $line\n";
52 $name{$line[1]} = 1;
53
54 push @feeds, [$line[0], $line[1], \@src];
55 }
56 close FEEDS;
57 }
58
59 sub update_location($$)
60 {
61 my $name = shift;
62 my $url = shift;
63 my $old_url;
64
65 -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
66
67 if( open LOC, "< ./feeds/$name.tmp/location" )
68 {
69 chomp($old_url = readline LOC);
70 close LOC;
71 }
72
73 if( !$old_url || $old_url ne $url )
74 {
75 if( open LOC, "> ./feeds/$name.tmp/location" )
76 {
77 print LOC $url, "\n";
78 close LOC;
79 }
80 return $old_url ? 1 : 0;
81 }
82
83 return 0;
84 }
85
86 sub update_index($)
87 {
88 my $name = shift;
89
90 -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
91 -d "./feeds/$name.tmp/info" or mkdir "./feeds/$name.tmp/info" or return 1;
92
93 system("$mk -s prepare-mk TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
94 system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"package\" SCAN_DEPS=\"$ENV{TOPDIR}/include/package*.mk\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
95 system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index");
96
97 return 0;
98 }
99
100 my %update_method = (
101 'src-svn' => {
102 'init' => "svn checkout '%s' '%s'",
103 'update' => "svn update",
104 'controldir' => ".svn"},
105 'src-cpy' => {
106 'init' => "cp -Rf '%s' '%s'",
107 'update' => ""},
108 'src-link' => {
109 'init' => "ln -s '%s' '%s'",
110 'update' => ""},
111 'src-git' => {
112 'init' => "git clone --depth 1 '%s' '%s'",
113 'update' => "git pull",
114 'controldir' => ".git"},
115 'src-bzr' => {
116 'init' => "bzr checkout --lightweight '%s' '%s'",
117 'update' => "bzr update",
118 'controldir' => ".bzr"},
119 'src-hg' => {
120 'init' => "hg clone '%s' '%s'",
121 'update' => "hg pull --update",
122 'controldir' => ".hg"}
123 );
124
125 # src-git: pull broken
126 # src-cpy: broken if `basename $src` != $name
127
128 sub update_feed_via($$$$) {
129 my $type = shift;
130 my $name = shift;
131 my $src = shift;
132 my $relocate = shift;
133
134 my $m = $update_method{$type};
135 my $localpath = "./feeds/$name";
136 my $safepath = $localpath;
137 $safepath =~ s/'/'\\''/;
138
139 if( $relocate || !$m->{'update'} || !-d "$localpath/$m->{'controldir'}" ) {
140 system("rm -rf '$safepath'");
141 system(sprintf($m->{'init'}, $src, $safepath)) == 0 or return 1;
142 } else {
143 system("cd '$safepath'; $m->{'update'}") == 0 or return 1;
144 }
145
146 return 0;
147 }
148
149 sub get_feed($) {
150 my $feed = shift;
151 my $file = "./feeds/$feed.index";
152
153 clear_packages();
154
155 -f $file or do {
156 print "Ignoring feed '$feed' - index missing\n";
157 return;
158 };
159 parse_package_metadata($file) or return;
160 return { %package };
161 }
162
163 sub get_installed() {
164 system("$mk -s prepare-tmpinfo");
165 clear_packages();
166 parse_package_metadata("./tmp/.packageinfo");
167 %installed = %package;
168 }
169
170 sub search_feed {
171 my $feed = shift;
172 my @substr = @_;
173 my $display;
174
175 return unless @substr > 0;
176 get_feed($feed);
177 foreach my $name (sort { lc($a) cmp lc($b) } keys %package) {
178 my $pkg = $package{$name};
179 my $substr;
180 my $pkgmatch = 1;
181
182 next if $pkg->{vdepends};
183 foreach my $substr (@substr) {
184 my $match;
185 foreach my $key (qw(name title description src)) {
186 $pkg->{$key} and $substr and $pkg->{$key} =~ m/$substr/i and $match = 1;
187 }
188 $match or undef $pkgmatch;
189 };
190 $pkgmatch and do {
191 $display or do {
192 print "Search results in feed '$feed':\n";
193 $display = 1;
194 };
195 printf "\%-25s\t\%s\n", $pkg->{name}, $pkg->{title};
196 };
197 }
198 return 0;
199 }
200
201 sub search {
202 my %opts;
203
204 getopt('r:', \%opts);
205 foreach my $feed (@feeds) {
206 search_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]);
207 }
208 }
209
210 sub list_feed {
211 my $feed = shift;
212
213 get_feed($feed);
214 foreach my $name (sort { lc($a) cmp lc($b) } keys %package) {
215 my $pkg = $package{$name};
216 next if $pkg->{vdepends};
217 if($pkg->{name}) {
218 printf "\%-32s\t\%s\n", $pkg->{name}, $pkg->{title};
219 }
220 }
221
222 return 0;
223 }
224
225 sub list {
226 my %opts;
227
228 getopts('r:sh', \%opts);
229 if ($opts{h}) {
230 usage();
231 return 0;
232 }
233 if ($opts{s}) {
234 foreach my $feed (@feeds) {
235 printf "\%-32s\tURL: %s\n", $feed->[1], join(", ", @{$feed->[2]});
236 }
237 return 0;
238 }
239 foreach my $feed (@feeds) {
240 list_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]);
241 }
242 return 0;
243 }
244
245 sub install_generic() {
246 my $feed = shift;
247 my $pkg = shift;
248 my $path = $pkg->{makefile};
249
250 if($path) {
251 $path =~ s/\/Makefile$//;
252
253 -d "./package/feeds" or mkdir "./package/feeds";
254 -d "./package/feeds/$feed->[1]" or mkdir "./package/feeds/$feed->[1]";
255 system("ln -sf ../../../$path ./package/feeds/$feed->[1]/");
256 } else {
257 warn "Package is not valid\n";
258 return 1;
259 }
260
261 return 0;
262 }
263
264 my %install_method = (
265 'src-svn' => \&install_generic,
266 'src-cpy' => \&install_generic,
267 'src-link' => \&install_generic,
268 'src-git' => \&install_generic,
269 'src-bzr' => \&install_generic,
270 'src-hg' => \&install_generic,
271 );
272
273 my %feed;
274
275 sub lookup_package($$) {
276 my $feed = shift;
277 my $package = shift;
278
279 foreach my $feed ($feed, @feeds) {
280 next unless $feed->[1];
281 next unless $feed{$feed->[1]};
282 $feed{$feed->[1]}->{$package} and return $feed;
283 }
284 return;
285 }
286
287 sub install_package {
288 my $feed = shift;
289 my $name = shift;
290 my $ret = 0;
291
292 $feed = lookup_package($feed, $name);
293 $feed or do {
294 $installed{$name} and return 0;
295 # TODO: check if it's already installed within ./package directory
296 $srcpackage{$name} or -d "./package/$name" or warn "WARNING: No feed for package '$name' found, maybe it's already part of the standard packages?\n";
297 return 0;
298 };
299
300 my $pkg = $feed{$feed->[1]}->{$name} or return 1;
301 $pkg->{name} or do {
302 $installed{$name} and return 0;
303 # TODO: check if this is an alias package, maybe it's known by another name
304 warn "WARNING: Package '$name' is not available in feed $feed->[1].\n";
305 return 0;
306 };
307 my $src = $pkg->{src};
308 my $type = $feed->[0];
309 $src or $src = $name;
310
311 # previously installed packages set the runtime package
312 # newly installed packages set the source package
313 $installed{$src} and return 0;
314
315 # check previously installed packages
316 $installed{$name} and return 0;
317 $installed{$src} = 1;
318 warn "Installing package '$src'\n";
319
320 $install_method{$type} or do {
321 warn "Unknown installation method: '$type'\n";
322 return 1;
323 };
324
325 &{$install_method{$type}}($feed, $pkg) == 0 or do {
326 warn "failed.\n";
327 return 1;
328 };
329
330 # install all dependencies
331 foreach my $vpkg (@{$srcpackage{$src}}, $pkg) {
332 foreach my $dep (@{$vpkg->{depends}}, @{$vpkg->{builddepends}}, @{$vpkg->{"builddepends/host"}}) {
333 next if $dep =~ /@/;
334 $dep =~ s/^\+//;
335 $dep =~ s/^.+://;
336 $dep =~ s/\/.+$//;
337 next unless $dep;
338 install_package($feed, $dep) == 0 or $ret = 1;
339 }
340 }
341
342 return $ret;
343 }
344
345 sub refresh_config {
346 my $default = shift;
347
348 # workaround for timestamp check
349 system("rm -f tmp/.packageinfo");
350
351 # refresh the config
352 if ($default) {
353 system("$mk oldconfig CONFDEFAULT=\"$default\" Config.in >/dev/null 2>/dev/null");
354 } else {
355 system("$mk defconfig Config.in >/dev/null 2>/dev/null");
356 }
357 }
358
359 sub install {
360 my $name;
361 my %opts;
362 my $feed;
363 my $ret = 0;
364
365 getopts('ap:d:h', \%opts);
366
367 if ($opts{h}) {
368 usage();
369 return 0;
370 }
371
372 get_installed();
373
374 foreach my $f (@feeds) {
375 # index all feeds
376 $feed{$f->[1]} = get_feed($f->[1]);
377
378 # look up the preferred feed
379 $opts{p} and $f->[1] eq $opts{p} and $feed = $f;
380 }
381
382 if($opts{a}) {
383 foreach my $f (@feeds) {
384 if (!defined($opts{p}) or $opts{p} eq $f->[1]) {
385 printf "Installing all packages from feed %s.\n", $f->[1];
386 get_feed($f->[1]);
387 foreach my $name (sort { lc($a) cmp lc($b) } keys %package) {
388 my $p = $package{$name};
389 next if $p->{vdepends};
390 if( $p->{name} ) {
391 install_package($feed, $p->{name}) == 0 or $ret = 1;
392 }
393 }
394 }
395 }
396 } else {
397 while ($name = shift @ARGV) {
398 install_package($feed, $name) == 0 or $ret = 1;
399 }
400 }
401
402 # workaround for timestamp check
403
404 # set the defaults
405 if ($opts{d} and $opts{d} =~ /^[ymn]$/) {
406 refresh_config($opts{d});
407 }
408
409 return $ret;
410 }
411
412 sub uninstall {
413 my %opts;
414 my $name;
415 my $uninstall;
416
417 getopts('ah', \%opts);
418
419 if ($opts{h}) {
420 usage();
421 return 0;
422 }
423
424 if ($opts{a}) {
425 system("rm -rvf ./package/feeds");
426 $uninstall = 1;
427 } else {
428 if($#ARGV == -1) {
429 warn "WARNING: no package to uninstall\n";
430 return 0;
431 }
432 get_installed();
433 while ($name = shift @ARGV) {
434 my $pkg = $installed{$name};
435 $pkg or do {
436 warn "WARNING: $name not installed\n";
437 next;
438 };
439 $pkg->{src} and $name = $pkg->{src};
440 warn "Uninstalling package '$name'\n";
441 system("rm -f ./package/feeds/*/$name");
442 $uninstall = 1;
443 }
444 }
445 $uninstall and refresh_config();
446 return 0;
447 }
448
449 sub update_feed($$$$)
450 {
451 my $type=shift;
452 my $name=shift;
453 my $src=shift;
454 my $perform_update=shift;
455 my $force_relocate=update_location( $name, "@$src" );
456
457 if( $force_relocate ) {
458 warn "Source of feed $name has changed, replacing copy\n";
459 }
460 $update_method{$type} or do {
461 warn "Unknown type '$type' in feed $name\n";
462 return 1;
463 };
464 $perform_update and do {
465 my $failed = 1;
466 foreach my $feedsrc (@$src) {
467 warn "Updating feed '$name' from '$feedsrc' ...\n";
468 next unless update_feed_via($type, $name, $feedsrc, $force_relocate) == 0;
469 $failed = 0;
470 last;
471 }
472 $failed and do {
473 warn "failed.\n";
474 return 1;
475 };
476 };
477 warn "Create index file './feeds/$name.index' \n";
478 update_index($name) == 0 or do {
479 warn "failed.\n";
480 return 1;
481 };
482 return 0;
483 }
484
485 sub update {
486 my %opts;
487 my $feed_name;
488 my $perform_update=1;
489
490 $ENV{SCAN_COOKIE} = $$;
491 $ENV{KBUILD_VERBOSE} = 99;
492
493 getopts('ahi', \%opts);
494
495 if ($opts{h}) {
496 usage();
497 return 0;
498 }
499
500 if ($opts{i}) {
501 # don't update from (remote) repository
502 # only re-create index information
503 $perform_update=0;
504 }
505
506 -d "feeds" or do {
507 mkdir "feeds" or die "Unable to create the feeds directory";
508 };
509
510 if ( ($#ARGV == -1) or $opts{a}) {
511 foreach my $feed (@feeds) {
512 my ($type, $name, $src) = @$feed;
513 update_feed($type, $name, $src, $perform_update);
514 }
515 } else {
516 while ($feed_name = shift @ARGV) {
517 foreach my $feed (@feeds) {
518 my ($type, $name, $src) = @$feed;
519 if($feed_name ne $name) {
520 next;
521 }
522 update_feed($type, $name, $src, $perform_update);
523 }
524 }
525 }
526
527 refresh_config();
528
529 return 0;
530 }
531
532 sub usage() {
533 print <<EOF;
534 Usage: $0 <command> [options]
535
536 Commands:
537 list [options]: List feeds and their content
538 Options:
539 -s : List of feed names and their URL.
540 -r <feedname>: List packages of specified feed.
541
542 install [options] <package>: Install a package
543 Options:
544 -a : Install all packages from all feeds or from the specified feed using the -p option.
545 -p <feedname>: Prefer this feed when installing packages.
546 -d <y|m|n>: Set default for newly installed packages.
547
548 search [options] <substring>: Search for a package
549 Options:
550 -r <feedname>: Only search in this feed
551
552 uninstall -a|<package>: Uninstall a package
553 Options:
554 -a : Uninstalls all packages.
555
556 update -a|<feedname(s)>: Update packages and lists of feeds in feeds.conf .
557 Options:
558 -a : Update all feeds listed within feeds.conf. Otherwise the spezified feeds will be updated.
559 -i : Recreate the index only. No feed update from repository is performed.
560
561 clean: Remove downloaded/generated files.
562
563 EOF
564 exit(1);
565 }
566
567 my %commands = (
568 'list' => \&list,
569 'update' => \&update,
570 'install' => \&install,
571 'search' => \&search,
572 'uninstall' => \&uninstall,
573 'clean' => sub {
574 system("rm -rf feeds");
575 }
576 );
577
578 my $arg = shift @ARGV;
579 $arg or usage();
580 parse_config;
581 foreach my $cmd (keys %commands) {
582 $arg eq $cmd and do {
583 exit(&{$commands{$cmd}}());
584 };
585 }
586 usage();
This page took 0.087597 seconds and 5 git commands to generate.