perl.gg / essentials

Local Packages

2024-07-09

Ever wanted to organize your Perl code into reusable pieces without installing anything to the system? Local packages are your answer.

No CPAN. No root access. No installation headaches. Just drop your modules next to your script and go.

Part 1: THE PROBLEM

You've got a script. It's growing. Functions everywhere. Copy-paste between files. It's a mess.

The solution: break it into modules. But installing modules system-wide feels like overkill for a personal project.

Local packages solve this. Your modules live right next to your script. Portable, simple, no fuss.

Part 2: FINDING YOURSELF

First, you need to know where your script lives:
use FindBin qw($RealBin);
$RealBin gives you the directory containing your script. Not where you ran it from - where it actually lives on disk.
VARIABLE WHAT IT GIVES YOU ---------- ---------------------------------------- $RealBin Directory containing the script $RealScript Name of the script itself $Bin Same as $RealBin (legacy name) $Script Same as $RealScript (legacy name)
The "Real" versions follow symlinks. If someone symlinked your script, $RealBin points to the actual location, not the symlink's directory.

Part 3: TELLING PERL WHERE TO LOOK

use lib ($RealBin, "$RealBin/lib");
The use lib pragma adds directories to @INC - Perl's module search path. Now Perl will look in:
1. The script's directory ($RealBin) 2. A lib subdirectory ($RealBin/lib) 3. All the standard system locations
Order matters. Perl searches left to right, stops at the first match. Your local modules override system ones.
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/

Part 4: YOUR FIRST LOCAL MODULE

Create MyUtils.pm in the same directory as your script:
package MyUtils; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(greet square); sub greet { my ($name) = @_; return "Hello, $name!"; } sub square { my ($n) = @_; return $n * $n; } 1;
That trailing 1; is required. It tells Perl "this module loaded successfully." Without it, your script dies with a cryptic error.

Part 5: USING YOUR MODULE

Now your main script can use it:
#!/usr/bin/env perl use strict; use warnings; use FindBin qw($RealBin); use lib ($RealBin, "$RealBin/lib"); use MyUtils qw(greet square); print greet("World"), "\n"; print "5 squared is ", square(5), "\n";
Output:
Hello, World! 5 squared is 25
The functions come in clean, no package prefix needed.

Part 6: THE EXPORTER DANCE

Exporter is Perl's mechanism for making functions available to importers. Let's break down what's happening:
use Exporter qw(import);
This imports Exporter's import() method into your package. That method handles the qw(...) list when someone use's your module.
our @EXPORT_OK = qw(greet square);
Functions listed here CAN be imported on request. The caller has to explicitly ask for them.

Compare to:

our @EXPORT = qw(greet);
Functions in @EXPORT are imported automatically. No request needed.
ARRAY BEHAVIOR ----------- ---------------------------------------- @EXPORT Auto-imported (considered impolite) @EXPORT_OK Imported on request (the polite way) %EXPORT_TAGS Named groups of exports
The Perl community prefers @EXPORT_OK. Let the caller decide what they want in their namespace.

Part 7: ORGANIZING INTO SUBDIRECTORIES

As your project grows, you'll want a lib directory:
my_project/ main.pl MyUtils.pm lib/ MathOps.pm StringOps.pm
Create lib/MathOps.pm:
package MathOps; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(add subtract multiply divide); sub add { $_[0] + $_[1] } sub subtract { $_[0] - $_[1] } sub multiply { $_[0] * $_[1] } sub divide { my ($a, $b) = @_; die "Division by zero" if $b == 0; return $a / $b; } 1;
Your script can use both:
use MyUtils qw(greet); use MathOps qw(add multiply);

Part 8: THE COMPLETE EXAMPLE

Here's a full working setup. Three files:

main.pl:

#!/usr/bin/env perl use strict; use warnings; use FindBin qw($RealBin); use lib ($RealBin, "$RealBin/lib"); use MyUtils qw(greet square); use MathOps qw(add multiply); # Use the imported functions print greet("Perl Hacker"), "\n"; print "3 squared: ", square(3), "\n"; print "5 + 7 = ", add(5, 7), "\n"; print "4 * 8 = ", multiply(4, 8), "\n";
MyUtils.pm (same directory as main.pl):
package MyUtils; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(greet square); sub greet { my ($name) = @_; return "Hello, $name!"; } sub square { my ($n) = @_; return $n * $n; } 1;
lib/MathOps.pm:
package MathOps; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(add subtract multiply divide); sub add { $_[0] + $_[1] } sub subtract { $_[0] - $_[1] } sub multiply { $_[0] * $_[1] } sub divide { my ($a, $b) = @_; die "Division by zero" if $b == 0; return $a / $b; } 1;

Part 9: IMPORTING EVERYTHING

Sometimes you want all exports. Use the :all tag:
package MyUtils; use Exporter qw(import); our @EXPORT_OK = qw(greet square cube); our %EXPORT_TAGS = ( all => \@EXPORT_OK );
Now callers can do:
use MyUtils qw(:all);
And they get greet, square, and cube without listing each one.

Part 10: NESTED PACKAGES

You can have hierarchical module names:
lib/ My/ Utils.pm Math/ Advanced.pm
lib/My/Utils.pm:
package My::Utils; # ... 1;
lib/My/Math/Advanced.pm:
package My::Math::Advanced; # ... 1;
Use them with:
use My::Utils qw(some_function); use My::Math::Advanced qw(factorial);
The :: in the package name maps to / in the filesystem.

Part 11: COMMON MISTAKES

Forgot the 1; at the end:
MyUtils.pm did not return a true value at main.pl line 5.
Fix: Add 1; as the last line of your module.

Wrong directory in use lib:

Can't locate MyUtils.pm in @INC...
Fix: Check that $RealBin points where you think. Add:
print "Looking in: $RealBin\n";
Package name doesn't match filename:
Undefined subroutine &MyUtilz::greet called...
Fix: The package statement must match the .pm filename exactly.

Part 12: WHY NOT JUST require?

You can skip Exporter entirely with require and explicit calls:
require "$RealBin/MyUtils.pm"; print MyUtils::greet("World");
But this has drawbacks:
* No import list - you always use full package names * Runs at runtime, not compile time - errors caught later * Less idiomatic - other Perl programmers expect use
For quick scripts, require works. For maintainable code, use Exporter.
.----------------. / \ | my_project/ | | +-----------+ | | | main.pl | | | +-----------+ | | | MyUtils.pm| | | +-----------+ | | | lib/ | | | | MathOps | | | +-----------+ | \ / '----------------'
perl.gg