446
|
1 |
#!/usr/bin/env perl
|
|
2 |
|
|
3 |
use strict;
|
|
4 |
use warnings FATAL => 'all';
|
|
5 |
use integer;
|
|
6 |
use Data::Dumper;
|
|
7 |
use Getopt::Long qw(:config no_ignore_case);
|
|
8 |
use File::Copy;
|
|
9 |
use Text::Wrap;
|
|
10 |
use File::Basename;
|
|
11 |
use Cwd;
|
|
12 |
use POSIX qw(strftime);
|
|
13 |
|
|
14 |
sub blab {
|
|
15 |
print 'debmaker: ', @_, "\n";
|
|
16 |
}
|
|
17 |
sub warning {
|
|
18 |
blab 'WARNING: ', @_;
|
|
19 |
sleep 2;
|
|
20 |
}
|
|
21 |
sub fatal {
|
|
22 |
blab 'FATAL: ', @_;
|
|
23 |
exit 1;
|
|
24 |
}
|
|
25 |
sub my_chdir {
|
|
26 |
my ($path) = @_;
|
|
27 |
chdir $path or fatal "Can't chdir() to `$path': $!";
|
|
28 |
}
|
|
29 |
sub my_symlink {
|
|
30 |
my ($src, $dst) = @_;
|
|
31 |
symlink $src, $dst
|
|
32 |
or fatal "Can't create symlink `$src' -> `$dst': $!"
|
|
33 |
}
|
|
34 |
sub my_hardlink {
|
|
35 |
my ($src, $dst) = @_;
|
|
36 |
|
|
37 |
# For harlink creating target file must be accessible:
|
|
38 |
my $pwd = getcwd;
|
|
39 |
my $dir = dirname $dst;
|
|
40 |
my_chdir $dir;
|
|
41 |
link $src, $dst
|
|
42 |
or fatal "Can't create hardlink `$src' -> `$dst': $!";
|
|
43 |
my_chdir $pwd;
|
|
44 |
}
|
|
45 |
sub my_copy {
|
|
46 |
my ($src, $dst) = @_;
|
|
47 |
copy $src, $dst
|
|
48 |
or fatal "Can't copy `$src' to `$dst': $!";
|
|
49 |
}
|
|
50 |
sub my_chown {
|
|
51 |
my ($u, $g, $path) = @_;
|
|
52 |
my $uid = getpwnam $u;
|
|
53 |
my $gid = getgrnam $g;
|
|
54 |
chown $uid, $gid, $path
|
|
55 |
or fatal "Can't chown ($u.$g) `$path': $!";
|
|
56 |
}
|
|
57 |
sub my_chmod {
|
|
58 |
my ($mode, $path) = @_;
|
|
59 |
chmod oct($mode), $path
|
|
60 |
or fatal "Can't chmod ($mode) `$path': $!";
|
|
61 |
}
|
|
62 |
sub my_mkdir {
|
|
63 |
my ($path, $mode) = @_;
|
|
64 |
if (defined $mode) {
|
|
65 |
mkdir $path, oct($mode)
|
|
66 |
or fatal "Can't create dir `$path' with mode `$mode': $!";
|
|
67 |
} else{
|
|
68 |
mkdir $path
|
|
69 |
or fatal "Can't create dir `$path': $!";
|
|
70 |
}
|
|
71 |
}
|
|
72 |
sub uniq {
|
|
73 |
my ($array_ref) = @_;
|
|
74 |
my %hash = map { $_, 1 } @$array_ref;
|
|
75 |
@$array_ref = keys %hash;
|
|
76 |
}
|
|
77 |
|
|
78 |
sub shell_exec {
|
|
79 |
my ($cmd) = @_;
|
|
80 |
blab "executing `$cmd'";
|
|
81 |
system($cmd);
|
|
82 |
if ($? == -1) {
|
|
83 |
fatal "failed to execute: $!";
|
|
84 |
} elsif ($? & 127) {
|
|
85 |
fatal (printf "child died with signal %d, %s coredump",
|
|
86 |
($? & 127), ($? & 128) ? 'with' : 'without')
|
|
87 |
} else {
|
|
88 |
my $rc = $? >> 8;
|
|
89 |
if ($rc != 0) {
|
|
90 |
warning "child exited with value $rc";
|
|
91 |
}
|
|
92 |
}
|
|
93 |
}
|
|
94 |
sub get_command_line {
|
|
95 |
my ($map_ref, $hash_ref) = @_;
|
|
96 |
my $res = '';
|
|
97 |
foreach my $k (keys %$map_ref) {
|
|
98 |
$res .= " $$map_ref{$k} '$$hash_ref{$k}'" if exists $$hash_ref{$k};
|
|
99 |
}
|
|
100 |
return $res;
|
|
101 |
}
|
|
102 |
sub write_file {
|
|
103 |
my ($filename, $content) = @_;
|
|
104 |
blab "Writing file `$filename'";
|
|
105 |
if (open FD, '>', $filename) {
|
|
106 |
print FD $content;
|
|
107 |
close FD;
|
|
108 |
} else {
|
|
109 |
fatal "Can't write to file `$filename': $!"
|
|
110 |
}
|
|
111 |
}
|
|
112 |
sub write_script {
|
|
113 |
my ($filename, $content) = @_;
|
|
114 |
$content = "#!/bin/sh\nset -e\n$content";
|
|
115 |
write_file $filename, $content;
|
|
116 |
my_chmod '0555', $filename;
|
|
117 |
}
|
|
118 |
|
|
119 |
sub get_output {
|
|
120 |
my ($cmd) = @_;
|
|
121 |
if (open OUT, "$cmd |") {
|
|
122 |
my @lines = <OUT>;
|
|
123 |
close OUT;
|
|
124 |
chomp @lines;
|
|
125 |
warning "Empty output from `$cmd'" unless @lines;
|
|
126 |
return \@lines;
|
|
127 |
} else {
|
|
128 |
fatal "Can't execute `$cmd': $!"
|
|
129 |
}
|
|
130 |
}
|
|
131 |
sub get_output_line {
|
|
132 |
return (@{get_output @_})[0];
|
|
133 |
}
|
|
134 |
|
|
135 |
sub trim {
|
|
136 |
# works with refs:
|
|
137 |
$$_ =~ s/^\s*(.*)\s*$/$1/ foreach @_;
|
|
138 |
}
|
|
139 |
|
|
140 |
|
|
141 |
# Expected input for @PROTO_DIRS:
|
|
142 |
# -d /root/oi-build/components/elinks/build/prototype/i386/mangled
|
|
143 |
# -d /root/oi-build/components/elinks/build/prototype/i386
|
|
144 |
# -d .
|
|
145 |
# -d /root/oi-build/components/elinks
|
|
146 |
# -d elinks-0.11.7
|
|
147 |
# (like debian/tmp)
|
|
148 |
my @PROTO_DIRS = ();
|
|
149 |
|
|
150 |
# Where to create debs prototypes
|
|
151 |
# (like debian/pkg-name)
|
|
152 |
my $DEBS_DIR = '';
|
|
153 |
|
|
154 |
# If true, will use manifests from command line
|
|
155 |
# to resolve dependencies:
|
|
156 |
my $BOOTSTRAP = 0;
|
|
157 |
|
|
158 |
my $MAINTAINER = 'Nexenta Systems <[email protected]>';
|
|
159 |
my $VERSION = '0.0.0';
|
|
160 |
my $ARCH = 'solaris-i386';
|
|
161 |
my $SOURCE = 'xxx'; # only for *.changes
|
|
162 |
my $DISTRIB = 'unstable'; # only for *.changes
|
|
163 |
|
|
164 |
# Mapping file => IPS FMRI, filled on bootstrap:
|
|
165 |
my %PATHS = ();
|
|
166 |
|
|
167 |
GetOptions (
|
|
168 |
'd=s' => \@PROTO_DIRS,
|
|
169 |
'D=s' => \$DEBS_DIR,
|
|
170 |
'V=s' => \$VERSION,
|
|
171 |
'A=s' => \$ARCH,
|
|
172 |
'M=s' => \$MAINTAINER,
|
|
173 |
'S=s' => \$SOURCE,
|
|
174 |
'N=s' => \$DISTRIB,
|
|
175 |
'bootstrap!' => \$BOOTSTRAP,
|
|
176 |
'help|h' => sub {usage()},
|
|
177 |
) or usage();
|
|
178 |
|
|
179 |
sub usage {
|
|
180 |
print <<USAGE;
|
|
181 |
Usage: $0 [options] -D <output dir> -d <proto dir> [-d <proto dir> ... ] manifests
|
|
182 |
|
|
183 |
Options:
|
|
184 |
|
|
185 |
-d <proto dir> where to find files (like debian/tmp)
|
|
186 |
|
|
187 |
-D <output dir> where to create package structure and debs,
|
|
188 |
<output dir>/pkg-name and
|
|
189 |
<output dir>/pkg-name*.deb will be created
|
|
190 |
|
|
191 |
-V <version> version of created packages (default is `$VERSION'),
|
|
192 |
may be 'ips' to use the same as for IPS system.
|
|
193 |
|
|
194 |
-A <architecture> package architecture, default is `$ARCH'
|
|
195 |
|
|
196 |
-S <source name> package source name to make reprepro happy
|
|
197 |
with *.changes files, default is `$SOURCE'
|
|
198 |
|
|
199 |
-N <dist name> distribution name to make reprepro happy
|
|
200 |
with *.changes files, default is `$DISTRIB'
|
|
201 |
|
|
202 |
-M <maintainer> Package maintainer - mandatory for debs,
|
|
203 |
default is `$MAINTAINER'
|
|
204 |
|
|
205 |
--bootstrap Search for dependencies within listed manifests,
|
|
206 |
not within installed system (for bootstraping)
|
|
207 |
** not implemented yet **
|
|
208 |
|
|
209 |
-h, --help Show help info
|
|
210 |
|
|
211 |
USAGE
|
|
212 |
exit 1;
|
|
213 |
}
|
|
214 |
|
|
215 |
sub parse_keys {
|
|
216 |
my ($line) = @_;
|
|
217 |
# parse:
|
|
218 |
# name=pkg.summary value="advanced text-mode WWW browser"
|
|
219 |
# into:
|
|
220 |
# 'name' => pkg.summary
|
|
221 |
# 'value' => "advanced text-mode WWW browser"
|
|
222 |
# http://stackoverflow.com/questions/168171/regular-expression-for-parsing-name-value-pairs
|
|
223 |
# TODO: add support for dublicates: dir=dir1 dir=dir2
|
|
224 |
my %pairs = ($line =~ m/((?:\\.|[^= ]+)*)=("(?:\\.|[^"\\]+)*"|(?:\\.|[^ "\\]+)*)/g);
|
|
225 |
foreach my $k (keys %pairs) {
|
|
226 |
$pairs{$k} =~ s/^"(.+)"$/$1/;
|
|
227 |
}
|
|
228 |
return \%pairs;
|
|
229 |
}
|
|
230 |
|
|
231 |
sub read_manifest {
|
|
232 |
my ($filename) = @_;
|
|
233 |
my %data = ();
|
|
234 |
$data{'dir'} = [];
|
|
235 |
$data{'file'} = [];
|
|
236 |
$data{'link'} = [];
|
|
237 |
$data{'hardlink'} = [];
|
|
238 |
$data{'depend'} = [];
|
|
239 |
$data{'legacy'} = [];
|
|
240 |
$data{'group'} = [];
|
|
241 |
$data{'user'} = [];
|
|
242 |
$data{'license'} = [];
|
|
243 |
|
|
244 |
if (open IN, '<', $filename) {
|
|
245 |
while (<IN>) {
|
|
246 |
study; chomp;
|
|
247 |
if (/^set +/) {
|
|
248 |
my $pairs = parse_keys $_;
|
|
249 |
$data{$$pairs{'name'}} = $$pairs{'value'};
|
|
250 |
} elsif (/^dir +/) {
|
|
251 |
my $pairs = parse_keys $_;
|
|
252 |
push @{$data{'dir'}}, $pairs;
|
|
253 |
} elsif (/^file +(\S+) +/) {
|
|
254 |
my $maybe_src = $1;
|
|
255 |
my $pairs = parse_keys $_;
|
|
256 |
$$pairs{'src'} = $maybe_src if $maybe_src ne 'NOHASH';
|
|
257 |
push @{$data{'file'}}, $pairs;
|
|
258 |
} elsif (/^link +/) {
|
|
259 |
my $pairs = parse_keys $_;
|
|
260 |
push @{$data{'link'}}, $pairs;
|
|
261 |
} elsif (/^hardlink +/) {
|
|
262 |
my $pairs = parse_keys $_;
|
|
263 |
push @{$data{'hardlink'}}, $pairs;
|
|
264 |
} elsif (/^depend +/) {
|
|
265 |
my $pairs = parse_keys $_;
|
|
266 |
push @{$data{'depend'}}, $pairs;
|
|
267 |
} elsif (/^legacy +/) {
|
|
268 |
my $pairs = parse_keys $_;
|
|
269 |
push @{$data{'legacy'}}, $pairs;
|
|
270 |
} elsif (/^group +/) {
|
|
271 |
my $pairs = parse_keys $_;
|
|
272 |
push @{$data{'group'}}, $pairs;
|
|
273 |
} elsif (/^user +/) {
|
|
274 |
my $pairs = parse_keys $_;
|
|
275 |
push @{$data{'user'}}, $pairs;
|
|
276 |
} elsif (/^license +(\S+) +/) {
|
|
277 |
my $maybe_src = $1;
|
|
278 |
my $pairs = parse_keys $_;
|
|
279 |
$$pairs{'src'} = $maybe_src if $maybe_src !~ /=/;
|
|
280 |
push @{$data{'license'}}, $pairs;
|
|
281 |
} elsif (/^\s*$/) {
|
|
282 |
# Skip empty lines
|
|
283 |
} elsif (/^\s*#/) {
|
|
284 |
# Skip comments
|
|
285 |
} else {
|
|
286 |
warning "Unknown action: `$_'";
|
|
287 |
}
|
|
288 |
# TODO:
|
|
289 |
# user - to create users (in postinstall?)
|
|
290 |
# restart_fmri - restart SMF
|
|
291 |
}
|
|
292 |
close IN;
|
|
293 |
return \%data;
|
|
294 |
} else {
|
|
295 |
fatal "Can't open `$filename': $!";
|
|
296 |
}
|
|
297 |
}
|
|
298 |
|
|
299 |
sub get_debpkg_names {
|
|
300 |
# pkg:/web/browser/[email protected],5.11-1.1
|
|
301 |
# => web-browser-elinks
|
|
302 |
# browser-elinks
|
|
303 |
# elinks
|
|
304 |
my ($fmri) = @_;
|
|
305 |
my @names = ();
|
|
306 |
if ($fmri =~ m,^(?:pkg:/)?([^@]+)(?:@.+)?$,) {
|
|
307 |
my $pkg = $1;
|
|
308 |
my @parts = split /\//, $pkg;
|
|
309 |
while (@parts) {
|
|
310 |
push @names, (join '-', @parts);
|
|
311 |
shift @parts;
|
|
312 |
}
|
|
313 |
return @names;
|
|
314 |
} else {
|
|
315 |
fatal "Can't parse FMRI to get dpkg name: `$fmri'";
|
|
316 |
}
|
|
317 |
}
|
|
318 |
|
|
319 |
sub get_ips_version {
|
|
320 |
# pkg:/web/browser/[email protected],5.11-1.1
|
|
321 |
# => 0.11.5-5.11-1.1
|
|
322 |
my ($fmri) = @_;
|
|
323 |
if ($fmri =~ m,^(?:pkg:/)?[^@]+@(.+)$,) {
|
|
324 |
my $ips = $1;
|
|
325 |
$ips =~ s/[,:]/-/g;
|
|
326 |
return $ips;
|
|
327 |
} else {
|
|
328 |
fatal "Can't parse FMRI to get IPS version: `$fmri'";
|
|
329 |
}
|
|
330 |
}
|
|
331 |
|
|
332 |
sub get_pkg_section {
|
|
333 |
my ($pkgname) = @_;
|
|
334 |
if ($pkgname =~ m,^([^-@]+)-.*,) {
|
|
335 |
return (split /-/, $pkgname)[0];
|
|
336 |
} elsif ($pkgname =~ m,^pkg:/([^/]+)/.*,) {
|
|
337 |
return $1;
|
|
338 |
} else {
|
|
339 |
fatal "Can't get section for package `$pkgname'"
|
|
340 |
}
|
|
341 |
}
|
|
342 |
|
|
343 |
sub get_dir_size {
|
|
344 |
my ($path) = @_;
|
|
345 |
# We get size just after files are copied
|
|
346 |
# and need sync() to get proper sizes:
|
|
347 |
my $out = get_output("sync && du -sk $path | cut -f 1");
|
|
348 |
return $$out[0];
|
|
349 |
}
|
|
350 |
|
|
351 |
sub find_pkgs_with_paths {
|
|
352 |
my @paths = @_;
|
|
353 |
s,^/+,,g foreach @paths;
|
|
354 |
my $dpkg = get_output('dpkg-query --search -- ' . join(' ', @paths) . ' | cut -d: -f1');
|
|
355 |
return $dpkg;
|
|
356 |
}
|
|
357 |
|
|
358 |
sub guess_required_deps {
|
|
359 |
my ($path) = @_;
|
|
360 |
my $elfs = get_output("find $path -type f -exec file {} \\; | grep ELF | cut -d: -f1");
|
|
361 |
my @deps = ();
|
|
362 |
if (@$elfs) {
|
|
363 |
# my $libs = get_output('ldd ' . join(' ', @$elfs) . ' | grep "=>"');
|
|
364 |
my $libs = get_output('elfdump -d ' . join(' ', @$elfs) . ' | grep NEEDED | awk \'{print $4}\'');
|
|
365 |
uniq $libs;
|
|
366 |
my $pkgs = find_pkgs_with_paths @$libs;
|
|
367 |
push @deps, @$pkgs;
|
|
368 |
}
|
|
369 |
return \@deps;
|
|
370 |
}
|
|
371 |
|
|
372 |
|
|
373 |
if (!$DEBS_DIR) {
|
|
374 |
fatal "Output dir is not set. Use -D option."
|
|
375 |
}
|
|
376 |
if (! -d $DEBS_DIR) {
|
|
377 |
fatal "Not a directory: `$DEBS_DIR'"
|
|
378 |
}
|
|
379 |
|
|
380 |
# Walk through all manifests
|
|
381 |
# and collect files, symlinks, hardlink
|
|
382 |
# mapping them to package names:
|
|
383 |
if ($BOOTSTRAP) {
|
|
384 |
blab "Bootstrap: collecting paths ...";
|
|
385 |
foreach my $manifest_file (@ARGV) {
|
|
386 |
my $manifest_data = read_manifest $manifest_file;
|
|
387 |
my $fmri = $$manifest_data{'pkg.fmri'};
|
|
388 |
my @items = ();
|
|
389 |
if (my @files = @{$$manifest_data{'file'}}) {
|
|
390 |
push @items, @files;
|
|
391 |
}
|
|
392 |
if (my @symlinks = @{$$manifest_data{'link'}}) {
|
|
393 |
push @items, @symlinks;
|
|
394 |
}
|
|
395 |
if (my @hardlinks = @{$$manifest_data{'hardlink'}}) {
|
|
396 |
push @items, @hardlinks;
|
|
397 |
}
|
|
398 |
foreach my $item (@items) {
|
|
399 |
my $path = $$item{'path'};
|
|
400 |
if (exists $PATHS{$path}) {
|
|
401 |
warning "`$path' already present in `$PATHS{$path}' and now found in `$fmri' (manifest `$manifest_file')"
|
|
402 |
} else {
|
|
403 |
$PATHS{$path} = $fmri;
|
|
404 |
}
|
|
405 |
}
|
|
406 |
}
|
|
407 |
blab 'Bootstrap: ' . (keys %PATHS) . ' known paths'
|
|
408 |
}
|
|
409 |
|
|
410 |
|
|
411 |
my %changes = ();
|
|
412 |
$changes{'Date'} = strftime '%a, %d %b %Y %T %z', localtime; # Sat, 11 Jun 2011 17:08:17 +0200
|
|
413 |
$changes{'Architecture'} = $ARCH;
|
|
414 |
$changes{'Format'} = '1.8';
|
|
415 |
$changes{'Maintainer'} = $MAINTAINER;
|
|
416 |
$changes{'Source'} = lc $SOURCE;
|
|
417 |
$changes{'Version'} = $VERSION;
|
|
418 |
$changes{'Distribution'} = $DISTRIB;
|
|
419 |
$changes{'Changes'} = 'Everything has changed';
|
|
420 |
$changes{'Description'} = '';
|
|
421 |
$changes{'Checksums-Sha1'} = '';
|
|
422 |
$changes{'Checksums-Sha256'} = '';
|
|
423 |
$changes{'Files'} = '';
|
|
424 |
$changes{'Binary'} = '';
|
|
425 |
|
|
426 |
|
|
427 |
foreach my $manifest_file (@ARGV) {
|
|
428 |
blab "****** Manifest: `$manifest_file'";
|
|
429 |
my $manifest_data = read_manifest $manifest_file;
|
|
430 |
my @provides = get_debpkg_names $$manifest_data{'pkg.fmri'};
|
|
431 |
my $debname = shift @provides; # main name (web-browser-elinks)
|
|
432 |
my $debsection = get_pkg_section $debname;
|
|
433 |
my $debpriority = exists $$manifest_data{'pkg.priority'} ? $$manifest_data{'pkg.priority'} : 'optional';
|
|
434 |
|
|
435 |
foreach my $l (@{$$manifest_data{'legacy'}}) {
|
|
436 |
push @provides, $$l{'pkg'};
|
|
437 |
}
|
|
438 |
my $provides_str = join(', ', @provides);
|
|
439 |
my $pkgdir = "$DEBS_DIR/$debname";
|
|
440 |
blab "Main package name: $debname";
|
|
441 |
blab "Other names: $provides_str";
|
|
442 |
|
|
443 |
my $ipsversion = get_ips_version $$manifest_data{'pkg.fmri'};
|
|
444 |
my $debversion = undef;
|
|
445 |
if ($VERSION eq 'ips') {
|
|
446 |
blab "Using IPS version scheme: $ipsversion";
|
|
447 |
$debversion = $ipsversion;
|
|
448 |
} else {
|
|
449 |
blab "Using given version: $VERSION";
|
|
450 |
$debversion = $VERSION;
|
|
451 |
}
|
|
452 |
|
|
453 |
# Make sure to work with empty tree:
|
|
454 |
# mkdir will fail if dir exists
|
|
455 |
my_mkdir $pkgdir;
|
|
456 |
|
|
457 |
# Believe that dirs are listed in proper order:
|
|
458 |
# usr, usr/bin, etc
|
|
459 |
if (my @dirs = @{$$manifest_data{'dir'}}) {
|
|
460 |
blab "Making dirs ...";
|
|
461 |
foreach my $dir (@dirs) {
|
|
462 |
my $dir_name = "$pkgdir/$$dir{'path'}";
|
|
463 |
my_mkdir $dir_name, $$dir{'mode'};
|
|
464 |
my_chown $$dir{'owner'}, $$dir{'group'}, $dir_name;
|
|
465 |
}
|
|
466 |
}
|
|
467 |
|
|
468 |
my @conffiles = ();
|
|
469 |
if (my @files = @{$$manifest_data{'file'}}) {
|
|
470 |
blab "Copying files ...";
|
|
471 |
foreach my $file (@files) {
|
|
472 |
my $dst = "$pkgdir/$$file{'path'}";
|
|
473 |
my $src = exists $$file{'src'} ? $$file{'src'} : $$file{'path'};
|
|
474 |
# find $src in @PROTO_DIRS:
|
|
475 |
my $src_dir = undef;
|
|
476 |
foreach my $d (@PROTO_DIRS) {
|
|
477 |
# http://stackoverflow.com/questions/2238576/what-is-the-default-scope-of-foreach-loop-in-perl
|
|
478 |
$src_dir = $d;
|
|
479 |
last if -f "$src_dir/$src";
|
|
480 |
}
|
|
481 |
fatal "file `$src' not found in ", join(', ', @PROTO_DIRS)
|
|
482 |
unless $src_dir;
|
|
483 |
|
|
484 |
$src = "$src_dir/$src";
|
|
485 |
my_copy $src, $dst;
|
|
486 |
my_chown $$file{'owner'}, $$file{'group'}, $dst;
|
|
487 |
my_chmod $$file{'mode'}, $dst;
|
|
488 |
|
|
489 |
push @conffiles, $$file{'path'} if exists $$file{'preserve'};
|
|
490 |
}
|
|
491 |
}
|
|
492 |
|
|
493 |
if (my @hardlinks = @{$$manifest_data{'hardlink'}}) {
|
|
494 |
blab "Creating hardlinks ...";
|
|
495 |
foreach my $link (@hardlinks) {
|
|
496 |
my_hardlink $$link{'target'}, "$pkgdir/$$link{'path'}";
|
|
497 |
}
|
|
498 |
}
|
|
499 |
if (my @symlinks = @{$$manifest_data{'link'}}) {
|
|
500 |
blab "Creating symlinks ...";
|
|
501 |
foreach my $link (@symlinks) {
|
|
502 |
my_symlink $$link{'target'}, "$pkgdir/$$link{'path'}";
|
|
503 |
}
|
|
504 |
}
|
|
505 |
|
|
506 |
if (my @license = @{$$manifest_data{'license'}}) {
|
|
507 |
# FIXME: install in usr/share/doc/<pkg>/copyright
|
|
508 |
# what are the owner, permissions?
|
|
509 |
# multiple licenses?
|
|
510 |
}
|
|
511 |
my $installed_size = get_dir_size($pkgdir);
|
|
512 |
|
|
513 |
my @depends = ();
|
|
514 |
my @predepends = ();
|
|
515 |
my @recommends = ();
|
|
516 |
my @conflicts = ();
|
|
517 |
blab "Getting dependencies ...";
|
|
518 |
foreach my $dep (@{$$manifest_data{'depend'}}) {
|
|
519 |
if ($$dep{'fmri'} ne '__TBD') {
|
|
520 |
my $dep_pkg = (get_debpkg_names($$dep{'fmri'}))[0];
|
|
521 |
blab "Dependency: $dep_pkg ($$dep{'type'})";
|
|
522 |
push @depends, $dep_pkg if $$dep{'type'} eq 'require';
|
|
523 |
push @predepends, $dep_pkg if $$dep{'type'} eq 'origin';
|
|
524 |
# push @recommends, $dep_pkg if $$dep{'type'} eq 'optional';
|
|
525 |
push @conflicts, $dep_pkg if $$dep{'type'} eq 'exclude';
|
|
526 |
}
|
|
527 |
}
|
|
528 |
push @depends, @{guess_required_deps($pkgdir)};
|
|
529 |
|
|
530 |
uniq \@depends;
|
|
531 |
# When a program and a library are in the same package:
|
|
532 |
@depends = grep {$_ ne $debname} @depends;
|
|
533 |
uniq \@predepends;
|
|
534 |
uniq \@recommends;
|
|
535 |
uniq \@conflicts;
|
|
536 |
|
|
537 |
|
|
538 |
my $control = '';
|
|
539 |
$control .= "Package: $debname\n";
|
|
540 |
$control .= "Source: $changes{Source}\n";
|
|
541 |
$control .= "Version: $debversion\n";
|
|
542 |
$control .= "Section: $debsection\n";
|
|
543 |
$control .= "Priority: $debpriority\n";
|
|
544 |
$control .= "Maintainer: $MAINTAINER\n";
|
|
545 |
$control .= "Architecture: $ARCH\n";
|
|
546 |
|
|
547 |
$control .= "Description: $$manifest_data{'pkg.summary'}\n";
|
|
548 |
$changes{'Description'} .= "\n $debname - $$manifest_data{'pkg.summary'}";
|
|
549 |
|
|
550 |
$control .= wrap(' ', ' ', $$manifest_data{'pkg.description'}) . "\n"
|
|
551 |
if exists $$manifest_data{'pkg.description'};
|
|
552 |
|
|
553 |
$control .= "Provides: $provides_str\n";
|
|
554 |
$control .= 'Depends: ' . join(', ', @depends) . "\n" if @depends;
|
|
555 |
$control .= 'Pre-Depends: ' . join(', ', @predepends) . "\n" if @predepends;
|
|
556 |
$control .= 'Recommends: ' . join(', ', @recommends) . "\n" if @recommends;
|
|
557 |
$control .= 'Conflicts: ' . join(', ', @conflicts) . "\n" if @conflicts;
|
|
558 |
$control .= "Installed-Size: $installed_size\n";
|
|
559 |
|
|
560 |
$control .= "Origin: $$manifest_data{'info.upstream_url'}\n"
|
|
561 |
if exists $$manifest_data{'info.upstream_url'};
|
|
562 |
$control .= "X-Source-URL: $$manifest_data{'info.source_url'}\n"
|
|
563 |
if exists $$manifest_data{'info.source_url'};
|
|
564 |
$control .= "X-FMRI: $$manifest_data{'pkg.fmri'}\n";
|
|
565 |
|
|
566 |
my_mkdir "$pkgdir/DEBIAN";
|
|
567 |
|
|
568 |
write_file "$pkgdir/DEBIAN/control", $control;
|
|
569 |
|
|
570 |
if (@conffiles) {
|
|
571 |
write_file "$pkgdir/DEBIAN/conffiles", (join "\n", @conffiles);
|
|
572 |
}
|
|
573 |
|
|
574 |
|
|
575 |
my $preinst = '';
|
|
576 |
my $postinst = '';
|
|
577 |
my $prerm = '';
|
|
578 |
my $postrm = '';
|
|
579 |
if (my @groups = @{$$manifest_data{'group'}}) {
|
|
580 |
foreach my $g (@groups) {
|
|
581 |
my $cmd = "if ! getent group $$g{'groupname'} >/dev/null; then\n";
|
|
582 |
$cmd .= "echo Adding group $$g{'groupname'}\n";
|
|
583 |
$cmd .= 'groupadd';
|
|
584 |
$cmd .= get_command_line {
|
|
585 |
'gid' => '-g'
|
|
586 |
}, $g;
|
|
587 |
$cmd .= " $$g{'groupname'} || true\n";
|
|
588 |
$cmd .= "fi\n";
|
|
589 |
$preinst .= $cmd;
|
|
590 |
}
|
|
591 |
}
|
|
592 |
if (my @users = @{$$manifest_data{'user'}}) {
|
|
593 |
foreach my $u (@users) {
|
|
594 |
my $cmd = "if ! getent passwd $$u{'username'} >/dev/null; then\n";
|
|
595 |
$cmd .= "echo Adding user $$u{'username'}\n";
|
|
596 |
$cmd .= 'useradd';
|
|
597 |
$cmd .= get_command_line {
|
|
598 |
'uid' => '-u',
|
|
599 |
'group' => '-g',
|
|
600 |
'gcos-field' => '-c',
|
|
601 |
'home-dir' => '-d',
|
|
602 |
'uid' => '-u',
|
|
603 |
'login-shell' => '-s',
|
|
604 |
'group-list' => '-G',
|
|
605 |
'inactive' => '-f',
|
|
606 |
'expire' => '-e',
|
|
607 |
}, $u;
|
|
608 |
$cmd .= " $$u{'username'} || true\n";
|
|
609 |
$cmd .= "fi\n";
|
|
610 |
$preinst .= $cmd;
|
|
611 |
}
|
|
612 |
}
|
|
613 |
|
|
614 |
write_script "$pkgdir/DEBIAN/preinst", $preinst if $preinst;
|
|
615 |
|
|
616 |
my $pkg_deb = "${pkgdir}_${debversion}_${ARCH}.deb";
|
|
617 |
# FIXME: we need GNU tar
|
|
618 |
shell_exec(qq|PATH=/usr/gnu/bin:/usr/bin dpkg-deb -b "$pkgdir" "$pkg_deb"|);
|
|
619 |
|
|
620 |
my $sha1 = get_output_line "sha1sum $pkg_deb | cut -d' ' -f1";
|
|
621 |
my $sha256 = get_output_line "sha256sum $pkg_deb | cut -d' ' -f1";
|
|
622 |
my $md5sum = get_output_line "md5sum $pkg_deb | cut -d' ' -f1";
|
|
623 |
my $size = (stat $pkg_deb)[7];
|
|
624 |
my $pkg_deb_base = basename $pkg_deb;
|
|
625 |
|
|
626 |
$changes{'Checksums-Sha1'} .= "\n $sha1 $size $pkg_deb_base";
|
|
627 |
$changes{'Checksums-Sha256'} .= "\n $sha256 $size $pkg_deb_base";
|
|
628 |
$changes{'Files'} .= "\n $md5sum $size $debsection $debpriority $pkg_deb_base";
|
|
629 |
$changes{'Binary'} .= " $debname";
|
|
630 |
}
|
|
631 |
|
|
632 |
my $changes_cnt = join "\n", map {"$_: $changes{$_}"} sort keys %changes;
|
|
633 |
write_file "$DEBS_DIR/$changes{'Source'}.changes", $changes_cnt;
|
|
634 |
|