How can I distribute my Perl application as a single file?
-
06-07-2019 - |
Question
I have a Perl script (foo.pl) that loads Foo.pm from the same directory using the require mechanism:
require "./Foo.pm";
...
my $foo = new Foo::Bar;
The Foo.pm adheres to the standard module format:
package Foo::Bar;
...
1;
Rather than distributing my application as two files (foo.pl and Foo.pm) I'd like to distribute only one file. More specifically I'd like to make Foo.pm part of the foo.pl script.
How do I achieve that?
The trivial approach of simply merging the two files (cat foo.pl Foo.pm > foo2.pl) does not work.
Solution
Your code did not work (although it would have been helpful to state the error message(s) that you received) because you attempted to use Foo::Bar before it had been defined. Try this:
use strict;
use warnings;
my $foo = Foo::Bar->new();
# more code...
# end code
# begin definitions
BEGIN {
package Foo::Bar;
use strict;
use warnings;
# definitions...
1;
package Foo::Baz;
# more stuff, if you need to define another class
}
Additions:
- strict checking everywhere.
- constructor called explicitly, rather than via the indrect-object method: What is the difference between new Some::Class and Some::Class->new() in Perl?
OTHER TIPS
If you're interested in packing up your Perl script into a binary with all the modules it depends upon included, you can use PAR Packager:
pp -o binary_name foo.pl
A file can contain multiple packages. Put your class first, followed by the main script:
package Foo::Bar;
sub new {
my $class = shift;
return bless {}, $class;
}
#...
package main;
my $foo = Foo::Bar->new();
print ref $foo; # Foo::Bar
The overall scheme would be to replace your "require ..." with the contents of what you're requiring. There's more to it than that (BEGIN { } may be needed), and I'm not exactly sure what's involved. For sure, you'd want to automate it.
Here's an alternative: generate a single executable file where modules you depend on are packed inside it using PAR/pp
You've already got a few good answers. In addition, it is possible to make a module than can be run directly as a script.
package Foo;
__PACKAGE__->run(@ARGV) unless caller();
sub run {
# Do stuff here if you are running the Foo.pm as
# a script rather than using it as a module.
}
For additional details, see brian d foy's How a Script Becomes a Module.