Root/scripts/feeds

1#!/usr/bin/perl
2use Getopt::Std;
3use FindBin;
4use Cwd;
5use lib "$FindBin::Bin";
6use metadata;
7use warnings;
8use strict;
9use Cwd 'abs_path';
10
11chdir "$FindBin::Bin/..";
12$ENV{TOPDIR}=getcwd();
13
14my $mk=`which gmake`; # select the right 'make' program
15chomp($mk); # trim trailing newline
16$mk or $mk = "make"; # default to 'make'
17
18# check version of make
19my @mkver = split /\s+/, `$mk -v`, 4;
20my $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
26my @feeds;
27my %build_packages;
28my %installed;
29
30sub 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
59sub 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
86sub 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
100my %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
128sub 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
149sub 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
163sub get_installed() {
164    system("$mk -s prepare-tmpinfo");
165    clear_packages();
166    parse_package_metadata("./tmp/.packageinfo");
167    %installed = %package;
168}
169
170sub 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
201sub 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
210sub 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
225sub 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
245sub 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
264my %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
273my %feed;
274
275sub 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
287sub 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
345sub 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
359sub 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
412sub 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
449sub 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
485sub 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
532sub usage() {
533    print <<EOF;
534Usage: $0 <command> [options]
535
536Commands:
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
563EOF
564    exit(1);
565}
566
567my %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
578my $arg = shift @ARGV;
579$arg or usage();
580parse_config;
581foreach my $cmd (keys %commands) {
582    $arg eq $cmd and do {
583        exit(&{$commands{$cmd}}());
584    };
585}
586usage();
587

Archive Download this file



interactive