Perl@PolettiX


S M T W T F S
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Fri, 30 Jul 2010

local::lib for distributions

local::lib is a love-it-or-hate-it module, with the additional feature that you don't get the hate-it part.

Recently, I had to develop a script to do a couple of HTTP redirections. I headed towards CPAN, quickly found that HTTP::Server::Simple (and in particular HTTP::Server::Simple::CGI) and in some twenty minutes I had a working prototype. Forget that I changed my mind a couple of times before having what I eventually used for my test...

Now, I knew I had to go into an environment that could possibly prevent me from using my machine to perform the test. As a matter of fact, I didn't know whether I could use the program anywhere, let alone know what kind of Perl environment I would have found. Nightmare!

Luckily enough, it turned out that I only needed modules that do not require compilation. I love Pure Perl modules! So now I had the problem to bundle all the needed non-core modules in a way that was convenient to deliver. This is where local::lib really saved the day, and in particular its --self-contained option. Well - yes - I've seen options that were commented way better... but at least the only reference in the synopsis made me curious enough to discover that it was hitting the nail right in the head.

On my machine I have my own compiled Perl version to tinker with, so I installed local::lib without the need to bootstrap anything. At this point, all I had to do was something along these lines:

shell$ perl -MCPAN -Mlocal::lib=--self-contained,my_lib -e 'CPAN::install($_) for @ARGV' HTTP::Server::Simple URI Log::Log4perl

Yes... I'm quite fond of Log::Log4perl, but that's another story.

The installation above went smooth and installed all the modules, and their needed non-core dependencies, under the directory tree starting from my_lib. I checked that there were actually no compiled components - dependencies could play some trick - and I verified that I had been lucky. Yay!

The directory structure you end up with is more or less the following:

my_lib/bin
my_lib/lib/perl5/...
my_lib/man

I didn't need either the bin or the man subdirectory, so I just moved the contents my_lib/lib/perl5 into a lib subdirectory, removed what remained of my_lib and... that's it! Well, wait a minute, I had to make a slight change to the code as well:

#...
use FindBin;
use lib $FindBin::Bin;

OK, now that's it! The funny part? I was actually able to use my laptop, so I didn't need anything of this...

Posted at 10:09:05 by Flavio Poletti
Sun, 9 May 2010

Carp::Always would have helped

Yesterday I wrote an article about Dist::Zilla and, at a certain point, I had issues with a missing Changes file, that triggered an error whose source was not evident:

~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.3 to 0.1.4
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.4
[DZ] writing archive to Sample-Module-0.1.4.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] ...
...
[@Filter/TestRelease] all's well; removing .build/OIeyjyhBZd
[FakeRelease] Fake release happening (nothing was really done)
can't open Changes for reading: No such file or directory\
 at /opt/perl-5.8.8/lib/site_perl/5.8.8/Dist/Zilla/File/OnDisk.pm line 31.
To find out the offending plugin I decided to hack Dist::Zilla::File::OnDisk, turning a die() into a Carp::confess() to see what was going wrong:
--- src.OnDisk.pm
+++ dst.OnDisk.pm
@@ -28,7 +28,8 @@
 sub _read_file {
   my ($self) = @_;
 
   my $fname = $self->_original_name;
-  open my $fh, '<', $fname or die "can't open $fname for reading: $!";
+  use Carp;
+  open my $fh, '<', $fname or Carp::confess "can't open $fname for reading: $!";
   my $content = do { local $/; <$fh> };
 }

Had I read the title of Tokuhiro Matsuno's post Carp::Always::Color (well, I can't read Japanese, so I can't go beyond an English title!), I would have avoided all the editing mess to just use Adriano Ferreira's Carp::Always.

So, instead of editing the file, I should have find out where dzil is installed in my system:

