#! /usr/bin/perl -W use strict; my %modules = ( repomgr => { paths => [ qw(repomgr) ] }, bid => { paths => [ qw(l4/Makefile l4/BENCHMARKING l4/COPYING-GPL-2 l4/COPYING-LGPL-2.1 l4/LICENSING l4/mk l4/conf l4/pkg/Makefile l4/pkg/README l4/tool/Makefile l4/tool/gendep l4/tool/kconfig l4/tool/vim l4/tool/lib l4/tool/elf-patcher l4/tool/gen_dopecode ) ] }, toolbin => { paths => [ map { "l4/tool/bin/$_" } qw(isocreator qemu-x86-launch ux-launch gengrub1iso gengrub2iso entry-selector mkspafs genexportpack gen_kconfig l4-bender ) ] }, l4re => { paths => [ map { "l4/pkg/$_" } qw( bootstrap l4re-core/libgcc l4re-core/libgcc-pure l4re-core/crtn l4re-core/cxx l4re-core/cxx_libc_io l4re-core/ldscripts l4re-core/l4re/ l4re-core/l4re_c/ l4re-core/l4re_kernel/ l4re-core/l4re_vfs/ l4re-core/l4sys/ l4re-core/l4util/ l4re-core/ldso/ l4re-core/libc_backends l4re-core/libkproxy/ l4re-core/libloader/ l4re-core/libsigma0/ l4re-core/libstdc++-headers/ l4re-core/libstdc++-v3/ l4re-core/libstdc++-v3-minimal l4re-core/libsupc++ l4re-core/libsupc++-minimal l4re-core/prj-config l4re-core/moe l4re-core/ned/ l4re-core/sigma0/ l4re-core/uclibc l4re-core/uclibc-minimal/ l4re-core/uclibc-headers/ l4re-core/lua/ io cxx_thread drivers-frst hello acpica libvcpu loader log ) ], pub_module => 1, modules => [ qw(bid toolbin) ], }, # ./mk/pkgdeps -A mk/aliases.d pkgdeps pkg ... l4linux_requirements => { paths => [ map { "l4/pkg/$_" } qw( libedid lxfuxlibc rtc shmc drivers fb-drv x86emu input libirq ) ], pub_module => 1, modules => [ qw(l4re) ], }, examples_core => { paths => [ qw(l4/pkg/hello), map { "l4/pkg/examples/$_" } qw(Makefile Control sys clntsrv misc/Makefile misc/cat misc/shared-hello libs/Makefile libs/l4re ) ], }, examples_all => { modules => [ qw(examples_core) ], paths => [ qw(l4/pkg/hello), map { "l4/pkg/examples/$_" } qw( fb/Makefile fb/spectrum misc/eb_leds misc/fortran-hello misc/reboot libs/inputtst libs/libc_thread_safe libs/libgomp libs/libio libs/libirq libs/libpng libs/rtc libs/shmc ) ], }, doc => { paths => [ qw(l4/doc/Makefile l4/doc/source) ], }, l4re_base => { paths => [ map { "l4/pkg/$_" } qw( cons drivers fb-drv gnu-efi input l4virtio libevent libc_be_stdin libfdt libirq mag mag-gfx readline rtc shmc tmpfs virtio-net uvmm x86emu lxfuxlibc libedid ) ], modules => [ qw(l4re examples_all doc toolbin) ], }, l4re_snapshot => { paths => [ map { "l4/pkg/$_" } qw(ankh boost-lite cons cunit dash demangle dde dde-libinput dope drivers expat2 fb-drv fbterminal ferret fuxfprov gnu-efi hello input l4con l4virtio lib_vt100 libbacktrace libbsd libc_be_stdin libcrypto libedid libevent libgfxbitmap libgfortran libgomp libfdt libiniparser libirq libjpeg libpng libsdl libsdl-image libquadmath linux-26-headers lwip lxfuxlibc mag mag-gfx ocaml plr python readline rtc scout scout-gfx serial-drv shmc slab spafs sqlite tlsf tmpfs udis86 valgrind virtio-net uvmm x86emu zlib ) ], modules => [ qw(l4re examples_all doc toolbin) ] }, l4re_all => { paths => [ qw(l4/pkg l4/tool) ], modules => [ qw(l4re_snapshot) ] }, fiasco => { pub_module => 1, paths => [ qw(kernel/fiasco/BENCHMARKING kernel/fiasco/COPYING kernel/fiasco/MAINTAINER kernel/fiasco/Makefile kernel/fiasco/README kernel/fiasco/src kernel/fiasco/tool ) ], }, kernel => { paths => [ qw(kernel/Makefile) ], }, grub => { paths => [ qw(grub) ], }, remote_repo_l4re => { modules => [ qw(l4re_snapshot) ], }, mips_tee => { paths => [ map { "l4/pkg/$_" } qw(cons l4virtio libfdt uvmm virtio-net virtio-trusty-proxy) ], modules => [ qw(fiasco l4re doc toolbin) ], }, remote_repo => { modules => [ qw(fiasco remote_repo_l4re) ] }, all => { modules => [ qw(remote_repo) ], }, # those are going away, we need to keep them to get the # dirs updated and thus removed __prev => { paths => [ map { "l4/pkg/$_" } qw(ferret io libio libio-io libio-direct ocaml_toys libstdc++-v3_r libsupc++_r uclibc_r boehm_gc libgcc libgcc-pure libbsd-full crtn cxx cxx_libc_io cxx_thread ldscripts l4re/ l4re_c l4re_kernel l4re_vfs l4sys l4util ldscripts ldso libc_backends libkproxy libloader libsigma0 libstdc++-headers libstdc++-v3 libstdc++-v3-minimal libsupc++ libsupc++-minimal libvbus lua moe ned sigma0 uclibc uclibc-minimal uclibc-headers ) ], }, ); my %commands; my $svnrev = $ENV{REPOMGR_SVN_REV} || 'HEAD'; sub get_root_url() { my $o = `svn info -r '$svnrev' --xml .`; die "Failed 'svn info'." if $?; $o =~ /(.+)<\/root>/m; $1; } sub merge_modules { my %paths; sub do_merge { my $pathsref = shift; my $count = shift; die "Possible looping in modules structure detected!" unless $count; foreach my $m (@_) { die "Unknown module '$m' referenced!" unless defined $modules{$m}; $$pathsref{$_} = 1 foreach @{$modules{$m}{paths}}; do_merge($pathsref, $count - 1, @{$modules{$m}{modules}}); } } do_merge(\%paths, 20, scalar @_ ? @_ : 'all'); sort keys %paths; } sub show_pub_modules() { print "Available modules:\n"; foreach (sort keys %modules) { print " $_\n" if $modules{$_}{pub_module}; } } sub usage { my $command = shift; # check alias $command = $commands{$command}{alias} if defined $command and defined $commands{$command}{alias}; if (!defined $command or $command eq 'help') { print "$0 command [option]\n"; print "Available commands, use '$0 help command' for help on the command:\n"; foreach (sort keys %commands) { print " $_\n" if defined $commands{$_}{public}; } } elsif ($command eq 'update') { print " 'update' will update in the following way:\n". " update itself and re-exec if necessary\n". " call 'make update' in l4/pkg\n". " call 'svn update' every directory in kernel\n"; } elsif ($command eq 'checkout') { print " 'checkout' will check out the given modules.\n"; show_pub_modules(); } elsif ($command eq 'modules') { show_pub_modules(); } else { print " No such command '$command'.\n"; } } sub check_module_structure() { # make sure the paths look ok foreach (merge_modules()) { #die "Trailing /'s in modules structure" if /\/$/; die "Double // detected in modules structure" if /\/\//; } } sub command_help { usage(@_); } sub command_update { print "XXX: todo\n"; } sub command_root { my $url = shift; my $dirname = shift; die "Need to give URL" unless defined $url and defined $dirname; system("svn co -r '$svnrev' $url --depth empty $dirname"); } sub init_config($) { my $config_blob = shift; $config_blob = '{}' unless defined $config_blob; my $c; unless ($c = eval($config_blob)) { die "Couldn't parse config file: $@" if $@; die "Couldn't do config file: $!" if $!; die "Couldn't run config file"; } my %a = %$c; $a{base} = "trunk" unless defined $a{base}; return %a; } sub convert_path($$%) { my $p = shift; my $partmatch = shift; my %path_roots = @_; $p =~ s/^\.\///; $p .= '/'; foreach my $key (keys %path_roots) { my $r = $key; $r .= '/' unless $r =~ /\/$/; if ($partmatch) { # for partly matches we also need to return the modified path # because otherwise we can't really know if ($p =~ /^($r)(.*)$/) { my $s = $path_roots{$key}.'/'.$2; # strip off last / again, it's just used for matching return substr($s, 0, length($s) - 1); } } else { return $path_roots{$key} if $p =~ /^$r$/; } } undef; } sub do_checkout(%) { my %args = @_; unless (defined ${$args{mods}}[0]) { print "Supply module to check out.\n"; usage("checkout"); exit 1; } die "Current directory is no SVN WC" unless -d ".svn"; my $root_url = get_root_url(); my @paths = merge_modules(@{$args{mods}}); foreach my $paths (@paths) { my @path_elems = split /\/+/, $paths; my $last_elem = pop @path_elems; my $path = '.'; foreach my $pe (@path_elems) { if (! -d "$path/$pe") { # if we find something from path_roots then switch to that my $changed_path = convert_path("$path/$pe", 0, %{$args{conf}{path_roots}}); print "Creating $path/$pe\n"; print " from $changed_path\n" if defined $changed_path; # there's some other little problem with the 'depth' thing # when we do checkout some specific list of files (and dirs), # we need to use depth=empty so that we only get those # specific files out of the directory, on the other side we'd # (probably) like to have updates on the directory contents # when we do 'svn up' which would would with depth=files (or # infinite) # As the first thing is merely only needed when doing a # checkout for another repo... let's have a config option. my $depth = 'files'; $depth = 'empty' if defined $ENV{REPOMGR_EXACT_CHECKOUT}; if (defined $changed_path) { my $cmd = "cd $path && svn co -r '$svnrev' --depth empty $root_url/$changed_path $pe"; #print "cmd: $cmd\n"; system($cmd); die "svn co failed" if $?; } else { my $cmd = "cd $path && svn update -q -r '$svnrev' --depth empty $pe"; #print "cmd: $cmd\n"; system($cmd); die "svn update failed" if $?; } } $path .= '/'.$pe; } } print "Getting sources\n"; my $c = "svn update -r '$svnrev' --set-depth infinity ".join(' ', map { s/^\/+//; $_; } @paths); #print "cmd: $c\n"; system($c); die "svn update failed" if $?; } sub read_file($) { my $fn = shift; return undef unless defined $fn; my $blob; if ($fn =~ /^(file|svn|ssh\+svn):\/\//) { $blob = `svn -r '$svnrev' cat $fn`; undef $blob if $?; } elsif (open(A, $fn)) { local undef $/; $blob = ; close A; } $blob; } sub command_checkout { my %conf = init_config(read_file("l4/conf/repomgr.conf")); do_checkout(conf => { %conf }, mods => [ @_ ]); } sub fix_repomgr_path(%) { my %conf = @_; # fix the path to the repomgr... @{$modules{repomgr}{paths}} = map { "$conf{repomgr_prefix}/$_" } @{$modules{repomgr}{paths}} if defined $conf{repomgr_prefix}; } sub command_init { my $repo_root = shift; my $repo_conf = ''; my $localdir = 'src'; while (defined $_[0] && ($_[0] eq '-c' or $_[0] eq '-l')) { if ($_[0] eq '-c') { $repo_conf = $_[1]; shift; shift; } elsif ($_[0] eq '-l') { $localdir = $_[1]; shift; shift; } } die "Usage: $0 init [-c ] [-l ] modules..." if not defined $repo_root or not defined $repo_conf or not defined $localdir; # for absolute path we assume a local config file, good for testing my $confblob; if ($repo_conf ne '') { if ($repo_conf =~ /^\//) { $confblob = read_file($repo_conf); die "Cannot open '$repo_conf': $!" unless defined $confblob; } else { my $cmd = "svn cat -r '$svnrev' $repo_root\/$repo_conf"; $confblob = `$cmd`; die "Command '$cmd' failed" if $?; } } my %conf = init_config($confblob); ($localdir = $conf{base}) =~ s/.*\/// unless defined $localdir; print "localdir: $localdir\n"; my $cmd = "svn co -r '$svnrev' --depth empty $repo_root/$conf{base} $localdir"; system($cmd); die "Command '$cmd' failed" if $?; chdir $localdir; fix_repomgr_path(%conf); do_checkout(conf => { %conf }, mods => [ "repomgr", @_ ]); } sub command_modules { foreach (sort keys %modules) { print "$_\n" if defined $modules{$_}{pub_module}; } } sub command_list { print "$_\n" foreach merge_modules(@_); } sub command_listmapped { my $blob = read_file(shift); die "Need config" unless defined $blob; my %conf = init_config($blob); fix_repomgr_path(%conf); print join("\n", map { my $p = convert_path($_, 1, %{$conf{path_roots}}); defined $p ? $p : $_; } merge_modules(@_)); print "\n"; } %commands = ( help => { func => \&command_help, }, init => { func => \&command_init, }, update => { func => \&command_update, }, up => { func => \&command_update, alias => 'update' }, checkout => { func => \&command_checkout, public => 1}, co => { func => \&command_checkout, alias => 'checkout'}, modules => { func => \&command_modules, public => 1}, list => { func => \&command_list, }, listmapped=> { func => \&command_listmapped, }, root => { func => \&command_root, }, ); # ---------------------------------------------------------------- check_module_structure(); my $command = shift; unless (defined $command) { usage(); exit 1; } if (defined $commands{$command}) { &{$commands{$command}{func}}(@ARGV); } else { print "Unknown command '$command'.\n"; usage(); exit 1; }