NAME
Perl::PrereqScanner::Scanner::Hint::Manual - Scanner::Hint
user manual
VERSION
Version v0.1.1, released on 2016-12-28 20:18 UTC.
WHAT?
Perl::PrereqScanner::Scanner::Hint
(or just Scanner::Hint
for brevity) is a plugin for Perl::PrereqScanner
tool. Scanner::Hint
looks for # REQUIRE: ModuleName VersionRange
comments in the code.
This is Scanner::Hint
user manual. Read this if you want to specify implicit prerequisites directly in Perl code.
If you are going to hack or extend Perl-PrereqScanner-Scanner-Hint
, read the module documentation. General topics like getting source, building, installing, bug reporting and some others are covered in the README.
SYNOPSIS
In dist.ini:
...
[AutoPrereqs]
extra_scanners = Hint
...
In Perl code:
...
use autodie ':all'; ## REQUIRE: IPC::System::Simple
...
DESCRIPTION
Intro
Dist::Zilla
is a tool for building distributions. Dist::Zilla
's plugin AutoPrereqs
automatically extracts prerequisites from distribution's sources, tests, etc. In great majority of cases all you need just two lines
[AutoPrereqs]
[MetaJSON]
in your dist.ini file to get prereq
key of distribution META.json populated with your distribution requirements. For example, if your module uses Module::Name
:
use Module::Name 0.008;
AutoPrereqs
finds this statement and adds requirement "Module::Name" => "0.008"
to distribution META.json.
When user installs your module from CPAN, CPAN client (either cpan
, or cpanp
, or cpanm
) makes sure all the requirements are satisfied, so your module can be installed and run smoothly.
AutoPrereqs
uses Perl::PrereqScanner
for extracting prerequisites from the distribution files. Perl::PrereqScanner
, in turn, uses scanners for scan the files. Few most frequently used scanners are bundled with the Perl::PrereqScanner
and utilized by default. For example, Perl::PrereqScanner::Scanner::Perl5
looks for Perl use
and require
statements, Perl::PrereqScanner::Scanner::Moose
looks for Moose's extends
and with
keywords, etc.
Perl::PrereqScanner::Scanner::Hint
is an additional scanner for Perl::PrereqScanner
. In contrast to other scanners, it does not look into Perl code but looks into Perl comments. It searches for hints leaved by the programmer.
Hints
A hint is a comment starting with two hashes, followed by REQUIRE
keyword, colon, module name, and optional version range and comment, e. g.:
## REQUIRE: Assa >= 1.2, != 1.5 # 1.5 has a nasty bug
REQUIRES
is an alternative keyword:
## REQUIRES: Assa >= 1.2, != 1.5 # ditto
Keyword must be in upper case. Number of whitespaces between elements (hash, keyword, colon, etc) does not matter:
##REQUIRE:...
## REQUIRE: ...
## REQUIRE : ...
A hint can be a standalone comment:
## REQUIRE: ...
or trailing one:
use Assa; ## REQUIRE: Assa != 1.5
Hashes, keyword and colon are critical for recognizing a hint. Following examples are not hints:
### REQUIRE: ... # More than two hashes
##~ REQUIRE: ... # Extra character between hashes and keyword
## Require: ... # Keyword is not in upper case
## REQUIRE ... # No colon
…and so, all such lines are silently ignored. However, if a comment is recognized as a hint, it is thoroughly validated. If a hint is not valid, Scanner::Hint
issues an error message and stops scanning.
The first word (up to the first whitespace or hash or end-of-line) must be a valid module name:
## REQUIRE: Module:Name # Error: 'Module:Name' is not a valid module name
## REQUIRE: Module-Name # Error: 'Module-Name' is not a valid module name
## REQUIRE: "ModuleName" # Error: '"ModuleName"' is not a valid module name
Version range (everything after the module name and up to comment or end-of-line) must be valid too:
## REQUIRE: Module::Name > 1.5 < 2.0 # Error!
## REQUIRE: Module::Name = 1.5 # Error!
See "Version Ranges" in CPAN::Meta::Spec for syntax and semantics of version ranges.
Empty version range is allowed, it stands for default value 0
which means "any version":
## REQUIRE: Assa # any version is ok
## REQUIRE: Assa 0 # ditto
And, as you already noticed, a hash starts a comment:
## REQUIRE: IPC::System::Simple # used by autodie
Using Scanner::Hint
with Dist::Zilla
and AutoPrereqs
AutoPrereqs
has extra_scanners
option which can be used for adding extra scanners. This option can be used for enabling Scanner::Hint
in your dist.ini:
[AutoPrereqs]
extra_scanners = Hint
EXAMPLES
All the examples below assume using Dist::Zilla
with AutoPrereqs
plugin, and enabled Scanner::Hint
, as shown in "Using Scanner::Hint
with Dist::Zilla
and AutoPrereqs
" section.
autodie
If pragma autodie
is used with ':system'
or ':all'
argument, e. g.:
use autodie ':all';
autodie
loads IPC::System::Simple
module. However, autodie
's META.json says autodie
recommends IPC::System::Simple
, but not requires it. Since IPC::System::Simple
is not required, a CPAN client (either cpan
, or cpanp
, or cpanm
) does not insist on installing IPC::System::Simple
. Thus, code may fail due to missed IPC::System::Simple
.
Using a hint avoids this trouble:
use autodie ':all'; ## REQUIRE: IPC::System::Simple
Pod::Weaver
plugin bundle
A plugin bundle for Pod::Weaver
uses many modules indirectly. For example:
package Pod::Weaver::PluginBundle::Author::VDB;
use Pod::Weaver::Config::Assembler;
sub _p($) { Pod::Weaver::Config::Assembler->expand_package( @_ ) };
sub mvp_bundle_config {
my $me = '@Author::VDB';
return (
[ "$me/CorePrep", _p( '@CorePrep' ), {} ],
[ "$me/SingleEncoding", _p( '-SingleEncoding' ), {} ],
[ "$me/Transformer", _p( '-Transformer' ), {
'transformer' => 'List',
} ],
[ "$me/Name", _p( 'Name' ), {} ],
[ "$me/Version", _p( 'Version' ), {
'format' => [ ... ],
} ],
...
);
};
1;
In code above AutoPrereqs
detects explicit dependency on Pod::Weaver::Config::Assembler
module. However, the bundle also implicitly uses:
Pod::Weaver::PluginBundle::CorePrep
,Pod::Weaver::Plugin::SingleEncoding
,Pod::Weaver::Plugin::Transformer
,Pod::Weaver::Section::Name
,Pod::Weaver::Section::Version
, andPod::Elemental::Transformer::List
.
All these implicit dependencies are not detected by AutoPrereqs
. When you install your bundle, CPAN client does not pull in all the dependencies, and you have to install them manually.
This can be avoided by using hints:
sub mvp_bundle_config {
my $me = '@Author::VDB';
return (
[ "$me/CorePrep", _p( '@CorePrep' ), {} ], ## REQUIRE: Pod::Weaver::PluginBundle::CorePrep
[ "$me/SingleEncoding", _p( '-SingleEncoding' ), {} ], ## REQUIRE: Pod::Weaver::Plugin::SingleEncoding
[ "$me/Transformer", _p( '-Transformer' ), { ## REQUIRE: Pod::Weaver::Plugin::Transformer
'transformer' => 'List', ## REQUIRE: Pod::Elemental::Transformer::List
} ],
[ "$me/Name", _p( 'Name' ), {} ], ## REQUIRE: Pod::Weaver::Section::Name
[ "$me/Version", _p( 'Version' ), { ## REQUIRE: Pod::Weaver::Section::Version
'format' => [ ... ],
} ],
...
);
};
In such a case all the required dependencies are installed automatically with the bundle.
Someone may notice that most of the implicitly used modules are from Pod::Weaver
distribution, and so they should be are already installed. That's not completely true. For example, Transformer
plugin was introduced in Pod::Weaver
3.093530, older Pod::Weaver
versions do not have it. If the bundle requires Pod::Weaver::Plugin::Transformer
, CPAN client detects missed plugin and updates Pod::Weaver
.
Also, the bundle may use third-party plugins and sections which are not part of Pod::Weaver
. So requiring all the modules is a right thing to do.
CAVEATS
Keyword Spelling
Be careful when writing "REQUIRE" keyword. Mistyped keywords are not recognized and silently ignored.
File Name in Error Messages
Scanner::Hint
does its best to report error location properly. However, when using with Dist::Zilla
and AutoPrereqs
, error in any file is reported as error in (*UNKNOWN*)
file, for example:
'Module:Name' is not a valid module name at (*UNKNOWN*) line 3
It caused by lack of cooperation between Dist::Zilla
and PPI
. PPI
does not allow to specify file name for a PPI::Document
object constructed from a string, while Dist::Zilla
does not pass file name to a PPI::Document
object via #line
directive. See:
Extended Regular Expressions
Comments in extended regular expressions (i. e. expressions with /x
modifier) are not recognized. For example:
my $re = qr{
... ## REQUIRE: ... # This is *not* a hint :-(
}x;
This is PPI
limitation, see PPI::Token::Comment.
WHY?
I use Dist::Zilla
to build my distributions. I also use AutoPrereqs
plugin to populate distribution's prerequisites. Usually all this stuff works great. But sometimes it does not because dependencies are implicit or hidden (see "EXAMPLES" in Perl::PrereqScanner::Scanner::Hint::Manual). In such cases I need to make implicit dependencies explicit. There are few ways to declare prerequisites manually:
- Using
Prereqs
plugin -
Prerequisites can be listed manually in dist.ini file by using
Prereqs
plugin, e. g.:[Prereqs] IPC::System::Simple
However, I do not like such approach. If
autodie
is used in source code and in tests, it complicatesPrereqs
usage:[Prereqs/Runtime] -phase = runtime IPC::System::Simple [Prereqs/Test] -phase = test IPC::System::Simple
Also, this approach breaks modularity and spreads implementation details. A module is required by a source file, but requirement is specified in different file, dist.ini. It complicates maintenance because I have to keep multiple files in sync: it is so easy to drop using
autodie
(and so, implicit dependency onIPC::System::Simple
) in source code but forget to update dist.ini. - Explicit
use
as a hint forAutoPrereqs
-
Prerequisites can be listed manually by explicit
use
statements:use autodie ':all'; use IPC::System::Simple qw{}; # AutoPrereqs hint: autodie uses it.
In such a case dependency on
IPC::System::Simple
is explicit and so detected byAutoPrereqs
.This approach looks better than using
Prereqs
plugin:AutoPrereqs
properly detects phase: if such code appears in test, it is a test prerequisite; if such code appears in a module — it is a runtime prerequisite.It is compact — a "hint" occupies just a line.
Such a hint is located in source code, not in separate dist.ini.
However, this is approach is not ideal:
I have to use
qw{}
with all the hints to avoid importing symbols.Such a hint is an executable code, it can interfere with normal module loading, which may be lazy or argument-depending.
Hints cannot be located freely.
For example, this layout (see "
Pod::Weaver
plugin bundle" in Perl::PrereqScanner::Scanner::Hint::Manual) does not work:sub mvp_bundle_config { my $me = '@Author::VDB'; return ( [ "$me/CorePrep", _p( '@CorePrep' ), {} ], use Pod::Weaver::PluginBundle::CorePrep qw{}, [ "$me/SingleEncoding", _p( '-SingleEncoding' ), {} ], use Pod::Weaver::Plugin::SingleEncoding qw{}, ... ); };
…it causes
"use" not allowed in expression
error, so I have to separate hints and code:sub mvp_bundle_config { my $me = '@Author::VDB'; use Pod::Weaver::PluginBundle::CorePrep qw{}; use Pod::Weaver::Plugin::SingleEncoding qw{}; ... return ( [ "$me/CorePrep", _p( '@CorePrep' ), {} ], [ "$me/SingleEncoding", _p( '-SingleEncoding' ), {} ], ... ); };
which is not desirable in case of long list of plugins/sections: it is easy to add/drop a plugin/section and forget to add/drop corresponding hint for
AutoPrereqs
.
Thus, I am not satisfied with existing solutions. I want to specify dependencies directly in source code, I want to locate it freely, and it should not interfere with existing code. To meet these requirements, dependency specification should be comment, not code. Like this one:
use autodie ':all'; ## REQUIRE: IPC::System::Simple
SEE ALSO
- Dist::Zilla
- Dist::Zilla::Plugin::AutoPrereqs
- Perl::PrereqScanner
- Perl::PrereqScanner::Scanner::Perl5
- Perl::PrereqScanner::Scanner::Moose
AUTHOR
Van de Bugger <van.de.bugger@gmail.com>
COPYRIGHT AND LICENSE
Copyright (C) 2015, 2016 Van de Bugger
License GPLv3+: The GNU General Public License version 3 or later <http://www.gnu.org/licenses/gpl-3.0.txt>.
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.