File: emdebcheck

package info (click to toggle)
emdebian-tools 1.4.3
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 1,112 kB
  • ctags: 274
  • sloc: perl: 6,297; xml: 4,828; sh: 1,902; php: 406; ansic: 189; makefile: 15
file content (407 lines) | stat: -rwxr-xr-x 11,894 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#!/usr/bin/perl

=pod

=head1 Name

emdebcheck - Check Emdebian builds with edos-debcheck before upload

=head1 Copyright and Licence

 Copyright (C) 2008  Neil Williams <codehelp@debian.org>

 This package is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

=cut

use Cwd;
use Encode;
use Config::Auto;
use File::HomeDir;
use File::Basename;
use Text::Wrap;
use Cache::Apt::Package;
use Cache::Apt::Config;
use Cache::Apt::Lookup;
use File::Spec;
use Emdebian::Tools;
use Term::ANSIColor qw(:constants);
use Debian::DpkgCross;
use strict;
use warnings;
use vars qw/$workdir $msg $arch $progname $our_version $package $verbose
 $suite $cache $name @filelist /;

$progname = basename($0);
$our_version = &tools_version();
$verbose = 1;
# read emsource config file.
$workdir = &get_workdir;
$workdir = "/" if ($workdir eq "");
$workdir.= "/trunk/";
$workdir =~ s://:/:;
&read_config();
$arch = &get_architecture();
$suite = &get_targetsuite();

sub usageversion {
	print(STDERR <<END)
$progname version $our_version

Usage:
$progname [-v] [-q] [-a|--arch ARCH] FILENAME
$progname -?|-h|--help|--version

Options:
 -a|--arch ARCH:          Set architecture (default: defined by dpkg-cross)
 -v|--verbose:            Be verbose (repeat for more verbosity)
 -q|--quiet:              Be quiet [default]

END
		or die "$0: failed to write usage: $!\n";
}

while( @ARGV ) {
	$_= shift( @ARGV );
	last if m/^--$/;
	if (!/^-/) {
		unshift(@ARGV,$_);
		last;
	}
	elsif (/^(-\?|-h|--help|--version)$/) {
		&usageversion();
		exit (0);
	}
	elsif (/^(-v|--verbose)$/) {
		$verbose++;
	}
	elsif (/^(-q|--quiet)$/) {
		$verbose--;
	}
	elsif (/^(-a|--arch)$/) {
		$arch = shift(@ARGV);
	}
	elsif (/^-$/) {
		@ARGV=split(/ /,<STDIN>);
	}
	else
	{
		&usageversion();
		die RED, "unrecognised option:  $_.", RESET . "\n";
	}
}

foreach my $file (@ARGV)
{
	chomp($file);
	if (! $file)
	{
		&usageversion;
		print RED, "Please specify a package filename to check.\n\n", RESET;
		exit (1);
	}
	$package = (File::Spec->file_name_is_absolute($file)) ? $package = $file :
		$package = File::Spec->rel2abs($file);
	if (! -f $package)
	{
		&usageversion;
		die RED, "Cannot find '$package'".RESET.".\n\n";
	}
	if ($file =~ /\.changes$/)
	{
		# convert a .changes file to an array of .deb
		my @debs = `dcmd ls $file  2>/dev/null | grep '\.deb\$'`;
		chomp (@debs);
		foreach my $d (@debs)
		{
			print "Adding $d from $file.\n" if ($verbose >= 2);
			push @filelist , $d if (-f $d);
			push @filelist, dirname($package)."/$d" if (-f dirname($package)."/$d");
		}
	}
	else
	{
		push @filelist, $package;
	}
}

my $target_gnu_type = &check_cache_arch($arch);
if ((not defined $arch)||($arch eq "")||(not defined $target_gnu_type))
{
	&usageversion;
	$msg = "\n$progname: Cannot determine the architecture to build";
	$msg .= " and no default architecture found.";
	$msg .= " Please use '$progname --arch ARCH'.\n";
	die RED, wrap('','',$msg), RESET, "\n";
}

my $quiet = "";
$quiet = "-q" if ($verbose < 1);
$cache = &get_aptcross_dir;
my @namelist = ();
foreach my $n (@filelist)
{
	print GREEN, "checking for '$n'.\n", RESET if ($verbose >= 1);
	die (RED, "Cannot find '$n'\n", RESET) if (! -f $n);
	$name = `dpkg -f $n Package`;
	chomp($name);
	push @namelist, $name
}
$name = join (" ", @namelist);
&set_verbose($verbose);
&set_suite($suite);

$msg = &check_workdir($workdir);
die $msg if ($msg ne "");
chdir ("$workdir") if ($workdir ne ".");