~/dzilla/Sample-Module$ which dzil
/opt/perl/bin/dzil
This way, the perl executable can be called directly to add Carp::Always in the command line:
~/dzilla/Sample-Module$ perl -MCarp::Always /opt/perl/bin/dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.3 to 0.1.4
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.4
[DZ] writing archive to Sample-Module-0.1.4.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] ...
...
[@Filter/TestRelease] all's well; removing .build/dE1qIIQWEG
[FakeRelease] Fake release happening (nothing was really done)
can't open Changes for reading: No such file or directory\
 at /opt/perl-5.8.8/lib/site_perl/5.8.8/Dist/Zilla/File/OnDisk.pm line 31
   Dist::Zilla::File::OnDisk::_read_file(...
   Dist::Zilla::File::OnDisk::__ANON__(...
   Class::MOP::Attribute::default(...
   Dist::Zilla::File::OnDisk::content(...
   Dist::Zilla::Plugin::Git::Commit::_get_changes(...
   Dist::Zilla::Plugin::Git::Commit::__ANON__(...
   String::Formatter::method_replace(...
   String::Formatter::format(...
   String::Formatter::__ANON__(...
   Dist::Zilla::Plugin::Git::Commit::get_commit_message(...
   Dist::Zilla::Plugin::Git::Commit::after_release(...
   Dist::Zilla::release(...
   Dist::Zilla::App::Command::release::execute(...
   App::Cmd::execute_command(...
   App::Cmd::run(...

Voilà, a perfect stack trace with the info I was after!

Posted at 14:45:40 by Flavio Poletti
Sat, 8 May 2010

Giving Dist::Zilla a try

I’m just new to Dist::Zilla, so these are the records of what I found about it... If you're impatient and only want the results, go to The Lucky Path section below, otherwise keep reading!

Installation

The installation was more or less flawless. There are a number of modules that get installed, other get updated. Curiously enough, the installation for Dist::Zilla itself went wrong due to a bug in perl 5.8.x’s regular expression operator qr that triggers an error in one of the tests.

Starting a new module

This step requires you to do work! From the official tutorial (as of May 8, 2010):

This document will be overhauled when dzil new is more useful.
For now, we won't even cover the new command, because it's so pointless.
So let's follow the hints here and create something:
~/dzilla$ mkdir Sample-Module
~/dzilla$ cd Sample-Module
~/dzilla/Sample-Module$ mkdir t
~/dzilla/Sample-Module$ mkdir -p lib/Sample
~/dzilla/Sample-Module$ vi t/00-load.t
~/dzilla/Sample-Module$ showfile t/00-load.t

|  # vim: filetype=perl :
|  use strict;
|  use warnings;
|
|  use Test::More tests => 1; # last test to print
|
|  BEGIN {
|     use_ok('Sample::Module');
|  }
|
|  diag("Testing Sample::Module $Sample::Module::VERSION");

~/dzilla/Sample-Module$ vi lib/Sample/Module.pm
~/dzilla/Sample-Module$ show lib/Sample/Module.pm

|  package Sample::Module;
|  use strict;
|  use warnings;
|  use English qw( -no_match_vars );
|  use Carp;
|
|  1;
|  __END__

~/dzilla/Sample-Module$ vi dist.ini
~/dzilla/Sample-Module$ show dist.ini

|  name    = Sample-Module
|  author  = Flavio Poletti 
|  license = Perl_5
|  copyright_holder = Flavio Poletti
|  
|  [@Basic]

In case you're wondering, I'm working in Linux and the show stuff above is a shell function defined as follows:

function show () { 
    echo
    sed 's/^/|  /' "$1"
    echo
}

Well... there's nothing to tell me what to do now, so let's just build it:

~/dzilla/Sample-Module$ dzil build
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
Unable to extract an abstract from lib/Sample.pm. Please add...
[DZ] no version was ever set...

Bummer! There seem to be two problems here (and with the tutorial): the first one is the absence of the abstract, the other one seems more serious because it triggers some error internally to Dist::Zilla.

Adding an ABSTRACT

Fixing the first issue should be simple, I remember reading about the abstract somewhere and it seems to just require an additional line in the module file:

~/dzilla/Sample-Module$ vi lib/Sample/Module.pm 
~/dzilla/Sample-Module$ show lib/Sample/Module.pm 

|   package Sample::Module;
|   # ABSTRACT: a sample module to play with Dist::Zilla
|   
|   use strict;
|   use warnings;
|   use English qw( -no_match_vars );
|   use Carp;
|   
|   1;
|   __END__

~/dzilla/Sample-Module$ dzil build
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] no version was ever set...
I just think that some explicit mention about the ABSTRACT would be nice on the reader in the tutorial!

Dealing with the version

Managing Version Numbering with Dist::Zilla seems to be a good candidate to get rid of the second error. I find particularly reassuring the following sentence:

The simplest way to specify your dist's version is to put it in dist.ini

so let's try it immediately:

~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini

|   name    = Sample-Module
|   version = 3.14.15
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Basic]

~/dzilla/Sample-Module$ dzil build
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-3.14.15
[DZ] writing archive to Sample-Module-3.14.15.tar.gz

WTF? Definitely not what I expected from a build command! I'll dig it later, now let's try to test it:

~/dzilla/Sample-Module$ dzil test
[DZ] building test distribution under .build/krERIMYW7M
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in .build/krERIMYW7M
Checking if your kit is complete...
Looks good
Writing Makefile for Sample::Module
cp lib/Sample/Module.pm blib/lib/Sample/Module.pm
PERL_DL_NONLAZY=1 /opt/perl-5.8.8/bin/perl...
t/00-load.t .. 1/1 Use of uninitialized value in concatenation (.) or string at t/00-load.t line 11.
# Testing Sample::Module 
t/00-load.t .. ok   
All tests successful.
Files=1, Tests=1,  0 wallclock secs ( 0.01 usr  0.02 sys +  0.02 cusr  0.00 csys =  0.05 CPU)
Result: PASS
[DZ] all's well; removing .build/krERIMYW7M
Yay, it's working... more or less. There is still no $VERSION in my module, and this is something that annoyed me a lot in these years. Maybe it's time to read some more from the tutorial:

There are a number of plugins that put your version number to use. PkgVersion inserts a $VERSION definition in all your packages so that they'll all have versions matching distribution's version. NextRelease adds version headers to your changelog file so you don't need to worry about what the next version number will be until it's built. PodVersion or PodWeaver can insert a =head1 VERSION section in your documentation. The Git integration plugins system uses your version to tag releases.

All of these plugins go a long way to taking care of version accounting for you. PkgVersion and the Pod mungers, especially, eliminate the need to update multiple files in multiple ways.

The next step to letting Dist::Zilla help manage your versions is to let it manange your version numbering, too.

Well, let's try this again then...

Dealing with the version, reloaded

I would actually have done this step in any case, because I'm lazy and I like my system to figure out stuff like version numbering, unless it's time that I really need to bump the version. I also use git, so it seems I'm quite lucky because of Dist::Zilla::Plugin::BumpVersionFromGit.

The installation of this module is not clean, anyway. In particular, it seems that using Debian's git is not liked very much by this module, that has tests fixed for version 1.7 while I have 1.5:

#   Failed test at t/basic.t line 45.
#                   'git: 'a-command-not-likely-to-exist' is not a git-command. See 'git --help'.
# '
#     doesn't match '(?-xism:which does not exist)'

How the hell did they decide to change error messages is obscure to me. Now the problem is... to install or not to install? Probably the best thing to do would be to self-compile git and go with the bleading edge, but I'm lazy enough to decide that git tag is probably unchanged... so I go for make install.

After the installation, following DZP::BumpVersionFromGit should be easy:

~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Basic]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

So let's try:
~/dzilla/Sample-Module$ dzil test
[DZ] building test distribution under .build/hYUzF1mKs7
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
fatal: Not a git repository
Ach! I didn't initialise the git repository!!! Let's fix this:
~/dzilla/Sample-Module$ git init
Initialized empty Git repository in .git/
~/dzilla/Sample-Module$ git add dist.ini lib/Sample/Module.pm t/00-load.t 
~/dzilla/Sample-Module$ git commit -m 'Initial import'
Created initial commit bf8a91c: Initial import
 3 files changed, 31 insertions(+), 0 deletions(-)
 create mode 100644 dist.ini
 create mode 100644 lib/Sample/Module.pm
 create mode 100644 t/00-load.t
and give it a new try:
~/dzilla/Sample-Module$ dzil test
[DZ] building test distribution under .build/6rSmgX1Gpw
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in .build/6rSmgX1Gpw
Checking if your kit is complete...
Looks good
Writing Makefile for Sample::Module
cp lib/Sample/Module.pm blib/lib/Sample/Module.pm
PERL_DL_NONLAZY=1 /opt/perl-5.8.8/bin/perl...
t/00-load.t .. 1/1 Use of uninitialized value in concatenation (.) or string at t/00-load.t line 11.
# Testing Sample::Module 
t/00-load.t .. ok   
All tests successful.
Files=1, Tests=1,  0 wallclock secs ( 0.00 usr  0.02 sys +  0.01 cusr  0.02 csys =  0.05 CPU)
Result: PASS
[DZ] all's well; removing .build/6rSmgX1Gpw
What's wrong now?!? OK I get it, it draws the whole bundle version number from git, but still does not set it inside modules; it seems that I did not read the tutorial with sufficient attention:

There are a number of plugins that put your version number to use. PkgVersion inserts a $VERSION definition in all your packages so that they'll all have versions matching distribution's version. NextRelease adds version headers to your changelog file so you don't need to worry about what the next version number will be until it's built. PodVersion or PodWeaver can insert a =head1 VERSION section in your documentation. The Git integration plugins system uses your version to tag releases.

Let's add PkgVersion then, and give it yet another try:
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Basic]
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~/dzilla/Sample-Module$ dzil test
[DZ] building test distribution under .build/uFlnF2gzDc
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in .build/uFlnF2gzDc
Checking if your kit is complete...
Looks good
Writing Makefile for Sample::Module
cp lib/Sample/Module.pm blib/lib/Sample/Module.pm
PERL_DL_NONLAZY=1 /opt/perl-5.8.8/bin/perl...
t/00-load.t .. 1/1 # Testing Sample::Module 0.1.0
t/00-load.t .. ok   
All tests successful.
Files=1, Tests=1,  1 wallclock secs ( 0.01 usr  0.02 sys +  0.01 cusr  0.01 csys =  0.05 CPU)
Result: PASS
[DZ] all's well; removing .build/uFlnF2gzDc
According to the docs, I can bump the version by myself:
~/dzilla/Sample-Module$ V=3.14.15 dzil build
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-3.14.15
[DZ] writing archive to Sample-Module-3.14.15.tar.gz

dzil build WTF?

I expected build to do something similar to either Module::Build's ./Build or ExtUtils::MakeMaker's make, but it seems that it actually builds up the distribution file, a-la ./Build dist or make dist. To be fair, it's written in the help:

~/dzilla/Sample-Module$ dzil | grep build:
     build: build your dist

but I'm not particularly fond of these changes in terminology, especially when they change from well-established conventions.

As a CPAN author that uses these modules from some time, dist would be much, much better.

So, what's inside that package?

This is what's inside the directory after running dzil build:

~/dzilla/Sample-Module$ dir
total 24
-rw-r--r-- 1 poletti dialout  148 2010-05-08 12:52 dist.ini
drwxr-xr-x 3 poletti dialout 4096 2010-05-08 12:20 lib
drwxr-xr-x 4 poletti dialout 4096 2010-05-08 12:52 Sample-Module-3.14.15
-rw-r--r-- 1 poletti dialout 7920 2010-05-08 12:52 Sample-Module-3.14.15.tar.gz
drwxr-xr-x 2 poletti dialout 4096 2010-05-08 12:21 t
~/dzilla/Sample-Module$ cd Sample-Module-3.14.15
~/dzilla/Sample-Module/Sample-Module-3.14.15$ dir
total 48
-rw-r--r-- 1 poletti dialout   148 2010-05-08 12:52 dist.ini
drwxr-xr-x 3 poletti dialout  4096 2010-05-08 12:52 lib
-rw-r--r-- 1 poletti dialout 18258 2010-05-08 12:52 LICENSE
-rw-r--r-- 1 poletti dialout   977 2010-05-08 12:52 Makefile.PL
-rw-r--r-- 1 poletti dialout    86 2010-05-08 12:52 MANIFEST
-rw-r--r-- 1 poletti dialout   386 2010-05-08 12:52 META.yml
-rw-r--r-- 1 poletti dialout   311 2010-05-08 12:52 README
drwxr-xr-x 2 poletti dialout  4096 2010-05-08 12:52 t

Well, it seems that promises have been kept!

Dealing with the version, revolution

There's one final thing to do with version numbering, anyway: git must be told to "remember" it, otherwise I'm still stuck in dealing with this stuff by myself. Dist::Zilla::Plugin::Git comes to the rescue, according to this tutorial page. Well, actually the tutorial page gives for granted that you already know the module and installed it:
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Basic]
|   [PkgVersion]
|   [@Git]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~/dzilla/Sample-Module$ dzil test
couldn't load plugin @Git given in config: Can't locate Dist/Zilla/PluginBundle/Git.pm
...
lots of error lines here
Unluckily, the module installation is not clean due to git's version... again. From the Changes file:
fix tests to work with git 1.7.0
This time the problem seems to be a bit more serious, so I decide to upgrade git and the module eventually gets installed. Time to give it a try:
~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.0
[DZ] writing archive to Sample-Module-0.1.0.tar.gz
[@Basic/TestRelease] ...
...
Result: PASS
[@Basic/TestRelease] all's well; removing .build/uFQW51GQq_

*** Preparing to upload Sample-Module-0.1.0.tar.gz to CPAN ***

Do you want to continue the release process? (yes/no) [no]
Hey, wait a moment! I have uncommitted files, why didn't it spot them?
~/dzilla/Sample-Module$ git status
# On branch master
# Changed but not updated:
...
# Untracked files:
...
It turns out to be an ordering problem inside dist.ini:
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Git]
|   [@Basic]
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.0
[DZ] writing archive to Sample-Module-0.1.0.tar.gz
[@Git/Check] branch master has some untracked files:
[@Git/Check]   Sample-Module-0.1.0.tar.gz
[@Git/Check]   Sample-Module-0.1.0/LICENSE
[@Git/Check]   Sample-Module-0.1.0/MANIFEST
[@Git/Check]   Sample-Module-0.1.0/META.yml
[@Git/Check]   Sample-Module-0.1.0/Makefile.PL
[@Git/Check]   Sample-Module-0.1.0/README
[@Git/Check]   Sample-Module-0.1.0/dist.ini
[@Git/Check]   Sample-Module-0.1.0/lib/Sample/Module.pm
[@Git/Check]   Sample-Module-0.1.0/t/00-load.t ...
The check works, at last. It' interesting (and correct) that dist.ini does not partecipate in the checks. Now let's see if it all works, it's necessary to add the release package names in the exclusion list for git:
~/dzilla/Sample-Module$ vi .git/info/exclude 
~/dzilla/Sample-Module$ show .git/info/exclude 

|   # git-ls-files --others --exclude-from=.git/info/exclude
|   # Lines that start with '#' are comments.
|   # For a project mostly in C, the following would be a good set of
|   # exclude patterns (uncomment them if you want to use them):
|   # *.[oa]
|   # *~
|   Sample-Module-*

and then re-run the release command:
~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.0
[DZ] writing archive to Sample-Module-0.1.0.tar.gz
[@Git/Check] branch master is in a clean state
[@Basic/TestRelease] Extracting ...
...
[@Basic/TestRelease] all's well; removing .build/EqUfLZpmQJ

*** Preparing to upload Sample-Module-0.1.0.tar.gz to CPAN ***

Do you want to continue the release process? (yes/no) [no]^C
[@Basic/ConfirmRelease] Aborting release...
Well, we're going a bit too far, I don't want to push this stuff to CPAN. It seems that this is due to some plugin in the @Basic bundle, so let's see how to disable it... In Dist::Zilla distribution page there's an interesting Dist::Zilla::PluginBundle::Filter, so I get rid of the basic bundle and go for the filter:
~/dzilla/Sample-Module$ vi dist.ini
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Git]
|   [@Filter]
|   bundle = @Basic
|   remove = UploadToCPAN
|   remove = ConfirmRelease
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

Time for a new test
~/dzilla/Sample-Module$ dzil release
you can't release without any Releaser plugins ...
Ouch! I hope DZP::FakeRelease will help me out:
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Git]
|   [@Filter]
|   bundle = @Basic
|   remove = UploadToCPAN
|   remove = ConfirmRelease
|   [FakeRelease]
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

Now, just to double-check the Git::Check module, I modify a file:
~/dzilla/Sample-Module$ vi lib/Sample/Module.pm 
~/dzilla/Sample-Module$ show lib/Sample/Module.pm 

|   package Sample::Module;
|   # ABSTRACT: a sample module to play with Dist::Zilla
|   # just a comment
|   
|   use strict;
|   use warnings;
|   use English qw( -no_match_vars );
|   use Carp;
|   
|   1;
|   __END__

and run the release command once again:
~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.0
[DZ] writing archive to Sample-Module-0.1.0.tar.gz
[@Git/Check] branch master has some uncommitted files:
[@Git/Check]   lib/Sample/Module.pm ...
Awesome! After committing let's try this again:
~/dzilla/Sample-Module$ git commit lib/Sample/Module.pm -m "added comment"
~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.0
[DZ] writing archive to Sample-Module-0.1.0.tar.gz
[@Git/Check] branch master is in a clean state
[@Filter/TestRelease] ...
...
[@Filter/TestRelease] all's well; removing .build/yqbgoYW_GV
[FakeRelease] Fake release happening (nothing was really done)
[@Git/Tag] Tagged v0.1.0
[@Git/Push] pushing to origin
fatal: 'origin' does not appear to be a git repository
fatal: The remote end hung up unexpectedly
Uhm, it seems that I'd better get rid of the Git::Push plugin, because my repository is local:
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Filter]
|   bundle = @Git
|   remove = Git::Push
|   [@Filter]
|   bundle = @Basic
|   remove = UploadToCPAN
|   remove = ConfirmRelease
|   [FakeRelease]
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.0 to 0.1.1
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.1
[DZ] writing archive to Sample-Module-0.1.1.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] Extracting ...
...
[@Filter/TestRelease] all's well; removing .build/E9CAlzs8sG
[FakeRelease] Fake release happening (nothing was really done)
can't open Changes for reading:  No such file or directory\
 at /opt/perl-5.8.8/lib/site_perl/5.8.8/Dist/Zilla/File/OnDisk.pm line 31
OK, the version bumping works, all's well but... there's an error at the end! First of all, I wonder why it didn't pop up last time the release process blocked in Git::Push, but whatever the reason I have to find who's preventing this release from happening. This requires a little hack into Dist::Zilla itself, in particular inside the offending module Dist::Zilla::File::OnDisk we change the die() into Carp::confess:
can't open Changes for reading: No such file or directory...
   Dist::Zilla::File::OnDisk::_read_file(...
   Dist::Zilla::File::OnDisk::__ANON__(...
   Class::MOP::Attribute::default(...
   Dist::Zilla::File::OnDisk::content(...
   Dist::Zilla::Plugin::Git::Commit::_get_changes(...
   Dist::Zilla::Plugin::Git::Commit::__ANON__(...
   String::Formatter::method_replace(...
   String::Formatter::format(...
   String::Formatter::__ANON__(...
   Dist::Zilla::Plugin::Git::Commit::get_commit_message(...
   Dist::Zilla::Plugin::Git::Commit::after_release(...
   Dist::Zilla::release(...
   Dist::Zilla::App::Command::release::execute(...
   App::Cmd::execute_command(...
   App::Cmd::run(...
So that's the culprit! For the $VERSION stuff we don't actually need it, so let's rule it away for the moment:
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Filter]
|   bundle = @Git
|   remove = Git::Push
|   remove = Git::Commit
|   [@Filter]
|   bundle = @Basic
|   remove = UploadToCPAN
|   remove = ConfirmRelease
|   [FakeRelease]
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.0 to 0.1.1
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.1
[DZ] writing archive to Sample-Module-0.1.1.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] Extracting ...
...
[@Filter/TestRelease] all's well; removing .build/ieACRWrTGf
[FakeRelease] Fake release happening (nothing was really done)
[@Filter/Tag] Tagged v0.1.1
At the very last!!! Anyway, having a Changes file is actually a good thing, so let's add one and restore the ruled out plugin:
~/dzilla/Sample-Module$ touch Changes
~/dzilla/Sample-Module$ git add Changes
~/dzilla/Sample-Module$ git commit Changes -m 'added Changes file'
[master 27e56cd] added Changes file
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 Changes
~/dzilla/Sample-Module$ vi dist.ini 
~/dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Filter]
|   bundle = @Git
|   remove = Git::Push
|   [@Filter]
|   bundle = @Basic
|   remove = UploadToCPAN
|   remove = ConfirmRelease
|   [FakeRelease]
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.1 to 0.1.2
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.2
[DZ] writing archive to Sample-Module-0.1.2.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] ...
...
[@Filter/TestRelease] all's well; removing .build/dvgGmImcg2
[FakeRelease] Fake release happening (nothing was really done)
[@Filter/Commit] Committed dist.ini
[@Filter/Tag] Tagged v0.1.2
~/dzilla/Sample-Module$ show Changes 


Much ado abouth nothing? Maybe I have to actually do some changes, let's try:
~/dzilla/Sample-Module$ vi lib/Sample/Module.pm 
~/dzilla/Sample-Module$ show lib/Sample/Module.pm 

|   package Sample::Module;
|   # ABSTRACT: a sample module to play with Dist::Zilla
|   # just a comment, extended
|   
|   use strict;
|   use warnings;
|   use English qw( -no_match_vars );
|   use Carp;
|   
|   1;
|   __END__

~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.2 to 0.1.3
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.3
[DZ] writing archive to Sample-Module-0.1.3.tar.gz
[@Filter/Check] branch master has some uncommitted files:
[@Filter/Check]   lib/Sample/Module.pm ...
Correct, let's commit and go forth:
~/dzilla/Sample-Module$ git commit -a -m "extended the comment"
[master 71a01c0] extended the comment
 1 files changed, 1 insertions(+), 1 deletions(-)
~/dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.2 to 0.1.3
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.3
[DZ] writing archive to Sample-Module-0.1.3.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] ...
...
[@Filter/TestRelease] all's well; removing .build/yn5wsMwfpC
[FakeRelease] Fake release happening (nothing was really done)
[@Filter/Tag] Tagged v0.1.3
~/dzilla/Sample-Module$ show Changes 


Still nothing, but this will probably be meat for some future article!

Update: I later tried to move the Git stuff before the Basic and release stuff, and it actually worked as expected:

~dzilla/Sample-Module$ show dist.ini 

|   name    = Sample-Module
|   author  = Flavio Poletti 
|   license = Perl_5
|   copyright_holder = Flavio Poletti
|   
|   [@Filter]
|   bundle = @Basic
|   remove = UploadToCPAN
|   remove = ConfirmRelease
|   [FakeRelease]
|   [@Filter]
|   bundle = @Git
|   remove = Git::Push
|   [PkgVersion]
|   
|   [BumpVersionFromGit]
|   first_version = 0.1.0
|   version_regexp  = ^v(.+)$

~dzilla/Sample-Module$ dzil release
[DZ] beginning to build Sample-Module
[BumpVersionFromGit] Bumping version from 0.1.3 to 0.1.4
[DZ] guessing dist's main_module is lib/Sample/Module.pm
[DZ] extracting distribution abstract from lib/Sample/Module.pm
[DZ] writing Sample-Module in Sample-Module-0.1.4
[DZ] writing archive to Sample-Module-0.1.4.tar.gz
[@Filter/TestRelease] Extracting ...
...
[@Filter/TestRelease] all's well; removing .build/zD6QJBMw7z
[@Filter/Check] branch master has some untracked files:
[@Filter/Check]   adjust.pl ...
I don't actually understand why this happens; anyway, I still think that keeping the Git::Check stuff before is better, because the whole build and test stuff is not performed if there are pending commits.

The Lucky Path

Well, back to the very beginning: what should I do to:
  • start using Dist::Zilla from scratch
  • with basic integration with git
  • and with automatic version numbering?
Here's my recipe!

Install needed stuff

You'll need a reasonably recent version of git, so install it beforehand. Then install the Perl modules:
~/dzilla$ cpan Dist::Zilla Dist::Zilla::Plugin::BumpVersionFromGit Dist::Zilla::Plugin::Git

Create module directory and stuff

This is up to you, just a minimal example:
~/dzilla$ mkdir Sample
~/dzilla$ cd Sample
~/dzilla/Sample$ mkdir lib
~/dzilla/Sample$ cat >lib/Sample.pm <<END_OF_MODULE
package Sample;
# ABSTRACT: whatever
1;
__END__
END_OF_MODULE
~/dzilla/Sample$ touch Changes
~/dzilla/Sample$ git init
Initialized empty Git repository in /home/poletti/sviluppo/perl/dzilla/Sample/.git/
~/dzilla/Sample$ git add .
~/dzilla/Sample$ git commit -m 'Initial import'
[master (root-commit) 3b19203] Initial import
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 Changes
 create mode 100644 lib/Sample.pm
~/dzilla/Sample$ echo 'Sample-*' >> .git/info/exclude
Remember to ensure that the Changes file is created, or DZP::Git::Commit will complain later.

Create dist.ini

Now create dist.ini, the configuration file for Dist::Zilla:
~/dzilla/Sample$ cat >dist.ini <<END_OF_FILE
name    = Sample
author  = A. U. Thor 
license = Perl_5
copyright_holder = A. U. Thor

[@Filter]
bundle = @Git
remove = Git::Push
[@Filter]
bundle = @Basic
remove = ConfirmRelease
remove = UploadToCPAN
[FakeRelease]
[PkgVersion]

[BumpVersionFromGit]
first_version = 0.1.0
version_regexp  = ^v(.+)$
END_OF_FILE
~/dzilla/Sample$ git add dist.ini
~/dzilla/Sample$ git commit -m 'added dist.ini'
[master 98feba7] added dist.ini
 1 files changed, 18 insertions(+), 0 deletions(-)
 create mode 100644 dist.ini

Enjoy your toolchain

At this point, you can check the toolchain:
~/dzilla/Sample$ dzil release
[DZ] beginning to build Sample
[DZ] guessing dist's main_module is lib/Sample.pm
[DZ] extracting distribution abstract from lib/Sample.pm
[DZ] writing Sample in Sample-0.1.0
[DZ] writing archive to Sample-0.1.0.tar.gz
[@Filter/Check] branch master is in a clean state
[@Filter/TestRelease] Extracting ~/dzilla/Sample/Sample-0.1.0.tar.gz to .build/3GuZPYcWef
Checking if your kit is complete...
Looks good
Writing Makefile for Sample
cp lib/Sample.pm blib/lib/Sample.pm
No tests defined for Sample extension.
[@Filter/TestRelease] all's well; removing .build/3GuZPYcWef
[FakeRelease] Fake release happening (nothing was really done)
[@Filter/Tag] Tagged v0.1.0
If you want, you can restore the automatic upload to CPAN feature of release, by changing the dist.ini file like this:
~/dzilla/Sample$ cat >dist.ini <<END_OF_FILE
name    = Sample
author  = A. U. Thor 
license = Perl_5
copyright_holder = A. U. Thor

[@Filter]
bundle = @Git
remove = Git::Push
[@Basic]
[PkgVersion]

[BumpVersionFromGit]
first_version = 0.1.0
version_regexp  = ^v(.+)$
END_OF_FILE

A shell script to rule them all

This script should do all that you need, just cross your fingers:
mkdir Sample
cd Sample

mkdir lib
cat >lib/Sample.pm <<END_OF_MODULE
package Sample;
# ABSTRACT: whatever
1;
__END__
END_OF_MODULE

touch Changes

cat >dist.ini <<END_OF_FILE
name    = Sample
author  = A. U. Thor 
license = Perl_5
copyright_holder = A. U. Thor

[@Filter]
bundle = @Git
remove = Git::Push
[@Filter]
bundle = @Basic
remove = ConfirmRelease
remove = UploadToCPAN
[FakeRelease]
[PkgVersion]

[BumpVersionFromGit]
first_version = 0.1.0
version_regexp  = ^v(.+)$
END_OF_FILE

git init
git add .
git commit -m 'Initial import'
echo 'Sample-*' >> .git/info/exclude

dzil release

That's all, folks

Well, it seems that Dist::Zilla has its sharp edges, but seems that it's actually possible to start using it and it's quite promising... Stay tuned for further adventures of this total newbie in Dist::Zilla's world!
Posted at 22:53:49 by Flavio Poletti
Tue, 15 Dec 2009

Why parsing should be simple?

Following an article by osfameron (found thanks to Planet Perl Iron Man) I landed on the interesting analysis performed by Aldo Cortesi. I was quite unsurprised at seeing yet another variant of the old thorny "Perl is dead or at least does not feel very well" infamous adage.

I was a bit more surprised at seeing this comment:

At the risk of inflaming more Perl programmers to come and manfully defend their language on my blog, I think there's a reason why Python has a nice BNF grammar, and Perl has 5600 lines of ad-hoc parsing code:
http://www.perlmonks.org/?node_id=663393
I wonder which reason Aldo is thinking about. IMHO, the reason is that probably Python development focuses more on language orthogonality and Perl development more on programmers' ease at the possible expense of a more complicated compiler, but it's just me.
Posted at 14:41:41 by Flavio Poletti
Sat, 5 Dec 2009

IO::Zlib and saved space

A few days ago a colleague asked me about using Perl for analysing some ASA firewall logs in order to spot how many public addresses are needed for NATting users towards the Internet. The basic regular expression to capture the bits of information that he needs is quite straightforward, but what was interesting is that the files he has to work on are gzipped, and he had already extracted a sample one to work on. I remembered that there is IO::Zlib and this is what I did:
for my $file (@ARGV) {
   eval {
      my $fh = _open($file);
      while (<$fh>) {
         my ($inside, $outside) = /Built\ dynamic\ translation\ from\ inside:(.*?)\ to\ outside:(.*?)/mxs
            or next;
         # use $inside and $outside
      }
      close $fh;
   } or warn "exception for '$file': $EVAL_ERROR";
}

sub _open {
   my ($file) = @_;
   my $fh;
   if ($file =~ /\.gz \z/mxs) {
      $fh = IO::Zlib->new();
      $fh->open($file, 'rb')
         or die "IO::Zlib complained: $OS_ERROR";
   }
   else {
      open $fh, '<', $file
         or die "open(): $OS_ERROR";
   }
   return $fh;
}
It worked pretty well so nothing to complain. Just before blogging about it, I paid a due visit to the documentation, and I discovered that I was more or less lucky: there are limitations in using the module, which basically boil down to $fh not being what you expect from a full fledged filehandle. But, at least, it should work out of the box if all you need is to read the file one line at a time.

The module isn't in the core distribution, but it's a common prerequisite so chances are that you already have it in your distro. It's a bit weird that it is known by corelist to have been included in 5.9.3:

IO::Zlib was first released with perl 5.009003
even though there is no trace of it in 5.10. Go figure. Anyway, it should be a bit more common to find than the alternative PerlIO::gzip, which would make the sub _open unneeded when substituted with this:
   open my $fh, '<:autopop', $file or die '...';
I wonder how much Perl IO layers are used out there.
Posted at 23:27:28 by Flavio Poletti
Sun, 29 Nov 2009

Equality is reflexive... isn't it?

I read about Perl6::Junction in an artile in blogs.perl.org and I was tickled. I quickly went on CPAN to see what the module was about beyond the post above, and saw two enthusiastic reviews by two bigs (at least this is what I consider both of them).

I have to say that I was a bit disappointed in seeing that they both talked about very clear documentation, while it seemed a bit too minimal for my taste. I do agree that the test suite is complete, anyway, and it's a useful source for examples too! The tests are indeed quite extensive, and there are also tests for something that made me curious, i.e.: "will it be possible to use junctions on both sides?". The answer turns out to be positive, and there are tests for those cases (see the T/join.t test file for details).

One funny thing in the module is that the following both apply:

my $is_true = (all(3, 4) == any(3, 4));
my $is_NOT_true = (any(3, 4) == all(3, 4));

It actually makes sense: the first says "do all elements in the {3, 4} set have something equal to them in the {3, 4} set?". Course they do, because 3 in the first set has 3 in the second, and 4 in the first set has 4 in the second. The second says "is any element in the {3, 4} equal to all the elements in {3, 4}?". Course there isn't, because 3 from the first set is equal to 3 in the second set, but fails to be equal to 4.

Hence, the (numeric) equality operator does not maintain the reflexive property here, and it seems just... weird, even though it makes perfectly sense.

Posted at 00:15:00 by Flavio Poletti
Tue, 24 Nov 2009

RTFM, you geek!

After the previous post, I shipped the script and the related libraries to the recipient and slept as a baby. Just to discover that they didn't figure out how to use it properly!

The main issue is that geeks and technical people generally don't read the documentation. I admit, most of the times I don't do this as well. In this case, my good colleague saw a Perl script, made the too-simple-to-seem-true association between Perl and the Web and installed Apache! Well, as a matter of fact it is a web application, but I strived to make it self-contained and you end up with Apache?!?

Luck was that I was there some thirty minutes after to correct the problem and let Perl shine: "hey bro, it's a single program with an embedded web server!". That was the lucky part, anyway: the environment we're working into is particularly hostile, to the point that two machines in the very same network can't ping one another! Fortunately when I did use the program to make some tests the network connectivity was not that bad and we managed to do them.

Another thing that annoyed me a bit was that the script was not fully self-contained. Yes, I have to ship a hole tar.gz file... so I thought that it would be great to distribute a single script without anything more. One option is PAR, of course, but I always regretted to use it because you basically have to compile for a target architecture (at least in my understanding). In case of Pure-Perl stuff this does not make me happy.

Some time ago I had a similar problem that I solved with a script to bundle Pure-Perl modules inside a script. I put it in repository in repo.org.cz, for anyone to have a good laugh.

Posted at 02:53:11 by Flavio Poletti
Sun, 15 Nov 2009

Perl.org has a new face!

Wow, I'm very pleased to see the new face of Perl.org. I have to thank Paul Fenwick for the hint!
Posted at 01:39:03 by Flavio Poletti
Thu, 29 Oct 2009

Italian Perl Workshop 2009

Divertito mi sono divertito! Rivedere gli amici perlisti, conoscerne di nuovi... Ho notato una cosa, però: la gente sembrava veramente poca quando si stava nella sala grande!

Le presentazioni che ho fatto sono su SlideShare:


Posted at 03:04:28 by Flavio Poletti

Older posts