Old Perls and new code

Sébastien Aperghis-Tramoni, sebastien@aperghis.net

Rationale

  • Why even care about old Perls?

    • Because there are large amounts of hidden Perl code running on old systems

    • Because you can face this very problem

  • Why not upgrade Perl?

    • Because of the potential impact (may be hard to estimate)

    • Because in some cases, you just can't do it

Rationale

  • Currently working as a Perl expert at a big French ISP

  • Mission: "maintain" old code, write new programs

  • Platform: about 2000 servers

    • Heterogeneous (Debian, Red Hat, Solaris, Windows)

    • Many were installed years ago

    • Old systems mean old Perls

A brief history of Perl

  • Perl 5.000 .. October 1994

  • Perl 5.001 .. March 1995

  • Perl 5.002 .. February 1996

  • Perl 5.003 .. June 1996

  • Perl 5.004 .. May 1997

  • Perl 5.005 .. July 1998

A brief history of Perl

  • Perl 5.6 .. March 2000

  • Perl 5.8 .. July 2002

  • Perl 5.10 .. RSN? JBDNF?^W^WRSN! \o/

  • Perl 5.12 .. JBTEOTU?^WJBDNF?

Compatibility problems

  • Language

    • version

    • syntax

    • features

  • Modules

    • version

    • features

    • availability

Language version

  • use 5.8 or require 5.8

  • Question the origin of this prerequisite

    • may have been added by a code generation tool, for example h2xs

    • may have been added on purpose by the author

Language version

  • Example

      maddingue@gwaihir:Speech-eSpeak-0.21 $ perl Makefile.PL && make all test
      Perl v5.8.8 required--this is only v5.8.7, stopped at Makefile.PL line 1.
      BEGIN failed--compilation aborted at Makefile.PL line 1.
  • "ZOMG!1! UR PERL IZ NOT TEH LATEST U L4M3RZ LOLOLOL!!1! BAI"

  • Yeah, sure

Language version

  • Pertinence

    • estimate the pertinence of this prerequisite

    • if non pertinent, remove it

    • you win

      • maybe

    • if pertinent, you need to search further

Language version

  • Example

    • use 5.008008 is typical of h2xs

    • In most cases, can be removed

Syntax

  • Keywords and operators

  • Functions

  • Strings

  • Regexps

Syntax

  • Keywords and operators

    • our (Perl 5.6)

    • state (Perl 5.10)

    • // (Perl 5.10 or 5.8+DOR)

Syntax - Keywords

  • our

    • IMHO, generally used in the wrong way

    • Use twice more memory than lexicals or plain globals

    • Package private variables can in most cases be declared with my

          package TwoFlowers;
          my $config;

Syntax - Keywords

  • our

    • Global variables used only once can be placed in a no strict block

          {   no strict 'vars';
              $VERSION = '1.23';
              @ISA = qw(Flower);
          }
    • Public variables can be declared with use vars

          use vars qw($DEBUG);

Syntax

  • Functions

    • open()

    • binmode()

Syntax - Functions

  • open()

    • Very protean syntax with several different modes

    • Most part of it here since a long time

    • But two recent additions are commonly used

Syntax - Functions - open()

  • open my $fh, $path

    • Feature added with Perl 5.6

    • Syntax is valid in old Perls

    • But $fh is not automatically affected

Syntax - Functions - open()

  • open my $fh, $path

    • Solution: manually create the file handle

          use FileHandle;
      
          open my $fh = new FileHandle, $path or die $!;
    • or, even better, go the OO way

          use FileHandle;
      
          my $fh = FileHandle->new($path) or die $!;

Syntax - Functions - open()

  • 3-arguments open()

    • open($fh, '<', $path);

  • Syntax introduced with Perl 5.6

Syntax - Functions - open()

  • 3-arguments open()

    • Unfortunately, the following obvious code doesn't work :-(

          my @args = ('>', $path);
          open($fh, @args);
    • Simple solution: transform it back to 2-arguments open()

          open($fh, '<'.$path);
    • Caveat: loosing the protecting feature

Syntax - Functions - open()

  • 3-arguments open()

    • Second solution: FileHandle (since 5.002), or IO::File (since 5.003_07)

          use FileHandle;
          my $fh = FileHandle->new($path, $mode);
          # where $mode is ANSI mode: r, w, r+, etc