if (not -d "$cache/host")
{
	# have to duplicate them or use sudo.
	mkdir ("$cache/host") or die ("Unable to create the host cache directory: $!");
	open (STATUS, ">$cache/host/status");
	close (STATUS);
	mkdir "$cache/host/$suite";
	mkdir "$cache/host/$suite/lists";
	mkdir "$cache/host/$suite/lists/partial";
}
open (SOURCE, ">$cache/host/sources.compare.$suite")
	or die ("Cannot create sources list: $!");
print SOURCE "deb http://www.emdebian.org/emdebian/ unstable main\n";
print SOURCE "deb-src http://www.emdebian.org/emdebian/ unstable main\n";
close (SOURCE);

&set_cachedir("$cache/host");
&use_mysources("sources.compare.$suite");
&force_update;
my $config = &init_cache($verbose);

my %h = ();
my $iter = &get_cache_iter();
my $pkg;
do {
	$pkg = $iter->next;
	$h{$pkg}++ if ($pkg);
} while ($pkg);

my @package_names = sort (keys %h);
my $file = `mktemp -t edosXXXXXX` or die ("unable to create temp file.\n");
chomp($file);
print GREEN, "Using temporary file: $file\n" if ($verbose >= 1);
open (TEMP, ">$file") or die (RED, "Cannot open $file: $!".RESET."\n");
foreach my $p (@package_names)
{
	my $emp = AptCrossPackage->new();
	$emp->Package($p);
	$emp = &lookup_pkg($emp);
	next unless ($$emp->Version);
	next if ($name =~ /\Q$p\E/);
	print TEMP get_cache_control($emp);
}
foreach my $pkg (@filelist)
{
	print TEMP &get_package_data($pkg);
}
close TEMP;

print CYAN, "Found " . scalar (@package_names) . " package names.\n", RESET
	if ($verbose >= 1);

&edos;

exit (0);

sub get_cache_control
{
	my $emp = shift;
	my $file = "Package: " . $$emp->Package . "\n" if ($$emp->Package);
	$file .= "Source: " . $$emp->Source . "\n"
		if (($$emp->Source) && ($$emp->Source ne $$emp->Package));
	$file .= "Version: " . $$emp->Version . "\n" if ($$emp->Version);
	$file .= "Architecture: " . $$emp->Architecture . "\n" if ($$emp->Architecture);
	my $dep = $$emp->Depends;
	if ($$emp->Provides)
	{
		# AptPkg::Dep::Or dependencies are borked in Cache::Apt::* 
		# and I can't work out how to handle OR so this hack is just to prevent
		# unnecessary work by forcing debconf-2.0 | debconf.
		my $hackalert = "";
		$hackalert = ", debconf" if ($$emp->Provides =~ /debconf-2.0/);
		$file .= "Provides: " . $$emp->Provides . "$hackalert\n";
	}
	my $deps;
	my $line = "";
	my @a=();
	foreach my $d (@$dep)
	{
		$deps = $$d->Type . ": ";
		$line .= $$d->Package . " (" . $$d->VersionLimit . ")" if ($$d->VersionLimit);
		# AptPkg::Dep::Or dependencies are borked in Cache::Apt::* 
		# and I can't work out how to handle OR so this hack is just to prevent
		# unnecessary work by forcing debconf-2.0 | debconf.
		$line =~ s/debconf/debconf-2.0 \| debconf/ if ($line !~ /[a-z]debconf/);
		$line =~ s/\s\s+/ /g;
		push @a, $line if ($line ne "");
		undef ($deps) if ($line eq "");
		$line="";
	}
	$file .= $deps . join(", ",@a) . "\n" if (defined($deps));
	return "$file\n";
}

sub get_package_data
{
	$package = shift;
	die (RED, "Cannot find '$package' in ".cwd.RESET."\n") if (! -f $package);
	my $data = "\n";
	my $pkg = "Package: " . `dpkg -f $package Package`;
	chomp ($pkg);
	print GREEN, "Adding control data for '$pkg'\n", RESET if ($verbose >= 2);
	$data .= $pkg;
	$data .= "\nVersion: " . `dpkg -f $package Version`;
	$data .= "Architecture: " . `dpkg -f $package Architecture`;
	my $dep = `dpkg -f $package Depends`;
	my $predep = `dpkg -f $package Pre-Depends`;
	my $prov = `dpkg -f $package Provides`;
	my $repl = `dpkg -f $package Replaces`;
	my $confl = `dpkg -f $package Conflicts`;
	chomp($dep);
	chomp($predep);
	chomp($prov);
	chomp($repl);
	chomp($confl);
	if ($predep ne "")
	{
		$data .= "Pre-Depends: " . $predep . "\n";
		print GREEN, "Adding Pre-Depends data: '$predep'\n", RESET
			if ($verbose >= 2);
	}
	if ($dep ne "")
	{
		$data .= "Depends: " . $dep . "\n";
		print GREEN, "Adding Depends data: '$dep'\n", RESET if ($verbose >= 2);
	}
	if ($prov ne "")
	{
		$data .= "Provides: " . $prov . "\n";
		print GREEN, "Adding Provides data:'$prov'\n", RESET if ($verbose >= 2);
	}
	if ($repl ne "")
	{
		$data .= "Replaces: " . $repl . "\n";
		print GREEN, "Adding Replaces data:'$repl'\n", RESET if ($verbose >= 2);
	}
	if ($confl ne "")
	{
		$data .= "Conflicts: " . $confl . "\n";
		print GREEN, "Adding Conflicts data:'$confl'\n", RESET if ($verbose >= 2);
	}
	return $data;
}

