summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog6
-rwxr-xr-xdh_gentdeb621
2 files changed, 626 insertions, 1 deletions
diff --git a/debian/changelog b/debian/changelog
index 847c70d..70f1b94 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,10 +1,14 @@
debhelper (7.1.1) UNRELEASED; urgency=low
+ [ Joey Hess ]
* dh_install(1): Order options alphabetically. Closes:# 503896
* Fix some docs that refered to --srcdir rather than --sourcedir.
Closes: #504742
- -- Joey Hess <joeyh@debian.org> Mon, 03 Nov 2008 18:50:03 -0500
+ [ Neil Williams ]
+ * Add experimental dh_gentdeb
+
+ -- Neil Williams <codehelp@debian.org> Fri, 28 Nov 2008 18:17:03 +0100
debhelper (7.1.0) experimental; urgency=low
diff --git a/dh_gentdeb b/dh_gentdeb
new file mode 100755
index 0000000..756c339
--- /dev/null
+++ b/dh_gentdeb
@@ -0,0 +1,621 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_gentdeb - build debian TDeb translation packages
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+# need to remove this module once debhelper recognises tdebs
+use Parse::DebControl;
+
+=head1 SYNOPSIS
+
+B<dh_gentdeb> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+dh_gentdeb prepares localisation content for a debian Tdeb
+package.
+
+Only the -p debhelper option is handled by dh_gentdeb, if none is
+specified the TDeb package name will be '$sourcepackage-tdeb'.
+
+=cut
+
+=head1 DESCRIPTION
+
+dh_gentdeb is a debhelper add-on created by Emdebian to create
+translation packages (tdebs). dh_gentdeb is intended to separate
+out the individual translation files from the current Debian packages
+into packages without any translation files and a single TDeb package,
+one per source package.
+
+Generated packages use the syntax:
+ $srcpackage-tdeb_$version_all.tdeb
+
+If a second tdeb is supported by one source package, the $srcpackage-tdeb
+package must contain any debconf templates used by any of the binary
+packages. The second tdeb is then used for translations of optional
+content.
+
+(Note that Debian TDebs are architecture-independent, Emdebian TDebs
+are architecture-dependent.)
+
+Once a package uses dh_gentdeb, translation files must be removed
+from all packages in the normal build. This includes all translated
+manpages and other translated content. Original, untranslated, content
+should remain.
+
+dh_gentdeb runs as a part of the normal package build - simply add
+the call to the binary-indep target of debian/rules, usually after
+dh_install and before dh_builddeb. dh_gentdeb handles locating the
+relevant files, a .install file is not normally necessary.
+
+Support for a tdeb diff1.gz will be added as dh_gentdeb develops.
+The extra diff is used by translators to build updated or new tdeb
+packages. Tdeb packages depend on the source:Version of the mainpackage
+but no packages may depend upon the tdeb. (Not even other TDebs). The
+mainpackage can be specified using the -p option.
+
+Use of diff1.gz should remove the need to create a customised source
+with a debian/rules stub etc. by allowing Emdebian TDebs to be created
+during an Emdebian build and Debian TDebs in a Debian build.
+Translators would then be able to use:
+
+ $ apt-get source $package
+ $ cd $package-$version/
+ $ poedit po/$lang.po
+
+To build the package, either dh_gentdeb can behave as em_installtdeb
+does now and run a build only of the TDeb components or
+dpkg-buildpackage could gain an option to only process the TDeb.
+
+dh_gentdeb currently only supports gettext translation.
+
+The locale package must use GETTEXT_PACKAGE for the eventual filename
+of the binary translation file - although this may be the same as the
+$dh{MAINPACKAGE}. GETTEXT_PACKAGE is determined by upstream, not Debian.
+When building the whole package, the binary translation file may be in
+debian/tmp/usr/share/locale/$lang/LC_MESSAGES but when in translator mode,
+this location is not available. Instead retrieve GETTEXT_PACKAGE from
+the POT filename, the Makefile GETTEXT_PACKAGE macro or if that is not set,
+use the upstream source package name. This may need extending.
+
+Some packages use multiple po directories and dh_gentdeb checks for
+a POT file in all usable po directories, including within the tdeb source,
+along with all po files: e.g.
+
+ po/fr.po
+ po-lib/fr.po
+ po/application.pot
+ po-lib/library.pot
+
+When packaged, the Debian tdeb built from this source would contain:
+
+ ./fr/usr/share/locale/fr/LC_MESSAGES/application.mo
+ ./de/usr/share/locale/de/LC_MESSAGES/application.mo
+ ./fr/usr/share/locale/fr/LC_MESSAGES/library.mo
+ ./de/usr/share/locale/de/LC_MESSAGES/library.mo
+
+For more detail on Tdebs, see:
+L<http://www.emdebian.org/emdebian/langupdate.html>
+L<http://wiki.debian.org/i18n/TranslationDebs>
+
+Note that the Debian implementation of tdebs differs from the
+tdebs for Emdebian because Emdebian does not care about manpages in
+general, let alone translated manpages. Once the 'nodocs'
+DEB_BUILD_OPTION is supported in debhelper, this will not be an issue
+as the tdebs can be built for Emdebian without any manpages. Other
+translated documentation would be omitted under 'nodocs' too. Images
+containing translated text are relatively few.
+
+=cut
+
+=head1 OPTIONS
+
+The default action is to process all available po files and all
+identifiable translated content.
+
+=cut
+
+use vars qw/@packages $mainpackage $lang $fullname $tdebname %package_types
+$section $priority %lang_equiv $file %lang_codes @new_locales $topdirprefix
+$finprefix $finsuffix $single $gettext_package @podirs %gettextdirs $version
+$sign @names $builddir $source /;
+
+&init();
+&getpackages();
+
+# need data from debian/control even if xcontrol does not exist.
+my $parser = new Parse::DebControl;
+my $options = { stripComments => 1};
+my $xcontrol = $parser->parse_file('./debian/control', $options);
+
+for my $stanza (@$xcontrol)
+{
+ my $type = $stanza->{'XC-Package-Type'};
+ $source = $stanza->{'Source'} if (defined $stanza->{'Source'});
+ if (defined $type)
+ {
+ $package_types{$stanza->{'Package'}}=$type;
+ }
+}
+$tdebname = "${source}-tdeb";
+
+# Return true if a given package is really a tdeb.
+# Needs to be supported by Dh_Lib.pm alongside udeb,
+# then the Parse::DebianControl module can be dropped.
+sub is_tdeb {
+ my $package=shift;
+ return 0 unless (exists $package_types{$package});
+ return $package_types{$package} eq 'tdeb';
+}
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ next unless is_tdeb ($package);
+ $tdebname = $package;
+}
+
+&parse_control;
+exit 0;
+
+sub get_gettext_names
+{
+ my $podir = shift;
+ # this fails if the package uses a build-tree.
+ if (-f "$podir/Makefile")
+ {
+ open (MK, "$podir/Makefile") or
+ die ("Failed to read $podir/Makefile: $!\n");
+ my @mkfile=<MK>;
+ close MK;
+ my @gp_ = grep /GETTEXT_PACKAGE*/, @mkfile;
+ foreach my $gp (@gp_)
+ {
+ chomp($gp);
+ $gp =~ s/ //g;
+ if ($gp =~ /^GETTEXT_PACKAGE=(.*)$/)
+ {
+ return $1;
+ }
+ }
+ @gp_ = grep /domainname*/i, @mkfile;
+ foreach my $gp (@gp_)
+ {
+ chomp($gp);
+ $gp =~ s/ //g;
+ if ($gp =~ /^domainname=(.*)$/i)
+ {
+ return $1;
+ }
+ }
+ }
+ else
+ {
+ opendir (POT, "$podir") or die ("Cannot open $podir\n");
+ my @potname=grep(/\.pot$/, readdir(POT));
+ closedir (POT);
+ if (@potname)
+ {
+ my $name = $potname[0];
+ $name =~ s/(.*)\.pot$/$1/;
+ return $name if (defined ($name));
+ }
+ }
+ # if no po/Makefile exists, try the top_srcdir Makefile
+ if (-f "Makefile")
+ {
+ open (MK, "Makefile") or die ("Failed to read Makefile: $!\n");
+ my @mkfile=<MK>;
+ close MK;
+ my @gp_ = grep /GETTEXT_PACKAGE*/, @mkfile;
+ foreach my $gp (@gp_)
+ {
+ chomp($gp);
+ $gp =~ s/ //g;
+ if ($gp =~ /^GETTEXT_PACKAGE=(.*)$/)
+ {
+ return $1;
+ }
+ }
+ }
+ # if all this fails, use debian/xcontrol instead.
+ return $source;
+}
+
+sub check_debian
+{
+ my $pkg;
+ # check this is a debian working directory
+ until (-f "debian/changelog")
+ {
+ chdir ".." or die "Cannot change directory ../ $!";
+ if (cwd() eq '/')
+ {
+ die "Cannot find debian/changelog anywhere!\nAre you in the source code tree?\n";
+ }
+ }
+ my $clog = `dpkg-parsechangelog`;
+ my $r = $clog;
+ $clog =~ /Version: (.*)\n/;
+ $version = $1;
+ # strip epoch
+ $version =~ s/[0-9]://;
+ # try to assume that po/ exists
+ @podirs= `find . -name 'po*' -a -type d`;
+ my @templist = ();
+ foreach my $podir (@podirs)
+ {
+ chomp($podir);
+ next unless $podir =~ /\/po[-]?.*/;
+ next unless (-d $podir);
+ # TODO: handle manpage translations source - wrap in nodocs support.
+ if (($podir =~ m#manpage#) or ($podir =~ m#^\./debian/po#)
+ or ($podir =~ m#man#))
+ {
+ next;
+# print "DEBUG: found PO content in $podir - needs to go into src package or diff1.gz\n";
+ }
+ push @templist, $podir;
+ }
+ @podirs = @templist;
+ # if custom support requested, avoid tampering with POT files.
+ return if (@names);
+ foreach my $podir (@podirs)
+ {
+ my @potfiles= `find $podir -maxdepth 1 -name '*po' -type f`;
+ next unless @potfiles;
+ $gettext_package = &get_gettext_names($podir);
+ # one package per po directory
+ $gettextdirs{$gettext_package} = $podir;
+ if (scalar @potfiles == 0)
+ {
+ # if no POT file exists, try to make it.
+ # the Makefile in the $podir always uses GETTEXT_PACKAGE
+ # for *this* POT file even if the top_srcdir Makefile
+ # uses more than one GETTEXT variable.
+ system ("make -C $podir $gettext_package.pot") if (-f "$podir/Makefile");
+ }
+ die "Cannot find POT file in $podir!\n"
+ if (not defined $gettext_package);
+ }
+}
+
+sub parse_xcontrol
+{
+ my $xcontrol;
+ return $xcontrol if (! -f "debian/xcontrol");
+ my $parser = new Parse::DebControl;
+ my $options;
+ $xcontrol = $parser->parse_file('./debian/xcontrol', $options);
+ return $xcontrol;
+}
+
+sub find_messages
+{
+ my $code;
+ my $v = "";
+ # skip our own packages
+ return if ($_[0] =~ m:/debian/:);
+ # gettext dirs *should* be the same as podirs but not all
+ # packages play by those rules.
+ foreach my $podir (@podirs)
+ {
+ chomp ($podir);
+ next unless $podir =~ /\/po[-]?.*/;
+ next unless (-d $podir);
+ my @pofiles=`find $podir -name '*po' -a -type f`;
+ next unless @pofiles;
+ foreach my $pofile (@pofiles)
+ {
+ chomp($pofile);
+ my $c = basename ($pofile);
+ $pofile = $c;
+ $pofile =~ /^(.*)\.po$/;
+ my $a = $code = $1;
+ next unless (defined ($code));
+ $code =~ s/[_]/-/;
+ $code =~ s/[@]/+/;
+ $code = lc ($code);
+ $code =~ s/\/.*//;
+ $a =~ s/\/.*//;
+ # if a package has more than one translation, only set one lang_code
+ $lang_codes{$code} = 1;
+ $lang_equiv{$code} = $a;
+ }
+ }
+}
+
+sub parse_control
+{
+ my @package_list = ();
+ my $pkg;
+ @names=();
+ my $xcontrol = &parse_xcontrol;
+ # only interested in top stanza (Source: )
+ my $stanza = $$xcontrol[0];
+ if (defined $stanza->{'XS-TDeb-Build-Directory'} or
+ defined $stanza->{'XS-TDeb-POT-Names'})
+ {
+ $builddir = $stanza->{'XS-TDeb-Build-Directory'};
+ my @tmp = split(',', $stanza->{'XS-TDeb-POT-Names'});
+ foreach my $n (@tmp)
+ {
+ $n =~ s/^\s+//;
+ push @names, $n;
+ }
+ }
+ # check if a changelog exists
+ &check_debian;
+ &find_messages($source);
+ # check to prevent duplication
+ my @sorted = sort (keys %lang_codes);
+ # nothing to do if @sorted is empty, unless templates exist
+ return if ((not @sorted) and (not -d "debian/po"));
+ foreach $lang (@sorted)
+ {
+ $fullname = $mainpackage;
+ &install_mofiles($lang);
+ }
+ &add_content();
+# &build_tdeb();
+}
+
+# look for and add other translated content
+# needs Dpkg::Class support
+sub add_content
+{
+ my @cmds = ();
+ $topdirprefix="debian/${tdebname}";
+ push @cmds, "install -d ${topdirprefix}/DEBIAN"
+ if (not -d "${topdirprefix}/DEBIAN");
+ my $contentprefix = "usr/share/"; # tdeb content should always be usr/share ?
+ my $location = "debian/tmp/${contentprefix}";
+ $location = "debian/${source}/${contentprefix}" if (not -d $location);
+ my $destination = "${topdirprefix}/${contentprefix}";
+ # debconf template handling - needs testing and dpkg support.
+
+=head1 Debconf Templates
+
+Packages may need to rename the templates file for the template file
+and change the reference in debian/po/POTFILES.in to the new file. This
+results in a lintian warning:
+
+ Now running lintian...
+ W: dpkg-cross: no-debconf-templates
+ Finished running lintian.
+
+The package probably now needs to Pre-Depend on the TDeb.
+Alternatively either dpkg or debconf should automatically install a
+TDeb prior to trying to configure the main package.
+
+Templates files are the most common reason for l10n rebuilds of
+packages prior to a release.
+
+=cut
+
+ my @templ_loc = `find debian -maxdepth 1 -name *templates`;
+ chomp (@templ_loc);
+ foreach my $line (@templ_loc)
+ {
+ next if grep (/udeb/, $line);
+ print "Migrating $line into TDeb.\n";
+ push @cmds, "install -m 0644 $line ${topdirprefix}/DEBIAN/templates"
+ if ((-f "$line") and ("$line" ne "${topdirprefix}/DEBIAN/templates"));
+ }
+ my @contentlist = qw: man info :;
+ my %removals=();
+ $destination = "${topdirprefix}";
+ foreach my $dir (@contentlist)
+ {
+ next if (! -d "$location$dir");
+ opendir (CONTENT, "$location$dir")
+ or die ("Unable to open existing directory $location$dir: $!\n");
+ my @files=grep(!/^\.\.?$/, readdir (CONTENT));
+ closedir (CONTENT);
+ foreach my $cdir (@files)
+ {
+ my $clocation = "$location${dir}/$cdir";
+ my $cdestination = "${cdir}/${dir}";
+ # skip untranslated content
+ next if ($cdir =~ /^man[0-9]$/);
+ opendir (TRANS, "$clocation/");
+ my @tdirs=grep(!/^\.\.?$/, readdir (TRANS));
+ closedir (TRANS);
+ foreach my $tdir (@tdirs)
+ {
+ my $troot = $cdir;
+ $troot =~ s/[-_].*$//;
+ my $tlocation = "$clocation/$tdir";
+ $removals{$clocation}++;
+ my $tdestination = "$destination/$troot/${contentprefix}$cdestination/$tdir";
+ next if (-f "$tlocation");
+ push @cmds, "install -d $tdestination";
+ opendir (FILES, "$tlocation");
+ my @tfiles=grep(!/^\.\.?$/, readdir (FILES));
+ closedir (FILES);
+ foreach my $tfile (@tfiles)
+ {
+ push @cmds, "install -m 0644 $tlocation/$tfile $tdestination"
+ }
+ }
+ }
+ }
+ foreach my $cmd (@cmds)
+ {
+ system ("$cmd");
+ }
+ # HACK ALERT! this system rm -rf must not survive into the released
+ # script.
+ # Either needs to be a recursive unlink or, even better, a fix
+ # in scripts like dh_installman so that the installing script
+ # understands the needs of the tdeb.
+ foreach my $rem (keys %removals)
+ {
+ system ("rm -rf $rem");
+ }
+ undef @cmds;
+}
+
+sub install_mofiles
+{
+ my $lang=shift;
+ $topdirprefix="debian/${tdebname}";
+ $finprefix="/usr/share/locale/";
+ $finsuffix="/LC_MESSAGES";
+ my $tmppo = $lang_equiv{$lang};
+ my $tdebprefix = $lang;
+ $tdebprefix =~ s/_.*$//;
+ $tdebprefix =~ s/-.*$//;
+ my @cmds=();
+ push @cmds, "install -d ${topdirprefix}/DEBIAN";
+ foreach my $cmd (@cmds)
+ {
+ system ($cmd);
+ }
+ @cmds=();
+ open (ORIG, "debian/control") or
+ die ("Cannot open debian/control: $!\n");
+ my @lines=<ORIG>;
+ close (ORIG);
+ open (DEB, ">${topdirprefix}/DEBIAN/control") or
+ die ("Cannot open ${topdirprefix}/DEBIAN/control: $!\n");
+ print DEB @lines;
+ close DEB;
+ push @cmds, "install -d ${topdirprefix}/${tdebprefix}/${finprefix}${tmppo}${finsuffix}"
+ if (defined keys %gettextdirs);
+ foreach my $gpkg (keys %gettextdirs)
+ {
+ my $pdir = $gettextdirs{$gpkg};
+ next unless $pdir =~ /\/po[-]?.*/;
+ next unless (-d $pdir);
+ next unless (-f "$pdir/$tmppo.po");
+ push @cmds, "msgfmt -o $pdir/$tmppo.gmo $pdir/$tmppo.po" if (! -f "$pdir/$tmppo.gmo");
+ push @cmds, "install -m 0644 $pdir/$tmppo.gmo ${topdirprefix}/${tdebprefix}/${finprefix}${tmppo}".
+ "${finsuffix}/${gpkg}.mo";
+ }
+ # some packages, like apt, use specialised handling which is
+ # supported using fields in debian/xcontrol.
+ if ((defined (@names)) and (defined ($builddir)))
+ {
+ push @cmds, "install -d ${topdirprefix}/${tdebprefix}/${finprefix}${tmppo}${finsuffix}"
+ if (defined @names);
+ foreach my $d (@names)
+ {
+ next unless (-d "$builddir/$d/");
+ my @custom_po = `find $builddir/$d/ -name $tmppo\.mo -type f`;
+ foreach my $custom (@custom_po)
+ {
+ chomp ($custom);
+ push @cmds, "install -m 0644 $custom ${topdirprefix}/${tdebprefix}/${finprefix}${tmppo}".
+ "${finsuffix}/$d.mo";
+ }
+ }
+ }
+ # do the real work here.
+ foreach my $cmd (@cmds)
+ {
+ system ("$cmd");
+ }
+ undef @cmds;
+}
+
+# code replaced by dpkg tdeb handlers
+sub build_tdeb
+{
+ my @cmds=();
+ push @cmds, "dpkg-gencontrol ".
+ "-p${mainpackage} ".
+ "-P${topdirprefix} -cdebian/control";
+
+ # XXX - debhelper bug. dh_builddeb fails to accept
+ # XC-Package-Type: tdeb, only udeb.
+ # Once debhelper fixed, remove the call to dpkg --build.
+
+ my $name = "../${mainpackage}_${version}_all.tdeb";
+# print "DEBUG: dpkg --build ${topdirprefix} $name ";
+ push @cmds, "dpkg --build ${topdirprefix} $name ";
+ # add clean up commands.
+# push @cmds, "rm -rf debian/${mainpackage}-locale*";
+ push @cmds, "rm -f po*/*.gmo";
+ # need to handle $dh{NO_ACT}
+ # do the real work here.
+ foreach my $cmd (@cmds)
+ {
+ system ("$cmd");
+ }
+ undef @cmds;
+}
+
+=head1 SEE ALSO
+
+debhelper (7)
+
+This program is based on debhelper.
+
+=head1 AUTHOR
+
+Neil Williams <codehelp@debian.org>
+
+=cut
+
+=head1 Copyright and Licence
+
+ Copyright (C) 2007-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
+
+=head1 Use in Debian
+
+generate_source will be removed before inclusion into Debian.
+
+At the same time, XC-Package-Type: tdeb needs support too. Notably,
+many of the scripts in the devscripts package fail to identify the
+TDeb in the .changes file and certain debhelper scripts fail to
+handle the TDeb package-type.
+
+reprepro needs a patch to accept .tdeb and allow .tdeb in
+the repository files:
+ $ reprepro --ignore=extension -b /path/ includedeb \
+ unstable ../qof-locale-sv_0.7.5-1em1_arm.tdeb
+ $ ls /opt/reprepro/locale/pool/main/q/qof/
+ qof-locale-sv_0.7.5-1em1_arm.deb
+
+ Filename: pool/main/q/qof/qof-locale-sv_0.7.5-1em1_arm.deb
+ Description: sv translation for qof (tdeb)
+
+reprepro also needs a way to handle a .tdeb in a .changes file.
+ reprepro -b /opt/reprepro/locale/ include unstable ../qof_0.7.5-1em1_arm.changes
+ 'qof-locale-id_0.7.5-1em1_arm.tdeb' is not .deb or .udeb!
+ There have been errors!
+
+=cut
+
+=head1 Other translations
+
+Packages may also contain translated manpages and translations for
+debconf templates. These translations are not yet packaged or processed
+by dpkg-gentdeb. For Tdebs to be supported in Debian, these issues will
+need to be resolved such that Emdebian can continue to only package the
+gettext program translations, omitting translated manpages and leaving
+debconf translation support to existing tools or implement sufficient
+changes in cdebconf.
+
+=cut