summaryrefslogtreecommitdiff
path: root/dh_python
blob: 02afea89a7fdfa20be35ecdf8d0342ebee5253f1 (plain)
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
#!/usr/bin/perl -w

=head1 NAME

dh_python - calculates python dependencies and adds postinst and prerm python scripts

=cut

use strict;
use File::Find;
use Debian::Debhelper::Dh_Lib;

=head1 SYNOPSIS

B<dh_python> [S<I<debhelper options>>] [B<-n>] [B<-V> I<version>] [S<I<module dirs ...>>]

=head1 DESCRIPTION

dh_python is a debhelper program that is responsible for generating the
${python:Depends} substitutions and adding them to substvars files. It
will also add a postinst and a prerm script if required.

The program will look at python scripts and modules in your package, and
will use this information to generate a dependency on python, with the
current major version, or on pythonX.Y if your scripts or modules need a
specific python version. The dependency will be substituted into your
package's control file wherever you place the token "${python:Depends}".

If some modules need to be byte-compiled at install time, appropriate
postinst and prerm scripts will be generated. If already byte-compiled
modules are found, they are removed.

If you use this program, your package should build-depend on python.

=head1 OPTIONS

=over 4

=item I<module dirs>

If your package installs python modules in non-standard directories, you
can make dh_python check those directories by passing their names on the
command line. By default, it will check /usr/lib/site-python,
/usr/lib/$PACKAGE, /usr/share/$PACKAGE, /usr/lib/games/$PACKAGE,
/usr/share/games/$PACKAGE and /usr/lib/python?.?/site-packages.

Note: only /usr/lib/site-python, /usr/lib/python?.?/site-packages and the
extra names on the command line are searched for binary (.so) modules.

=item B<-V> I<version>

If the .py files your package ships are meant to be used by a specific
pythonX.Y version, you can set this option with the desired X.Y python
version.
Do not use if you ship modules in /usr/lib/site-python.

=item B<-n>, B<--noscripts>

Do not modify postinst/postrm scripts.

=back

=head1 CONFORMS TO

Debian policy, version 3.5.7

Python policy, version 0.3.7

=cut

init();

my $python = 'python';

# The current python major version
my $python_major;
my $python_version = `$python -V 2>&1`;
if (! defined $python_version || $python_version eq "") {
	error("Python is not installed, aborting. (Probably forgot to Build-Depend on python.)");
}
elsif ($python_version =~ m/^Python\s+(\d+)\.(\d+)(\.\d+)*/) {
	$python_version = "$1.$2" ;
	$python_major = $1 ;
} else { 
	error("Unable to parse python version out of \"$python_version\".");
}

# The next python version
my $python_nextversion = $python_version + 0.1;
my $python_nextmajor = $python_major + 1;

my @python_allversions = ('1.5','2.1','2.2','2.3');
foreach (@python_allversions) {
	s/^/python/;
}

# Check for -V
my $usepython = "python$python_version";
if($dh{V_FLAG_SET}) {
	$usepython = $dh{V_FLAG};
	$usepython =~ s/^/python/;
}

# Cleaning the paths given on the command line
foreach (@ARGV) {
	s#/$##;
	s#^/##;
}

# dependency types
use constant PROGRAM   => 1;
use constant PY_MODULE => 2;
use constant PY_MODULE_NONSTANDARD => 4;
use constant SO_MODULE => 8;
use constant SO_MODULE_NONSTANDARD => 16;

