NAME
Perl::PrereqScanner::Scanner::Hint::Manual - Scanner::Hint
user manual
VERSION
Version v0.0.0_01, released on 2015-10-21 23:22 UTC. This is a trial release.
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 your 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 one hash, 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
Hash, keyword and colon are critical for recognizing a hint. Following examples are not hints:
## REQUIRE: ... # More than one hash
#~ REQUIRE: ... # Extra character between hash 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!
# REQUIRE: Module-Name # Error!
# REQUIRE: "ModuleName" # Error!
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
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, CPAN client does not insist on installing IPC::System::Simple
. Thus, code may fail due to missed IPC::System::Simple
.
A hint allows to avoid 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 in writing "REQUIRE" keyword. Mistyped keywords are not recognized and silently ignored.
File name
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 © 2015 Van de Bugger
This file is part of perl-Perl-PrereqScanner-Scanner-Hint.
perl-Perl-PrereqScanner-Scanner-Hint is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
perl-Perl-PrereqScanner-Scanner-Hint is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with perl-Perl-PrereqScanner-Scanner-Hint. If not, see <http://www.gnu.org/licenses/>.