about summary refs log tree commit diff
path: root/pkgs/build-support/vm/deb/deb-closure.pl
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support/vm/deb/deb-closure.pl')
-rw-r--r--pkgs/build-support/vm/deb/deb-closure.pl162
1 files changed, 162 insertions, 0 deletions
diff --git a/pkgs/build-support/vm/deb/deb-closure.pl b/pkgs/build-support/vm/deb/deb-closure.pl
new file mode 100644
index 000000000000..f35af6ca2dad
--- /dev/null
+++ b/pkgs/build-support/vm/deb/deb-closure.pl
@@ -0,0 +1,162 @@
+use strict;
+use Dpkg::Cdata;
+use Dpkg::Deps;
+
+my $packagesFile = shift @ARGV;
+my $urlPrefix = shift @ARGV;
+my @toplevelPkgs = @ARGV;
+
+my %packages;
+
+
+# Parse the Packages file.
+open PACKAGES, "<$packagesFile" or die;
+
+while (1) {
+    my $cdata = parsecdata(\*PACKAGES, $packagesFile);
+    last unless defined $cdata;
+    #print $cdata->{Package}, "\n";
+    die unless defined $cdata->{Package};
+    $packages{$cdata->{Package}} = $cdata;
+}
+
+close PACKAGES;
+
+
+# Flatten a Dpkg::Deps dependency value into a list of package names.
+sub getDeps {
+    my $deps = shift;
+    #print "$deps\n";
+    if ($deps->isa('Dpkg::Deps::AND')) {
+        my @res = ();
+        foreach my $dep ($deps->get_deps()) {
+            push @res, getDeps($dep);
+        }
+        return @res;
+    } elsif ($deps->isa('Dpkg::Deps::OR')) {
+        # Arbitrarily pick the first alternative.
+        return getDeps(($deps->get_deps())[0]);
+    } elsif ($deps->isa('Dpkg::Deps::Simple')) {
+        return ($deps->{package});
+    } else {
+        die "unknown dep type";
+    }
+}
+
+
+# Process the "Provides" fields to be able to resolve virtual dependencies.
+my %provides;
+
+foreach my $cdata (values %packages) {
+    next unless defined $cdata->{Provides};
+    my @provides = getDeps(Dpkg::Deps::parse($cdata->{Provides}));
+    foreach my $name (@provides) {
+        #die "conflicting provide: $name\n" if defined $provides{$name};
+        $provides{$name} = $cdata->{Package};
+    }
+}
+
+
+# Determine the closure of a package.
+my %donePkgs;
+my %depsUsed;
+my @order = ();
+
+sub closePackage {
+    my $pkgName = shift;
+    print STDERR ">>> $pkgName\n";
+    my $cdata = $packages{$pkgName};
+    
+    if (!defined $cdata) {
+        die "unknown (virtual) package $pkgName"
+            unless defined $provides{$pkgName};
+        print STDERR "virtual $pkgName: using $provides{$pkgName}\n";
+        $pkgName = $provides{$pkgName};
+        $cdata = $packages{$pkgName};
+    }
+    
+    die "unknown package $pkgName" unless defined $cdata;
+    return if defined $donePkgs{$pkgName};
+    $donePkgs{$pkgName} = 1;
+
+    if (defined $cdata->{Provides}) {
+        foreach my $name (getDeps(Dpkg::Deps::parse($cdata->{Provides}))) {
+            $provides{$name} = $cdata->{Package};
+        }
+    }
+    
+    my @depNames = ();
+    
+    if (defined $cdata->{Depends}) {
+        print STDERR "    $pkgName: $cdata->{Depends}\n";
+        my $deps = Dpkg::Deps::parse($cdata->{Depends});
+        die unless defined $deps;
+        push @depNames, getDeps($deps);
+    }
+
+    if (defined $cdata->{'Pre-Depends'}) {
+        print STDERR "    $pkgName: $cdata->{'Pre-Depends'}\n";
+        my $deps = Dpkg::Deps::parse($cdata->{'Pre-Depends'});
+        die unless defined $deps;
+        push @depNames, getDeps($deps);
+    }
+
+    foreach my $depName (@depNames) {
+        closePackage($depName);
+    }
+
+    push @order, $pkgName;
+    $depsUsed{$pkgName} = \@depNames;
+}
+
+foreach my $pkgName (@toplevelPkgs) {
+    closePackage $pkgName;
+}
+
+
+# Generate the output Nix expression.
+print "# This is a generated file.  Do not modify!\n";
+print "# Following are the Debian packages constituting the closure of: @toplevelPkgs\n\n";
+print "{fetchurl}:\n\n";
+print "[\n\n";
+
+# Output the packages in strongly connected components.
+my %done;
+my %forward;
+my $newComponent = 1;
+foreach my $pkgName (@order) {
+    $done{$pkgName} = 1;
+    my $cdata = $packages{$pkgName};
+    my @deps = @{$depsUsed{$pkgName}};
+    foreach my $dep (@deps) {
+        $dep = $provides{$dep} if defined $provides{$dep};
+        $forward{$dep} = 1 unless defined $done{$dep};
+    }
+    delete $forward{$pkgName};
+
+    print "  [\n\n" if $newComponent;
+    $newComponent = 0;
+    
+    print "    (fetchurl {\n";
+    print "      url = $urlPrefix/$cdata->{Filename};\n";
+    print "      sha256 = \"$cdata->{SHA256}\";\n";
+    print "    })\n";
+    print "\n";
+
+    if (keys %forward == 0) {
+        print "  ]\n\n";
+        $newComponent = 1;
+    }
+}
+
+foreach my $pkgName (@order) {
+    my $cdata = $packages{$pkgName};
+}
+
+print "]\n";
+
+if ($newComponent != 1) {
+    print STDERR "argh: ", keys %forward, "\n";
+    exit 1;
+}
+