add initial version of a package feeds management script
[openwrt.git] / scripts / metadata.pl
1 #!/usr/bin/perl
2 use FindBin;
3 use lib "$FindBin::Bin";
4 use strict;
5 use metadata;
6
7 my %board;
8
9 sub confstr($) {
10         my $conf = shift;
11         $conf =~ tr#/\.\-/#___#;
12         return $conf;
13 }
14
15 sub parse_target_metadata() {
16         my $file = shift @ARGV;
17         my ($target, @target, $profile);
18         open FILE, "<$file" or do {
19                 warn "Can't open file '$file': $!\n";
20                 return;
21         };
22         while (<FILE>) {
23                 chomp;
24                 /^Target:\s*(.+)\s*$/ and do {
25                         $target = {
26                                 id => $1,
27                                 conf => confstr($1),
28                                 profiles => []
29                         };
30                         push @target, $target;
31                 };
32                 /^Target-Board:\s*(.+)\s*$/ and do {
33                         $target->{board} = $1;
34                         $target->{boardconf} = confstr($1);
35                 };
36                 /^Target-Kernel:\s*(\d+\.\d+)\s*$/ and $target->{kernel} = $1;
37                 /^Target-Name:\s*(.+)\s*$/ and $target->{name} = $1;
38                 /^Target-Path:\s*(.+)\s*$/ and $target->{path} = $1;
39                 /^Target-Arch:\s*(.+)\s*$/ and $target->{arch} = $1;
40                 /^Target-Features:\s*(.+)\s*$/ and $target->{features} = [ split(/\s+/, $1) ];
41                 /^Target-Description:/ and $target->{desc} = get_multiline(*FILE);
42                 /^Linux-Version:\s*(.+)\s*$/ and $target->{version} = $1;
43                 /^Linux-Release:\s*(.+)\s*$/ and $target->{release} = $1;
44                 /^Linux-Kernel-Arch:\s*(.+)\s*$/ and $target->{karch} = $1;
45                 /^Default-Packages:\s*(.+)\s*$/ and $target->{packages} = [ split(/\s+/, $1) ];
46                 /^Target-Profile:\s*(.+)\s*$/ and do {
47                         $profile = {
48                                 id => $1,
49                                 name => $1,
50                                 packages => []
51                         };
52                         push @{$target->{profiles}}, $profile;
53                 };
54                 /^Target-Profile-Name:\s*(.+)\s*$/ and $profile->{name} = $1;
55                 /^Target-Profile-Packages:\s*(.*)\s*$/ and $profile->{packages} = [ split(/\s+/, $1) ];
56                 /^Target-Profile-Description:\s*(.*)\s*/ and $profile->{desc} = get_multiline(*FILE);
57                 /^Target-Profile-Config:/ and $profile->{config} = get_multiline(*FILE, "\t");
58                 /^Target-Profile-Kconfig:/ and $profile->{kconfig} = 1;
59         }
60         close FILE;
61         foreach my $target (@target) {
62                 @{$target->{profiles}} > 0 or $target->{profiles} = [
63                         {
64                                 id => 'Default',
65                                 name => 'Default',
66                                 packages => []
67                         }
68                 ];
69         }
70         return @target;
71 }
72
73 sub gen_kconfig_overrides() {
74         my %config;
75         my %kconfig;
76         my $package;
77         my $pkginfo = shift @ARGV;
78         my $cfgfile = shift @ARGV;
79
80         # parameter 2: build system config
81         open FILE, "<$cfgfile" or return;
82         while (<FILE>) {
83                 /^(CONFIG_.+?)=(.+)$/ and $config{$1} = 1;
84         }
85         close FILE;
86
87         # parameter 1: package metadata
88         open FILE, "<$pkginfo" or return;
89         while (<FILE>) {
90                 /^Package:\s*(.+?)\s*$/ and $package = $1;
91                 /^Kernel-Config:\s*(.+?)\s*$/ and do {
92                         my @config = split /\s+/, $1;
93                         foreach my $config (@config) {
94                                 my $val = 'm';
95                                 my $override;
96                                 if ($config =~ /^(.+?)=(.+)$/) {
97                                         $config = $1;
98                                         $override = 1;
99                                         $val = $2;
100                                 }
101                                 if ($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {
102                                         $kconfig{$config} = $val;
103                                 } elsif (!$override) {
104                                         $kconfig{$config} or $kconfig{$config} = 'n';
105                                 }
106                         }
107                 };
108         };
109         close FILE;
110
111         foreach my $kconfig (sort keys %kconfig) {
112                 if ($kconfig{$kconfig} eq 'n') {
113                         print "# $kconfig is not set\n";
114                 } else {
115                         print "$kconfig=$kconfig{$kconfig}\n";
116                 }
117         }
118 }
119
120 sub merge_package_lists($$) {
121         my $list1 = shift;
122         my $list2 = shift;
123         my @l = ();
124         my %pkgs;
125
126         foreach my $pkg (@$list1, @$list2) {
127                 $pkgs{$pkg} = 1;
128         }
129         foreach my $pkg (keys %pkgs) {
130                 push @l, $pkg unless ($pkg =~ /^-/ or $pkgs{"-$pkg"});
131         }
132         return sort(@l);
133 }
134
135 sub target_config_features(@) {
136         my $ret;
137
138         while ($_ = shift @_) {
139                 /broken/ and $ret .= "\tdepends BROKEN\n";
140                 /pci/ and $ret .= "\tselect PCI_SUPPORT\n";
141                 /usb/ and $ret .= "\tselect USB_SUPPORT\n";
142                 /pcmcia/ and $ret .= "\tselect PCMCIA_SUPPORT\n";
143                 /squashfs/ and $ret .= "\tselect USES_SQUASHFS\n";
144                 /jffs2/ and $ret .= "\tselect USES_JFFS2\n";
145                 /ext2/ and $ret .= "\tselect USES_EXT2\n";
146                 /tgz/ and $ret .= "\tselect USES_TGZ\n";
147         }
148         return $ret;
149 }
150
151
152 sub gen_target_config() {
153         my @target = parse_target_metadata();
154
155         @target = sort {
156                 $a->{name} cmp $b->{name}
157         } @target;
158         
159         
160         print <<EOF;
161 choice
162         prompt "Target System"
163         default TARGET_brcm_2_4
164         reset if !DEVEL
165         
166 EOF
167
168         foreach my $target (@target) {
169                 my $features = target_config_features(@{$target->{features}});
170                 my $help = $target->{desc};
171                 my $kernel = $target->{kernel};
172                 $kernel =~ tr/./_/;
173
174                 chomp $features;
175                 $features .= "\n";
176                 if ($help =~ /\w+/) {
177                         $help =~ s/^\s*/\t  /mg;
178                         $help = "\thelp\n$help";
179                 } else {
180                         undef $help;
181                 }
182         
183                 print <<EOF;
184 config TARGET_$target->{conf}
185         bool "$target->{name}"
186         select $target->{arch}
187         select LINUX_$kernel
188 EOF
189                 if ($target->{id} ne $target->{board}) {
190                         print "\tselect TARGET_".$target->{boardconf}."\n";
191                 }
192                 print "$features$help\n\n"
193         }
194
195         print <<EOF;
196 endchoice
197
198 config TARGET_BOARD
199         string
200 EOF
201         foreach my $target (@target) {
202                 print "\t\tdefault \"".$target->{board}."\" if TARGET_".$target->{conf}."\n";
203         }
204
205         # add hidden target config options 
206         foreach my $target (@target) {
207                 next if $board{$target->{board}};
208                 if ($target->{id} ne $target->{board}) {
209                         print "\nconfig TARGET_".$target->{boardconf}."\n\tbool\n";
210                         $board{$target->{board}} = 1;
211                 }
212         }
213         print <<EOF;
214
215 choice
216         prompt "Target Profile"
217
218 EOF
219         
220         foreach my $target (@target) {
221                 my $profiles = $target->{profiles};
222                 
223                 foreach my $profile (@$profiles) {
224                         print <<EOF;
225 config TARGET_$target->{conf}_$profile->{id}
226         bool "$profile->{name}"
227         depends TARGET_$target->{conf}
228 $profile->{config}
229 EOF
230                         $profile->{kconfig} and print "\tselect PROFILE_KCONFIG\n";
231                         my @pkglist = merge_package_lists($target->{packages}, $profile->{packages});
232                         foreach my $pkg (@pkglist) {
233                                 print "\tselect DEFAULT_$pkg\n";
234                         }
235                         print "\n";
236                 }
237         }
238
239         print "endchoice\n";
240 }
241
242 my %dep_check;
243 sub __find_package_dep($$) {
244         my $pkg = shift;
245         my $name = shift;
246         my $deps = ($pkg->{vdepends} or $pkg->{depends});
247
248         return 0 unless defined $deps;
249         foreach my $dep (@{$deps}) {
250                 next if $dep_check{$dep};
251                 $dep_check{$dep} = 1;
252                 return 1 if $dep eq $name;
253                 return 1 if ($package{$dep} and (__find_package_dep($package{$dep},$name) == 1));
254         }
255         return 0;
256 }
257
258 # wrapper to avoid infinite recursion
259 sub find_package_dep($$) {
260         my $pkg = shift;
261         my $name = shift;
262
263         %dep_check = ();
264         return __find_package_dep($pkg, $name);
265 }
266
267 sub package_depends($$) {
268         my $a = shift;
269         my $b = shift;
270         my $ret;
271
272         return 0 if ($a->{submenu} ne $b->{submenu});
273         if (find_package_dep($a, $b->{name}) == 1) {
274                 $ret = 1;
275         } elsif (find_package_dep($b, $a->{name}) == 1) {
276                 $ret = -1;
277         } else {
278                 return 0;
279         }
280         return $ret;
281 }
282
283 sub mconf_depends($$) {
284         my $depends = shift;
285         my $only_dep = shift;
286         my $res;
287
288         $depends or return;
289         my @depends = @$depends;
290         foreach my $depend (@depends) {
291                 my $m = "depends";
292                 $depend =~ s/^([@\+]+)//;
293                 my $flags = $1;
294                 my $vdep;
295         
296                 if ($vdep = $package{$depend}->{vdepends}) {
297                         $depend = join("||", map { "PACKAGE_".$_ } @$vdep);
298                 } else {
299                         $flags =~ /\+/ and do {
300                                 next if $only_dep;
301                                 $m = "select";
302
303                                 # Menuconfig will not treat 'select FOO' as a real dependency
304                                 # thus if FOO depends on other config options, these dependencies
305                                 # will not be checked. To fix this, we simply emit all of FOO's
306                                 # depends here as well.
307                                 $package{$depend} and $res .= mconf_depends($package{$depend}->{depends}, 1);
308                         };
309                         $flags =~ /@/ or $depend = "PACKAGE_$depend";
310                 }
311                 $res .= "\t\t$m $depend\n";
312         }
313         return $res;
314 }
315
316 sub print_package_config_category($) {
317         my $cat = shift;
318         my %menus;
319         my %menu_dep;
320         
321         return unless $category{$cat};
322         
323         print "menu \"$cat\"\n\n";
324         my %spkg = %{$category{$cat}};
325         
326         foreach my $spkg (sort {uc($a) cmp uc($b)} keys %spkg) {
327                 foreach my $pkg (@{$spkg{$spkg}}) {
328                         my $menu = $pkg->{submenu};
329                         if ($menu) {
330                                 $menu_dep{$menu} or $menu_dep{$menu} = $pkg->{submenudep};
331                         } else {
332                                 $menu = 'undef';
333                         }
334                         $menus{$menu} or $menus{$menu} = [];
335                         push @{$menus{$menu}}, $pkg;
336                         print "\tconfig DEFAULT_".$pkg->{name}."\n";
337                         print "\t\tbool\n\n";
338                 }
339         }
340         my @menus = sort {
341                 ($a eq 'undef' ?  1 : 0) or
342                 ($b eq 'undef' ? -1 : 0) or
343                 ($a cmp $b)
344         } keys %menus;
345
346         foreach my $menu (@menus) {
347                 my @pkgs = sort {
348                         package_depends($a, $b) or
349                         ($a->{name} cmp $b->{name})
350                 } @{$menus{$menu}};
351                 if ($menu ne 'undef') {
352                         $menu_dep{$menu} and print "if $menu_dep{$menu}\n";
353                         print "menu \"$menu\"\n";
354                 }
355                 foreach my $pkg (@pkgs) {
356                         my $title = $pkg->{name};
357                         my $c = (72 - length($pkg->{name}) - length($pkg->{title}));
358                         if ($c > 0) {
359                                 $title .= ("." x $c). " ". $pkg->{title};
360                         }
361                         print "\t";
362                         $pkg->{menu} and print "menu";
363                         print "config PACKAGE_".$pkg->{name}."\n";
364                         print "\t\ttristate \"$title\"\n";
365                         print "\t\tdefault y if DEFAULT_".$pkg->{name}."\n";
366                         foreach my $default (split /\s*,\s*/, $pkg->{default}) {
367                                 print "\t\tdefault $default\n";
368                         }
369                         print mconf_depends($pkg->{depends}, 0);
370                         print "\t\thelp\n";
371                         print $pkg->{description};
372                         print "\n";
373
374                         $pkg->{config} and print $pkg->{config}."\n";
375                 }
376                 if ($menu ne 'undef') {
377                         print "endmenu\n";
378                         $menu_dep{$menu} and print "endif\n";
379                 }
380         }
381         print "endmenu\n\n";
382         
383         undef $category{$cat};
384 }
385
386 sub gen_package_config() {
387         parse_package_metadata($ARGV[0]) or exit 1;
388         print "menuconfig UCI_PRECONFIG\n\tbool \"Image configuration\"\n";
389         foreach my $preconfig (keys %preconfig) {
390                 foreach my $cfg (keys %{$preconfig{$preconfig}}) {
391                         my $conf = $preconfig{$preconfig}->{$cfg}->{id};
392                         $conf =~ tr/\.-/__/;
393                         print <<EOF
394         config UCI_PRECONFIG_$conf
395                 string "$preconfig{$preconfig}->{$cfg}->{label}" if UCI_PRECONFIG
396                 depends PACKAGE_$preconfig
397                 default "$preconfig{$preconfig}->{$cfg}->{default}"
398
399 EOF
400                 }
401         }
402         print_package_config_category 'Base system';
403         foreach my $cat (keys %category) {
404                 print_package_config_category $cat;
405         }
406 }
407
408 sub gen_package_mk() {
409         my %conf;
410         my %dep;
411         my $line;
412
413         parse_package_metadata($ARGV[0]) or exit 1;
414         foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
415                 my $config;
416                 my $pkg = $package{$name};
417                 
418                 next if defined $pkg->{vdepends};
419                 if ($ENV{SDK}) {
420                         $conf{$pkg->{src}} or do {
421                                 $config = 'm';
422                                 $conf{$pkg->{src}} = 1;
423                         };
424                 } else {
425                         $config = "\$(CONFIG_PACKAGE_$name)"
426                 }
427                 if ($config) {
428                         print "package-$config += $pkg->{subdir}$pkg->{src}\n";
429                         $pkg->{prereq} and print "prereq-$config += $pkg->{subdir}$pkg->{src}\n";
430                 }
431         
432                 my $hasdeps = 0;
433                 my $depline = "";
434                 foreach my $dep (@{$pkg->{depends}}, @{$pkg->{builddepends}}) {
435                         next if $dep =~ /@/;
436                         $dep =~ s/\+//;
437                         my $idx;
438                         my $pkg_dep = $package{$dep};
439                         next if defined $pkg_dep->{vdepends};
440
441                         if (defined $pkg_dep->{src}) {
442                                 ($pkg->{src} ne $pkg_dep->{src}) and $idx = $pkg_dep->{subdir}.$pkg_dep->{src};
443                         } elsif (defined($srcpackage{$dep})) {
444                                 $idx = $subdir{$dep}.$dep;
445                         }
446                         undef $idx if $idx =~ /^(kernel)|(base-files)$/;
447                         if ($idx) {
448                                 next if $dep{$pkg->{src}."->".$idx};
449                                 $depline .= " \$(curdir)/$idx/compile";
450                                 $dep{$pkg->{src}."->".$idx} = 1;
451                         }
452                 }
453                 if ($depline) {
454                         $line .= "\$(curdir)/".$pkg->{subdir}."$pkg->{src}/compile += $depline\n";
455                 }
456         }
457         
458         if ($line ne "") {
459                 print "\n$line";
460         }
461         foreach my $preconfig (keys %preconfig) {
462                 my $cmds;
463                 foreach my $cfg (keys %{$preconfig{$preconfig}}) {
464                         my $conf = $preconfig{$preconfig}->{$cfg}->{id};
465                         $conf =~ tr/\.-/__/;
466                         $cmds .= "\techo \"uci set '$preconfig{$preconfig}->{$cfg}->{id}=\$(subst \",,\$(CONFIG_UCI_PRECONFIG_$conf))'\"; \\\n";
467                 }
468                 next unless $cmds;
469                 print <<EOF
470
471 \$(TARGET_DIR)/etc/uci-defaults/$preconfig: FORCE
472         ( \\
473 $cmds \\
474         ) > \$@
475         
476 ifneq (\$(UCI_PRECONFIG)\$(CONFIG_UCI_PRECONFIG),)
477   package/preconfig: \$(TARGET_DIR)/etc/uci-defaults/$preconfig
478 endif
479 EOF
480         }
481 }
482
483
484 sub parse_command() {
485         my $cmd = shift @ARGV;
486         for ($cmd) {
487                 /^target_config$/ and return gen_target_config();
488                 /^package_mk$/ and return gen_package_mk();
489                 /^package_config$/ and return gen_package_config();
490                 /^kconfig/ and return gen_kconfig_overrides();
491         }
492         print <<EOF
493 Available Commands:
494         $0 target_config [file]         Target metadata in Kconfig format
495         $0 package_mk [file]            Package metadata in makefile format
496         $0 package_config [file]        Package metadata in Kconfig format
497         $0 kconfig [file] [config]      Kernel config overrides
498
499 EOF
500 }
501
502 parse_command();