New debhelper update
Hi,
I've uploaded a NMU 5.0.37.3 in DELAYED/1-day fixing several issues
concerning dh_python:
* Update of dh_python
- when buidling for a non-standard Python version, generate more
reasonable Depends like "python (>= X.Y) | pythonX.Y"
Closes: #375576
- fix handling of private extensions. Closes: #375948
- fix parsing of XS-Python-Version, it didn't work if only fixed versions
were listed in XS-Python-Version.
- fix use of unitialized value. Closes: #374776
- fix typos in POD documentation. Closes: #375936
Please find attached the cumulative patch of the last 3 uploads (5.0.37 -> 5.0.37.3).
You can also find a copy of the package here:
http://people.debian.org/~hertzog/python/
http://people.debian.org/~hertzog/python/debhelper_5.0.37.3_all.deb
http://people.debian.org/~hertzog/python/debhelper_5.0.37.3.dsc
Dear debian-python readers, please test this updated dh_python
and report any problem to me.
Cheers,
--
Raphaël Hertzog
Premier livre français sur Debian GNU/Linux :
http://www.ouaza.com/livre/admin-debian/
diff -Nru debhelper-5.0.37/debian/changelog debhelper-5.0.37.3/debian/changelog
--- debhelper-5.0.37/debian/changelog 2006-06-12 03:33:28.000000000 +0200
+++ debhelper-5.0.37.3/debian/changelog 2006-07-10 13:27:50.000000000 +0200
@@ -1,3 +1,52 @@
+debhelper (5.0.37.3) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * Update of dh_python
+ - when buidling for a non-standard Python version, generate more
+ reasonable Depends like "python (>= X.Y) | pythonX.Y"
+ Closes: #375576
+ - fix handling of private extensions. Closes: #375948
+ - fix parsing of XS-Python-Version, it didn't work if only fixed versions
+ were listed in XS-Python-Version.
+ - fix use of unitialized value. Closes: #374776
+ - fix typos in POD documentation. Closes: #375936
+
+ -- Raphael Hertzog <hertzog@debian.org> Mon, 10 Jul 2006 13:20:06 +0200
+
+debhelper (5.0.37.2) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * Update of dh_python
+ - vastly refactored, easier to understand, and the difference
+ between old policy and new policy is easier to grasp
+ - it supports an -X option which can be used to not scan some files
+ - uses debian/pyversions as reference source of information for
+ dependencies but also parse the XS-Python-Version header as fallback.
+ - ${python:Versions}'s default value is XS-Python-Version's value
+ instead of "all" when the package doesn't depend on a
+ specific python version. Closes: #373853
+ - always generate ${python:Provides} and leave the responsibility to the
+ maintainer to not use ${python:Provides} if he doesn't want the
+ provides.
+ - uses debian/pycompat or DH_PYCOMPAT as reference field to run in new
+ policy mode. The presence of XS-Python-Version will also trigger the
+ new policy mode (this is for short-term compatibility, it may be removed in
+ the not too-distant future).
+ DH_PYCOMPAT=1 is the default mode and is compatible to the old policy.
+ DH_PYCOMPAT=2 is the new mode and is compatible with the new policy.
+ * Use "grep ^Version:" instead of "grep Version:" on the output of
+ dpkg-parsechangelog since the above changelog entry matched "Version:" and
+ thus made the build fail.
+
+ -- Raphael Hertzog <hertzog@debian.org> Sat, 17 Jun 2006 20:44:29 +0200
+
+debhelper (5.0.37.1) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * Integrate the new dh_python implementing the new Python policy. Closes: #370833
+
+ -- Raphael Hertzog <hertzog@debian.org> Mon, 12 Jun 2006 08:58:22 +0200
+
debhelper (5.0.37) unstable; urgency=low
* dh_installmodules: depmod -a is no longer run during boot, so if a module
diff -Nru debhelper-5.0.37/debian/rules debhelper-5.0.37.3/debian/rules
--- debhelper-5.0.37/debian/rules 2006-06-03 23:06:41.000000000 +0200
+++ debhelper-5.0.37.3/debian/rules 2006-06-19 18:31:06.000000000 +0200
@@ -26,7 +26,7 @@
}'
# Figure out the `current debhelper version.
-VERSION=$(shell expr "`dpkg-parsechangelog |grep Version:`" : '.*Version: \(.*\)')
+VERSION=$(shell expr "`dpkg-parsechangelog |grep ^Version:`" : '.*Version: \(.*\)')
PERLLIBDIR=$(shell perl -MConfig -e 'print $$Config{vendorlib}')
diff -Nru debhelper-5.0.37/dh_python debhelper-5.0.37.3/dh_python
--- debhelper-5.0.37/dh_python 2006-04-24 22:09:12.000000000 +0200
+++ debhelper-5.0.37.3/dh_python 2006-07-10 14:14:53.000000000 +0200
@@ -12,7 +12,7 @@
=head1 SYNOPSIS
-B<dh_python> [S<I<debhelper options>>] [B<-n>] [B<-V> I<version>] [S<I<module dirs ...>>]
+B<dh_python> [S<I<debhelper options>>] [B<-n>] [B<-X>I<item>] [B<-V> I<version>] [S<I<module dirs ...>>]
=head1 DESCRIPTION
@@ -21,7 +21,40 @@
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
+will use this information to generate adequate dependencies. Additionally,
+it will also use debian/pyversions as a list of python versions that the
+package is known to support. That file contains a single line with a
+comma delimited list of python versions in the format "X.Y". You can
+specify ranges of python versions with "W.X-Y.Z". You can omit one side of
+the range like in "X.Y-" or "-X.Y" which will respectively indicate all
+python versions greater or equal to "X.Y" or smaller or equal to "X.Y".
+
+In order to support two different Python policies, dh_python looks for
+an environment variable DH_PYCOMPAT or the file debian/pycompat. The
+content of those is a single number representing the compatibility mode
+that will be used by dh_python. This number can be 1 or 2.
+
+In both modes, the ${python:Depends} variable contains appropriate
+dependencies on python or pythonX.Y.
+
+=head2 Compatibility level 2 (new policy)
+
+dh_python will only generate substitution variables, it won't take care of
+the byte-compilation of modules any more.
+
+The ${python:Provides} variable will contain versioned provides of the
+package (if the package's name starts with "python-"). A python-foo
+package could provide "python2.3-foo" and "python2.4-foo" at the same
+time. Python extensions have to provide those whereas it's only option
+for pure python modules.
+
+The ${python:Versions} variable can be used to provide an optional
+XB-Python-Version field listing the python versions supported by the
+package.
+
+=head2 Compatibility level 1 (old policy)
+
+It looks at scripts and modules in your package and adds 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}".
@@ -32,6 +65,8 @@
If you use this program, your package should build-depend on python.
+Note: in the old policy, /usr/lib/site-python is also scanned for modules.
+
=head1 OPTIONS
=over 4
@@ -40,11 +75,10 @@
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,
+command line. By default, it will check /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
+Note: only /usr/lib/python?.?/site-packages and the
extra names on the command line are searched for binary (.so) modules.
=item B<-V> I<version>
@@ -53,17 +87,26 @@
pythonX.Y version, you can use this option to specify the desired version,
such as 2.3. Do not use if you ship modules in /usr/lib/site-python.
+With the new policy, this option is mostly deprecated. Use the
+XS-Python-Field to indicate that you're using a specific python version.
+
=item B<-n>, B<--noscripts>
Do not modify postinst/postrm scripts.
+=item B<-X>I<item>, B<--exclude=>I<item>
+
+Exclude files that contain "item" anywhere in their filename from being
+taken into account to generate the python dependency. You may use this
+option multiple times to build up a list of things to exclude.
+
=back
=head1 CONFORMS TO
-Debian policy, version 3.5.7
+Debian policy, version 3.7.2
-Python policy, version 0.3.7
+Python policy, version 0.4.1 (2006-06-20)
=cut
@@ -85,10 +128,10 @@
}
# The next python version
-my $python_nextversion = $python_version + 0.1;
+my $python_nextversion = next_minor_version($python_version);
my $python_nextmajor = $python_major + 1;
-my @python_allversions = ('1.5','2.1','2.2','2.3','2.4');
+my @python_allversions = ('1.5','2.1','2.2','2.3','2.4','2.5');
foreach (@python_allversions) {
s/^/python/;
}
@@ -109,39 +152,96 @@
s#^/##;
}
+# Check the compatibilty mode to use
+my $compat_mode = "";
+my $pyversions_field = "";
+my $python_header = "";
+{
+ local $/ = ""; # Grab until empty line
+ open(CONTROL, "debian/control"); # Can't fail, dh_testdir has already been called
+ my $source = <CONTROL>;
+ close(CONTROL);
+ if ($source =~ m/^XS-Python-Version: \s*(.*)$/m) {
+ $python_header = $1;
+ chomp($python_header);
+ $pyversions_field = convert_python_header($python_header);
+ $compat_mode = 2 unless $compat_mode; #
+ }
+}
+if (defined($ENV{DH_PYCOMPAT})) {
+ $compat_mode = $ENV{DH_PYCOMPAT};
+} elsif (-e "debian/pycompat") {
+ open (COMPAT_IN, "debian/pycompat") || error "debian/pycompat: $!";
+ my $l=<COMPAT_IN>;
+ chomp $l;
+ if ($l) {
+ $compat_mode = $l
+ } else {
+ warning("debian/pycompat is empty, assuming dh_python compatibility level 1");
+ }
+}
+$compat_mode = 1 unless $compat_mode; # Default to old policy
+error("Maximum dh_python compatibility mode supported is 2.") if $compat_mode > 2;
+
+# pyversions describes the list of python versions that this package can
+# work with
+if (-e "debian/pyversions") {
+ open(PYVERS, "debian/pyversions") || error("Can't open debian/pyversions: $!");
+ $pyversions_field = <PYVERS>;
+ chomp($pyversions_field);
+ close(PYVERS);
+}
+verbose_print("Pyversions field: $pyversions_field");
+
+# Extract a list of officially supported Python versions
+my %officially_supported;
+if (-e "/usr/share/python/debian_defaults") {
+ open(DEFAULTS, "/usr/share/python/debian_defaults") ||
+ error("Can't open /usr/share/python/debian_defaults: $!");
+ foreach (<DEFAULTS>) {
+ if (/^supported-versions\s*=\s*(.*)$/) {
+ my $supported = $1;
+ foreach my $version (split(/\s+/, $supported)) {
+ if ($version =~ /python([\d\.]+)/) {
+ $officially_supported{$1} = 1;
+ }
+ }
+ last;
+ }
+ }
+ close(DEFAULTS);
+}
+
# 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;
+use constant PY_PRIVATE_MODULE => 2;
+use constant PY_PUBLIC_MODULE => 4;
+use constant PY_OFFICIAL_MODULE => 8;
+use constant PY_UNKNOWN_MODULE => 16;
+use constant SO_PRIVATE_MODULE => 32;
+use constant SO_PUBLIC_MODULE => 64;
+use constant SO_OFFICIAL_MODULE => 128;
+use constant SO_UNKNOWN_MODULE => 256;
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 @dirs = ("usr/lib/$package", "usr/share/$package", "usr/lib/games/$package", "usr/share/games/$package", @ARGV );
+ my @dirs_so = (@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/$usepython/site-packages";
- push @dirs_so, "usr/lib/$usepython/site-packages";
- $look_for_pythonXY = 0;
+ # Fail early if the package use usr/lib/site-python
+ if (-d "$tmp/usr/lib/site-python") {
+ if ($compat_mode >= 2) {
+ # Error only on new policy
+ error("The package $package puts files in /usr/lib/site-python: forbidden by policy");
+ } else {
+ # Old policy allowed that directory, so scan it
+ push @dirs, "usr/lib/site-python";
+ push @dirs_so, "usr/lib/site-python";
+ }
}
@dirs = grep -d, map "$tmp/$_", @dirs;
@@ -153,114 +253,514 @@
$verdeps{$_} = 0;
}
- # Find scripts
+ # Global scan
+ my $private_pydirs_regex = join('|', map { "\Q$_\E" } @dirs);
+ my $private_sodirs_regex = join('|', map { "\Q$_\E" } @dirs_so);
+ my %private_dirs_list;
+ my %pyversions_found;
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;
+ return unless -f;
+ # See if we were asked to exclude this file.
+ # Note that we have to test on the full filename, including directory.
+ my $fn="$File::Find::dir/$_";
+ if (excludefile($fn)) {
+ verbose_print("Ignoring $fn");
+ return;
+ }
+ # Find scripts
+ if (-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%) {
+ verbose_print("Found program using $2: $fn");
+ if ( "python" eq $2 ) {
+ $deps |= PROGRAM;
+ } elsif(defined $verdeps{$2}) {
+ $verdeps{$2} |= PROGRAM;
+ }
}
+ close F;
}
- close F;
- }, $tmp;
+ # Continue only with .py or .so
+ return unless (/\.py$/ or /\.so$/);
- # 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"));
+ # Remove any byte-compiled file
+ doit(("rm","-f",$_."c",$_."o")) if /\.py$/;
+
+ # Classify the file in the right category
+ if (/\.py$/ and $private_pydirs_regex and $fn =~ m/(?:$private_pydirs_regex)/) {
+ # Private python module
+ verbose_print("Found private module: $fn");
+ my $dir;
+ foreach $dir (@dirs) {
+ if ($fn =~ m/\Q$dir\E/) {
+ $dir =~ s/^$tmp//;
+ verbose_print("Memorizing dir to byte-compile: $dir");
+ $private_dirs_list{"$dir"} = 1;
+ }
+ }
+ if ($dh{V_FLAG_SET}) {
+ $verdeps{$usepython} |= PY_PRIVATE_MODULE;
+ } else {
+ $deps |= PY_PRIVATE_MODULE;
+ }
+ } elsif (/\.so$/ and $private_sodirs_regex and $fn =~ m/(?:$private_sodirs_regex)/) {
+ # Private python extension
+ verbose_print("Found private extension: $fn");
+ if ($dh{V_FLAG_SET}) {
+ $verdeps{$usepython} |= SO_PRIVATE_MODULE;
+ } else {
+ $deps |= SO_PRIVATE_MODULE;
+ }
+ } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/site-packages/|) {
+ $pyversions_found{$1} = 1;
+ my $v = $1;
+ if (/\.py$/) {
+ verbose_print("Found public module: $fn");
+ $verdeps{"python$v"} |= PY_PUBLIC_MODULE;
+ } else {
+ verbose_print("Found public extension: $fn");
+ $verdeps{"python$v"} |= SO_PUBLIC_MODULE;
+ }
+ } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/|) {
+ $pyversions_found{$1} = 1;
+ my $v = $1;
+ if (/\.py$/) {
+ verbose_print("Found official module: $fn");
+ $verdeps{"python$v"} |= PY_OFFICIAL_MODULE;
+ } else {
+ verbose_print("Found official extension: $fn");
+ $verdeps{"python$v"} |= SO_OFFICIAL_MODULE;
+ }
+ } elsif ($fn =~ m|$tmp/usr/lib/python-support/[^/]+/python([\d\.]+)/|) {
+ $pyversions_found{$1} = 1;
+ my $v = $1;
+ if (/\.py$/) {
+ verbose_print("Found public module: $fn");
+ $verdeps{"python$v"} |= PY_PUBLIC_MODULE;
+ } else {
+ verbose_print("Found public extension: $fn");
+ $verdeps{"python$v"} |= SO_PUBLIC_MODULE;
+ }
+ } elsif ($fn =~ m{$tmp(?:/usr/share/pycentral/|/usr/share/python-support/)}) {
+ if (/\.py$/) {
+ verbose_print("Found public module: $fn");
+ $deps |= PY_PUBLIC_MODULE;
+ } # No extensions here
+ } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/site-packages/|) {
+
+ } elsif ($fn =~ m|$tmp/usr/share/doc/|) {
+ # Ignore .py files in doc directory
+ } else {
+ # Unknown pyfiles
+ if (/\.py$/) {
+ verbose_print("Found unclassified module: $fn");
+ if ($dh{V_FLAG_SET}) {
+ $verdeps{$usepython} |= PY_UNKNOWN_MODULE;
+ } else {
+ $deps |= PY_UNKNOWN_MODULE;
}
- }, "$tmp/$curdir" ;
- if ($has_module) {
+ } else {
+ verbose_print("Found unclassified extension: $fn");
if ($dh{V_FLAG_SET}) {
- $verdeps{$usepython} |= PY_MODULE_NONSTANDARD;
+ $verdeps{$usepython} |= SO_UNKNOWN_MODULE;
} else {
- $deps |= PY_MODULE;
+ $deps |= SO_UNKNOWN_MODULE;
}
- $dirlist="$dirlist /$curdir";
}
}
+ }, $tmp;
+
+ # Common dependency handling
+ foreach my $pyver (keys %verdeps) {
+ # Always add pythonX.Y dependency if a script uses that interpreter
+ if ($verdeps{$pyver} & PROGRAM) {
+ addsubstvar($package, "python:Depends", $pyver);
+ }
+ # Always add pythonX.Y dependency if some private modules are
+ # byte-compiled with it (or if extensions are
+ # byte-compiled with it)
+ if ($verdeps{$pyver} & (PY_PRIVATE_MODULE|SO_PRIVATE_MODULE)) {
+ addsubstvar($package, "python:Depends", $pyver);
+ }
}
- 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;
+
+ if ($compat_mode == 2) {
+ #
+ # NEW POLICY
+ #
+ # Generate the depends to accept all python
+ # versions that this package effectively provides
+ my ($min_version, $max_version, @versions) = analyze_pyversions($pyversions_field);
+ my $stop_version = "";
+ $stop_version = next_minor_version($max_version) if $max_version;
+ my $versions_field = "";
+ verbose_print("Pyversions analysis gives: min=$min_version, max=$max_version (@versions)");
+
+ # Reset again, analysis using new policy follows
+ $dep_on_python = 0;
+
+ # Private extensions, must be rebuilt for each python version
+ if ($deps & SO_PRIVATE_MODULE) {
+ $dep_on_python++;
+ # Packages using a private extension can only
+ # support one version
+ # Unless min/max are the same we put $python_version
+ if ($min_version and ($min_version eq $max_version)) {
+ $versions_field = $min_version;
+ } else {
+ if (compare_version($min_version, $python_version) > 0) {
+ # The current version is unsupported, use the min version instead
+ $versions_field = $min_version;
+ } else {
+ # Use the current version by default
+ $versions_field = $python_version;
+ $min_version = $python_version;
}
- else {
- $deps |= SO_MODULE;
+ $stop_version = next_minor_version($versions_field);
+ }
+ }
+
+ # Private modules
+ if ($deps & PY_PRIVATE_MODULE) {
+ # Package with private modules can only support one version at a time
+ # (even if the modules can be automatically byte-compiled for any new
+ # python version).
+ unless ($versions_field) {
+ # Unless min/max are the same we put "current"
+ if ($min_version and ($min_version eq $max_version)) {
+ $versions_field = $min_version;
+ } else {
+ $versions_field = "current";
}
}
}
- }
-
- # 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");
+ # Python scripts & public modules
+ if ($deps & (PROGRAM|PY_PUBLIC_MODULE)) {
+ $dep_on_python++;
+ }
+
+ # Public extensions
+ if (scalar keys %pyversions_found) {
+ # Extensions will always trigger this (as well as public
+ # modules not handled by python-support/python-central)
+ $dep_on_python++;
+ if (scalar grep { $python_version eq $_ } keys %pyversions_found) {
+ # Current versions is supported by the package
+ # It's safe to depends on the corresponding
+ # interval of python versions
+ $min_version = min(keys %pyversions_found);
+ unless ($stop_version) {
+ $max_version = max(keys %pyversions_found);
+ $stop_version = next_minor_version($max_version);
+ }
+ }
+ # Generate the Python-Version field
+ foreach (keys %pyversions_found) {
+ addsubstvar($package, "python:Versions", $_);
+ }
+ # Generate provides for the python2.X-foo packages that we emulate
+ if ($package =~ /^python-/) {
+ foreach (keys %pyversions_found) {
+ my $virtual = $package;
+ $virtual =~ s/^python-/$python$_-/;
+ addsubstvar($package, "python:Provides", $virtual);
+ }
+ }
} else {
- addsubstvar($package, "python:Depends", $python, "<< $python_nextmajor");
+ # Still try to describe the versions that the package support
+ $versions_field = $python_header unless $versions_field;
+ $versions_field = "all" unless $versions_field;
+ addsubstvar($package, "python:Versions", $versions_field);
+
+ # Generate provides for all python versions supported
+ if ($package =~ /^python-/) {
+ foreach (grep { $officially_supported{$_} } @versions) {
+ my $virtual = $package;
+ $virtual =~ s/^python-/$python$_-/;
+ addsubstvar($package, "python:Provides", $virtual);
+ }
+ }
+ }
+
+ if ($dep_on_python) {
+ # At least a script has been detected
+ if ($min_version) {
+ if (compare_version($min_version, $python_version) <= 0 ) {
+ # Min-version is less or equal to current version
+ addsubstvar($package, "python:Depends", $python, ">= $min_version");
+ } else {
+ # Min version is greater to current version
+ # Supposition: new stuff working only with the latest python,
+ # depend on that specific python version
+ addsubstvar($package, "python:Depends",
+ "python (>= $min_version) | python$min_version");
+ }
+ }
+ # If a stronger dependency is needed
+ if ($stop_version) {
+ if (compare_version($stop_version, $python_version) > 0 ) {
+ # Stop version is strictly bigger than current version
+ addsubstvar($package, "python:Depends", $python, "<< $stop_version");
+ } else {
+ # Only works with old versions,
+ # package is mostly deprecated,
+ # no need for a python dependency
+ }
+ }
+ # Let's depend on python anyway
+ addsubstvar($package, "python:Depends", $python) unless ($min_version or $stop_version);
}
- }
- my $need_prerm = 0;
+ } elsif ($compat_mode == 1) {
+ #
+ # OLD POLICY
+ #
+
+ # First, the case of python-foo and pythonX.Y-foo
+ if ($package =~ /^python-/) {
+ $dep_on_python = 1;
+ $strong_dep = 1;
+ # This adds a dependency to python<current>-foo in python-foo
+ my $pack = $package;
+ $pack =~ s/^python/python$python_version/;
+ if (grep { "$_" eq "$pack" } getpackages()) {
+ addsubstvar($package, "python:Depends", $pack);
+ }
+ }
- # 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;
+ # Dependencies on current python
+ $dep_on_python = 1 if ($deps &
+ (PROGRAM|PY_PUBLIC_MODULE|SO_PUBLIC_MODULE|PY_PRIVATE_MODULE|SO_PRIVATE_MODULE));
+ $strong_dep = 1 if ($deps & (PY_PUBLIC_MODULE|SO_PUBLIC_MODULE));
+
+ # Add python dependencies
+ if ($dep_on_python) {
+ addsubstvar($package, "python:Depends", $python, ">= $python_version");
+ if ($strong_dep) {
+ addsubstvar($package, "python:Depends", $python, "<< $python_nextversion");
} else {
- $pydir = $dirlist;
+ addsubstvar($package, "python:Depends", $python, "<< $python_nextmajor");
}
- $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;
+
+ # Add postinst/preinst
+ my $need_prerm = 0;
+ foreach my $pyver (@python_allversions) {
+ my $pydir="/usr/lib/$pyver/site-packages";
+ my @dirlist = (); # List of directories to byte-compile
+
+ # If we have public modules, byte-compile /usr/lib/pythonX.Y/site-packages/
+ if ($verdeps{$pyver} & PY_PUBLIC_MODULE) {
+ push @dirlist, $pydir;
+ }
+
+ # Byte-compile private modules with current python
+ # (or the one indicated by -V)
+ if (($pyver eq "$usepython") and scalar(keys %private_dirs_list)) {
+ push @dirlist, keys %private_dirs_list;
+ }
+
+ # If we have something to byte-compile, add the
+ # corresponding postinst snippet
+ if (scalar(@dirlist) && ! $dh{NOSCRIPTS}) {
+ autoscript($package,"postinst","postinst-python","s%#PYVER#%$pyver%;s%#DIRLIST#%@dirlist%");
+ $need_prerm = 1;
+ }
+ }
+ if ($need_prerm && ! $dh{NOSCRIPTS}) {
+ autoscript($package,"prerm","prerm-python","s%#PACKAGE#%$package%");
}
}
- if ($need_prerm && ! $dh{NOSCRIPTS}) {
- autoscript($package,"prerm","prerm-python","s%#PACKAGE#%$package%");
+}
+
+sub next_minor_version {
+ my $version = shift;
+ # Handles 2.10 -> 2.11 gracefully
+ my @items = split(/\./, $version);
+ $items[1] += 1;
+ $version = join(".", @items);
+ return $version;
+}
+
+sub compare_version {
+ my ($a, $b) = @_;
+ my @A = split(/\./, $a);
+ my @B = split(/\./, $b);
+ my $diff = 0;
+ for (my $i = 0; $i <= $#A; $i++) {
+ $diff = $A[$i] - $B[$i];
+ return $diff if $diff;
+ }
+ # They are the same
+ return 0;
+}
+
+sub max {
+ my $max = shift;
+ foreach (@_) {
+ $max = $_ if (compare_version($_, $max) > 0);
+ }
+ return $max;
+}
+
+sub min {
+ my $min = shift;
+ foreach (@_) {
+ $min = $_ if (compare_version($_, $min) < 0);
+ }
+ return $min;
+}
+
+# Extract min, max and list of versions from
+# a string like this "-1.2,1.4-1.6,2.0,2.3-"
+sub analyze_pyversions {
+ my $field = shift;
+ my ($min, $max, @versions);
+
+ my @all_versions = ("0.0");
+ foreach (@python_allversions) {
+ if (m/python([\d\.]+)/) {
+ push @all_versions, $1;
+ }
+ }
+ push @all_versions, "100.0";
+
+ foreach my $range (split /,/, $field) {
+ if ($range =~ /^([\d\.]+)?-([\d\.]+)?$/) {
+ $min = defined($1) && $1 ? $1 : "0.0";
+ $max = defined($2) && $2 ? $2 : "100.0";
+ push @versions, grep {
+ compare_version($_, $min) >= 0 and
+ compare_version($_, $max) <= 0 } @all_versions;
+ } else {
+ push @versions, $range if (grep { $range eq $_ } @all_versions);
+ }
+ }
+
+ $min = min(@versions);
+ $max = max(@versions);
+ $min = "" if (!defined($min) or $min eq "0.0");
+ $max = "" if (!defined($max) or $max eq "100.0");
+
+ @versions = grep { $_ ne "0.0" and $_ ne "100.0" } @versions;
+
+ return ($min, $max, @versions);
+}
+
+# Convert the Python-Version field in a standardized "pyversions" field
+sub convert_python_header {
+ my $header = shift;
+ my %pyversions_expected;
+ my %pyversions_additional;
+ my $use_additional = 0;
+ my @all_versions = ();
+ foreach (@python_allversions) {
+ if (m/python([\d\.]+)/) {
+ $pyversions_additional{$1} = 1;
+ push @all_versions, $1;
+ }
+ }
+ # Add two fakes minima, maxima to check if we have limits
+ $pyversions_additional{"0.0"} = 1;
+ $pyversions_additional{"100.0"} = 1;
+
+ # Compute the list of versions that are supported
+ foreach my $check (split(/,\s*/, $header)) {
+ if ($check =~ /^=?\s*([\d\.]+)/) {
+ # Add any fixed version
+ $pyversions_expected{$1} = 1 if (grep { $_ eq $1 } @all_versions);
+ next;
+ }
+ # Deactivate versions which do not match the other
+ # criteria
+ if ($check =~ /^<<\s*([\d\.]+)/) {
+ my $v = $1;
+ $use_additional = 1;
+ foreach (keys %pyversions_additional) {
+ $pyversions_additional{$_} = 0 unless (compare_version($_, $v) < 0);
+ }
+ } elsif ($check =~ /^<=\s*([\d\.]+)/) {
+ my $v = $1;
+ $use_additional = 1;
+ foreach (keys %pyversions_additional) {
+ $pyversions_additional{$_} = 0 unless (compare_version($_, $v) <= 0);
+ }
+ } elsif ($check =~ /^>=\s*([\d\.]+)/) {
+ my $v = $1;
+ $use_additional = 1;
+ foreach (keys %pyversions_additional) {
+ $pyversions_additional{$_} = 0 unless (compare_version($_, $v) >= 0);
+ }
+ }
+ }
+ if ($use_additional) {
+ foreach (grep { $pyversions_additional{$_} } keys %pyversions_additional) {
+ $pyversions_expected{$_} = 1;
+ }
+ }
+
+ my @supported = sort { compare_version($a, $b) }
+ grep { $pyversions_expected{$_} == 1 } keys %pyversions_expected;
+
+ verbose_print("List of versions supported according to XS-Python-Version: @supported");
+
+ # Generate the corresponding information in standardized "pyversions" format
+ # XXX: I go to great length to generate the shortest pyversions
+ # syntax but it may not be necessary for the usage that we make of it.
+ my ($result, $index, $range_is_open, $last_real_version) = ("", 0, 0, "");
+ foreach (@supported) {
+ if ($_ eq "0.0") {
+ $result .= "-"; # No lower limit
+ $range_is_open = 1;
+ } elsif ($_ eq "100.0") {
+ $result .= "-" unless $range_is_open; # No upper limit
+ $range_is_open = 0; # Proper end
+ } else {
+ $last_real_version = $_;
+ if ($range_is_open) {
+ # Range is open
+ if ($index <= $#all_versions and $all_versions[$index] eq $_) {
+ # This version still in the range
+ } else {
+ # Previous version ended the range
+ $result .= $all_versions[$index-1] . ",$_";
+ $range_is_open = 0;
+ }
+ # Find the current version in all_versions
+ while ($index <= $#all_versions) {
+ last if ($all_versions[$index] eq $_);
+ $index++;
+ }
+ } else {
+ # There's no range yet
+ if ($result) {
+ if ($index <= $#all_versions and $all_versions[$index] eq $_) {
+ # This version still match, we can start a range
+ $result .= "-";
+ $range_is_open = 1;
+ } else {
+ # Next version doesn't match, no range but a list
+ $result .= ",$_";
+ }
+ } else {
+ # Empty field, start with something!
+ $result = "$_";
+ }
+ while ($index <= $#all_versions) {
+ last if ($all_versions[$index] eq $_);
+ $index++;
+ }
+ }
+ $index++;
}
+ }
+ if ($range_is_open) {
+ # Close the range properly
+ $result .= $last_real_version;
+ }
+ return $result;
}
=head1 SEE ALSO
@@ -269,9 +769,10 @@
This program is a part of debhelper.
-=head1 AUTHOR
+=head1 AUTHORS
Josselin Mouette <joss@debian.org>
+Raphael Hertzog <hertzog@debian.org>
most ideas stolen from Brendan O'Dea <bod@debian.org>
Reply to: