add a packaging method that installs files into a subdirectory of bin/ instead of...
[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
10 chdir "$FindBin::Bin/..";
11 $ENV{TOPDIR}=getcwd();
12
13 my @feeds;
14 my %build_packages;
15 my %installed;
16
17 sub parse_config() {
18         my $line = 0;
19         my %name;
20
21         open FEEDS, "feeds.conf";
22         while (<FEEDS>) {
23                 chomp;
24                 s/#.+$//;
25                 next unless /\S/;
26                 my @line = split /\s+/, $_, 3;
27                 $line++;
28
29                 my $valid = 1;
30                 $line[0] =~ /^src-\w+$/ or $valid = 0;
31                 $line[1] =~ /^\w+$/ or $valid = 0;
32                 $line[2] =~ /\s/ and $valid = 0;
33                 $valid or die "Syntax error in feeds.list, line: $line\n";
34
35                 $name{$line[1]} and die "Duplicate feed name '$line[1]', line: $line\n";
36                 $name{$line[1]} = 1;
37
38                 push @feeds, [@line];
39         }
40         close FEEDS;
41 }
42
43 sub update_svn($$) {
44         my $name = shift;
45         my $src = shift;
46
47         system("svn co $src ./feeds/$name") == 0 or return 1;
48         -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
49         -d "./feeds/$name.tmp/info" or mkdir "./feeds/$name.tmp/info" or return 1;
50
51         system("make -s prepare-mk TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
52         system("make -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=4 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
53         system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index");
54
55         return 0;
56 }
57
58 sub get_feed($) {
59         my $feed = shift;
60
61         clear_packages();
62         parse_package_metadata("./feeds/$feed.index") or return;
63         return { %package };
64 }
65
66 sub get_installed() {
67         system("make -s prepare-tmpinfo");
68         clear_packages();
69         parse_package_metadata("./tmp/.packageinfo");
70         %installed = %package;
71 }
72
73 sub search_feed {
74         my $feed = shift;
75         my @substr = @_;
76         my $display;
77         
78         return unless @substr > 0;
79         get_feed($feed);
80         foreach my $name (sort { lc($a) cmp lc($b) } keys %package) {
81                 my $pkg = $package{$name};
82                 my $substr;
83                 my $pkgmatch = 1;
84
85                 foreach my $substr (@substr) {
86                         my $match;
87                         foreach my $key (qw(name title description)) {
88                                 $substr and $pkg->{$key} =~ m/$substr/i and $match = 1;
89                         }
90                         $match or undef $pkgmatch;
91                 };
92                 $pkgmatch and do {
93                         $display or do {
94                                 print "Search results in feed '$feed':\n";
95                                 $display = 1;
96                         };
97                         printf "\%-25s\t\%s\n", $pkg->{name}, $pkg->{title};
98                 };
99         }
100         return 0;
101 }
102
103
104 sub search {
105         my %opts;
106
107         getopt('r:', \%opts);
108         foreach my $feed (@feeds) {
109                 search_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]);
110         }
111 }
112
113 sub install_svn() {
114         my $feed = shift;
115         my $pkg = shift;
116         my $path = $pkg->{makefile};
117         $path =~ s/\/Makefile$//;
118
119         -d "./package/feeds" or mkdir "./package/feeds";
120         -d "./package/feeds/$feed->[1]" or mkdir "./package/feeds/$feed->[1]";
121         system("ln -sf ../../../$path ./package/feeds/$feed->[1]/");
122
123         return 0;
124 }
125
126 my %install_method = (
127         'src-svn' => \&install_svn
128 );
129
130 my %feed;
131
132 sub lookup_package($$) {
133         my $feed = shift;
134         my $package = shift;
135
136         foreach my $feed ($feed, @feeds) {
137                 next unless $feed->[1];
138                 next unless $feed{$feed->[1]};
139                 $feed{$feed->[1]}->{$package} and return $feed;
140         }
141         return;
142 }
143
144 sub install_package {
145         my $feed = shift;
146         my $name = shift;
147         my $ret = 0;
148
149         $feed = lookup_package($feed, $name);
150         $feed or do {
151                 $installed{$name} and return 0;
152                 warn "WARNING: Package '$name' is not available.\n";
153                 return 1;
154         };
155
156         my $pkg = $feed{$feed->[1]}->{$name} or return 1;
157         my $src = $pkg->{src};
158         my $type = $feed->[0];
159         $src or $src = $name;
160
161         # previously installed packages set the runtime package
162         # newly installed packages set the source package
163         $installed{$src} and return 0;
164
165         # install all dependencies
166         foreach my $dep (@{$pkg->{depends}}) {
167                 next if $dep =~ /@/;
168                 $dep =~ s/^\+//;
169                 install_package($feed, $dep) == 0 or $ret = 1;
170         }
171
172         # check previously installed packages
173         $installed{$name} and return 0;
174         $installed{$src} = 1;
175         warn "Installing package '$src'\n";
176
177         $install_method{$type} or do {
178                 warn "Unknown installation method: '$type'\n";
179                 return 1;
180         };
181
182         &{$install_method{$type}}($feed, $pkg) == 0 or do {
183                 warn "failed.\n";
184                 return 1;
185         };
186
187         return $ret;
188 }
189
190 sub refresh_config {
191         my $default = shift;
192         $default or $default = "o";
193
194         # workaround for timestamp check
195         system("rm -f tmp/.packageinfo");
196
197         # refresh the config 
198         system("make oldconfig CONFDEFAULT=\"$default\" Config.in >/dev/null 2>/dev/null");
199 }
200
201 sub install {
202         my $name;
203         my %opts;
204         my $feed;
205         my $ret = 0;
206         
207         getopt('p:d:', \%opts);
208         get_installed();
209
210         foreach my $f (@feeds) {
211                 # index all feeds
212                 $feed{$f->[1]} = get_feed($f->[1]);
213
214                 # look up the preferred feed
215                 $opts{p} and $f->[1] eq $opts{p} and $feed = $f;
216         }
217
218         while ($name = shift @ARGV) {
219                 install_package($feed, $name) == 0 or $ret = 1;
220         }
221
222         # workaround for timestamp check
223         
224         # set the defaults
225         if ($opts{d} and $opts{d} =~ /^[ymn]$/) {
226                 refresh_config($opts{d});
227         }
228
229         return $ret;
230 }
231
232 sub uninstall {
233         my $name;
234         my $uninstall;
235
236         if ($ARGV[0] eq '-a') {
237                 system("rm -rf ./package/feeds");
238                 $uninstall = 1;
239         } else {
240                 get_installed();
241                 while ($name = shift @ARGV) {
242                         my $pkg = $installed{$name};
243                         $pkg or do {
244                                 warn "WARNING: $name not installed\n";
245                                 next;
246                         };
247                         $pkg->{src} and $name = $pkg->{src};
248                         warn "Uninstalling package '$name'\n";
249                         system("rm -f ./package/feeds/*/$name");
250                         $uninstall = 1;
251                 }
252         }
253         $uninstall and refresh_config();
254         return 0;
255 }
256
257 sub usage() {
258         print <<EOF;
259 Usage: $0 <command> [options]
260
261 Commands:
262         install [options] <package>: Install a package
263         Options:
264             -p <feedname>: Prefer this feed when installing packages
265             -d <y|m|n>:    Set default for newly installed packages
266
267         search [options] <substring>: Search for a package
268         Options:
269             -r <feedname>: Only search in this feed
270
271         uninstall -a|<package>: Uninstall a package
272             -a uninstalls all packages
273
274         update: Update packages and lists of feeds in feeds.list
275         clean: Remove downloaded/generated files
276
277 EOF
278         exit(1);
279 }
280
281 my %update_method = (
282         'src-svn' => \&update_svn
283 );
284
285 my %commands = (
286         'update' => sub {
287                 -d "feeds" or do {
288                         mkdir "feeds" or die "Unable to create the feeds directory";
289                 };
290                 $ENV{SCAN_COOKIE} = $$;
291                 $ENV{KBUILD_VERBOSE} = 99;
292                 foreach my $feed (@feeds) {
293                         my ($type, $name, $src) = @$feed;
294                         $update_method{$type} or do {
295                                 warn "Unknown type '$type' in feed $name\n";
296                                 next;
297                         };
298                         warn "Updating feed '$name'...\n";
299                         &{$update_method{$type}}($name, $src) == 0 or do {
300                                 warn "failed.\n";
301                                 return 1;
302                         };
303                 }
304                 return 0;
305         },
306         'install' => \&install,
307         'search' => \&search,
308         'uninstall' => \&uninstall,
309         'clean' => sub {
310                 system("rm -rf feeds");
311         }
312 );
313
314 my $arg = shift @ARGV;
315 $arg or usage();
316 parse_config;
317 foreach my $cmd (keys %commands) {
318         $arg eq $cmd and do {
319                 exit(&{$commands{$cmd}}());
320         };
321 }
322 usage();