4 use vars qw(@ISA @EXPORT);
6 @EXPORT = qw(get_module_entry search_file get_entries);
8 my @internal_searchpaths;
12 sub get_command_and_cmdline($)
14 my ($file, $args) = split /\s+/, $_[0], 2;
17 $full .= " $args" if defined $args;
20 if (length($full) > $arglen) {
21 print "$.: \"$full\" too long...\n";
36 my $optstring = shift;
39 foreach (split /\s*,\s*/, $optstring)
41 if (/(\S+)\s*=\s*(.+)/)
62 if (exists $opts{perl})
65 die "perl: ".$@ if $@;
69 if (exists $opts{shell})
71 my @m = split /\n/, `$r`;
72 error "$mod_file:$.: Shell command failed\n" if $?;
76 return ( glob $r ) if exists $opts{glob};
79 # Deprecated start -- remove in 2016
80 if ($r =~ /^((perl|glob|shell):\s+)/)
82 substr $r, 0, length($1), "";
84 print STDERR "ATTENTION:\n".
85 " Using deprecated syntax '$2:' on line $.\n".
86 " Use option syntax now: <command>[$2] $r\n";
91 die "perl: ".$@ if $@;
96 my @m = split /\n/, `$r`;
97 error "$mod_file:$.: Shell command failed\n" if $?;
106 die "should not happen";
114 sub handle_line_first
116 return (handle_line(shift, shift, @_))[0];
125 my @mod_files_for_include;
132 push @mod_files_for_include, $mod_file;
136 if (@mod_files_for_include)
138 my $f = shift @mod_files_for_include;
140 if (grep { /^$f$/ } @fs_filenames)
142 print STDERR "$mod_file:$.: Warning: $f already included, skipping.\n";
146 push @fs_filenames, $mod_file;
151 open($fd, $f) || error "Cannot open '$f': $!\n";
153 $id_to_file{$file_id_cur} = $f;
154 $file_to_id{$f} = $file_id_cur++;
164 my ($cmd, $remaining) = split /\s+/, $_, 2;
167 if ($cmd eq 'include')
169 my @f = handle_line($remaining, $mod_file);
179 my @tmp = split /\/+/, $mod_file;
181 $abs = join('/', @tmp);
183 unshift @mod_files_for_include, glob $abs;
189 push @contents, [ $file_to_id{$mod_file}, $., $_ ];
197 $mod_file = pop @fs_filenames;
199 last unless defined $fd;
206 print "$id_to_file{$$_[0]}($$_[0]):$$_[1]: $$_[2]\n" foreach (@contents);
210 contents => [ @contents ],
211 file_to_id => { %file_to_id },
212 id_to_file => { %id_to_file },
216 # extract an entry with modules from a modules.list file
217 sub get_module_entry($$)
219 my ($mod_file, $entry_to_pick) = @_;
223 if ($entry_to_pick eq 'auto-build-entry') {
224 # Automatic build entry is being built.
225 # This image is useless but it always builds.
227 $mods[0] = { command => 'Makefile', cmdline => 'Makefile', type => 'bin'};
228 $mods[1] = { command => 'Makefile', cmdline => 'Makefile', type => 'bin'};
229 $mods[2] = { command => 'Makefile', cmdline => 'Makefile', type => 'bin'};
232 bootstrap => { command => 'bootstrap',
233 cmdline => 'bootstrap' },
239 # preseed first 3 modules
240 $mods[0] = { command => 'fiasco', cmdline => 'fiasco', type => 'bin'};
241 $mods[1] = { command => 'sigma0', cmdline => 'sigma0', type => 'bin'};
242 $mods[2] = { command => 'roottask', cmdline => 'moe', type => 'bin'};
244 my $process_mode = undef;
249 my $bootstrap_command = "bootstrap";
250 my $bootstrap_cmdline = "bootstrap";
254 my %mod_file_db = readin_config($mod_file);
256 foreach my $fileentry (@{$mod_file_db{contents}})
266 if (/^modaddr\s+(\S+)/) {
267 $modaddr_global = $1 if $global;
268 $modaddr_title = $1 if !$global and $process_mode;
272 /^([^\s\[]+)(\[\s*(.*)\s*\])?(\s+(.*))?/;
273 my ($type, $opts, $remaining) = ($1, $3, $5);
276 %opts = handle_options($opts) if defined $opts;
279 join(", ", map { $opts{$_} ? "$_=$opts{$_}" : $_ } keys %opts),
285 $type = 'bin' if $type eq 'module';
287 if ($type =~ /^(entry|title)$/) {
288 ($remaining) = handle_line($remaining, $mod_file, %opts);
289 if (lc($entry_to_pick) eq lc($remaining)) {
290 $process_mode = 'entry';
293 $process_mode = undef;
299 if ($type eq 'searchpath') {
300 push @internal_searchpaths, handle_line($remaining, $mod_file, %opts);
302 } elsif ($type eq 'group') {
303 $process_mode = 'group';
304 $current_group_name = (split /\s+/, handle_line_first($remaining, $mod_file, %opts))[0];
306 } elsif ($type eq 'default-bootstrap') {
307 my ($file, $full) = get_command_and_cmdline(handle_line_first($remaining, $mod_file, %opts));
308 $bootstrap_command = $file;
309 $bootstrap_cmdline = $full;
311 } elsif ($type eq 'default-kernel') {
312 my ($file, $full) = get_command_and_cmdline(handle_line_first($remaining, $mod_file, %opts));
313 $mods[0]{command} = $file;
314 $mods[0]{cmdline} = $full;
316 } elsif ($type eq 'default-sigma0') {
317 my ($file, $full) = get_command_and_cmdline(handle_line_first($remaining, $mod_file, %opts));
318 $mods[1]{command} = $file;
319 $mods[1]{cmdline} = $full;
321 } elsif ($type eq 'default-roottask') {
322 my ($file, $full) = get_command_and_cmdline(handle_line_first($remaining, $mod_file, %opts));
323 $mods[2]{command} = $file;
324 $mods[2]{cmdline} = $full;
328 next unless $process_mode;
330 my @params = handle_line($remaining, $mod_file, %opts);
332 my @valid_types = ( 'bin', 'data', 'bin-nostrip', 'data-nostrip',
333 'bootstrap', 'roottask', 'kernel', 'sigma0',
334 'module-group', 'moe', 'initrd', 'set');
335 error "$mod_file_db{id_to_file}{$$fileentry[0]}:$$fileentry[1]: Invalid type \"$type\"\n"
336 unless grep(/^$type$/, @valid_types);
339 if ($type eq 'set') {
340 my ($varname, $value) = split /\s+/, $params[0], 2;
341 $is_mode_linux = 1 if $varname eq 'mode' and lc($value) eq 'linux';
344 if ($type eq 'module-group') {
346 foreach (split /\s+/, join(' ', @params)) {
347 error "$mod_file_db{id_to_file}{$$fileentry[0]}:$$fileentry[1]: Unknown group '$_'\n" unless defined $groups{$_};
348 push @m, @{$groups{$_}};
352 } elsif ($type eq 'moe') {
353 $mods[2]{command} = 'moe';
354 $mods[2]{cmdline} = "moe rom/$params[0]";
358 next if not defined $params[0] or $params[0] eq '';
360 if ($process_mode eq 'entry') {
361 foreach my $m (@params) {
363 my ($file, $full) = get_command_and_cmdline($m);
366 if ($type eq 'bootstrap') {
367 $bootstrap_command = $file;
368 $bootstrap_cmdline = $full;
369 } elsif ($type =~ /(rmgr|roottask)/i) {
370 $mods[2]{command} = $file;
371 $mods[2]{cmdline} = $full;
372 } elsif ($type eq 'kernel') {
373 $mods[0]{command} = $file;
374 $mods[0]{cmdline} = $full;
375 } elsif ($type eq 'sigma0') {
376 $mods[1]{command} = $file;
377 $mods[1]{cmdline} = $full;
378 } elsif ($type eq 'initrd') {
379 $linux_initrd = $file;
389 } elsif ($process_mode eq 'group') {
390 push @{$groups{$current_group_name}}, @params;
392 error "$mod_file_db{id_to_file}{$$fileentry[0]}:$$fileentry[1]: Invalid mode '$process_mode'\n";
396 error "$mod_file: Unknown entry \"$entry_to_pick\"!\n" unless $found_entry;
398 if (defined $is_mode_linux)
400 error "No Linux kernel image defined\n" unless defined $mods[0]{cmdline};
401 print STDERR "Entry '$entry_to_pick' is a Linux type entry\n";
403 # @files is actually redundant but eases file selection for entry
405 push @files, $mods[0]{command};
406 push @files, $linux_initrd if defined $linux_initrd;
409 # actually bootstrap is always the kernel in this
410 # environment, for convenience we use $mods[0] because that
411 # are the contents of 'kernel xxx' which sounds more
413 bootstrap => { command => $mods[0]{command},
414 cmdline => $mods[0]{cmdline}},
418 $r{initrd} = { cmdline => $linux_initrd } if defined $linux_initrd;
422 # now some implicit stuff
423 my $m = $modaddr_title || $modaddr_global;
426 if ($bootstrap_cmdline =~ /-modaddr\s+/)
428 $bootstrap_cmdline =~ s/(-modaddr\s+)%modaddr%/$1$m/;
432 $bootstrap_cmdline .= " -modaddr $m";
436 my @files; # again, this is redundant but helps generator functions
437 push @files, $bootstrap_command;
438 push @files, $_->{command} foreach @mods;
441 bootstrap => { command => $bootstrap_command,
442 cmdline => $bootstrap_cmdline },
444 modaddr => $modaddr_title || $modaddr_global,
450 sub entry_is_linux(%)
453 return defined $e{type} && $e{type} eq 'Linux';
459 return defined $e{type} && $e{type} eq 'MB';
467 my %mod_file_db = readin_config($mod_file);
469 foreach my $fileentry (@{$mod_file_db{contents}})
471 push @entry_list, $2 if $$fileentry[2] =~ /^(entry|title)\s+(.+)\s*$/;
477 # Search for a file by using a path list (single string, split with colons
478 # or spaces, see the split)
479 # return undef if it could not be found, the complete path otherwise
485 foreach my $p (split(/[:\s]+/, $paths), @internal_searchpaths) {
486 return "$p/$file" if -e "$p/$file" and ! -d "$p/$file";
489 return $file if $file =~ /^\// && -e $file;
494 sub search_file_or_die($$)
498 my $f = search_file($file, $paths);
499 error "Could not find '$file' with path '$paths'\n" unless defined $f;
503 sub get_or_copy_file_uncompressed_or_die($$$$)
507 my $targetdir = shift;
510 my $fp = L4::ModList::search_file_or_die($command, $paths);
512 open F, $fp || error "Cannot open '$fp': $!\n";
517 (my $tf = $fp) =~ s|.*/||;
518 $tf = $targetdir.'/'.$tf;
520 if (length($buf) >= 2 && unpack("n", $buf) == 0x1f8b) {
521 print STDERR "'$fp' is a zipped file, uncompressing to '$tf'\n";
522 system("zcat $fp >$tf");
525 print("cp $fp $tf\n");
526 system("cp $fp $tf");
533 sub get_file_uncompressed_or_die($$$)
535 return get_or_copy_file_uncompressed_or_die(shift, shift, shift, 0);
538 sub copy_file_uncompressed_or_die($$$)
540 return get_or_copy_file_uncompressed_or_die(shift, shift, shift, 1);
544 sub generate_grub1_entry($$%)
546 my $entryname = shift;
548 $prefix = '' unless defined $prefix;
549 $prefix = "/$prefix" if $prefix ne '' and $prefix !~ /^\//;
551 my $s = "title $entryname\n";
552 my $c = $entry{bootstrap}{cmdline};
553 $c =~ s/^\S*\/([^\/]+\s*)/$1/;
554 $s .= "kernel $prefix/$c\n";
556 if (entry_is_linux(%entry) and defined $entry{initrd})
558 $c = $entry{initrd}{cmdline};
559 $c =~ s/^\S*\/([^\/]+\s*)/$1/;
560 $s .= "initrd $prefix/$c\n";
564 foreach my $m (@{$entry{mods}})
567 $c =~ s/^\S*\/([^\/]+\s*)/$1/;
568 $s .= "module $prefix/$c\n";
573 sub generate_grub2_entry($$%)
575 my $entryname = shift;
577 $prefix = '' unless defined $prefix;
578 $prefix = "/$prefix" if $prefix ne '' and $prefix !~ /^\//;
580 # basename of first path
581 my ($c, $args) = split(/\s+/, $entry{bootstrap}{cmdline}, 2);
582 my $bn = (reverse split(/\/+/, $c))[0];
583 my $s = "menuentry \"$entryname\" {\n";
585 if (entry_is_linux(%entry))
587 $s .= " echo Loading '$prefix/$bn $args'\n";
588 $s .= " linux $prefix/$bn $args\n";
589 if (defined $entry{initrd})
591 $bn = (reverse split(/\/+/, $entry{initrd}{cmdline}))[0];
592 my $c = $entry{initrd}{cmdline};
593 $c =~ s/^\S*(\/[^\/]+\s*)/$1/;
594 $s .= " initrd $prefix$c\n";
599 $s .= " echo Loading '$prefix/$bn $prefix/$bn $args'\n";
600 $s .= " multiboot $prefix/$bn $prefix/$bn $args\n";
601 foreach my $m (@{$entry{mods}})
604 $bn = (reverse split(/\/+/, $m->{command}))[0];
605 my $c = $m->{cmdline};
606 $c =~ s/^\S*(\/[^\/]+\s*)/$1/;
607 $s .= " echo Loading '$prefix/$bn $c'\n";
608 $s .= " module $prefix/$bn $c\n";
611 $s .= " echo Done, booting...\n";