=pod =encoding utf-8 =head1 lib::projectroot =head2 META =head3 Author Thomas Klausner =head3 Email domm AT plix.at =head3 URL http://domm.plix.at =head3 URL_slides http://domm.plix.at/talks/2015_dresden_lib_projectroot =head3 Date 2015-05-06 =head3 Location Dresden =head3 Event German Perl Workshop 2015 =head2 The problem You are working on some DarkPAN code. Of course you depend on CPAN modules. Which you "install" into a C powered directory in your project You also depend on some other of your DarkPAN code. Your scripts and tests may be located deep in some dir structure in your project =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── init.pl │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── RT666_fix_up_fubared_data.pl │ │ │ └── import_data.pl │ │ └── web.psgi │ ├── lib │ └── local ├── MyHelperStuff │ └── lib └── CoolLib-NotYetOnCPAN └── lib =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── init.pl │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── RT666_fix_up_fubared_data.pl │ │ │ └── import_data.pl │ │ └── **web.psgi** 1 Level │ ├── lib │ └── local ├── MyHelperStuff │ └── lib └── CoolLib-NotYetOnCPAN └── lib =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── **init.pl** 2 Level │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── RT666_fix_up_fubared_data.pl │ │ │ └── **import_data.pl** 2 Level │ │ └── **web.psgi** 1 Level │ ├── lib │ └── local ├── MyHelperStuff │ └── lib └── CoolLib-NotYetOnCPAN └── lib =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── **init.pl** 2 Level │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── **RT666_fix_up_fubared_data.pl** 3 Level │ │ │ └── **import_data.pl** 2 Level │ │ └── **web.psgi** 1 Level │ ├── lib │ └── local ├── MyHelperStuff │ └── lib └── CoolLib-NotYetOnCPAN └── lib =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── init.pl │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── RT666_fix_up_fubared_data.pl │ │ │ └── import_data.pl │ │ └── web.psgi │ ├── **lib** │ └── local ├── MyHelperStuff │ └── lib └── CoolLib-NotYetOnCPAN └── lib =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── init.pl │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── RT666_fix_up_fubared_data.pl │ │ │ └── import_data.pl │ │ └── web.psgi │ ├── **lib** │ └── **local** ├── MyHelperStuff │ └── lib └── CoolLib-NotYetOnCPAN └── lib =for newslide . ├── AProject │ ├── bin │ │ ├── db │ │ │ └── init.pl │ │ ├── onetime │ │ │ ├── fixup │ │ │ │ └── RT666_fix_up_fubared_data.pl │ │ │ └── import_data.pl │ │ └── web.psgi │ ├── **lib** │ └── **local** ├── MyHelperStuff │ └── **lib** └── CoolLib-NotYetOnCPAN └── **lib** =for newslide How can you tell the scripts where the libraries are located? How can you tell your libs where the other libraries are located? =head2 Various solutions =head3 FindBin Find out where the current binary/script is located, but no @INC manipulation. In the Perl core since forever. Also used by lib::projectroot. =for newslide use FindBin; use lib "$FindBin::Bin/../lib"; move the script to another dir and it breaks.. =head3 Find::Lib Combines FindBin and lib But does not search for the actual location of lib, so you'll need to know where your scripts is located relative to lib. =for newslide use Find::Lib '../bootstrap/lib'; You again need to know the scripts location in the tree =head3 FindBin::libs use FindBin::libs; finds C by walking up the path from the current location and Ces it. =for newslide No local::lib support. But lots of other features =head3 File::FindLib use File::FindLib 'lib'; Basically the same as C Find and use a file or dir based on the script location. Again no local::lib support. =head3 And probably many more We just like to fork everything.. =head2 lib::projectroot use lib::projectroot qw(**lib**); Exactly the same as C & C Walk up from the current directory until we find a 'sibling' directory named C C it! =for newslide use lib::projectroot qw(lib **local::lib**=%%local%%); Looks for a directory named C in the same dir that also contains C. Uses C to add C to C<@INC> =for newslide use lib::projectroot qw( lib local::lib=local **extra**=%%MyHelperStuff%%;%%CoolLib-NotYetOnCPAN%% ); Looks in the B directory of the directory containing C and C If there is a directory C containing a directory called C, add it to C<@INC> If there is a directory C containing a directory called C, add it to C<@INC> =for newslide use lib::projectroot qw( lib local::lib=local ); lib::projectroot->load_extra( qw( MyHelperStuff )); =for newslide # some script use lib::projectroot qw( lib local::lib=local ); use MyProject::Run; =for newslide package MyProject::Run; use lib::projectroot (); lib::projectroot->load_extra( qw( MyHelperStuff SomeModule AnotherModule OurToolkit Util-Helper-Role-Plugin )); # other always-run init stuff, eg Log::Any =for newslide In fact B was just added this Monday (1.003): use lib::projectroot qw( **extra**=MyHelperStuff ); CDD++ Conference Driven Development =head3 Errors If C cannot find the requested directories, it will C with a hopefully helpful error message =for newslide use lib::projectroot qw( lip ); Could not find root dir containing lip =for newslide use lib::projectroot qw( lib lokal ); Could not find root dir containing lib, lokal =for newslide use lib::projectroot qw( lib local::lib=local extra=NoSuchThing ); Cannot load_extra NoSuchThing, directory ../NoSuchThing/lib does not exist =head3 Also works in tests! #!/usr/bin/perl use lib::projectroot qw(lib local::lib=local **t**); use Test::Most; use testlib::Fixtures; =for newslide #!/usr/bin/perl use lib::projectroot qw(lib local::lib=local **t**); use Test::Most; use @@testlib::Fixtures@@; =head3 Important! If you want to use the C feature, you need to install C "properly" into your system Perl / perlbrew. =head3 Future use lib::projectroot qw( lib local::lib=local extra=MyHelperStuff **extraLL**=CoolLib-NotYetOnCPAN ); Load C B add C via C =head2 Give it a try cpanm --sudo lib::projectroot and tell me what you like / hate! =head2 __END__