There are two options to install Perl:
- ActiveState Perl: ActiveState offers both a free community version and a commercially supported binary distribution of Perl for Win32 and Perl for Win64.
- Strawberry Perl: A 100% Open Source Perl for Windows that is exactly the same as Perl everywhere else; this includes using modules from CPAN, without the need for binary packages.
Install Perlbrew that is an admin-free installation management tool for Perl.
-
To install:
curl -L https://install.perlbrew.pl | bash -
To see the available versions:
perlbrew available
-
To install a Perl version:
perlbrew install perl-5.42.0
-
To switch to a Perl version:
perlbrew switch perl-5.42.0
-
Install the followings:
cpan App::cpanminus cpan local::lib perl -Mlocal::lib cpanm Reply cpan Module::Build cpan ExtUtils::MakeMaker cpan Module::Starter cpanm Getopt::Long cpanm Test::More cpanm Test::File cpanm Test::Output cpanm Test::MockObject cpanm Benchmark cpanm Devel::Cover cpanm Devel::NYTProf
-
Install Perl's LanguageServer:
-
on Unix:
cpanm Perl::LanguageServer
-
on Windows install PerlNavigator
-
Add
.vscode/extensions.jsonwith the following contents:{ "recommendations": [ "Nihilus118.perl-debugger", "formulahendry.code-runner", "bscan.perlnavigator", "richterger.perl", "d9705996.perl-toolbox", "JOROL.perl-completions", "ms-vscode.test-adapter-converter", "hbenl.vscode-test-explorer" ] } -
Add
.vscode/settings.jsonwith the following contents:"[perl]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
Create the project scaffold:
module−starter --builder="Module::Build" --module=TestModule --author="Mojtaba Mansour Abadi" --email=myemail.email.com --verboseIn case the script fails, the solution is to start from a project code with all boilerplate files (e.g. https://github.com/rouzier/perl-Boilerplate.git or https://github.com/lskatz/template-perl.git) or create the project structure manually:
Perl_Project/
├── lib
│ ├── Mylib
│ │ └── Part
│ │ └── Function.pm
│ ├── SomeModule
│ │ └── Command
│ │ ├── initialize.pm
│ │ └── reset.pm
│ ├── SomeModule.pm
│ └── Mylib.pm
├── src
│ └── main.pl
└── t
└── test.t
The only downside is that the project cannot be built the same way.
Another solution is to create the Perl file setup.pl:
use strict;
use warnings;
use v5.20;
use Module::Starter::App;
Module::Starter::App->run;And then use it to create the project:
perl setup.pl --builder="Module::Build" --module=TestModule --author="Mojtaba Mansour Abadi" --email=myemail.email.com --verbose-
Create
src/main.pl#!/usr/bin/perl # src/main.pl package main; use strict; use warnings; use diagnostics; # to include the user-defined modules from "lib" folder, there are two options: # 1. use the following two lines of codes use FindBin; use lib "$FindBin::Bin/../lib"; ## points to /path/to/lib use FindBin qw($Bin); use lib "$Bin/../lib"; # and run the script using: # perl main.pl # 2. include -Ilib when running the script from the command line in the root directory. # perl -Ilib src/main.pl use MyLib; # To pass on the arguments use the following command # perl src/main.pl --length=1 --file=data.txt --verbose --find # or # perl -Ilib src/main.pl --length=1 --file=data.txt --verbose --find use Getopt::Long; my $data = "file.dat"; my $length = 24; my $verbose; my $find; GetOptions ("length=i" => \$length, # numeric "file=s" => \$data, # string "verbose" => \$verbose, # flag "find+" => \$find) # flag; both --find and --nofind are allowed (oprional argument) or die("Error in command line arguments\n"); run() unless caller; # call run() is there is no caller (main is invoked as a script) sub run { # at the bottom of the file. # code to run in the main loop MyLib::func1(); MyLib->func1(); MyLib::func3(); say $data; say $length; say $verbose; say $MyLib::VERSION; }
-
The module files in the
libfolder have the following template:package Some::Module; # assumes Some/Module.pm use strict; use warnings; use diagnostics; use v5.36; # Get the import method from Exporter to export functions and # variables use Exporter 5.57 'import'; BEGIN { ... } # module initialisation code here (global constructor) # set the version for version checking our $VERSION = '1.00'; # Functions and variables which are exported by default our @EXPORT = qw(func1 func2); # Functions and variables which can be optionally exported our @EXPORT_OK = qw($Var1 %Hashit func3); # exported package globals go here our $Var1 = ''; our %Hashit = (); # non-exported package globals go here # (they are still accessible as $Some::Module::stuff) our @more = (); our $stuff = ''; # file-private lexicals go here, before any functions which use them my $priv_var = ''; my %secret_hash = (); # here's a file-private function as a closure, # callable as $priv_func->(); my $priv_func = sub { ... }; # make all your functions, whether exported or not; # remember to put something interesting in the {} stubs sub func1 { ... } sub func2 { ... } # this one isn't always exported, but could be called directly # as Some::Module::func3() sub func3 { ... } END { ... } # module clean-up code here (global destructor) 1; # don't forget to return a true value from the file
-
Create the test code
test-mymodule.tbased on the following template:use Test::More tests => 23; # the number is used only if the total number of tests are known # or use Test::More skip_all => $reason; # to skip an entire testing script # or use Test::More; # see done_testing() use Test::File; # file-related tests use Test::Output; # test STDOUT or STDERR use Test::MockObject; # mock object use Benchmark; # to benchmark a code require_ok( 'Some::Module' ); # Various ways to say "ok" ok($got eq $expected, $test_name); is ($got, $expected, $test_name); isnt($got, $expected, $test_name); # Rather than print STDERR "# here's what went wrong\n" diag("here's what went wrong"); like ($got, qr/expected/, $test_name); unlike($got, qr/expected/, $test_name); cmp_ok($got, '==', $expected, $test_name); is_deeply($got_complex_structure, $expected_complex_structure, $test_name); diag(@diagnostic_message); # to print out a message without interfering with test output note(@diagnostic_message); # to print out a message only in verbose mode my @dump = explain @diagnostic_message; # to dump contents of any references in a human readable format is_deeply($have, $want) || diag @dump; SKIP: { # declares a block of tests that might be skipped, $how_many tests there are, $why and under what $condition to skip them. skip $why, $how_many unless $have_some_feature; ok( foo(), $test_name ); is( foo(42), 23, $test_name ); }; TODO: { # declares a block of tests you expect to fail and $why. local $TODO = $why; ok( foo(), $test_name ); is( foo(42), 23, $test_name ); }; can_ok($module, @methods); isa_ok($object, $class); pass($test_name); fail($test_name); # Test::File file_exists_ok(file_name); file_not_exists_ok(file_name); file_not_empty_ok(file_name); file_readable_ok(file_name); file_min_size_ok(file_name, size); # Test::Output stdout_is(\&function, "expected value"); stderr_like(\&function, regular_expression); # Test::MockObject my $fake_obj = Test::MockObject->New(); $fake_obj->set_true( 'fake_func1' ); $fake_obj->set_false( 'fake_func2' ); $fake_obj->mock( 'list_of_values' => sub { qw ( list of values ) } ); $fake_obj->set_series( 'amicae', 'Sunny', 'Kylie', 'Bella' ); my @values = $fake_obj->list_of_values; ok( $fake_obj->fake_func1, "Test 1 is a pass"); ok( ! $fake_obj->fake_func1, "Test 2 is a pass"); is( $values[0], 'Val', 'Test 3 is a pass') # Benchmark my $start = Benchmark->new; # # code to benchmark # my $end = Benchmark->new; my $diff = timediff($end, $start); BAIL_OUT($why); # indicates to the harness that things are going so badly all testing should terminate. done_testing(); # if you don't know the total number of tests, use this line instead
-
To test the converage:
Build testcover cover
-
To run test manually:
perl -I/path/DUT/module test_file.t
-
To debug a perl program:
perl -d some_perl.pl
-
To profile a perl program and generate HTML or CSV reports:
perl -d:NYTProf some_perl.pl nytprofhtml --open nytprofcsv
-
Perl documentation is based on POD comments that is extracted by:
perldoc -d output.pod some_perl.pl
-
Once the POD file is ready, generate HTML by running:
pod2html -outfile index.html some_perl.pl
For the documentation use this template:
#!/usr/bin/perl
package GenerateAboutPage;
=pod
=head1 <Title>
=head2 Written by Mojtaba Mansour Abadi
To use this script, install Perl v5.40, then run the following in the command line:
B<perl> F<ScriptName.pl> I<argument>
I<argument> can be any of the followings:
=over 4
=item B<arg1>: <description of argument>
=back
=cut
################################################################################
# required packages
use v5.20;
use strict;
use warnings;
use diagnostics;
################################################################################
# constants
use constant CONSTANT1 => "value";
use constant MY_OS => $^O; # use (MY_OS) in a hash, to avoid bareword quoting mechanism.
use constant VERSIONS =>
{ "MSWin32" => "Scripts", "linux" => "bin", "darwin" => "bin" };
use constant NAMES => qw/N1 N2 N3/;
################################################################################
# declaration of heredoc definitions
my $content_text_1; # <descriiption>
################################################################################
# declaration and definition of global variables
my $ignore_errors; # ignore all errors and continue with scaffolding
my $ReportError = sub {
if ( $ignore_errors == 0 ) {
die shift;
}
else {
say shift;
}
};
################################################################################
# format definitions
my $Parameter_Index = 1, my $Parameter_Name, my $Parameter_Value;
format DATA_TOP =
@|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"'About' Page Parameters"
@<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<<<<<<<<
"Index", "Name", "Value"
==================================================================
.
format DATA =
@<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<<<<<<<<
$Parameter_Index, $Parameter_Name, $Parameter_Value
.
################################################################################
# <short description of the function>
=pod
=head3 <Function Name>
=head4 Description
<Longer descrioption with list of arguments>
=cut
sub <Function Name> {
# ...
}
#...
################################################################################
# main logic function called from main scope
=pod
=head3 MainFile
=head4 Description
<long description>
=cut
sub MainFile {
# ...
system( Command ) == 0
or $ReportError->("Error: $?");
# ...
my $old_h = select(STDOUT);
my $Parameter_Index = 0;
$^ = "DATA_TOP";
$~ = "DATA";
# print a row of the table
my $PrintParams = sub {
$Parameter_Name = shift;
$Parameter_Value = shift;
write;
++$Parameter_Index;
};
$PrintParams->( 'Output Directory', $output_dir );
$PrintParams->( 'Output File', $output_file_name );
select $old_h;
# ...
my $file_content = "";
while ( my $data_line = <DATA> ) {
if ( $data_line eq "\n" ) {
next;
}
$file_content .= $data_line;
}
# ...
}
################################################################################
# definition of heredoc definitions
# <descriiption>
$content_text_1 = <<'FILE_CONTENT';
content
FILE_CONTENT
################################################################################
# main entry of the script
main: {
&MainFile(@ARGV);
}
1;
################################################################################
# POD
=pod
=back
=head2 VERSION
00.02.00.a
=head2 AUTHOR
Mojtaba Mansour Abadi
=head2 LICENCE
This program is licenced under MIT License.
=cut
################################################################################
# embedded data
__DATA__
<Embedded Data>