nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at devShellTools-shell 180 lines 4.9 kB view raw
1use strict; 2use Dpkg::Control; 3use Dpkg::Deps; 4use File::Basename; 5 6my $packagesFile = shift @ARGV; 7my $urlPrefix = shift @ARGV; 8my @toplevelPkgs = @ARGV; 9 10 11my %packages; 12 13 14# Parse the Packages file. 15open PACKAGES, "<$packagesFile" or die; 16 17while (1) { 18 my $cdata = Dpkg::Control->new(type => CTRL_INFO_PKG); 19 last if not $cdata->parse(\*PACKAGES, $packagesFile); 20 die unless defined $cdata->{Package}; 21 #print STDERR $cdata->{Package}, "\n"; 22 $packages{$cdata->{Package}} = $cdata; 23} 24 25close PACKAGES; 26 27 28# Flatten a Dpkg::Deps dependency value into a list of package names. 29sub getDeps { 30 my $deps = shift; 31 #print "$deps\n"; 32 if ($deps->isa('Dpkg::Deps::AND')) { 33 my @res = (); 34 foreach my $dep ($deps->get_deps()) { 35 push @res, getDeps($dep); 36 } 37 return @res; 38 } elsif ($deps->isa('Dpkg::Deps::OR')) { 39 # Arbitrarily pick the first alternative. 40 return getDeps(($deps->get_deps())[0]); 41 } elsif ($deps->isa('Dpkg::Deps::Simple')) { 42 return ($deps->{package}); 43 } else { 44 die "unknown dep type"; 45 } 46} 47 48 49# Process the "Provides" and "Replaces" fields to be able to resolve 50# virtual dependencies. 51my %provides; 52 53foreach my $cdata (sort {$a->{Package} cmp $b->{Package}} (values %packages)) { 54 if (defined $cdata->{Provides}) { 55 my @provides = getDeps(Dpkg::Deps::deps_parse($cdata->{Provides})); 56 foreach my $name (@provides) { 57 #die "conflicting provide: $name\n" if defined $provides{$name}; 58 #warn "provide by $cdata->{Package} conflicts with package with the same name: $name\n"; 59 next if defined $packages{$name}; 60 $provides{$name} = $cdata->{Package}; 61 } 62 } 63 # Treat "Replaces" like "Provides". 64 if (defined $cdata->{Replaces}) { 65 my @replaces = getDeps(Dpkg::Deps::deps_parse($cdata->{Replaces})); 66 foreach my $name (@replaces) { 67 next if defined $packages{$name}; 68 $provides{$name} = $cdata->{Package}; 69 } 70 } 71} 72 73 74# Determine the closure of a package. 75my %donePkgs; 76my %depsUsed; 77my @order = (); 78 79sub closePackage { 80 my $pkgName = shift; 81 print STDERR ">>> $pkgName\n"; 82 my $cdata = $packages{$pkgName}; 83 84 if (!defined $cdata) { 85 die "unknown (virtual) package $pkgName" 86 unless defined $provides{$pkgName}; 87 print STDERR "virtual $pkgName: using $provides{$pkgName}\n"; 88 $pkgName = $provides{$pkgName}; 89 $cdata = $packages{$pkgName}; 90 } 91 92 die "unknown package $pkgName" unless defined $cdata; 93 return if defined $donePkgs{$pkgName}; 94 $donePkgs{$pkgName} = 1; 95 96 if (defined $cdata->{Provides}) { 97 foreach my $name (getDeps(Dpkg::Deps::deps_parse($cdata->{Provides}))) { 98 $provides{$name} = $cdata->{Package}; 99 } 100 } 101 102 my @depNames = (); 103 104 if (defined $cdata->{Depends}) { 105 print STDERR " $pkgName: $cdata->{Depends}\n"; 106 my $deps = Dpkg::Deps::deps_parse($cdata->{Depends}); 107 die unless defined $deps; 108 push @depNames, getDeps($deps); 109 } 110 111 if (defined $cdata->{'Pre-Depends'}) { 112 print STDERR " $pkgName: $cdata->{'Pre-Depends'}\n"; 113 my $deps = Dpkg::Deps::deps_parse($cdata->{'Pre-Depends'}); 114 die unless defined $deps; 115 push @depNames, getDeps($deps); 116 } 117 118 foreach my $depName (@depNames) { 119 closePackage($depName); 120 } 121 122 push @order, $pkgName; 123 $depsUsed{$pkgName} = \@depNames; 124} 125 126foreach my $pkgName (@toplevelPkgs) { 127 closePackage $pkgName; 128} 129 130 131# Generate the output Nix expression. 132print "# This is a generated file. Do not modify!\n"; 133print "# Following are the Debian packages constituting the closure of: @toplevelPkgs\n\n"; 134print "{fetchurl}:\n\n"; 135print "[\n\n"; 136 137# Output the packages in strongly connected components. 138my %done; 139my %forward; 140my $newComponent = 1; 141foreach my $pkgName (@order) { 142 $done{$pkgName} = 1; 143 my $cdata = $packages{$pkgName}; 144 my @deps = @{$depsUsed{$pkgName}}; 145 foreach my $dep (@deps) { 146 $dep = $provides{$dep} if defined $provides{$dep}; 147 $forward{$dep} = 1 unless defined $done{$dep}; 148 } 149 delete $forward{$pkgName}; 150 151 print " [\n\n" if $newComponent; 152 $newComponent = 0; 153 154 my $origName = basename $cdata->{Filename}; 155 my $cleanedName = $origName; 156 $cleanedName =~ s/~//g; 157 158 print " (fetchurl {\n"; 159 print " url = \"$urlPrefix/$cdata->{Filename}\";\n"; 160 print " sha256 = \"$cdata->{SHA256}\";\n"; 161 print " name = \"$cleanedName\";\n" if $cleanedName ne $origName; 162 print " })\n"; 163 print "\n"; 164 165 if (keys %forward == 0) { 166 print " ]\n\n"; 167 $newComponent = 1; 168 } 169} 170 171foreach my $pkgName (@order) { 172 my $cdata = $packages{$pkgName}; 173} 174 175print "]\n"; 176 177if ($newComponent != 1) { 178 print STDERR "argh: ", keys %forward, "\n"; 179 exit 1; 180}