sub edos
{
	my $version = `dpkg -f $package Version`;
	chomp($version);
	print GREEN, wrap('','',"Running edos-debcheck for $name ($version)\n"), RESET
		if ($verbose >= 1);
	my $edos = `edos-debcheck $name -explain -failures < $file 2>/dev/null`;
	my @output = split('\n', $edos);
	print @output if ((@output) and ($verbose >= 3));
	my ($fail, $excuse);
	foreach my $line (@output)
	{
		if (defined $fail)
		{
			next if ($fail =~ /\Q$line\E/);
		}
		$fail .= "$line\n" if ($line =~ /(.*):\sFAILED$/);
		$excuse .= "$line\n" if ($line =~ /^\s/);
	}
	# for some reason, unlink can fail for this file.
	my $test = unlink($file);
	system("rm $file") if ($test == 0);
	if ((defined $fail) && (defined $excuse))
	{
		# drop unprintable characters (still need to use ANSI_COLORS_DISABLED
		# to remove escape sequences if appending to a text log).
		$fail = encode("UTF-8", $fail);
		my $logdir = dirname ($package);
		my $default = "edos-debcheck revealed a problem with this build, the package ";
		$default .= "should not be uploaded until the dependencies are fixed.\n";
		$default .= "Explanation follows:\n";
		open (FLAG, ">>$logdir/emrecent_error.log") or 
			die ("$default\n$fail\n$excuse\n".
			"In addition, $progname cannot create log file in $logdir: $!");
		print FLAG "$fail\n$excuse\n";
		print FLAG `date`;
		close FLAG;
		die (RED, "\n$fail\n$excuse", RESET, "\n");
	}
	print GREEN, wrap('','',"Success: edos-debcheck finished.\n"), RESET
		if ($verbose >= 1);
}

=head1 Usage

 emdebcheck [-a|--arch ARCH] FILENAME
 emdebcheck -?|-h|--help|--version

=head1 Options

 -a|--arch ARCH:          Set architecture (default: defined by dpkg-cross)

=head1 Description

emdebcheck is similar to the debcheck routines in Debian, except this is run
before the upload is made.

Whilst native packages can (more or less) rely on pbuilder to ensure that
dependencies are actually available prior to the upload, cross builds use
build dependencies from Debian but get uploaded to a separate repository so
there is no guarantee that the dependencies generated using -cross packages
during the build are actually present in the Emdebian repository, even when
the package is cross built within a chroot.

emdebcheck attempts to cover this gap by parsing the dependency information
from the built package, inserting this data into a copy of the apt cache
for the Emdebian target repository (replacing any data from the current package)
and runs the modified cache data against 'edos-debcheck'.

Note that some packages will <B>always</B> be broken - particularly
-dev packages. Errors are output to the emrecent error logfile - fix the
issue(s) and remove the error log before trying to upload this package
using emrecent.

=head1 Use in pipes

emdebcheck can also read filenames from STDIN, use:
 emdebcheck -
at the end of your pipe.

You can parse a .changes with the 'dcmd' tool from devscripts:
 dcmd ls -1 /$path/foo_vers_arm.changes | grep '.deb$' | grep -v '\-dev_' | sed 'N;s/\n/ /' | emdebcheck -

Note the removal of the -dev package, depending on the source package, you may
also want to remove other packages from the list output by dcmd.

When emdebcheck is passed more than one file <B>these must all
be from the same source package<B> - i.e. scripts can parse the .changes
file, identify the .deb packages and pass them all to emdebcheck. This is
supported so that a binary like bzip2 can be checked alongside the library
that is built from the same source (libbz2-1.0) as both will be uploaded
together, e.g. by emrecent. emrecent also removes -dev packages that are
always likely to fail.

=cut