Home / Alt manpages / perlmodlib(1)

  • perlmodlib(1)
  • User command
  • linux

Build and Check a Small Perl Module Safely

You will finish with a small, reusable Perl module in a temporary project, a test that checks its public interface, and a reliable way to inspect the modules already installed on your machine. The examples match Perl 5.38.2 and perl-doc 5.38.2-3.2ubuntu0.6, installed here on Linux.

Allow about 20 minutes. You need Perl, a shell, and an ordinary writable directory. No command in this guide needs sudo. The release and installation notes are planning guidance; they do not upload or install anything.

1. Confirm the Perl and documentation versions

Start by recording the interpreter and documentation package versions. This is read-only and does not need elevated privileges:

$ perl -v
This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
$ dpkg-query -W -f='${Package} ${Version}\n' perl perl-doc
perl 5.38.2-3.2ubuntu0.6
perl-doc 5.38.2-3.2ubuntu0.6

Your architecture or distribution revision may differ. Keep the version with the project notes if you are debugging behaviour that changes between Perl releases.

Checkpoint

You have a working perl command and know which perlmodlib documentation describes it.

2. Check whether a module is already available

Do not start a new module before checking for an existing solution. Ask Perl to load a candidate and print its version when the module provides one:

$ perl -MJSON::PP -e 'print JSON::PP->VERSION, "\n"'
4.16

A successful exit status proves that Perl found the module in its current search path. A missing module normally ends with a message such as Can't locate JSON/PP.pm in @INC. That is a search-path or installation result, not a reason to copy a module into the current directory without checking its provenance.

To list all installed .pm files, use the method documented by perlmodlib. The -T option prevents PERL5LIB, PERLLIB and PERL_USE_UNSAFE_INC from changing the search path:

$ perl -MFile::Find=find -MFile::Spec::Functions -Tlwe \
  'find { wanted => sub { print canonpath $_ if /\.pm\z/ }, no_chdir => 1 }, @INC' \
  | head
/usr/lib/x86_64-linux-gnu/perl5/5.38/DBI.pm
/usr/lib/x86_64-linux-gnu/perl5/5.38/Clone.pm
/usr/lib/x86_64-linux-gnu/perl5/5.38/Socket6.pm

The list is host-specific. Piping to head only shortens the display; it does not limit the search. For an audit record, omit the pipe and redirect the output to a file in your project.

3. Choose a module name and file layout

Use a descriptive, capitalised, nested name for a public module. The name Local::Greeting is suitable for this private example because the Local:: category is reserved for code that will not be released publicly. The path must mirror the package name:

$ mkdir -p /tmp/perl-module-demo/lib/Local
$ cd /tmp/perl-module-demo
$ touch lib/Local/Greeting.pm

Safety warning

These commands create only a disposable directory under /tmp. Do not use rm -rf or replace an existing project path while adapting them. If you already have a project, create the matching lib/Name/Here.pm path inside that project instead.

A module is a .pm file containing a package. A package is a namespace, not automatically a class. If you provide methods, keep object state in the object rather than in package globals. That makes more than one object and an empty subclass easier to support later.

4. Write a minimal module with a narrow interface

Put the public function in @EXPORT_OK, so callers must request it explicitly. Do not export methods or common names by default. The version is a package variable, not a lexical, and follows the format recommended by perlmodlib:

package Local::Greeting;

use strict;
use warnings;
use Exporter qw(import);

our $VERSION = '0.01';
our @EXPORT_OK = qw(greet);

sub greet {
    my ($name) = @_;
    die "name is required\n" unless defined $name && length $name;
    return "Hello, $name!";
}

1;

use strict and use warnings catch many interface and spelling mistakes during development. The final 1; is required because Perl expects the module file to return a true value when it is loaded.

Keep the implementation small until the interface is clear. For a class, use a constructor that blesses into the class passed by the caller, rather than hard-coding a package name. For a function library such as this example, an explicit export list is enough.

5. Test the module through its public boundary

Run Perl with the project library directory added to @INC. The -Ilib option is local to this invocation and does not change the system installation:

$ perl -Ilib -MLocal::Greeting=greet -e 'print greet("Ada"), "\n"'
Hello, Ada!

Now test the failure path and check the exit status. This should fail because the example deliberately requires a name:

$ perl -Ilib -MLocal::Greeting=greet -e 'greet()'
name is required
$ printf 'exit status: %s\n' "$?"
exit status: 255

The precise status can differ if you change the implementation. The useful checks are that a valid call returns the documented value and invalid input is rejected. Test the import boundary too:

$ perl -Ilib -MLocal::Greeting -e 'print Local::Greeting::greet("Linus"), "\n"'
Hello, Linus!
$ perl -Ilib -MLocal::Greeting -e 'print defined &greet ? "unexpected export\n" : "no default export\n"'
no default export

If Perl cannot locate the file, compare lib/Local/Greeting.pm with the package name and rerun with perl -Ilib -V. Do not fix a missing module by setting a global PERL5LIB blindly; that can make a test load a different copy than the one you inspected.

6. Prepare the module for other people

Before release, add a README that states what the module does, its prerequisites, how to build and install it, recent changes, planned changes, and its copyright and licence. Add tests that exercise the supported interface, including failure cases. Record incompatible changes instead of silently changing behaviour.

If the module is public, search CPAN and coordinate with an existing module family before choosing a name. CPAN is the normal distribution network for Perl modules, but publishing is a separate, security-sensitive action. Review the archive, metadata, licence, credentials and destination before using an upload tool. This guide intentionally performs no upload.

For a private project, keep the source under version control and install it through that project's documented build process. For a public distribution, consult the current CPAN and PAUSE instructions rather than relying on an old command copied from a blog post.

7. Clean up the disposable example

The example has changed state only under /tmp. Remove that exact directory after you have captured anything useful:

$ test -f /tmp/perl-module-demo/lib/Local/Greeting.pm && echo demo-present
demo-present
$ rm -rf -- /tmp/perl-module-demo
$ test ! -e /tmp/perl-module-demo && echo demo-removed
demo-removed

Warning

The final command is destructive for the disposable directory. Do not substitute a project path. If you need to keep the example, skip this step; there is no system-wide undo for deleting a directory.

Done means

  • You checked the installed Perl and perl-doc versions.
  • You searched for an existing module before writing a new one.
  • The package name, file path and -Ilib search path agree.
  • The module uses strictness, warnings, a package version and an explicit export list.
  • Valid and invalid calls were tested through the public interface.
  • A README, tests, licence and compatibility plan are ready before any release or upload.