foreach my $package (@{$dh{DOPACKAGES}}) {
	my $tmp = tmpdir($package);

	delsubstvar($package, "python:Depends");

	my @dirs = ("usr/lib/site-python", "usr/lib/$package", "usr/share/$package", "usr/lib/games/$package", "usr/share/games/$package", @ARGV );
	my @dirs_so = ("usr/lib/site-python", @ARGV );

	my $dep_on_python = 0;
	my $strong_dep = 0;
	my $look_for_pythonXY = 1;

	# First, the case of python-foo and pythonX.Y-foo
	if ($package =~ /^python-/) {
		$dep_on_python = 1;
		$strong_dep = 1;
		my $pack = $package;
		$pack =~ s/^python/python$python_version/;
		if (grep { "$_" eq "$pack" } GetPackages()) {
			addsubstvar($package, "python:Depends", $pack);
		}
	}
	if ($package !~ /^python[0-9].[0-9]-/) {
		push @dirs, "usr/lib/python$python_version/site-packages" ;
		push @dirs_so, "usr/lib/python$python_version/site-packages" ;
		$look_for_pythonXY = 0;
	}

	@dirs = grep -d, map "$tmp/$_", @dirs;
	@dirs_so = grep -d, map "$tmp/$_", @dirs_so;

	my $deps = 0;
	my %verdeps = ();
	foreach (@python_allversions) {
		$verdeps{$_} = 0;
	}

	# Find scripts
	find sub {
		return unless -f and (-x or /\.py$/);
		local *F;
		return unless open F, $_;
		if (read F, local $_, 32 and m%^#!\s*/usr/bin/(env\s+)?(python(\d+\.\d+)?)\s%) {
			if ( "python" eq $2 ) {
				$deps |= PROGRAM;
			} elsif(defined $verdeps{$2}) {
				$verdeps{$2} |= PROGRAM;
			}
		}
		close F;
	}, $tmp;

	# Look for python modules
	my $dirlist="";
	if (@dirs) {
		foreach my $curdir (@dirs) {
			my $has_module = 0;
			$curdir =~ s%^$tmp/%%;
			find sub {
				return unless -f;
				if (/\.py$/) {
					$has_module = 1;
					doit(("rm","-f",$_."c",$_."o"));
				}
			}, "$tmp/$curdir" ;
			if ($has_module) {
				if ($dh{V_FLAG_SET}) {
					$verdeps{$usepython} |= PY_MODULE_NONSTANDARD;
				} else {
					$deps |= PY_MODULE;
				}
				$dirlist="$dirlist /$curdir";
			}
		}
	}
	if (@dirs_so) {
		foreach my $curdir (@dirs_so) {
			my $has_module = 0;
			$curdir =~ s%^$tmp/%%;
			find sub {
				return unless -f;
				$has_module = 1 if /\.so$/;
			}, "$tmp/$curdir" ;
			if ($has_module) {
				if ($dh{V_FLAG_SET}) {
					$verdeps{$usepython} |= SO_MODULE_NONSTANDARD;
				}
				else {
					$deps |= SO_MODULE;
				}
			}
		}
	}

	# Dependencies on current python
	$dep_on_python = 1 if $deps;
	$strong_dep = 1 if($deps & (PY_MODULE|SO_MODULE));

	if ($dep_on_python) {
		addsubstvar($package, "python:Depends", $python, ">= $python_version");
		if ($strong_dep) {
			addsubstvar($package, "python:Depends", $python, "<< $python_nextversion");
		} else {
			addsubstvar($package, "python:Depends", $python, "<< $python_nextmajor");
		}
	}

	my $need_prerm = 0;

	# Look for specific pythonX.Y modules
	foreach my $pyver (@python_allversions) {
		my $pydir="/usr/lib/$pyver/site-packages";
		if ($look_for_pythonXY) {
			if (grep -d,"$tmp$pydir") {
				find sub {
					return unless -f;
					if (/\.py$/) {
						$verdeps{$pyver} |= PY_MODULE;
						doit(("rm","-f",$_."c",$_."o"));
					}
					$verdeps{$pyver} |= SO_MODULE if /\.so$/;
				}, "$tmp$pydir";
			}
		}
	
		# Go for the dependencies
		addsubstvar($package, "python:Depends", $pyver) if $verdeps{$pyver};

		# And now, the postinst and prerm stuff
		if ($pyver eq "$usepython") {
			if ($verdeps{$pyver} & PY_MODULE) {
				$pydir = $pydir.$dirlist;
			} else {
				$pydir = $dirlist;
			}
			$verdeps{$pyver} |= PY_MODULE if($deps & PY_MODULE);
		}
		if ($verdeps{$pyver} & (PY_MODULE|PY_MODULE_NONSTANDARD) && ! $dh{NOSCRIPTS}) {
			autoscript($package,"postinst","postinst-python","s%#PYVER#%$pyver%;s%#DIRLIST#%$pydir%");
			$need_prerm = 1;
		}
	}
	if ($need_prerm && ! $dh{NOSCRIPTS}) {
		autoscript($package,"prerm","prerm-python","s%#PACKAGE#%$package%");
	}
}

=head1 SEE ALSO

L<debhelper(7)>

This program is a part of debhelper.

=head1 AUTHOR

Josselin Mouette <joss@debian.org>

most ideas stolen from Brendan O'Dea <bod@debian.org>

=cut