From: w00t Date: Sun, 7 Sep 2008 01:51:21 +0000 (+0000) Subject: Add basic module manager, patch courtesy of danieldg.. still needs some work/review... X-Git-Tag: v2.0.23~2635 X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=8424e1367b33e8605361cd8aa4584405e4a74fd7;p=user%2Fhenk%2Fcode%2Finspircd.git Add basic module manager, patch courtesy of danieldg.. still needs some work/review, but looks good git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@10426 e03df62e-2008-0410-955e-edbf42e46eb7 --- diff --git a/make/configure.pm b/make/configure.pm index e9c1a04c3..ba586e3e6 100644 --- a/make/configure.pm +++ b/make/configure.pm @@ -20,7 +20,7 @@ use warnings FATAL => qw(all); use Exporter 'import'; use POSIX; use make::utilities; -our @EXPORT = qw(promptnumeric dumphash is_dir getmodules getrevision getcompilerflags getlinkerflags getdependencies nopedantic resolve_directory yesno showhelp promptstring_s); +our @EXPORT = qw(promptnumeric dumphash is_dir getmodules getrevision getcompilerflags getlinkerflags getdependencies getmodversion nopedantic resolve_directory yesno showhelp promptstring_s); my $no_svn = 0; @@ -117,6 +117,21 @@ sub getdependencies { return ""; } +sub getmodversion { + my ($file) = @_; + open(FLAGS, $file) or return ""; + while () { + if ($_ =~ /^\/\* \$ModVersion: (.+) \*\/$/) { + my $x = translate_functions($1, $file); + next if ($x eq ""); + close(FLAGS); + return $x; + } + } + close(FLAGS); + return ""; +} + sub nopedantic { my ($file) = @_; open(FLAGS, $file) or return ""; diff --git a/modulemanager b/modulemanager new file mode 100755 index 000000000..126c74218 --- /dev/null +++ b/modulemanager @@ -0,0 +1,234 @@ +#!/usr/bin/perl +use strict; +use warnings FATAL => qw(all); +use LWP::Simple; + +use make::configure; + +our @modlist; + +my %installed; +# $installed{name} = $version + +my %modules; +# $modules{$name}{$version} = { +# url => URL of this version (or INSECURE/DEPRECATED) +# depends => [ 'm_foo 1.2.0-1.3.0', ... ] +# conflicts => [ ] +# from => URL of source document +# } + +my %url_seen; + +sub parse_url; + +sub parse_url { + my $src = shift; + return if $url_seen{$src}; + $url_seen{$src}++; + + my $doc = get($src); + die "Could not retrieve $_" unless defined $doc; + + my $mod; + for (split /\n+/, $doc) { + s/^\s+//; # ignore whitespace at start + if (/^module (\S+) ([0-9.]+) (\S+)/) { + my($name, $ver, $url) = ($1,$2,$3); + if ($modules{$name}{$ver}) { + my $origsrc = $modules{$name}{$ver}{from}; + warn "Overriding module $name $ver defined from $origsrc with one from $src"; + } + $mod = { + from => $src, + url => $url, + depends => [], + conflicts => [], + }; + $modules{$name}{$ver} = $mod; + } elsif (/^depends (.*)/) { + push @{$mod->{depends}}, $1; + } elsif (/^conflicts (.*)/) { + push @{$mod->{conflicts}}, $1; + } elsif (m#^source (http://\S+)#) { + parse_url $1; + } else { + print "Unknown line in $src: $_\n"; + } + } +} + +open SRC, 'sources.list' or die "Could not open sources.list: $!"; +while () { + parse_url($_); +} +close SRC; + +getmodules(); + +open my $verfile, 'src/version.sh' or die "Cannot determine inspircd version: $!"; +$_ = join '', <$verfile>; +die "Cannot determine inspircd version" unless /InspIRCd-([0-9.]+)/; +$installed{core} = $1; +$modules{core}{$1} = { + url => 'NONE', + depends => [], + conflicts => [], + from => 'local file', +}; +close $verfile; + +for my $modname (@modlist) { + my $mod = "m_$modname"; + my $modfile = "src/modules/$mod.cpp"; + my $ver = getmodversion($modfile) || '0.0'; + $installed{$mod} = $ver; + next if $modules{$mod}{$ver}; + $modules{$mod}{$ver} = { + url => 'NONE', + depends => [], + conflicts => [], + from => 'local file', + }; +} + +my %todo = %installed; + +sub ver_cmp { + ($a,$b) = @_ if @_; + my @a = split /\./, $a; + my @b = split /\./, $b; + push @a, 0 while $#a < $#b; + push @b, 0 while $#b < $#a; + for my $i (0..$#a) { + my $d = $a[$i] <=> $b[$i]; + return $d if $d; + } + return 0; +} + +sub ver_in_range { + my($ver, $range) = @_; + return 1 unless defined $range; + if ($range =~ /(.*)-(.*)/) { + my($l,$h) = ($1,$2); + return 0 unless ver_cmp($ver, $l) >= 0; + return 0 unless ver_cmp($ver, $h) <= 0; + return 1; + } + return !ver_cmp($ver, $range); +} + +sub find_mod_in_range { + my($mod,$vers) = @_; + my @versions = keys %{$modules{$mod}}; + @versions = sort { ver_cmp() } @versions; + for my $ver (reverse @versions) { + return $ver if ver_in_range($ver, $vers); + } + return undef; +} + +sub resolve_deps { + my $tries = 100; + my $changes = 'INIT'; + my $fail = undef; + while ($changes && $tries) { + $tries--; + $changes = ''; + $fail = undef; + my @modsnow = sort keys %todo; + for my $mod (@modsnow) { + my $ver = $todo{$mod}; + my $info = $modules{$mod}{$ver} or die "no dependency information on $mod $ver"; + for my $dep (@{$info->{depends}}) { + $dep =~ /^(\S+)(?: ([-0-9.]+))?/ or die "Bad dependency $dep from $info->{from}"; + my($depmod, $depvers) = ($1,$2); + next if $todo{$depmod} && ver_in_range($todo{$depmod}, $depvers); + # need to install a dependency + my $depver = find_mod_in_range($depmod, $depvers); + if (defined $depver) { + $todo{$depmod} = $depver; + $changes .= " $mod-$ver->$depmod-$depver"; + } else { + $fail ||= "Could not find module $depmod $depver required by $mod $ver"; + } + } + for my $dep (@{$info->{conflicts}}) { + $dep =~ /^(\S+)(?: ([-0-9.]+))?/ or die "Bad dependency $dep from $info->{from}"; + my($depmod, $depvers) = ($1,$2); + next unless $todo{$depmod} && ver_in_range($todo{$depmod}, $depvers); + # if there are changes this round, maybe the conflict won't come up after they are resolved. + $fail ||= "Cannot install: module $mod ($ver) conflicts with versions $depmod version $todo{depmod}"; + } + } + } + if ($changes) { + print "Infinite dependency loop:$changes\n"; + exit 1; + } + if ($fail) { + print "$fail\n"; + exit 1; + } +} + +my $action = lc shift @ARGV; + +if ($action eq 'install') { + for my $mod (@ARGV) { + my $vers = $mod =~ s/=([-0-9.]+)// ? $1 : undef; + $mod = lc $mod; + unless ($modules{$mod}) { + print "Cannot find module $mod\n"; + exit 1; + } + my $ver = find_mod_in_range($mod, $vers); + unless ($ver) { + print "Cannot find suitable version of $mod\n"; + exit 1; + } + $todo{$mod} = $ver; + } +} elsif ($action eq 'upgrade') { + for my $mod (keys %installed) { + next unless $mod =~ /^m_/; + $todo{$mod} = find_mod_in_range($mod); + } +} else { + die "Unknown action $action" +} + +resolve_deps(); + +$| = 1; # immediate print of lines without \n + +for my $mod (keys %installed) { + next if $todo{$mod}; + print "Uninstalling $mod $installed{$mod}\n"; + unlink "src/modules/$mod.cpp"; +} +for my $mod (sort keys %todo) { + my $ver = $todo{$mod}; + my $oldver = $installed{$mod}; + my $url = $modules{$mod}{$ver}{url}; + if ($url eq 'INSECURE') { + print "WARNING: Version $ver of $mod is insecure!\n"; + next; + } elsif ($url eq 'DEPRECATED') { + print "Note: version $ver of $mod is deprecated\n"; + next; + } + next if $oldver && $oldver eq $ver; + if ($oldver) { + print "Upgrading $mod from $oldver to $ver using $url" + } else { + print "Installing $mod $ver from $url"; + } + my $stat = getstore($url, "src/modules/$mod.cpp"); + if ($stat == 200) { + print " - done\n"; + } else { + print " - HTTP $stat\n"; + } +}