The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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, and

  • Pod::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 complicates Prereqs 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 on IPC::System::Simple) in source code but forget to update dist.ini.

Explicit use as a hint for AutoPrereqs

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 by AutoPrereqs.

This approach looks better than using Prereqs plugin:

  1. 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.

  2. It is compact — a "hint" occupies just a line.

  3. Such a hint is located in source code, not in separate dist.ini.

However, this is approach is not ideal:

  1. I have to use qw{} with all the hints to avoid importing symbols.

  2. Such a hint is an executable code, it can interfere with normal module loading, which may be lazy or argument-depending.

  3. 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.