following autarch's advice on yesterday's post, i just uploaded dist-zilla-plugin-prepender 0.2.0 that accepts a copyright option to automatically add a boilerplate copyright at the top of the files.
yet another things that ease an author's life.
i'm still unsure with the strict and warnings options. not because of the test argument, but because of the editing argument. indeed, with padre (the perl ide) doing live syntax check, i want the syntax check to be done with all due stricture...
2009-06-20
new distzilla plugin: prepender
when i first heard of dist::zilla, its concepts definitely appealed to me. it took me quite some time (never the good time, plus i needed to create the rpms for mandriva), but i finally managed to try it...
and although i still need to get used to it, i'm already really happy with it. on the new project that i recently started (more on that later on), i'm letting dist::zilla deal with all those pesky copyright, version, pod, etc. stuff.
however, i'm used to add some boilerplate at the top of my files, as the fsf recommends. and i found nothing in dist::zilla to do that... so i just wrote a new plugin for it: dist::zilla::plugin::prepender.
just add the following in your dist.ini file:
and although i still need to get used to it, i'm already really happy with it. on the new project that i recently started (more on that later on), i'm letting dist::zilla deal with all those pesky copyright, version, pod, etc. stuff.
however, i'm used to add some boilerplate at the top of my files, as the fsf recommends. and i found nothing in dist::zilla to do that... so i just wrote a new plugin for it: dist::zilla::plugin::prepender.
just add the following in your dist.ini file:
but it can also be used to enforce some common pragma: since it will be inserted at the top of the file, the pragma will be applied to its lexical scope - the whole file itself in this case:[Prepender]
line = #
line = # This file is part of Foo::Bar
line = #
line = # Foo::Bar is copyright...
since those usages are quite common, i might as well in a future version propose them as options:[Prepender]
line = use strict;
line = use warnings;
but i'm not really sure... wdyt?[Prepender]
copyright = 1
strict = 1
warnings = 1
2009-06-18
which parrot version to package?
after some lengthy discussions finding the final versioning scheme for parrot, allison finally decided that parrot will use the following version numbers:
as parrot packager for mandriva, this is good to know. however, i don't really know what to do: should i package the stable versions? or should i update the package for each new devel version?
since parrot is quite in flux those times, version 1.0.0 (which is 3 monthes old) is really useless... if you add that almost nothing production-ready relies on it currently, i have decided to update the package on a monthly basis. which means that rpm for parrot 1.3.0 is available on cooker right now.
but let's think forward a bit... when rakudo will be able to use an existing parrot, when perl 6 will be in production, i won't continue like that: i'll just stick (of course) with the production releases... but when will this point happen? when will i switch from devel to stable versions? this remains to be seen...
- 1.0 (March, deprecation point)
- 1.1 (April)
- 1.2 (May)
- 1.3 (June)
- 1.4 (July, deprecation point)
- 1.5 (August)
- 1.6 (September)
- 1.7 (October)
- 1.8 (November)
- 1.9 (December)
- 2.0 (January, deprecation point)
- 2.1 (February)
- 2.2 (March)
- 2.3 (April)
- 2.4 (May)
- 2.5 (June)
- 2.6 (July, deprecation point)
- 2.7 (August)
- 2.8 (September)
- 2.9 (October)
- 2.10 (November)
- 2.11 (December)
- 3.0 (January, deprecation point)
as parrot packager for mandriva, this is good to know. however, i don't really know what to do: should i package the stable versions? or should i update the package for each new devel version?
since parrot is quite in flux those times, version 1.0.0 (which is 3 monthes old) is really useless... if you add that almost nothing production-ready relies on it currently, i have decided to update the package on a monthly basis. which means that rpm for parrot 1.3.0 is available on cooker right now.
but let's think forward a bit... when rakudo will be able to use an existing parrot, when perl 6 will be in production, i won't continue like that: i'll just stick (of course) with the production releases... but when will this point happen? when will i switch from devel to stable versions? this remains to be seen...
2009-06-11
redefining exported subs in perl
i've explained in a previous post that i changed the way i was logging debug statements within language::befunge. i mentioned that i applied some tricks and promised to explain them - so here are the explanations.
the goal is to minimize time spent for debug statements. previously, i was doing:
so, to log a debug message, i was doing:
so, one obvious way to improve was to move from a method to a plain sub. this would remove the run-time cost of resolving the method. the debug mode can be stored as a package scalar instead of an attribute.
but we can do even better. knowing that:
here's our code at that point:
so, to redefine exported subs, one is forced to walk the symbol table of all packages and redefine subs on the fly. here's what i ended up doing:
now, do you think this would warrant a sub::redefine module on cpan? after all, i found nothing on cpan that would achieve that. otoh, i'm not sure it's that common to do this kind of things... so tell if you're interested, and i'll turn that in a cpan module for your own use.
the goal is to minimize time spent for debug statements. previously, i was doing:
and debug was a method defined as:$interpreter->debug(@stuff);
sub debug {
my ($self, @stuff) = @_;
return unless $self->debug_mode;
warn @stuff;
}
so, to log a debug message, i was doing:
- a method call on $interpreter
- a second method call to check an attribute
- finally the actual logging (skipped if we're not in debug mode)
so, one obvious way to improve was to move from a method to a plain sub. this would remove the run-time cost of resolving the method. the debug mode can be stored as a package scalar instead of an attribute.
but we can do even better. knowing that:
- perl optimizes out calls to empty subs
- we are not in debug mode most of the time
here's our code at that point:
of course, we need to provide a way to activate debugging. a naive approach would be to redefine our debug() sub in our debug package:package Language::Befunge::Debug;
use 5.010;
use strict;
use warnings;
use base qw{ Exporter };
our @EXPORT = qw{ debug };
sub debug {}
alas, this won't work. well, it will work for calls such as:sub enable {
*debug = sub { warn @_; };
}
but calls using exported debug() will still log nothing. indeed, it's important to understand that exporter installs a copy of exported sub in the package. therefore, changing the definition of the original does not change the exported copies.
Language::Befunge::Debug::debug(@stuff);
so, to redefine exported subs, one is forced to walk the symbol table of all packages and redefine subs on the fly. here's what i ended up doing:
there, it will redefine my sub in all packages, even the ones that hold an exported copy.
my %redef;
sub enable {
%redef = ( debug => sub { warn @_; } );
_redef();
}
sub disable {
%redef = ( debug => sub {} );
_redef();
}
my %orig; # original subs
sub _redef {
my $parent = shift;
if ( not defined $parent ) {
$parent = '::';
foreach my $sub ( keys %redef ) {
$orig{ $sub } = \&$sub;
}
}
no strict 'refs';
no warnings 'redefine';
foreach my $ns ( grep /^\w+::/, keys %{$parent} ) {
$ns = $parent . $ns;
_redef($ns) unless $ns eq '::main::';
foreach my $sub (keys %redef) {
next # before replacing, check that...
unless exists ${$ns}{$sub} # ... named sub exist...
&& \&{ ${$ns}{$sub} } == $orig{$sub}; # ... and refer to the one we want to replace
*{$ns . $sub} = $redef{$sub};
}
}
}
now, do you think this would warrant a sub::redefine module on cpan? after all, i found nothing on cpan that would achieve that. otoh, i'm not sure it's that common to do this kind of things... so tell if you're interested, and i'll turn that in a cpan module for your own use.
2009-06-06
cpan2dist oddity
cpanplus is great - really, i mean it. i don't like its internals, and having to adapt to it for cpanplus::dist::mdv is not my fondest memory. however, it gets the job done, and that's what counts in the end.
but cpanplus has some oddities in the user-space also. cpan2dist, which is a tool allowing you to create a native package for your platform, sometimes makes me cry.
it insists on building prereqs for the module you want. that's fine, except for one thing: it will also build packages for modules that are not up to date... and this is where the fun starts.
you can think that --nobuildprereqs would prevent this, but nope, it's the default and really means: "don't rebuild prereqs if we're at latest version, but rebuild anyway if we're not at latest version". the --buildprereqs indeed means "i really really want to loose my time, please rebuild a package for all the prereqs even if i already have them installed and working". like this option is of any use... erm.
so in app::cpan2pkg, in order not to build the prereqs, i ended up using --ignore flags (flags as in plural form, since using a negative look-ahead regex such as /(?<!$name)$/ do not work). it took me quite some time to come up with this solution, since --ban was not working at that time.
so life was good, i've used cpan2pkg quite some time to create rpms for mandriva, and updated it till v1.0.0. however, since some time, i can see strange things in the packages submitted on upstream buildsystem: the build prereqs were not set. and i had to update the spec file manually, adding all those missing prereqs. i got tired of that, and investigated what happened.
after some experiments, it appears that cpan2dist seems to have somehow changed the way it handles its --ignore flags. ignored modules now are removed from the prereqs completely, and thus the cpanplus::dist backend do not even see them any more. and thus, buildrequires end up empty. of course, --nobuildprereqs still retained its old silly behaviour...
fortunately, it seems that --ban now works correctly (at least on 2 tests that i made). so i've just updated cpan2pkg, and version 1.1.0 should be available soon on cpan, this time really adding the needed buildrequires.
till the next cpan2dist change, of course... :-|
note: i don't want to sound too harsh to jos in this post... i totally understand that cpanplus is still 0.x software - moreover, this is a bug fix in my mind, since --ban was not working.
but cpanplus has some oddities in the user-space also. cpan2dist, which is a tool allowing you to create a native package for your platform, sometimes makes me cry.
it insists on building prereqs for the module you want. that's fine, except for one thing: it will also build packages for modules that are not up to date... and this is where the fun starts.
you can think that --nobuildprereqs would prevent this, but nope, it's the default and really means: "don't rebuild prereqs if we're at latest version, but rebuild anyway if we're not at latest version". the --buildprereqs indeed means "i really really want to loose my time, please rebuild a package for all the prereqs even if i already have them installed and working". like this option is of any use... erm.
so in app::cpan2pkg, in order not to build the prereqs, i ended up using --ignore flags (flags as in plural form, since using a negative look-ahead regex such as /(?<!$name)$/ do not work). it took me quite some time to come up with this solution, since --ban was not working at that time.
so life was good, i've used cpan2pkg quite some time to create rpms for mandriva, and updated it till v1.0.0. however, since some time, i can see strange things in the packages submitted on upstream buildsystem: the build prereqs were not set. and i had to update the spec file manually, adding all those missing prereqs. i got tired of that, and investigated what happened.
after some experiments, it appears that cpan2dist seems to have somehow changed the way it handles its --ignore flags. ignored modules now are removed from the prereqs completely, and thus the cpanplus::dist backend do not even see them any more. and thus, buildrequires end up empty. of course, --nobuildprereqs still retained its old silly behaviour...
fortunately, it seems that --ban now works correctly (at least on 2 tests that i made). so i've just updated cpan2pkg, and version 1.1.0 should be available soon on cpan, this time really adding the needed buildrequires.
till the next cpan2dist change, of course... :-|
note: i don't want to sound too harsh to jos in this post... i totally understand that cpanplus is still 0.x software - moreover, this is a bug fix in my mind, since --ban was not working.
2009-06-05
some befunge love
i took some time to review my befunge modules. trying to speed up things is always fun, so thanks to devel::nytprof, i saw that i was spending quite some time on my debug statements.
it should be noted that those statements were method calls on the main language::befunge::interpreter object. and the method then was outputing things depending on the value of a debug attribute of the interpreter. the interpreter was not used outside of this.
knowing that method calls are expensive (since perl doesn't know until run-time where to find the method), i therefore created a language::befunge::debug module that exports a debug() sub. 2 other subs are provided (but not exported) to turn on/off the debug. (there's a trick here, that i will explain in another post).
net result? around 20% speedup (a bit more in fact). not bad for one hour spent on the subject. :-)
other than that, language::befunge tests got sanitized (using test::more, test::output and test::exception everywhere instead of crafting stuff by hand). part of this code was not touched since 2002...
finally, language::befunge got some new extensions, still passing all mycology tests. you can now enjoy the following in jqbef98:
which leaded me to update language::befunge::debugger to load mycology correctly, with a new option to run without delay till the next breakpoint. using it, things were easier to get right. still not perfect, but already more than usable...
so, enjoy language::befunge 4.11 and language::befunge::debugger 0.3.6, now available on cpan!
it should be noted that those statements were method calls on the main language::befunge::interpreter object. and the method then was outputing things depending on the value of a debug attribute of the interpreter. the interpreter was not used outside of this.
knowing that method calls are expensive (since perl doesn't know until run-time where to find the method), i therefore created a language::befunge::debug module that exports a debug() sub. 2 other subs are provided (but not exported) to turn on/off the debug. (there's a trick here, that i will explain in another post).
net result? around 20% speedup (a bit more in fact). not bad for one hour spent on the subject. :-)
other than that, language::befunge tests got sanitized (using test::more, test::output and test::exception everywhere instead of crafting stuff by hand). part of this code was not touched since 2002...
finally, language::befunge got some new extensions, still passing all mycology tests. you can now enjoy the following in jqbef98:
- CPLI - complex numbers extension
- DIRF - directory operations
- FILE - file i/o operations
- FIXP - fixed point operations
- STRN - string operations
- SUBR - subroutines extension
- TIME - date/time operations
which leaded me to update language::befunge::debugger to load mycology correctly, with a new option to run without delay till the next breakpoint. using it, things were easier to get right. still not perfect, but already more than usable...
so, enjoy language::befunge 4.11 and language::befunge::debugger 0.3.6, now available on cpan!
2009-05-29
image resizing in perl
perl is really versatile, and can do a lot of things. moreover, you can do it in a lot of different ways. the difficult part being sometimes to choose the way you want to do it...
so i need to resize an image within perl. this is for games::risk, a perl implementation of the famous board game (btw, try it and tell me if you like it!). i want the background image to fit the window size on resize events. of course, i want it to be fast - but i also want something maintainable.
looking on cpan, i found 3 modules: gd, image::magick and image::imlib2. using gd to resize a picture is a nightmare, therefore i ditched it for image::resize which is a convenient wrapper around gd.
here are the needed incantations to resize:
however, image::imlib2 has a drawback: the only way to get back the new image is to save it to a file, where one can get the new image as a scalar directly:
therefore, one must also take this into account in the benchmarking! so here's the latest version of the bench:
and i was quite astonished to see that image::imlib2 is still 2 or 3 times faster that image::magick or image::resize!
so, imlib2 may not the best api around, but sure it's fast...
so i need to resize an image within perl. this is for games::risk, a perl implementation of the famous board game (btw, try it and tell me if you like it!). i want the background image to fit the window size on resize events. of course, i want it to be fast - but i also want something maintainable.
looking on cpan, i found 3 modules: gd, image::magick and image::imlib2. using gd to resize a picture is a nightmare, therefore i ditched it for image::resize which is a convenient wrapper around gd.
here are the needed incantations to resize:
- with imlib2
- with image::magick - note that the operation is done inplace, so one needs to clone the image first to compare the same things.
- with image::resize
my $old = Image::Imlib2->load($src);
my $new = $old->create_scaled_image($w, $h);
my $old = Image::Magick->new;
$old->Read($src);
my $new = $old->Clone;
$new->Scale(width=>$w, height=>$h);
my $old = Image::Resize->new($src);
my $new = $old->resize($w, $h);
however, image::imlib2 has a drawback: the only way to get back the new image is to save it to a file, where one can get the new image as a scalar directly:
- image::resize with $img->jpeg
- image::magick with $img->ImageToBlob
therefore, one must also take this into account in the benchmarking! so here's the latest version of the bench:
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use Benchmark qw{ :all };
use Image::Size;
use Image::Resize;
use Image::Imlib2;
use Image::Magick;
my $src = "src.jpg";
my $dst = "dst.jpg";
my ($w, $h) = imgsize($src);
my @sizes = (
[10,10], [100,100], [1000,1000],
[640,400], [840,600], [1024,768],
[$w,$h], [$w/2,$h/2], [$w/4,$h/4],
[$h,$w], [$h/2,$w/2], [$h/4,$w/4],
);
my $imlib2 = Image::Imlib2->load($src);
my $resize = Image::Resize->new($src);
my $magick = Image::Magick->new; $magick->Read($src);
foreach my $s ( @sizes ) {
my ($width, $height) = @$s;
say "-> ${width}x${height}";
local $/;
cmpthese( -3, {
imlib2 => sub {
my $img = $imlib2->create_scaled_image($width, $height);
$img->save($dst);
open my $fh, '<', $dst;
return <$fh>;
},
magick => sub {
my $img = $magick->Clone;
$img->Scale(width=>$width,height=>$height);
return $img->ImageToBlob;
},
resize => sub {
my $img = $resize->resize($width,$height);
return $img->jpeg; # $img->png
},
} );
say '';
}
and i was quite astonished to see that image::imlib2 is still 2 or 3 times faster that image::magick or image::resize!
so, imlib2 may not the best api around, but sure it's fast...
Subscribe to:
Posts (Atom)