#!/usr/bin/perl =head1 NAME dh_elpa - install emacs lisp packages into package build directories =cut use strict; use Cwd qw{ getcwd }; use File::Temp qw{tempfile}; use IO::Handle; use File::Path; use Debian::Debhelper::Dh_Lib; =head1 SYNOPSIS B [S>] [S>] =head1 DESCRIPTION B is a debhelper program that is responsible for installing elpa style emacs lisp packages into package build directories. =head1 FILES =over 4 =item debian/I.elpa List of files to be installed into I as an elpa package. =back =cut init(options => { "byte-compile!" => \$dh{BYTECOMPILE}, "fix-autoload-date!" => \$dh{FIXAUTOLOADDATE}, }); =head1 OPTIONS =over 4 =item B<--byte-compile>, B<--no-byte-compile> Enable (default) or disable byte compilation of installed emacs lisp files. Disabling byte compilation changes the destination directory to one that is found by the emacs package system. =back =over 4 =item B<--fix-autoload-date>, B<--no--fix-autoload-date> Enable (default) or disable munging the dates in Emacs generated autoload files to match debian/changelog. =back =cut sub doit_quietly { my ($handle,$tmpfile) = tempfile(UNLINK=>1); my $exitcode; verbose_print(escape_shell(@_)); open (CPERR,">&STDERR") or error "$!"; open (CPOUT,">&STDOUT") or error "$!"; STDOUT->fdopen($handle,'w'); STDERR->fdopen($handle,'w'); my $ret=doit_noerror(@_); STDOUT->fdopen(\*CPOUT,'w'); STDERR->fdopen(\*CPERR,'w'); if (!$ret){ $exitcode=$?; seek $handle, 0, 0 or error "$!"; print while (<$handle>); my $command=join(" ",@_); error("$command returned exit code ".($exitcode >> 8)); } } # simplified version of private sub autoscript_sed in Dh_Lib sub sed_file { my ($sed, $infile, $outfile) = @_; open(IN, $infile) or die "$infile: $!"; open(OUT, ">>$outfile") or die "$outfile: $!"; while () { $sed->(); print OUT } close(OUT) or die "$outfile: $!"; close(IN) or die "$infile: $!"; } sub read_package_desc { my ($descdir, $package) = @_; my %desc = (); my $descfile="${descdir}/${package}.desc"; my $fh; open $fh,'<', $descfile or error "failed to open $descfile"; while (<$fh>) { if (m/([^:]+):\s*(.*)\s*$/) { $desc{$1} = $2; } } return \%desc; } my $templatedir = "/usr/share/debhelper/dh_elpa/emacsen-common"; sub maybe_install_helper{ my ($package,$piece, $mode, $desc)=@_; my $file=pkgfile($package,"emacsen-$piece"); my $tmp=tmpdir($package); my $ecdest="$tmp/usr/lib/emacsen-common/packages"; my $target="$ecdest/$piece/$package"; # if there is file, leave it for dh_installemacsen if ($file eq '') { if (! -d "$ecdest/$piece") { doit("install","-d","$ecdest/$piece"); } unlink $target; # ignore errors my $elpapackage = $desc->{'ELPA-Name'} or error "elpa package name not found"; my $elpaversion = $desc->{'ELPA-Version'} or error "elpa version not found"; sed_file (sub {s/#ELPAPACKAGE#/$elpapackage/; s/#ELPAVERSION#/$elpaversion/; }, "$templatedir/$piece", $target); chmod oct($mode), $target; } } $dh{BYTECOMPILE} = 1 unless defined($dh{BYTECOMPILE}); $dh{FIXAUTOLOADDATE} = 1 unless defined($dh{FIXAUTOLOADDATE}); my $elpadir; my $dhelpadir="/usr/share/emacs/site-lisp/elpa"; # TODO: do we really need a seperate elpa-src hierarchy? if ($dh{BYTECOMPILE}) { $elpadir="/usr/share/emacs/site-lisp/elpa-src"; } else { $elpadir=$dhelpadir; } PACKAGE: foreach my $package (@{$dh{DOPACKAGES}}) { my $tmp=tmpdir($package); my $file=pkgfile($package,"elpa"); my $elpapkg=$package; # TODO do this more sanely or at least allow an override $elpapkg =~ s/^elpa-//; verbose_print("Using elpa package name $elpapkg"); my @files; # Call isnative because it sets $dh{VERSION} # as a side effect. isnative($package); if ($file) { @files=filearray($file, "."); scalar(@files) == 1 || grep { m/\b${elpapkg}-pkg.el$/ } @files or error "missing ${elpapkg}-pkg.el"; } if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { push @files, @ARGV; } next PACKAGE if (scalar(@files) == 0); my $pkg_file; my $cwd = getcwd(); my $tempdir = "${cwd}/debian/.debhelper/elpa"; my $helper_version = '@HELPER_VERSION@'; my @extra_args = ( $tempdir ); if ($dh{FIXAUTOLOADDATE}) { push @extra_args, get_source_date_epoch(); } File::Path::rmtree $tempdir || error "cleaning $tempdir"; File::Path::make_path $tempdir || error "creating $tempdir"; addsubstvar($package,'misc:Built-Using',"dh-elpa (= ${helper_version})"); if (scalar(@files) == 1) { my $pkg_file=$files[0]; doit_quietly(qw{emacs -batch -Q -l package}, '--eval',"(add-to-list 'package-directory-list \"$dhelpadir\")", '--eval',"(add-to-list 'package-directory-list \"$elpadir\")", qw{-f package-initialize -l dh-elpa.el}, qw{-f dhelpa-batch-install-file}, "$tmp/$elpadir", $pkg_file, @extra_args); } else { my $stagedir = "$tempdir/$elpapkg"; File::Path::make_path $stagedir || error "creating $stagedir"; # copy files into stagedir, flattening hierarchy # TODO: do this more correctly foreach my $el_file (@files) { doit("cp", "-a", $el_file, "$stagedir"); } doit_quietly(qw{emacs -batch -Q -l package}, '--eval',"(add-to-list 'package-directory-list \"$dhelpadir\")", '--eval',"(add-to-list 'package-directory-list \"$elpadir\")", qw{-f package-initialize -l dh-elpa.el}, qw{-f dhelpa-batch-install-directory}, "$tmp/$elpadir", $stagedir, @extra_args); } if ($dh{BYTECOMPILE}) { my $desc = read_package_desc ($tempdir,$elpapkg); addsubstvar($package,'misc:Depends','emacsen-common'); maybe_install_helper($package, 'compat', '0644', $desc); maybe_install_helper($package, 'install', '0755', $desc); maybe_install_helper($package, 'remove', '0755', $desc); if (! $dh{NOSCRIPTS}) { autoscript($package,"postinst","postinst-emacsen", "s/#PACKAGE#/$package/"); autoscript($package,"prerm","prerm-emacsen", "s/#PACKAGE#/$package/"); } } } =head1 SUBSTVARS dh_elpa currently defines two substvars (cf. deb-substvars(5)) that can be used in debian/control =over 4 =item ${misc:Depends} These are dependencies needed by every dh_elpa based package. =item ${misc:Built-Using} This adds a value suitable for a Built-Using header identifying the version of dh_elpa used at build time. =back =head1 EXAMPLES Here is an example of using the helper in a dh(1) style debian/rules =over 4 #!/usr/bin/make -f %: dh $@ --with elpa =back Here is an example of a binary package stanza using dh_elpa generated substvars =over 4 Package: elpa-hello Architecture: all Depends: ${misc:Depends} Built-Using: ${misc:Built-Using} Description: Emacs addon to say hello The Emacs editor addon likes to wave and say hello. =back =cut