Syntax - Functions

  • binmode()

    • Originally to switch the given filehandle to binary mode

    • Now can also be used to select the PerlIO layer for the filehandle (added in 5.6)

    • binmode(FILEHANDLE, LAYER)

    • Produces a warning

Syntax

  • Strings

    • v-strings

    • qr//

Syntax - Strings

  • v-strings

    • Syntax introduced with Perl 5.6

    • Any usage for anything other than version number should be verboten

    • Haven't encountered many v-strings yet

Syntax - Strings - v-strings

  • Comparing versions

    • Solution: get back to strings or floats

      • Pro: well-known method, used since years

      • Con: less reliable

      • Example:

            if ($version > 2.034) {
                ...
            }

Syntax - Strings - v-strings

  • Comparing versions

    • Solution: use version.pm (works with 5.005_04 and later, core in 5.10)

      • Pro: object interface with overloaded operators

      • Con: doesn't provide much portability beyond 5.6

      • Example:

            use version;
        
            my $vers1 = version->new("1.2.3");
            my $vers2 = version->new("1.2.5");
        
            if ($vers1 < $vers2) {
                ...
            }

Syntax - Strings - v-strings

  • Comparing versions

    • Solution: use Perl::Version (pure Perl, works with at least 5.004)

      • Pro: classic object module, good for modifying versions

      • Con: must numify to compare versions

      • Example:

            use Perl::Version;
        
            my $vers1 = Perl::Version->new("1.2.3");
            my $vers2 = Perl::Version->new("1.2.5");
        
            if ($vers1->numify < $vers2->numify) {
                ...
            }

Syntax - Strings - v-strings

  • Printing Perl version

    •   printf "version is v%vd\n", $^V;
    • can be changed to

    •   if ($] > 5.006) {
            printf "version is v%vd\n", $^V;
        } else {
            printf "version is v%s\n", $];
        }

Syntax - Strings

  • qr//

    • Syntax introduced with Perl 5.005

    • Often seen in test suites

      • Typical use: like($result, qr/regexp/)

      • Can be safely replaced with a simple string with no functional risk

    • When used in actual code

      • If used for a good reason, they can't be replaced (see Test::Harness 3.0, Regexp::Assemble, Regexp::Common)

Syntax

  • Regexps

    • Assertions

    • Classes

    • Extended patterns

