# Table of Contents

* [NAME](#name)
* [SYNOPSIS](#synopsis)
* [DESCRIPTION](#description)
* [OPTIONS](#options)
  * [Examples](#examples)
* [OPTION DETAILS](#option-details)
* [WHAT IS A DEPENDENCY?](#what-is-a-dependency)
* [MINOR IMPROVEMENTS TO `perl.req`](#minor-improvements-to-perlreq)
* [CAVEATS](#caveats)
* [METHODS AND SUBROUTINES](#methods-and-subroutines)
  * [new](#new)
    * [Options](#options)
  * [get\_require](#get\require)
  * [get\_perlreq](#get\perlreq)
  * [parse](#parse)
  * [get\_dependencies](#get\dependencies)
  * [format\_text](#format\text)
  * [format\_json](#format\json)
  * [is\_core](#is\core)
  * [min\_core\_version](#min\core\version)
  * [get\_module\_version](#get\module\version)
  * [add\_require](#add\require)
  * [to\_rpm](#to\rpm)
* [VERSION](#version)
* [AUTHOR](#author)
* [LICENSE](#license)
# NAME

Module::ScanDeps::Static - a cleanup of rpmbuild's perl.req

# SYNOPSIS

    scandeps-static [options] Module

If "Module" is not provided, the script will read from STDIN.

    my $scanner = Module::ScanDeps::Static->new({ path => 'myfile.pl' });
    $scanner->parse;
    print $scanner->format_text;

# DESCRIPTION

This module is a mashup (and cleanup) of the `/usr/lib/rpm/perl.req`
file found in the rpm build tools library (see ["LICENSE"](#license)) below.

Successful identification of the required Perl modules for a module or
script is the subject of more than one project on CPAN. While each
approach has its pros and cons I have yet to find a better scanner
than the simple parser that Ken Estes wrote for the rpm build tools
package.

`Module::ScanDeps::Static` is a simple static scanner that
essentially uses regular expressions to locate `use`, `require`,
`parent`, and `base` in all of their disguised forms inside your
Perl script or module.  It's not perfect and the regular expressions
could use some polishing, but it works on a broad enough set of
situations as to be useful.

_Only direct dependencies are returned by this module. If you
want a recursive search for dependencies, use `find-requires`
included in this distribution._

# OPTIONS

    --add-version, -a        add version numbers to output
    --no-add-version         don't add version numbers to output
    --core                   include core modules (default)
    --no-core                don't include core modules
    --file-list, -L PATH     scan a batch of files listed one per line in PATH
    --help, -h               help
    --include-require, -i    include 'require'd modules
    --no-include-require     don't include required modules
    --json, -j               output JSON formatted list
    --min-core-version, -m   minimum version of perl to consider core
    --path, -p PATH          file to scan (alternative to the positional argument)
    --raw, -r                raw output
    --separator, -s          separator for output (default: =>)
    --text, -t               output as text (default)
    --version, -v            version

## Examples

    scandeps-static --no-core lib/Some/Module.pm

    scandeps-static --json lib/Some/Module.pm

_Use the `find-requires` script included in this distribution to
recurse directories and create dependency files like `cpanfile`_.

# OPTION DETAILS

- --add-version, -a, --no-add-version

    Add the version number to the dependency list by inspecting the version of
    the module in your @INC path.

    default: **--add-version**

- --core, -c, --no-core

    Include or exclude core modules. See --min-core-version for
    description of how core modules are identified.

    default: **--core**

- --file-list, -L PATH

    Scan a batch of files in a single process instead of one file per
    invocation. `PATH` is a file containing one source file path per
    line (relative or absolute); each listed file is scanned in turn and
    the results are aggregated. There is currently no way to supply the
    list via `STDIN` - `PATH` must be a real file.

    Batching this way avoids paying Perl's own process-startup cost and
    the `Module::CoreList` module-load cost (non-trivial - loading
    `Module::CoreList` alone is roughly an order of magnitude slower
    than a bare `perl` startup) once per file scanned, which matters a
    great deal once a project has more than a handful of source files.

    Because more than one JSON document cannot be safely concatenated
    into a single valid JSON result, `--json` is silently ignored
    whenever `--file-list` resolves to more than one file - output is
    always text in that case. If the list happens to contain exactly one
    file, `--json` is honored normally.

- --help, -h

    Show usage.

- --include-require, -i, --no-include-require

    Include statements that have `Require` in them but are not
    necessarily on the left edge of the code (possibly in tests).

    default: **--include-require**

- --json, -j

    Output the dependency list as a JSON encode string. Silently ignored
    in favor of text output when combined with `--file-list` and more
    than one file is being scanned - see ["--file-list, -L PATH"](#file-list-l-path).

- --min-core-version, -m

    The minimum version of Perl that is considered core. Use this to
    consider some modules non-core if they did not appear until after the
    `min-core-version`.

    Core modules are identified using `Module::CoreList` and comparing
    the first release value of the module with the minimum version of
    Perl considered as a baseline.  If you're using this module to
    identify the dependencies for your script **AND** you know you will be
    using a specific version of Perl, then set the `min-core-version` to
    that version of Perl.

    Note: this only governs the "not yet in core as of my baseline" case.
    A module that was core at some point but has since been **removed**
    from core is always treated as non-core, regardless of
    `min-core-version` - there is no way to know which specific Perl an
    end user actually has installed, so the only safe answer once a
    module has ever been removed is to require it explicitly. See
    ["is\_core"](#is_core) for details.

    default: `5.8.9` (the `Module::ScanDeps::Static` constructor's
    `min_core_version` option defaults this to the running Perl's version
    instead)

- --path, -p PATH

    Path to the file to scan. Equivalent to supplying the path as a bare
    positional argument; provided as a named option for clarity in
    scripts that build up the command line programmatically. Ignored if
    `--file-list` is also given.

- --separator, -s

    Use the specified string to separate modules and version numbers in formatted output.

    default: ' => '

- --text, -t

    Output the dependency list as a simple text listing of module name and
    version in the same manner as `scandeps.pl`.

    default: **--text**

- --raw, -r

    Output the list with no quotes separated by a single whitespace
    character.

# WHAT IS A DEPENDENCY?

For the purposes of this module, dependencies are identified by
looking for Perl modules and other Perl artifacts declared using
`use`, `require`, `parent`, or `base`. The script will also
consider Moo/Role::Tiny modules include using `with`.

If the module contains a `require` statement, by default the
`require` must be flush up against the left edge of your script
without any whitespace between it and beginning of the line.  This is
the default behavior to avoid identifying `require` statements that
are embedded in `if` statements. If you want to include all of
the targets of `require` statements as dependencies, set the
`include-require` option to a true value.

# MINOR IMPROVEMENTS TO `perl.req`

- Allow detection of `require` not at beginning of line.

    Use the `--include-require` to expand the definition of a dependency
    to any module or Perl script that is the argument of the `require`
    statement.

- Allow detection of the `parent`, `base` statements use of curly braces.

    The regular expression and algorithm in `parse` has been enhanced to
    detect the use of curly braces in `use` or `parent` declarations.

- Exclude core modules.

    Use the `--no-core` option to ignore core modules.

- Add the current version of an installed module if the version
is not explicitly specified.

# CAVEATS

There are still many situations (including multi-line statements) that
may prevent this module from properly identifying a dependency. As
always, YMMV.

# METHODS AND SUBROUTINES

## new

    new(options)

Returns a `Module::ScanDeps::Static` object.

### Options

- path

    Path to a file to scan. When set, `parse()` opens this file and reads
    from it.

    default: **none** (if neither `path` nor `handle` is given, `parse()`
    reads from `STDIN`)

- handle

    An open filehandle (or any `IO::Handle`-like object) to read from
    instead of a file. Ignored when `path` is set.

    default: **none**

- core

    Boolean value that determines whether to include core modules as part
    of the dependency listing.

    default: **true**

- include\_require

    Boolean value that determines whether to consider `require`
    statements that are not left-aligned to be considered dependencies.

    default: **false** (the `scandeps-static.pl` CLI defaults this to true)

- add\_version

    Boolean value that determines whether to include the version of the
    module currently installed if there is no version specified.

    default: **true**

- min\_core\_version

    The minimum version of Perl which will be used to decide if a module
    is included in Perl core. See `is_core` and the `--min-core-version`
    option for details.

    default: **the running Perl's version** (`$PERL_VERSION`). The
    `scandeps-static.pl` CLI defaults this to `5.8.9`.

- json

    Boolean value that indicates output should be in JSON format.

    default: **false**

- text

    Boolean value that indicates output should be in the same format as
    `scandeps.pl`. This is the default output format for `get_dependencies`
    when neither `json` nor `raw` is set.

    default: **true**

- raw

    Boolean value that indicates output should be in raw format
    (module version).

    default: **false**

- separator

    Character string used to separate the module name from the version in
    text output.

    default: **none** from the constructor; `format_text` falls back to a
    single space. The `scandeps-static.pl` CLI sets this to ` =` >.

## get\_require

After calling the `parse()` method, call this method to retrieve a
hash containing the dependencies and (potentially) their version
numbers.

    $scanner->parse;
    my $requires = $scanner->get_require;

## get\_perlreq

Returns a hash ref of Perl version requirements discovered while
parsing (keyed by `'perl'`). Populated for `use 5.010;` /
`require 5.010;` style statements. Pair with `get_require`.

    $scanner->parse;
    my $perlreq = $scanner->get_perlreq;  # { perl => '5.010', ... }

## parse

- parse a file

        my @dependencies = Module::ScanDeps::Static->new({ path => $path })->parse;

- parse from file handle

        my @dependencies = Module::ScanDeps::Static->new({ handle => $path })->parse;

- parse STDIN

        my @dependencies = Module::ScanDeps::Static->new->parse(\$script);

- parse string

        my @dependencies = parse(\$script);

Scans the specified input and returns a list of Perl module dependencies.

Use the `get_dependencies` method to retrieve the dependencies as a
formatted string or as a list of dependency objects. Use the
`get_require` and `get_perlreq` methods to retrieve dependencies as
a list of hash refs.

    my $scanner = Module::ScanDeps::Static->new({ path => 'my-script.pl' });
    my @dependencies = $scanner->parse;

## get\_dependencies

Returns a formatted list of dependencies or a list of dependency objects.

As JSON:

    print $scanner->get_dependencies( format => 'json' )

    [
      {
       "name" : "Module::Name",
       "version" : "version"
      },
      ...
    ]

..or as text:

    print $scanner->get_dependencies( format => 'text' )

    Module::Name => version
    ...

In scalar context in the absence of an argument returns a JSON
formatted string. In list context will return a list of hashes that
contain the keys "name" and "version" for each dependency.

Note: this context-sensitivity only applies when none of `json`,
`text`, or `raw` is set (or when `format => 'json'` /
`format => 'text'` is passed explicitly). If the `json` option is
true, `get_dependencies` always returns a scalar JSON string, even
when called in list context.

## format\_text

    $scanner->parse;
    print $scanner->format_text;

Returns the dependency list as a formatted text string, one module per
line, honoring the `separator` and `raw` options. Core modules are
omitted when `core` is false.

## format\_json

    my $json     = $scanner->format_json;   # scalar context
    my @requires = $scanner->format_json;   # list context

In scalar context returns a pretty-printed JSON string; in list context
returns a list of hash refs of the form `{ name => ..., version =>
... }`. Core modules are omitted when `core` is false. Any arguments
are treated as a seed list and prepended to the results.

## is\_core

    my $bool = $scanner->is_core($module);
    my $bool = $scanner->is_core("$module $version");

Returns true if `$module` is considered core. A module is core when
`Module::CoreList` reports its first release at or before
`min_core_version` **and** the module has never been removed from
core at any point in its history.

A module that was core at some earlier Perl but has since been
removed is always treated as non-core, regardless of how
`min_core_version` compares to the version it was removed at. There
is no way to know which Perl an end user actually has installed - a
version comparison against a single reference point only protects
against removals that happen to fall on one particular side of it, so
the only safe answer once a module has ever been removed is to always
require it explicitly.

## min\_core\_version

    my $numified = $scanner->min_core_version;

Returns the `min_core_version` option numified via `version` (e.g.
`5.008009`) for comparison inside `is_core`. Note this is distinct
from the generated `get_min_core_version` accessor, which returns the
raw stored value.

## get\_module\_version

    my $info = $scanner->get_module_version($module, @include_path);

Returns a hash ref describing `$module`:

    { module => ..., version => ..., path => ..., file => ... }

Searches `@include_path` (defaulting to `@INC`) for the module and
extracts its version via `ExtUtils::MM-`parse\_version>. If `$module`
already carries a version (`"Foo::Bar 1.23"`), that version is returned
without a filesystem lookup.

## add\_require

    $scanner->add_require($module);
    $scanner->add_require($module, $version);

Registers `$module` as a dependency, optionally with `$version`. When
no version is supplied and the `add_version` option is true, the
installed version is looked up. Retains the higher of two versions if
the module is added more than once. Returns `$self`.

## to\_rpm

    my $deps = $scanner->to_rpm;

Returns the dependency list as RPM-style requirement expressions
(`perl(Module) >= version`, plus `perl >= version` for any
Perl version requirement). Core modules are omitted when `core` is
false.

# VERSION

This documentation refers to version 1.8.2

# AUTHOR

This module is largely a lift and drop of Ken Este's `perl.req` script
lifted from rpm build tools.

Ken Estes Mail.com kestes@staff.mail.com

The method `parse` is a cleaned up version of `process_file` from the
same script.

Rob Lauer - <bigfoot@cpan.org>

# LICENSE

This statement was lifted directly from `perl.req`...

> _The entire code base may be distributed under the terms of the
> GNU General Public License (GPL), which appears immediately below.
> Alternatively, all of the source code in the lib subdirectory of the
> RPM source code distribution as well as any code derived from that
> code may instead be distributed under the GNU Library General Public
> License (LGPL), at the choice of the distributor. The complete text of
> the LGPL appears at the bottom of this file._
>
> _This alternatively is allowed to enable applications to be linked
> against the RPM library (commonly called librpm) without forcing
> such applications to be distributed under the GPL._
>
> _Any questions regarding the licensing of RPM should be addressed to
> Erik Troan &lt;ewt@redhat.com_.>
