Splitting a perl program

Migrated. Originally posted: 2008-02-07

Like me, you have probably inherited a program or two that someone else wrote, and that could stand tweaking.

Any build system program needs updating as the system changes, with new demands, new technology, new builds. Perl programs can be structured to make this easy, or hard. I have a large program to tweak, and it’s all in one file – about 3500 lines long. 3500 lines is hard to work with in one file, so I am splitting it up.

There are several large functions of a few hundred lines each, and some smaller functions. There is a data declaration area at the top, about 200 lines long, and the main program. The main program is about 6 lines long. It calls one of the two largest functions based on data passed on the command line.

I am splitting it up into a bunch of files. One file for each of the functions that is over 100 lines long, plus one for all the other functions. I have no reason or desire to make the files into proper modules and make the whole thing object oriented. I just want to be able to “include” the files and run the functions. I also want to put all these files in a separate directory so they don’t clutter up a “bin” directory with several existing scripts in it. And of course all that data at the top of the program is global data that is used from within the functions.

I could go into a lengthy description here of what I did first, and what failed paths I took, but that can wait for the Beer SIG after an OCLUG meeting. Here are just the things you need to know to make this work.

Following is a sample program with a simple subroutine that uses global data. The subroutine, “one”, is in a separate file, “one.pl”, in a subdirectory, “lib”. The main program declares an array and populates it, then calls the subroutine.

First, Perl has to be able to find the “module”. Perl has a path-like structure, an array called “@INC”, to keep track of the folders it will look in to find modules. I used the “use lib” command to tell Perl to add the “mylibs” folder to that list, where “lib” is a subdirectory of my program’s current directory when it starts.

use lib 'mylibs';

Second, you have to tell Perl to “include” the file. I used “require” to do this, as “use” seems to need a lot of setting up that is intended for class modules and I wasn’t trying to do that.

Third, Perl expects to find out that the require “succeeded”. If you declare a variable, or execute something, this will happen naturally, as I understand it. I was just storing subroutines so nothing was being returned. Add a line to each subroutine file, return 1;, so it will succeed for Perl. It works to put it at the end, and it makes a useful standard.

Fourth, those pesky global variables declared “my” at the top of the file. You still need to declare them under “use strict” rules, but with “my” they will not be visible to subroutines in other files. So you declare them with “use vars qw()”. And then populate them in a separate step.

Now here’s the sample code. The main program is first:


#!/usr/bin/perl -w 
# I use strict and "-w" all the time
# this task is initially easier without using them, cause you just don't declare your global vars then
# But that's risky and kills me time and again
use strict;      
# "use lib '/usr/lib/foo' adds it to @INC so we can find files in it
use lib 'mylibs';  
# just using require is good enough for simple inclusion of subroutines
require 'one.pl';     
# Don't use "my", use this so the required functions can use it
use vars qw( @stuff );  
@stuff = ('one', 'two', 'three');
&one;

And now the module in the “mylibs” folder:


sub one {
   # "@stuff" is declared in the calling program with 'use vars qw()'
   print "@stuff";
   print "\n";
}   
# module must return something when "required"
return 1;

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s