diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2016-10-08 18:26:36 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2016-10-08 18:26:36 -0700 |
commit | 10781fa029232f73ec90d8e482b5d54559bc3b2c (patch) | |
tree | 32ac8ad90994276241aaed91da844e3b22f132e2 |
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/changelog | 74 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 28 | ||||
-rw-r--r-- | debian/copyright | 20 | ||||
-rw-r--r-- | debian/install | 3 | ||||
-rw-r--r-- | debian/lintian-overrides | 2 | ||||
-rw-r--r-- | debian/manpages | 1 | ||||
-rwxr-xr-x | debian/rules | 12 | ||||
-rw-r--r-- | debian/source/format | 1 | ||||
-rw-r--r-- | debian/source/lintian-overrides | 7 | ||||
-rw-r--r-- | debian/watch | 3 | ||||
-rwxr-xr-x | dh-make-elpa | 110 | ||||
-rw-r--r-- | lib/DhMakeELPA.pm | 46 | ||||
-rw-r--r-- | lib/DhMakeELPA/Command/Packaging.pm | 325 | ||||
-rw-r--r-- | lib/DhMakeELPA/Command/make.pm | 172 | ||||
-rw-r--r-- | lib/DhMakeELPA/Config.pm | 63 | ||||
-rw-r--r-- | lib/DhMakeELPA/MELPA.pm | 89 | ||||
-rw-r--r-- | share/gbp.conf | 12 | ||||
-rwxr-xr-x | share/rules.dh.tiny | 4 |
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 |