summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--make/configure.pm17
-rwxr-xr-xmodulemanager234
2 files changed, 250 insertions, 1 deletions
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 (<FLAGS>) {
+ 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 (<SRC>) {
+ 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";
+ }
+}