summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2016-10-08 18:26:36 -0700
committerSean Whitton <spwhitton@spwhitton.name>2016-10-08 18:26:36 -0700
commit10781fa029232f73ec90d8e482b5d54559bc3b2c (patch)
tree32ac8ad90994276241aaed91da844e3b22f132e2
dh-make-elpa (0.6.0) unstable; urgency=medium
* Upload to unstable: - Don't output "experimental script" warning text on every run. - Add disclaimer to generated d/copyright. This ensures that if the file has not been reviewed by a human, Lintian will emit a warning. * Output useful error when there are no *.el files (Closes: #838550). * Generate debhelper build dependency bound "(>= 10)". [dgit import package dh-make-elpa 0.6.0]
-rw-r--r--debian/changelog74
-rw-r--r--debian/compat1
-rw-r--r--debian/control28
-rw-r--r--debian/copyright20
-rw-r--r--debian/install3
-rw-r--r--debian/lintian-overrides2
-rw-r--r--debian/manpages1
-rwxr-xr-xdebian/rules12
-rw-r--r--debian/source/format1
-rw-r--r--debian/source/lintian-overrides7
-rw-r--r--debian/watch3
-rwxr-xr-xdh-make-elpa110
-rw-r--r--lib/DhMakeELPA.pm46
-rw-r--r--lib/DhMakeELPA/Command/Packaging.pm325
-rw-r--r--lib/DhMakeELPA/Command/make.pm172
-rw-r--r--lib/DhMakeELPA/Config.pm63
-rw-r--r--lib/DhMakeELPA/MELPA.pm89
-rw-r--r--share/gbp.conf12
-rwxr-xr-xshare/rules.dh.tiny4
19 files changed, 973 insertions, 0 deletions
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..6b1f5b6
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,74 @@
+dh-make-elpa (0.6.0) unstable; urgency=medium
+
+ * Upload to unstable:
+ - Don't output "experimental script" warning text on every run.
+ - Add disclaimer to generated d/copyright.
+ This ensures that if the file has not been reviewed by a human,
+ Lintian will emit a warning.
+ * Output useful error when there are no *.el files (Closes: #838550).
+ * Generate debhelper build dependency bound "(>= 10)".
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Sat, 08 Oct 2016 18:26:36 -0700
+
+dh-make-elpa (0.5.0) experimental; urgency=medium
+
+ * Add "typical usage" to manpage.
+ * Add Testsuite: field to generated d/control.
+ * Add --version option.
+ * Output useful info when we can't generate d/watch & d/docs.
+ Thanks to Lev Lamberov for the suggestion.
+ * Fix exiting non-zero after successful run.
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Sat, 10 Sep 2016 10:16:58 -0700
+
+dh-make-elpa (0.4.0) experimental; urgency=medium
+
+ * Improve package version detection regexs (Closes: #829095).
+ Thanks to Lev Lamberov for providing the test case.
+ * If foo-pkg.el exists, prefer it to foo.el for determining the upstream
+ version.
+ * create_watch no longer emits an error if .git is not present
+ (Closes: #829096).
+ - New dependency: libtrycatch-perl
+ * create_docs no longer emits an error if it can't find any docs
+ (Closes: #829096).
+ * Check whether upstream prepends their release tags with 'v' and adjust
+ gbp.conf accordingly.
+ * Generated watch files now have trailing newline.
+ * Add TIPS section to manpage.
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Mon, 04 Jul 2016 13:58:54 +0900
+
+dh-make-elpa (0.3.0) experimental; urgency=medium
+
+ * Generate standards version 3.9.8.
+ * Binary dependencies on dash-el and s-el detected and added.
+ * find_bins() now never fails to populate the bins hash.
+ Falls back to a single binary package.
+ * find_bins() always populates the values of the bins hash with list
+ references.
+ Previously, in the case of a single binary package, it was inserting a
+ string rather than a list reference.
+ * Use getcwd() rather than relying on the PWD env var.
+ Thanks to Dmitry Bogatov for reporting the problem, which occurs with
+ the rc interactive shell.
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Fri, 24 Jun 2016 17:31:04 +0900
+
+dh-make-elpa (0.2.0) experimental; urgency=medium
+
+ * Fix Vcs-Git: URI.
+ * Bump standards version to 3.9.8 (no changes required).
+ * Generate compat level 10 to activate dh_elpa_test.
+ * Try to detect Buttercup tests, and add elpa-buttercup build-dep so
+ that dh_elpa_test tries to run them.
+ * Add File::Find::Rule dependency.
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Sat, 07 May 2016 08:59:19 -0700
+
+dh-make-elpa (0.1.0) experimental; urgency=medium
+
+ * Initial release: experimental script to generate ELPA packages.
+ (Closes: #820103)
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Wed, 06 Apr 2016 09:26:23 -0700
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..e506545
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,28 @@
+Source: dh-make-elpa
+Section: devel
+Priority: optional
+Maintainer: Debian Emacs addons team <pkg-emacsen-addons@lists.alioth.debian.org>
+Uploaders: Sean Whitton <spwhitton@spwhitton.name>
+Build-Depends:
+ debhelper (>=9),
+Standards-Version: 3.9.8
+Vcs-Git: https://anonscm.debian.org/git/pkg-emacsen/pkg/dh-make-elpa.git
+Vcs-Browser: https://anonscm.debian.org/cgit/pkg-emacsen/pkg/dh-make-elpa.git/
+
+Package: dh-make-elpa
+Architecture: all
+Built-Using: ${misc:Built-Using}
+Depends:
+ dh-elpa,
+ dh-make-perl (>= 0.90),
+ libarray-utils-perl,
+ libfile-grep-perl,
+ libgit-repository-perl,
+ libfile-find-rule-perl,
+ libtrycatch-perl,
+ ${misc:Depends},
+ ${perl:Depends},
+Description: helper for creating Debian packages from ELPA packages
+ dh-make-elpa will try to create the files required to build a Debian
+ source package from an unpacked GNU Emacs ELPA package. The source
+ packages produced are suitable for building with the dh_elpa tool.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..23ca0a2
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,20 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files: *
+Copyright: (C) 2016 Sean Whitton <spwhitton@spwhitton.name>
+License: GPL-3+
+ dh-make-elpa 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.
+ .
+ dh-make-elpa 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 dh-make-elpa. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3.
diff --git a/debian/install b/debian/install
new file mode 100644
index 0000000..7383c5f
--- /dev/null
+++ b/debian/install
@@ -0,0 +1,3 @@
+usr/bin
+lib/* /usr/share/perl5
+share/* /usr/share/dh-make-elpa
diff --git a/debian/lintian-overrides b/debian/lintian-overrides
new file mode 100644
index 0000000..5208f57
--- /dev/null
+++ b/debian/lintian-overrides
@@ -0,0 +1,2 @@
+# this is a template file, not a script to be executed
+missing-dep-for-interpreter make => make | build-essential | dpkg-dev (usr/share/dh-make-elpa/rules.dh.tiny)
diff --git a/debian/manpages b/debian/manpages
new file mode 100644
index 0000000..969cf8f
--- /dev/null
+++ b/debian/manpages
@@ -0,0 +1 @@
+dh-make-elpa.1
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..fc42a72
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,12 @@
+#!/usr/bin/make -f
+
+DESTDIR=$(CURDIR)/debian/tmp
+
+%:
+ dh $@
+
+override_dh_auto_build:
+ pod2man dh-make-elpa > dh-make-elpa.1
+
+override_dh_auto_install:
+ install -m 755 -D dh-make-elpa $(DESTDIR)/usr/bin/dh-make-elpa
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides
new file mode 100644
index 0000000..3a09ab8
--- /dev/null
+++ b/debian/source/lintian-overrides
@@ -0,0 +1,7 @@
+# watch file is just for informational output at
+# <https://pet.debian.net/pkg-emacsen/pet.cgi> so no need for a
+# signature check
+debian-watch-may-check-gpg-signature
+
+# the watch file is still useful for <https://pet.debian.net/pkg-emacsen/pet.cgi>
+debian-watch-file-in-native-package
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..fe0666f
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,3 @@
+version=4
+opts="mode=git" https://anonscm.debian.org/git/pkg-emacsen/pkg/dh-make-elpa.git \
+ refs/tags/([\d\.\d\.]+) debian
diff --git a/dh-make-elpa b/dh-make-elpa
new file mode 100755
index 0000000..f9aeb91
--- /dev/null
+++ b/dh-make-elpa
@@ -0,0 +1,110 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh-make-elpa - helper for creating Debian packages from ELPA packages
+
+=cut
+
+use strict;
+use warnings;
+
+use DhMakeELPA;
+
+exit DhMakeELPA->run;
+
+__END__
+
+=head1 SYNOPSIS
+
+=over
+
+=item dh-make-elpa [--pkg-emacsen] [make]
+
+=back
+
+=head1 DESCRIPTION
+
+B<dh-make-elpa> will try to create the files required to build a
+Debian source package from an unpacked GNU Emacs ELPA package.
+
+B<dh-make-elpa> is an experimental script that performs a lot of
+guesswork. You should throughly verify the contents of the debian/
+directory before uploading
+
+=head2 TYPICAL USAGE
+
+=over 4
+
+% git clone -o upstream https://foo.org/foo.git
+
+% cd foo
+
+% git reset --hard 1.0.0 # package latest stable release
+
+% dh-make-elpa --pkg-emacsen
+
+% git add debian && git commit -m "initial Debianisation"
+
+% gbp buildpackage -us -uc # generates an orig tarball
+
+=back
+
+=head2 COMMANDS
+
+=over
+
+=item make
+
+Default command if no command is given. Creates debianisation from
+scratch. Fails with an error if F<debian/> directory already
+exists.
+
+=back
+
+=head2 OPTIONS
+
+=over
+
+=item B<--pkg-emacsen>
+
+Sets C<Maintainer>, C<Uploaders>, C<Vcs-Git> and C<Vcs-Browser> fields
+in F<debian/control> according to the Team's conventions.
+
+This option is useful when preparing a package for the Debian Emacs
+Addon Packaging Team L<https://pkg-emacsen.alioth.debian.org>.
+
+=item B<--version>
+
+Specify the ELPA package version. Useful when upstream failed to
+include either a Package-Version: header or a -pkg.el file.
+
+=back
+
+=head2 TIPS
+
+=over
+
+=item Upstream remote
+
+The remote for upstream's git repository should be called 'upstream'. You can use:
+
+=over 4
+
+% git clone -o upstream https://foo/bar
+
+=back
+
+=back
+
+=cut
+
+=head1 AUTHOR
+
+Written and maintained by Sean Whitton <spwhitton@spwhitton.name>.
+
+A great deal of the library code used by B<dh-make-elpa>, and its
+object-oriented structure, comes from dh-make-perl(1), written and
+maintained by the Debian Perl Group.
+
+=cut
diff --git a/lib/DhMakeELPA.pm b/lib/DhMakeELPA.pm
new file mode 100644
index 0000000..b7a9b5e
--- /dev/null
+++ b/lib/DhMakeELPA.pm
@@ -0,0 +1,46 @@
+package DhMakeELPA;
+
+use warnings;
+use strict;
+
+use base 'Class::Accessor';
+
+__PACKAGE__->mk_accessors( qw( cfg ) );
+
+use DhMakeELPA::Config;
+
+=item run( I<%init> )
+
+Runs DhMakeELPA.
+
+Unless the %init contains an I<cfg> member, constructs and instance of
+L<DhMakeELPA::Config> and assigns it to I<$init{cfg}>.
+
+Then determines the dh-make-elpa command requested (via cfg->command),
+loads the appropriate I<DhMakeELPA::Command::$command> class,
+constructs an instance of it and calls its I<execute> method.
+
+=cut
+
+sub run {
+ my ( $class, %c ) = @_;
+
+ unless ( $c{cfg} ) {
+ my $cfg = DhMakeELPA::Config->new;
+ $cfg->parse_command_line_options;
+ $c{cfg} = $cfg;
+ }
+
+ my $cmd_mod = $c{cfg}->command;
+ $cmd_mod =~ s/-/_/g;
+ require "DhMakeELPA/Command/$cmd_mod.pm";
+
+ $cmd_mod =~ s{/}{::}g;
+ $cmd_mod = "DhMakeELPA::Command::$cmd_mod";
+
+ my $self = $cmd_mod->new( \%c );
+
+ return $self->execute;
+}
+
+1;
diff --git a/lib/DhMakeELPA/Command/Packaging.pm b/lib/DhMakeELPA/Command/Packaging.pm
new file mode 100644
index 0000000..a729600
--- /dev/null
+++ b/lib/DhMakeELPA/Command/Packaging.pm
@@ -0,0 +1,325 @@
+package DhMakeELPA::Command::Packaging;
+
+use strict;
+use warnings;
+
+use Cwd;
+use File::Basename qw{basename};
+use File::Grep qw{fgrep};
+use Array::Utils qw{array_minus};
+use DhMakeELPA::MELPA;
+use File::Copy;
+use File::Find::Rule;
+use File::Spec::Functions qw(catfile);
+use Git::Repository;
+use TryCatch;
+
+use base 'DhMakePerl::Command::Packaging';
+
+__PACKAGE__->mk_accessors(
+ qw( main_dir debian_dir bins control pkgname homepage elpa_version copyright gpl_version )
+ );
+
+use constant debstdversion => '3.9.8';
+
+sub extract_basic {
+ my $self = shift;
+
+ $self->debian_dir( $self->main_file('debian') );
+ $self->find_bins();
+ $self->pkgname(basename(cwd())); # TODO better?
+ if ( $self->cfg->version ) {
+ $self->elpa_version($self->cfg->version);
+ } else {
+ $self->elpa_version($self->extract_version());
+ }
+
+ # Find the homepage, license and copyright by looking at the root
+ # .el file, if it exists, of the binary package with the shortest
+ # name, then the second shortest etc. This might leave any of
+ # these three undefined
+ foreach my $bin (sort {length($a) <=> length($b)} keys %{$self->bins}) {
+ my $fh = $self->_file_r( $self->main_file("$bin.el") );
+ while (my $line = $fh->getline()) {
+ if ($line =~ /^;; (X-)*URL: /) {
+ chomp(my $homepage = $');
+ $self->homepage($homepage);
+ } elsif ($line =~ /^;; Copyright /) {
+ chomp(my $copyright = $');
+ $copyright =~ s/ / /g;
+ $self->copyright($copyright);
+ } elsif ($line =~ /either version 2/) {
+ $self->gpl_version("2");
+ } elsif ($line =~ /either version 3/) {
+ $self->gpl_version("3");
+ }
+ }
+ $fh->close;
+ if (defined $self->homepage) {
+ last;
+ }
+ }
+}
+
+sub find_bins {
+ my $self = shift;
+
+ my @el_files = glob($self->main_dir . "/*.el");
+ unless ( @el_files ) {
+ print "no *.el files in the current directory -- are you running\n";
+ print "dh-make-elpa in the right place?\n";
+ exit 1;
+ }
+
+ my @pkg_files = glob($self->main_dir . "/*-pkg.el");
+ @el_files = array_minus( @el_files, @pkg_files );
+ # try to ensure that the 'root' .el file is at the front of the list
+ @el_files = sort { length $a cmp length $b } @el_files;
+ my $single_bin = sub {
+ my $bin = basename($el_files[0]) =~ s/\.el$//r;
+ $self->bins({ "$bin" => ["*.el"] });
+ };
+
+ if (@el_files == 1) {
+ &$single_bin();
+ } else {
+ $self->bins({});
+ # there could be -pkg.el files that don't have an associated .el
+ # file (e.g. helm-core-pkg.el) so add those back in to the list
+ # we're going to check
+ @el_files = (@el_files, grep { $_ =~ s/-pkg//; ! -f $_} @pkg_files);
+ foreach my $el (@el_files) {
+ my $pkg = $el =~ s/\.el$/-pkg.el/r;
+ my $name = basename($el) =~ s/\.el$//r;
+ # see if this package is a root package file: either it has an
+ # accompanying -pkg.el, or it contains a Package-Version: line
+ if (-f "$pkg" || fgrep { /^;; Package-Version:/ } $el) {
+ my @files = package_files_list($name);
+ $self->bins->{$name} = \@files;
+ }
+ }
+ # fallback: if we failed to figure out the bins, just use a
+ # single one
+ if ( scalar %{$self->bins} eq 0) {
+ &$single_bin();
+ }
+ }
+}
+
+sub create_elpa {
+ my $self = shift;
+ if (keys %{$self->bins} le 1) {
+ my $fh = $self->_file_w( $self->debian_file('elpa') );
+ $fh->print( "*.el\n" );
+ $fh->close;
+ } else {
+ foreach my $bin ( keys %{$self->bins} ) {
+ my @files = @{$self->bins->{$bin}};
+ my $fh = $self->_file_w( $self->debian_file("elpa-$bin.elpa") );
+ foreach my $file (@files) {
+ $fh->print("$file\n");
+ }
+ $fh->close;
+ }
+ }
+}
+
+# from dh-make-perl
+sub fill_maintainer {
+ my $self = shift;
+
+ my $src = $self->control->source;
+ my $maint = $self->get_developer;
+
+ if ( $self->cfg->pkg_emacsen ) {
+ my $pkg_emacsen_maint
+ = "Debian Emacs addons team <pkg-emacsen-addons\@lists.alioth.debian.org>";
+ unless ( ( $src->Maintainer // '' ) eq $pkg_emacsen_maint ) {
+ my $old_maint = $src->Maintainer;
+ $src->Maintainer($pkg_emacsen_maint);
+ $src->Uploaders->add($old_maint) if $old_maint;
+ }
+
+ $src->Uploaders->add($maint);
+ }
+ else {
+ $src->Maintainer($maint);
+ }
+}
+
+sub fill_vcs {
+ my $self = shift;
+ my $src = $self->control->source;
+
+ if ( $self->cfg->pkg_emacsen ) {
+ $src->Vcs_Git(
+ sprintf( "https://anonscm.debian.org/git/pkg-emacsen/pkg/%s.git",
+ $self->pkgname )
+ );
+ $src->Vcs_Browser(
+ sprintf( "https://anonscm.debian.org/cgit/pkg-emacsen/pkg/%s.git/",
+ $self->pkgname )
+ );
+ }
+}
+
+# TODO Emacs might be able to extract this information better than we
+# can: it can generate -pkg.el files from .el files
+sub extract_description {
+ my ($self, $el_file) = @_;
+ my ($short_desc, $long_desc);
+ my $pkg_file = $el_file =~ s/\.el/-pkg.el/r;
+
+ if ( -e $pkg_file ) {
+ my $fh = $self->_file_r($pkg_file);
+ my $lines = join("", $fh->getlines);
+ $lines =~ s/\n//g;
+ $short_desc = $lines =~ s/.*\(define-package\s+"[^"]+"\s+"[^"]+"\s+"([^"]+)".*/$1/rg;
+ $fh->close;
+ }
+
+ if ( -e $el_file ) { # helm-core.el doesn't exist, though helm-core-pkg.el does
+ my $fh = $self->_file_r($el_file);
+ unless (defined $short_desc) {
+ my $line = $fh->getline;
+ $short_desc = $line =~ s/^.*--- (.*)$/$1/r =~ s/-\*- .* -\*-//r;
+ }
+ my $lines = join("", $fh->getlines);
+ if ( $lines =~ /;;; Commentary:/ ) {
+ $lines =~ /.*Commentary:\n\n(.*?)\n\n;;; Code:.*/s;
+ if ( defined $1 ) {
+ $long_desc = $1 =~ s/;; //rsg;
+ }
+ }
+ $fh->close;
+ }
+
+ unless (defined $short_desc) {
+ $short_desc = "couldn't determine short description"
+ }
+ unless (defined $long_desc) {
+ $long_desc = "couldn't determine long description"
+ }
+ return ($short_desc, $long_desc);
+}
+
+# TODO we might fall back to lookingg at the git tags
+sub extract_version {
+ my $self = shift;
+ my $version;
+ foreach my $bin ( keys %{$self->bins} ) {
+ if ( -f $self->main_file("$bin-pkg.el") ) {
+ my $fh = $self->_file_r("$bin-pkg.el");
+ while (my $line = $fh->getline) {
+ if ( $line =~ /.*\(define-package\s+"[^"]+"\s+"([0-9.a-zA-Z~+-]*)"/ ) {
+ $version = "$1";
+ last;
+ }
+ }
+ $fh->close;
+ last;
+ } elsif ( -f $self->main_file("$bin.el") ) {
+ my $fh = $self->_file_r("$bin.el");
+ while (my $line = $fh->getline) {
+ if ( $line =~ /^;; (Package-)*Version\s*:\s+([0-9.a-zA-Z~+-]*)$/ ) {
+ $version = "$2";
+ last;
+ }
+ }
+ $fh->close;
+ last;
+ }
+ }
+ if (defined $version) {
+ return $version;
+ } else {
+ die "Could not determine package version by examining *.el files.\nPlease specify --version.";
+ }
+}
+
+sub create_gbp_conf {
+ my $self = shift;
+
+ my ( $content, $repo );
+ my $file = $self->debian_file('gbp.conf');
+
+ my $gbpname = "gbp.conf";
+ for my $source (
+ catfile( $self->cfg->home_dir, $gbpname ),
+ catfile( $self->cfg->data_dir, $gbpname )
+ ) {
+ if ( -e $source ) {
+ print "Using gbp-conf: $source\n" if $self->cfg->verbose;
+ my $fh = $self->_file_r($source);
+ $content = join "", $fh->getlines();
+ last;
+ };
+ }
+
+ # Now we update upstream tag format if necessary: strip the
+ # leading 'v' if there are no tags that match /^v[0-9]/
+ try {
+ $repo = Git::Repository->new( git_dir => $self->main_file('.git') );
+ } catch {
+ my $fh = $self->_file_w($file);
+ $fh->write($content);
+ return;
+ };
+ my @tags = split "\n", $repo->run(("tag"));
+ unless ( grep { /^v[0-9]/ } @tags ) {
+ $content =~ s/upstream-tag\s*=\s*v/upstream-tag = /;
+ }
+ my $fh = $self->_file_w($file);
+ $fh->write($content);
+}
+
+# TODO more complex case with more than one binary package
+# TODO support .markdown, .mdwn etc.
+sub create_docs {
+ my $self = shift;
+ my @docs = glob($self->main_dir . "/*.md");
+
+ if ( keys %{$self->bins} le 1 && scalar @docs gt 0 ) {
+ my $fh = $self->_file_w( $self->debian_file('docs') );
+ $fh->print( "*.md\n" );
+ $fh->close;
+ } else {
+ print "I: couldn't generate d/docs: not fully implemented\n";
+ }
+}
+
+# TODO again, assumes tags are of the form v1.0.0
+sub create_watch {
+ my $self = shift;
+ my ( $repo, $upstream_url );
+
+ try {
+ $repo = Git::Repository->new( git_dir => $self->main_file('.git') );
+ $upstream_url = $repo->run(( "remote", "get-url", "upstream" ));
+ } catch {
+ print "I: couldn't generate d/watch -- no git remote named 'upstream'\n";
+ return;
+ };
+
+ my $fh = $self->_file_w( $self->debian_file("watch") );
+ $fh->printf("version=4\nopts=\"mode=git\" %s refs/tags/v([\\d\\.\\d\\.]+) debian\n",
+ $upstream_url);
+ $fh->close;
+}
+
+sub detect_buttercup_tests {
+ my $self = shift;
+
+ my @files = File::Find::Rule
+ ->file()
+ ->grep( "\\(describe " )
+ ->in('.');
+
+ if ( scalar @files ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+1;
diff --git a/lib/DhMakeELPA/Command/make.pm b/lib/DhMakeELPA/Command/make.pm
new file mode 100644
index 0000000..f39800f
--- /dev/null
+++ b/lib/DhMakeELPA/Command/make.pm
@@ -0,0 +1,172 @@
+package DhMakeELPA::Command::make;
+
+use strict;
+use warnings;
+no warnings "experimental::smartmatch";
+
+use Debian::Control;
+use File::Spec::Functions qw(catfile);
+use Email::Date::Format qw(email_date);
+use File::Grep qw(fgrep);
+use Cwd;
+
+use base 'DhMakeELPA::Command::Packaging';
+
+sub execute {
+ my $self = shift;
+
+ # extract basic information from package
+ $self->main_dir(getcwd());
+ $self->extract_basic;
+
+ # initial sanity check
+ die "debian/ subdir already exists! Won't clobber it.\n"
+ if ( -d $self->debian_dir );
+
+ # now start writing out package data
+ mkdir( $self->debian_dir, 0755 )
+ or die "Cannot create " . $self->debian_dir . " dir: $!\n";
+ $self->create_compat( $self->debian_file('compat') );
+ $self->create_elpa();
+ $self->write_source_format(
+ catfile( $self->debian_dir, 'source', 'format' ) );
+ $self->create_changelog( $self->debian_file('changelog'),
+ $self->cfg->closes // $self->get_wnpp( $self->pkgname ) );
+ $self->create_rules();
+ $self->create_control();
+ $self->create_copyright();
+ $self->create_docs();
+ $self->create_watch();
+ $self->create_gbp_conf() if $self->cfg->pkg_emacsen;
+
+ return(0);
+}
+
+sub create_control {
+ my $self = shift;
+ my $src = $self->control->source;
+
+ $src->Source( $self->pkgname );
+ $src->Section("lisp");
+ $src->Priority("optional");
+ $src->Testsuite("autopkgtest-pkg-elpa");
+ $src->Standards_Version( $self->debstdversion );
+ $src->Build_Depends->add( "debhelper (>= 10)" );
+ $src->Build_Depends->add( "dh-elpa" );
+ $src->Build_Depends->add( "elpa-buttercup" )
+ if ( $self->detect_buttercup_tests() );
+ $src->Homepage( $self->homepage ) if ( defined $self->homepage );
+
+ $self->fill_maintainer();
+ $self->fill_vcs();
+
+ foreach my $bin (keys %{$self->bins}) {
+ my @files = @{$self->bins->{$bin}};
+ # resolve special case so we can use fgrep
+ @files = glob($self->main_dir . "/*.el")
+ if ( @files ~~ ["*.el"] );
+
+ my ($short_desc, $long_desc) =
+ $self->extract_description( $self->main_file("$bin.el") );
+ my $depends = '${misc:Depends}, emacs, ${elpa:Depends}';
+
+ # append depends that aren't included in ${elpa:Depends}
+ $depends .= ", dash-el"
+ if fgrep { /^;; Package-Requires: .*\(dash \"/ } @files;
+ $depends .= ", s-el"
+ if fgrep { /^;; Package-Requires: .*\(s \"/ } @files;
+
+ my $stanza = { Package => "elpa-$bin",
+ Architecture => "all",
+ Depends => "$depends",
+ Built_Using => '${misc:Built-Using}',
+ Recommends => "emacs (>= 46.0)",
+ Enhances => "emacs, emacs24",
+ short_description => "$short_desc",
+ long_description => "$long_desc",
+ };
+ # black magic from dh-make-perl:
+ $self->control->binary_tie->Push( $bin => Debian::Control::Stanza::Binary->new($stanza) );
+ }
+
+ $self->control->write( $self->debian_file('control') );
+}
+
+sub create_changelog {
+ my ( $self, $file, $bug ) = @_;
+
+ my $fh = $self->_file_w($file);
+
+ my $closes = $bug ? " (Closes: #$bug)" : '';
+ my $changelog_dist = $self->cfg->pkg_emacsen ? "UNRELEASED" : "unstable";
+
+ $fh->printf( "%s (%s) %s; urgency=medium\n",
+ $self->pkgname, $self->elpa_version . "-1", $changelog_dist );
+ $fh->print("\n * Initial release.$closes\n\n");
+ $fh->printf( " -- %s %s\n", $self->get_developer,
+ email_date(time) );
+
+ $fh->close;
+}
+
+# ELPA packages are almost always GPL-2+ or GPL-3+, so for now those
+# are the only cases we support
+sub create_copyright {
+ my $self = shift;
+ my ( $gpl_version, @res );
+ if ( defined $self->gpl_version ) {
+ $gpl_version = $self->gpl_version;
+ } else {
+ # give up
+ return;
+ }
+
+ my $fh = $self->_file_w( $self->debian_file("copyright") );
+ my $year = (localtime)[5] + 1900;
+
+ push @res, 'Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/';
+ push @res, 'Upstream-Name: ' . $self->pkgname;
+ push @res, 'Source: ' . $self->homepage if ( defined $self->homepage );
+ push @res, "";
+ push @res, 'Files: *';
+ push @res, 'Copyright: ' . $self->copyright;
+ push @res, 'License: GPL-' . $gpl_version . '+';
+ push @res, "";
+ push @res, 'Files: debian/*';
+ push @res, "Copyright: (C) $year " . $self->get_developer;
+ push @res, 'License: GPL-' . $gpl_version . '+';
+ push @res, "";
+ push @res, "License: GPL-$gpl_version+";
+ push( @res,
+ " This program 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 $gpl_version 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/>.",
+ " .",
+ " On Debian systems, the complete text of the GNU General",
+ " Public License version 3 can be found in `/usr/share/common-licenses/GPL-$gpl_version'",
+ );
+
+ push( @res,
+ "",
+ "DISCLAIMER: This copyright info was automatically extracted",
+ " from the ELPA package. It might not be accurate. You need",
+ " to throughly verify that the code passes the DFSG, and that",
+ " the info in this file is accurate.",
+ "NOTE: Don't forget to remove this disclaimer when you've",
+ " performed this verification."
+ );
+
+ $fh->print( join( "\n", @res, '' ) );
+ $fh->close;
+}
+
+1;
diff --git a/lib/DhMakeELPA/Config.pm b/lib/DhMakeELPA/Config.pm
new file mode 100644
index 0000000..7820191
--- /dev/null
+++ b/lib/DhMakeELPA/Config.pm
@@ -0,0 +1,63 @@
+package DhMakeELPA::Config;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.1.0';
+
+use base 'DhMakePerl::Config';
+
+use Getopt::Long;
+
+=head1 NAME
+
+DhMakeELPA::Config - dh-make-elpa configuration class
+
+=cut
+
+my @OPTIONS = ( 'pkg-emacsen!', 'version=s' );
+
+my @COMMANDS = ( 'make' );
+
+# black magic from dh-make-perl:
+__PACKAGE__->mk_accessors(
+ do {
+ my @opts = ( @OPTIONS, @COMMANDS );
+ for (@opts) {
+ s/[=:!|].*//;
+ s/-/_/g;
+ }
+ @opts;
+ },
+ 'command',
+ '_explicitly_set',
+);
+
+use constant DEFAULTS => {
+ dh => 10,
+ data_dir => '/usr/share/dh-make-elpa',
+ home_dir => "$ENV{HOME}/.dh-make-elpa",
+ source_format => '3.0 (quilt)',
+ };
+
+# most of this function is from dh-make-perl
+sub parse_command_line_options {
+ my $self = shift;
+
+ Getopt::Long::Configure( qw( pass_through no_auto_abbrev no_ignore_case ) );
+ my %opts;
+ GetOptions( \%opts, @OPTIONS )
+ or die "Error parsing command-line options\n";
+
+ while ( my ( $k, $v ) = each %opts ) {
+ my $field = $k;
+ $field =~ s/-/_/g;
+ $self->$field( $opts{$k} );
+ $self->_explicitly_set->{$k} = 1;
+ }
+
+ # TODO
+ $self->command("make");
+}
+
+1;
diff --git a/lib/DhMakeELPA/MELPA.pm b/lib/DhMakeELPA/MELPA.pm
new file mode 100644
index 0000000..ab70568
--- /dev/null
+++ b/lib/DhMakeELPA/MELPA.pm
@@ -0,0 +1,89 @@
+package DhMakeELPA::MELPA;
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+DhMakeELPA::MELPA - functions to interact with MELPA
+
+=cut
+
+use Array::Utils qw{array_minus};
+use LWP::Simple qw{get};
+
+use Exporter qw(import);
+our @EXPORT = qw(package_files_list);
+
+=head1 METHODS
+
+=over
+
+=item package_files_list(I<package>)
+
+Query MELPA to return the list of files that the recipe for the latest
+version of the given package claims to belong to the package. If the
+recipe does not specify a list of files (i.e. all files in upstream's
+repository are part of one ELPA package), this function will return an
+array containing the item "*.el".
+
+Note that this can give the wrong result if upstream has re-organised
+their repo since the release being packaged.
+
+=cut
+
+# TODO what if the .el files are in subdirectories? Look at magit's
+# *.elpa files for an example of this
+
+# TODO option to turn this query off during a refresh to avoid the
+# caveat given above
+
+sub package_files_list {
+ my ($package) = @_;
+ my $recipe = get("https://raw.githubusercontent.com/melpa/melpa/master/recipes/" . $package)
+ or die "Could not download recipe for $package from MELPA!";
+
+ # insert MELPA's default globs if necessary (edited slightly)
+ my $defaults = <<EOT;
+"*.el"
+ (:exclude ".dir-locals.el" "test.el" "tests.el" "*-test.el" "*-tests.el")
+EOT
+ $recipe =~ s/:defaults/$defaults/;
+
+ # now see if there is a files array at all
+ if ($recipe =~ m/:files\s\((.*)\)/s)
+ {
+ # parse out the lisp list into perl arrays
+ my $lisp_files_list = $1;
+ $lisp_files_list =~ s/\s+/ /g;
+ my $includes, my $excludes;
+ # is there an :exclude list?
+ if ($lisp_files_list =~ m/(.*)\(:exclude\s(.*)\)(.*)/) {
+ $includes = $1 . $3;
+ $excludes = $2;
+ }
+ # no exclude list: include all
+ else {
+ $includes = $lisp_files_list;
+ $excludes = "";
+ }
+ # trim and split them up
+ $includes =~ s/^\s*\"(.*)\"[\s)]*$/$1/;
+ $excludes =~ s/^\s*\"(.*)\"[\s)]*$/$1/;
+ my @includes = split /\" \"/, $includes;
+ my @excludes = split /\" \"/, $excludes;
+
+ # perform the globs
+ @includes = map { glob($_) } @includes;
+ @excludes = map { glob($_) } @excludes;
+
+ # sometimes MELPA includes non-el files in its :files
+ @includes = grep { $_ =~ /\.el$/ } @includes;
+
+ return array_minus( @includes, @excludes );
+ }
+ # no files array: we can assume that all the *.el files belong to this package
+ else {
+ return ("*.el");
+ }
+}
diff --git a/share/gbp.conf b/share/gbp.conf
new file mode 100644
index 0000000..1d74925
--- /dev/null
+++ b/share/gbp.conf
@@ -0,0 +1,12 @@
+[DEFAULT]
+# upstream-branch = upstream
+debian-branch = master
+upstream-tag = v%(version)s
+debian-tag = debian/%(version)s
+
+sign-tags = True
+pristine-tar = True
+pristine-tar-commit = True
+
+compression = xz
+compression-level = 9
diff --git a/share/rules.dh.tiny b/share/rules.dh.tiny
new file mode 100755
index 0000000..8eb7ccb
--- /dev/null
+++ b/share/rules.dh.tiny
@@ -0,0 +1,4 @@
+#!/usr/bin/make -f
+
+%:
+ dh $@ --parallel --with elpa