In a couple of projects I’ve been working on (about which I expect to write more in the future), I’ve found myself wanting to re-use some set of functions or classes I’ve defined elsewhere, but that are not yet fully ready for public release. Or wanting to use a version of an upstream module that I’ve patched locally.
It turns out this is not at all hard to do, and it works really well. The trick is to use Carton to bundle the dependencies, and then install them either using Carton itself, or a recent-enough version of cpanm.
Bundling your dependencies
You’ll need carton 1.0.32 or greater in order to generate the package
index. Older versions might still work, but like it says in the changelog, this
version is the first to write the bundled index to a file that can be read by
cpanm, which will come in handy if you don’t want to depend on
itself in order to install your project.
Once you have them, the first thing to do will be to specify your dependencies
in a way
carton can understand them. For that you’ll need to use a cpanfile
(although you should probably always be using a cpanfile).
Once that is done, you can install your dependencies using
in the root of your project (where you have your
Running this command will read the dependencies from your
them, and install them into
local. While doing this, it will also cache
the tarballs of all the distributions it downloads into
Once that is done, you can run
carton bundle to store a copy of this cache
vendor directory, which will look like this:
$ tree vendor/ vendor/ └── cache ├── authors │ └── id │ └── F │ └── FO │ └── FOOBAR │ ├── Some-Dist-1.337.tar.gz │ └── Another-Dist-0.001001.tar.gz └── modules └── 02packages.details.txt.gz
And that generated
02packages.details.txt.gz file will look like this:
File: 02packages.details.txt URL: http://www.perl.com/CPAN/modules/02packages.details.txt Description: Package names found in cpanfile.snapshot Columns: package name, version, path Intended-For: Automated fetch routines, namespace documentation. Written-By: Carton v1.0.34 Line-Count: 2 Last-Updated: Mon Jan 27 12:34:56 2020 Some::Dist 1.337 F/FO/FOOBAR/Some-Dist-1.337.tar.gz Another::Dist 0.001001 F/FO/FOOBAR/Another-Dist-0.001001.tar.gz
Note that this will include all the dependencies in your cpanfile. If you
only want to bundle some dependencies with your project you’ll have to
manually remove all the ones you don’t care about (from both
02packages.details.txt.gz). Alternatively, you can read
a follow-up post where I investigate this in more detail and
provide some different solutions.
Once that is done, this
vendor directory can be added to your source control
and distributed as normal.
Installing your dependencies
The good thing about this is that this process does not require
the installing side.
Of course, if you have Carton, you can tell it to install the dependencies of
your project as normal, and give it the
--cached option to tell it to use
the bundled modules.
If not, starting from version 1.7016
cpanm includes a
option which allows you to specify where it should get the packages to install.
In order to fully emulate the
carton install --cached you can use the
cpanm -L local \ --from "$PWD/vendor/cache" \ --installdeps --notest --quiet .
Or, if you’re using
# Note that the --resolver option is marked experimental! cpm install \ --resolver "02packages,file://$PWD/vendor/cache" \ --resolver snapshot \ --resolver metadb
All of these will install your dependencies under
local, so you can use them
# Using carton carton exec some/path.pl # Using plain perl perl -Ilocal/lib/perl5 some/path.pl
Hope this helps! It has helped me a lot.