Syntax - Regexps

  • Assertions

    • \z (Perl 5.006)

      • \Z matches only at end of string, or before newline at the end

      • \z matches only at end of string

    • Will be seen as a "z" by Perl 5.004

    • Therefore must protect the use of this assertion

          my $end = $] >= 5.005 ? "\\z" : "\\Z";
      
          substr($path, 0, -$ignore) =~ s/X(?=X*$end)/$CHARS[int(rand($#CHARS))]/ge;

Syntax - Regexps

  • Classes

    • POSIX classes [:class:] (Perl 5.6)

      • Solution: replace these by their explicit equivalent, see perlre

    • Unicode classes (Perl 5.6)

       \x{263a}  wide hex character
       \N{nom}   named character
       \pP  Match P, named property. Use \p{Prop} for longer names.
       \X   Match eXtended Unicode "combining character sequence",
            equivalent to (?:\PM\pM*)
       \C   Match a single C char (octet) even under Unicode.
      • Abandon all hope

Syntax - Regexps

  • Extended patterns

    • (?#text) (Perl 5.003)

    • (?imsx-imsx) (Perl 5.003)

    • (?:pattern) (Perl 5.003)

    • (?=pattern) (Perl 5.003)

    • (?!pattern) (Perl 5.003)

Syntax - Regexps

  • Extended patterns

    • (?<=pattern) (Perl 5.005)

    • (?<!pattern) (Perl 5.005)

    • (?>pattern) (Perl 5.005, experimental)

    • (?(condition)yes-pattern|no-pattern) (Perl 5.005, experimental)

    • (?{ code }) (Perl 5.005, experimental)

    • (??{ code }) (Perl 5.6, experimental)

Modules

  • Possible dependencies:

    • pragmas

    • core modules

Pragmas

  • warnings

    • Appeared in Perl 5.6

    • Lexically enables and disables warnings

          use warnings;
      
          sub init {
              no warnings 'redefine';
              *DEBUG = \&debug if $debug;
          }
    • Customised warnings categories with warnings::register

Pragmas

  • warnings

    • It is the first cause of incompatibility with old Perls

    • However $^W is available since Perl 5.0

    • ...

Pragmas

  • warnings

    • It was the first cause of incompatibility with old Perls

    • JFDI: warnings-compat

    • $CPAN/S/SA/SAPER/warnings-compat-0.04.tar.gz

          $ perl5.00405 -e 'use warnings; warnings::warnif "hello"'
          hello at -e line 1
          $ perl5.00405 -e 'no warnings;  warnings::warnif "hello"'
          $ 

Pragmas

  • warnings-compat

    • Several limitations, but it works

          use warnings::register;
          
          {
              no warnings 'redefine';
              ...
          }
          
          warnings::warnif "...";
    • Caveats:

      • Not lexical

      • Categories are not used

Pragmas

  • warnings

    • Other solution: directly use $^W instead

    • Example:

          package My::Module;
          local $^W = 1;

Pragmas

  • base

    • Introduced in Perl 5.005

    • Establish "is-a" relationships with base classes

    • But quite complex for just this aim

    • and therefore quite fragile

    • Plus, some P5Porters don't like it as well

      • This is a personal bias of course, but we're talking about portability on older Perls, where this module isn't available :-)

Pragmas

  • base

    • Solution: replace it with the equivalent code

          use base qw(Parse::Syslog);
    • becomes

          use Parse::Syslog;
      
          {   no strict;
              push @ISA, qw(Parse::Syslog);
          }

Pragmas

  • base

    • Solution: use parent.pm

      • Was discussed and approved on P5P

      • Simple, pure Perl, 5.004 compatible

    • Example

          package LolPerl;
          sub exclaim { "I CAN HAS PERL" }
      
          package Cheezburger;
          use parent "LolPerl";

Pragmas

  • constant

    • Introduced in Perl 5.004

    • But its features have evolved

    • In particular, grouped declarations appeared in Perl 5.8.0

Pragmas

  • constant

    • Code like

          use constant {
              PROTOCOL_NAME   => "http", 
              PROTOCOL_PORT   => 80, 
              PROTOCOL_TYPE   => "stream", 
          };
    • must be rewritten:

          use constant PROTOCOL_NAME   => "http";
          use constant PROTOCOL_PORT   => 80;
          use constant PROTOCOL_TYPE   => "stream";

Pragmas

  • constant

    • Other solution: upgrade the module

    • Was dual-lifed after Nicholas Clark saw this talk at YAPC::Europe 2007

    • $CPAN/S/SA/SAPER/constant-1.11.tar.gz

    • Works on 5.005

Core modules

  • Important: the "core" is a moving target

  • Check the date and version of inclusion

  • Module::CoreList, corelist

        $ corelist Data::Dumper
        Data::Dumper  was first released with perl 5.005

Core modules

  • Also check availability on the CPAN

    • More and more modules are dual-lifed

    • CPAN modules are regularly integrated to the core

    • Core modules are regularly put on the CPAN on their own

Core modules

  • Corified modules:

    • 5.004 : CGI, CPAN, IO (IO::File, IO::Socket, ...)

    • 5.005 : Test

    • 5.6 : File::Temp, Pod::Parser, Term::ANSIColor

    • 5.8 : Encode, Filter::Simple, Locale::Maketext, Scalar::Util, List::Util, Storable, Test::More

    • 5.10 : Module::Build, CPANPLUS and its numerous dependencies (Archive::Tar, IO::Zlib, Module::Pluggable, ...)

Core modules

  • CPANized modules:

    • 2001 : Test::Harness

    • 2002 : Safe, ExtUtils::MakeMaker, version

    • 2003 : Data::Dumper

    • 2004 : XSLoader

    • 2005 : Sys::Syslog

    • 2006 : ExtUtils::Install, threads, threads::shared, Exporter

    • 2007 : B::Lint, File::Path, constant, File::Copy (maybe)

Core modules

  • Core modules change as well

    • New features are added

    • Bugs are corrected

  • Can be updated independently of Perl

  • So do it!

Any questions?

Thank you