nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
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}