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