The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

package above;
use strict;
use Cwd qw(getcwd);
use File::Spec qw();
our $VERSION = '0.03'; # No BumpVersion
sub import {
my $package = shift;
for (@_) {
use_package($_);
}
}
our %used_libs;
BEGIN {
%used_libs = ($ENV{PERL_USED_ABOVE} ? (map { $_ => 1 } split(":", $ENV{PERL_USED_ABOVE})) : ());
for my $path (keys %used_libs) {
my $error = do {
local $@;
eval "use lib '$path';";
$@;
};
die "Failed to use library path '$path' from the environment PERL_USED_ABOVE?: $error" if $error;
}
};
sub _caller_use {
my ($caller, $class) = @_;
my $error = do {
local $@;
eval "package $caller; use $class";
$@;
};
die $error if $error;
}
sub _dev {
my $path = shift;
return (stat($path))[0];
}
sub use_package {
my $class = shift;
my $caller = (caller(1))[0];
my $module = File::Spec->join(split(/::/, $class)) . '.pm';
## paths already found in %used_above have
## higher priority than paths based on cwd
for my $path (keys %used_libs) {
if (-e File::Spec->join($path, $module)) {
_caller_use($caller, $class);
return;
}
}
my $xdev = $ENV{ABOVE_DISCOVERY_ACROSS_FILESYSTEM};
my $cwd = getcwd();
unless ($cwd) {
die "cwd failed: $!";
}
my $dev = _dev($cwd);
my $abort_crawl = sub {
my @parts = @_;
return 1 if (@parts == 0); # nothing left to try
return 1 if (@parts == 1 && $parts[0] eq ''); # hit root dir
my $path = File::Spec->join(@parts);
return !($xdev || _dev($path) == $dev); # crossed device
};
my $found_module_at = sub {
my $path = shift;
return (-e File::Spec->join($path, $module));
};
my @parts = File::Spec->splitdir($cwd);
my $path;
do {
$path = File::Spec->join(@parts);
pop @parts;
} until ($found_module_at->($path) || $abort_crawl->(@parts));
if ($found_module_at->($path)) {
while ($path =~ s:/[^/]+/\.\./:/:) { 1 } # simplify
unless ($used_libs{$path}) {
print STDERR "Using libraries at $path\n" unless $ENV{PERL_ABOVE_QUIET} or $ENV{COMP_LINE};
my $error = do {
local $@;
eval "use lib '$path';";
$@;
};
die $error if $error;
$used_libs{$path} = 1;
my $env_value = join(":", sort keys %used_libs);
$ENV{PERL_USED_ABOVE} = $env_value;
}
}
_caller_use($caller, $class);
};
1;
=pod
=head1 NAME
above - auto "use lib" when a module is in the tree of the PWD
=head1 SYNOPSIS
use above "My::Module";
=head1 DESCRIPTION
Used by the command-line wrappers for Command modules which are developer tools.
Do NOT use this in modules, or user applications.
Uses a module as though the cwd and each of its parent directories were at the beginnig of @INC.
If found in that path, the parent directory is kept as though by "use lib".
Set ABOVE_DISCOVERY_ACROSS_FILESYSTEM shell variable to true value to crawl past device boundaries.
=head1 EXAMPLES
# given
/home/me/perlsrc/My/Module.pm
# in
/home/me/perlsrc/My/Module/Some/Path/
# in myapp.pl:
use above "My::Module";
# does this ..if run anywhere under /home/me/perlsrc:
use lib '/home/me/perlsrc/'
use My::Module;
=head1 AUTHOR
Scott Smith
Nathaniel Nutter
=cut