CPAN::Maker::Bootstrapper - A complete build, dependency, and release framework for CPAN distributions
# Bootstrap an existing project
cd /path/to/Foo-Bar
cmb --import .
# Create a configuration file (recommended first-time setup)
cmb create-config > ~/.cpan-makerrc
export CPAN_MAKER_CONFIG=$HOME/.cpan-makerrc
# Create a new plain Perl module project
cmb --module My::New::Module
# Create a CLI module project (inherits from CLI::Simple)
cmb --module My::New::CLI --stub cli
# Use a custom stub
cmb --module My::Module --stub /path/to/mystub.pm
# Import files from another project
mkdir My-Module
cd My-Module
cmb -I /path/to/my-module/lib -I /path/to/my-module/bin \
--installdir .
# Install into a specific directory
cmb --module My::Module --installdir ~/git/My-Module
# Override git identity
cmb --module My::Module --username "Rob Lauer" --email rob@example.org
CPAN::Maker::Bootstrapper provides a complete development framework
for building, testing, maintaining, and releasing CPAN distributions.
It can scaffold a new Perl project or import an existing one, installing a managed build framework that carries the project from source through testing, dependency management, distribution, and release.
Core features include:
project.mk
without modifying managed build filesInstall the bootstrapper and its dependencies:
cpanm CPAN::Maker::Bootstrapper
Note: Before scaffolding your first project, consider running create-config
to set up a personal configuration file - it pre-populates your git
identity, GitHub username, and preferred project directory so you never
have to pass them on the command line. See "CONFIGURATION" for details.
cmb --module My::Module
or
# create a directory for the new CPAN distribution
mkdir My-Module
# cd into the directory
cd My-Module
# scaffold the project
cmb --installdir .
The bootstrapper derives the primary module name from the directory
name (My::Module in this example). It then installs the build
system, generates the stub source and test files, and runs make
automatically to create the first distribution tarball.
By default the final build step applies full linting: syntax
checking (perl -wc), perltidy conformance, and perlcritic at its
default severity (5 - the most severe violations only).
If you already have a Perl project, run cmb from the project root
and import the current directory:
cd Foo-Bar
cmb --import .
When exactly one import directory is supplied and neither --module
nor --installdir determines the module name, the bootstrapper derives
the primary module name from the import directory. In this example,
Foo-Bar implies Foo::Bar.
Use --dry-run first if you want to inspect the import plan without
creating files or running the generated build:
cmb --import . --dry-run
You may also import from multiple directories and specify the primary module explicitly:
cmb --module Foo::Bar
--import lib
--import bin
--installdir /tmp/Foo-Bar
For projects that require explicit control over which files are imported and their destinations, use an import manifest:
cmb import import.yml
The manifest defines the source directory, primary module, and
file-selection rules. Use --dry-run to preview the import plan
before creating the project:
cmb --dry-run import import.yml
See "IMPORTING FILES" for automatic discovery and manifest-driven imports.
Review the project source
For a newly scaffolded project, edit the generated source file:
lib/My/Module.pm.in
For an imported project, review the .pm.in and .pl.in files
created from the imported modules and scripts.
Files ending in .in are the editable project sources. The generated
.pm and .pl files are build artifacts and will be overwritten by
subsequent make invocations.
Review the framework's populated artifacts
buildspec.yml - controls how the distribution is built
See CPAN::Maker for details regarding buildspec.yml.
requires - found dependencies
test-requires - test dependenciesrecommends - recommended dependenciessuggests - optional dependenciesManage the project version
The bootstrapper creates a VERSION file containing the semantic
version number for the project. New projects begin at 1.0.0.
Use the version targets to increment it:
make release # 1.0.0 -> 1.0.1
make minor # 1.0.1 -> 1.1.0
make major # 1.1.0 -> 2.0.0
If you imported the primary module rather than generating it from a stub, make sure its version declaration uses the project version token:
our $VERSION = C<E<64>PACKAGE_VERSIONE<64>>;
When make generates the .pm file from its .pm.in source,
@PACKAGE_VERSION@ is replaced with the value from
VERSION.
Add More Modules, Scripts, and Tests
As your project grows, add new modules beneath lib/ as .pm.in
files and scripts beneath bin/ as .pl.in files. The build system
discovers them automatically - no Makefile changes are required.
Distribution tests belong beneath t/.
Additional test suites may use the conventional extended-test directories:
xt/author/
xt/release/
xt/smoke/
These suites can be run explicitly with make test-author,
make test-release, and make test-smoke, or together with
make test-all.
Add additional files to the distribution
Edit buildspec.yml to declare additional files that should be
included in the distribution.
Files may be added at the distribution root or installed beneath the distribution's share directory.
For example:
extra-files:
- ChangeLog
- share:
- config/example.ini
- defaults.json
Use root-level entries for files that should be packaged but not installed into the share directory.
Use the share section for files that should be installed as
distribution data.
See "How do I include additional files in the distribution?" for more details.
Rebuild Your Distribution
When you are ready to rebuild the project:
make
The default build applies the project's dependency and quality gates, regenerates derived source files and documentation as needed, and produces the CPAN distribution tarball.
When dependency scanning is enabled, changed source files are scanned and the dependency artifacts are updated only when their contents have changed:
requires
test-requires
recommends
suggests
The build also regenerates README.md when required and rebuilds only
those derived artifacts whose prerequisites have changed.
For a faster development build that skips dependency scanning and both lint tools while retaining syntax checking, use:
make quick
Verify the Distribution Installs Cleanly
After building the distribution, install the generated tarball with
cpanm to verify that it can be consumed independently of the source
tree:
cpanm --local-lib=$HOME My-Module-*.tar.gz
This exercises the packaged distribution and its declared dependencies through the normal CPAN installation path rather than through the project's development build environment.
Put Your Project Under Source Control
Initialize the repository and stage the project files recommended by the build system:
make git
By default, make git creates the initial commit after staging the
managed build files, project configuration, editable source files, and
other tracked project artifacts.
If you want to initialize and stage the project without creating the commit yet, use:
make git NO_COMMIT=1
Learn More About CPAN::Maker::Bootstrapper
See "CUSTOM BUILD TARGETS" for adding project-specific build logic and customizing the managed build framework.
See "Dependencies Management" for details on dependency discovery, classification, filtering, and generated dependency artifacts.
See "FAQ" for common questions and practical recipes.
Many build systems place the build procedure inside the CI platform: workflow files describe the steps, the CI runner executes them, and a separate local workflow is needed to reproduce the same build.
CPAN::Maker::Bootstrapper takes the opposite approach.
The build belongs to the project.
The same dependency graph, quality gates, generated artifacts, tests,
and distribution rules run whether make is invoked on your laptop,
on a remote host, inside make build-ci, or from GitHub Actions.
CI is therefore a caller of the build system rather than the place where the build system lives.
A fresh checkout remains directly buildable with:
git clone <repository>
cd <project>
make
The CI layer can add isolation and automation, but it does not define a second build procedure that must be kept synchronized with local development.
The build system is built on three tools with deliberately different responsibilities:
GNU make - models causality.
Targets, prerequisites, timestamps, pattern rules, and order-only dependencies describe what must be rebuilt and why. Make decides what work is necessary.
bash - performs process orchestration.
Shell recipes connect command-line tools, manage files and temporary state, and express the small imperative steps needed to carry out a build action.
Perl - handles transformations that deserve a real programming language.
Dependency analysis, metadata generation, configuration processing, distribution assembly, and other non-trivial transformations remain readable and testable Perl rather than growing into increasingly complex shell fragments.
CPAN::Maker::Bootstrapper provides the conventions that connect
those layers.
GNU make owns the dependency graph, bash performs the orchestration,
and Perl implements the complex transformations. The result is a
build system that remains visible and auditable: make -n shows what
would run, bash -x exposes shell execution, make help documents
the available targets, and project.mk provides an upgrade-safe place
for project-specific behavior.
The installed build system makes several professional development practices part of the project structure without forcing them on the developer on every build:
.pm.in and .pl.in files are the project sources; the corresponding
.pm and .pl files are generated artifacts. Changes belong in the
.in files and are propagated by make.scandeps-static.pl maintains requires, test-requires,
recommends, and suggests as the source changes. Pinning, sticky
entries, and skip lists allow the generated dependency model to be
adjusted when necessary.perltidy, perlcritic, and POD validation are
build gates rather than separate release-time procedures. The gates
remain independently controllable when a project needs different
development or runtime behavior.make update refreshes the build framework installed beneath
.includes/, while make upgrade checks for and installs newer
versions of CPAN::Maker::Bootstrapper.project.mk is the upgrade-safe extension point for custom targets,
additional dependencies, lifecycle hooks, and project-specific
variables. Projects extend the framework without modifying the files
maintained by the bootstrapper.The build system supports syntax checking, perltidy, and perlcritic as build-time quality gates.
Perltidy and perlcritic are controlled by LINT, which acts as the
master switch for both tools:
make LINT=OFF
The tools may also be disabled individually:
make PERLTIDY=""
make PERLCRITIC=""
This allows one linting tool to remain enabled while the other is disabled.
PERLTIDYRC and PERLCRITICRC configure the corresponding tools;
they do not enable or disable them.
For example:
make PERLTIDYRC=.perltidyrc
make PERLCRITICRC=.perlcriticrc
PERLTIDYRC and PERLCRITICRC are initialized in the
autogenerated build-config.mk file. During generation,
Bootstrapper searches for the corresponding configuration
files in the following order:
PERLTIDYRC, PERLCRITICRC).perltidyrc, .perlcriticrc in the project rootperltidyrc, perlcriticrc in the project root$HOME/.perltidyrc, $HOME/.perlcriticrcIf no profile is specified, the corresponding tool uses its default configuration.
If a profile variable names a file that does not exist, the operation fails.
NOTE: For reproducible builds, keep your lint configuration files in the project. Relying on configuration files in individual developers' home directories may produce inconsistent results across development environments and CI.
Syntax checking is controlled independently with SYNTAX_CHECKING:
make SYNTAX_CHECKING=OFF
Modules or scripts that cannot be syntax-checked outside their runtime
environment may be added to PERLWC_SKIP in project.mk:
PERLWC_SKIP = bin/startup.pl
Inter-module build dependencies may also be declared explicitly in
project.mk when they cannot be inferred automatically:
lib/Foo/Bar.pm: lib/Foo.pm
make quick provides a fast development build that disables
distribution dependency scanning and both lint tools while leaving
syntax checking enabled:
make quick
This is equivalent to:
make SCAN=OFF LINT=OFF
To establish consistent quality gate settings across development
environments and CI, define them in config.mk and commit that
file to source control. See "THE PROJECT MAKEFILE".
Because the build system is implemented with ordinary GNU make, the
managed files beneath .includes/ are also working examples of the
techniques used to model a non-trivial build.
They include:
define/endef blocks for reusable shell and Perl fragments$(shell ...), $(eval ...), $(call ...),
$(filter-out ...), $(addprefix ...), and $(patsubst ...) for
deriving and transforming build state?=, :=, +=, and = with their distinct evaluation
semantics.DEFAULT_GOAL, -include, and
.SHELLFLAGS := -ecmktemp, shell traps, and bash conditionals inside recipesThe build system is deliberately transparent to not only reveal how
the sausage is made but to expose these techniques to the developer
so you can incorporate them in your recipes. However, the build system
framework itself is read-only to discourage you from tampering with a
complex set of recipes that have been carefully crafted and can easily
be broken. Your extension point remains the project.mk file and the
double-colon targets.
The --import|-I option allows you to bring existing Perl source
files into a new Bootstrapper project. This is the primary mechanism
for migrating an existing project or consuming a scaffold tarball
generated by cli-simple -scaffold.
The --import option may be specified multiple times to import from
several directories in a single operation:
cmb --module My::Script \
--import /path/to/roles \
--import /path/to/bin \
--installdir .
The bootstrapper determines the primary module name in the following order:
--module If --module is supplied, that value is used.
If no module name was supplied and --stub names a file, the first
package found in that file is used.
If the module name is still unknown and --installdir was supplied,
the bootstrapper derives the module name from the installation directory
name.
If the module name is still unknown and exactly one --import path was
supplied, the bootstrapper derives the module name from the basename of
that directory.
For example:
cd Foo-Bar
cmb --import .
infers Foo::Bar.
When deriving a module name from a directory name, hyphens are converted
to ::, so Foo-Bar implies Foo::Bar.
The resulting name must be a valid Perl module name.
When importing an existing project, the corresponding module file must
also exist beneath one of the import paths. For example, Foo::Bar
must be found as Foo/Bar.pm somewhere beneath one of the directories
supplied with --import.
The import command provides an alternative to automatic file
discovery when importing an existing Perl project.
cmb import import.yml
An import manifest explicitly identifies the source directory, primary module, and files to import. It also allows source files to be mapped to specific destinations in the generated project.
For example:
base-dir: /path/to/existing/project
module: Foo::Bar
import:
- lib:
path: lib
recurse: true
ext: pm
- bin:
path: bin
recurse: true
- t:
path: t
recurse: true
The following top-level keys are required:
base-dir
The root directory containing the files to import. Relative source paths in the manifest are resolved against this directory.
module
The primary Perl module for the generated project.
import
A list of import specifications. Each entry identifies a destination directory and the source files or directories to import.
Each import specification supports the following keys:
path
A source file, directory, or list of paths relative to base-dir.
Absolute paths are also accepted, provided they remain within
base-dir.
recurse
When true, recursively searches the specified source directories. The default is false.
ext
Restricts imported files to matching filename extensions. Accepts a single extension or a list of extensions. When omitted, all extensions are eligible.
exclude
Excludes matching files or directories from the import. Exclusions may also be specified at the top level of the manifest.
dest
An optional subdirectory appended to the import destination.
Each entry in import must specify exactly one destination.
Use --dry-run to preview the file mappings before generating
the project:
cmb --dry-run import import.yml
The import command uses the same underlying project construction
mechanism as install, but obtains its file-selection plan from
the manifest rather than automatic discovery.
Syntax checking is disabled during the initial import build. This allows projects with dependencies or runtime environments that have not yet been configured to be imported before addressing those requirements.
The --import option remains available for automatic discovery
without a manifest.
When importing an existing project, the bootstrapper scans each
--import directory and builds an explicit import plan.
The following files are recognized:
Perl modules
Files ending in .pm are imported as distribution modules and placed
beneath lib/.
For example:
lib/Foo/Bar.pm
becomes:
lib/Foo/Bar.pm.in
The package declared by the module determines its destination beneath
lib/.
Perl scripts
Files ending in .pl are imported beneath bin/ and converted to
generated source files ending in .in.
Executable files
Executable files not otherwise classified are imported beneath bin/
and converted to generated source files ending in .in.
Test files and test helpers
Files beneath the following recognized test directories are preserved in place:
t/
xt/author/
xt/release/
xt/smoke/
Within those directories, files with the following extensions are treated as test material:
.pm
.pl
.t
.sh
.dat
The directory takes precedence over the file extension. For example:
t/lib/TestHelper.pm
remains:
t/lib/TestHelper.pm
and is not imported as:
lib/TestHelper.pm.in
Likewise:
xt/author/check.pl
remains beneath xt/author/.
Change logs
The following root-level change log files are preserved when present:
ChangeLog
CHANGELOG
Changes
CHANGES
Their original names are retained.
Files not matching one of the recognized categories are not imported.
Use --exclude to omit directories beneath an import root.
The option may be supplied more than once:
cmb --import .
--exclude local
--exclude Foo-Bar-1.2.3
Each exclusion is interpreted relative to the import root and excludes that directory and everything beneath it.
For example:
--exclude local
excludes:
local/
local/lib/
local/bin/
but does not exclude an unrelated directory with the same name outside the import root.
The following source-control directories are always excluded and do not need to be specified explicitly:
.git
.hg
.svn
These directories are pruned wherever they occur beneath an import root.
Use --dry-run to inspect the import plan without modifying the
filesystem or running the generated build.
For example:
cmb --import .
--exclude local
--dry-run
The bootstrapper scans the import directories, applies exclusions, classifies the recognized files, determines their destinations, and prints the resulting import plan.
No installation directory is created, no files are copied, and make
is not run.
This is useful when importing an existing project because it allows the proposed mapping to be reviewed before any project files are generated.
Use --project-tarball to create an archive containing the complete
generated CPAN::Maker::Bootstrapper project instead of installing that
project into a directory.
For example:
cmb --import .
--exclude local
--project-tarball
The bootstrapper performs the normal import and build process in a
temporary working directory and then writes a project archive to the
directory from which cmb was invoked.
For a module named Foo::Bar, the resulting archive is named:
Foo-Bar-cmb.tar.gz
The archive contains a top-level project directory:
Foo-Bar/
and includes the generated CPAN::Maker::Bootstrapper project, including the Makefile, build configuration, imported source files, test files, build support files, logs, and the generated CPAN distribution tarball.
This is different from the CPAN distribution tarball produced by the build. The CPAN distribution contains the files intended for release to CPAN; the project tarball contains the complete CPAN::Maker::Bootstrapper development project used to build that distribution.
The temporary dependency installation directory used during the import build is not included in the project archive.
When importing an existing project into a directory, the bootstrapper refuses to create the generated project inside one of the directories being imported.
For example, this is not allowed:
cmb --import . --installdir ./converted
when converted/ would be created beneath the import root.
The bootstrapper also refuses to use the import root itself as the installation directory.
These checks prevent the generated project from becoming part of the source tree while that source tree is being scanned and imported.
--force does not override this safety check.
The check applies only when installing the generated project into a
directory. --project-tarball does not create an installation
directory and therefore does not require this restriction.
After constructing the imported project, the bootstrapper runs the generated build to verify that the project can be built successfully.
The initial import build enforces the following settings:
SCAN=on
SYNTAX_CHECKING=off
LINT=off
SKIP_TESTS=1
Dependency scanning remains enabled during the initial import build.
After a successful import you either have a project tree built for you
or a tarball if you used the --project-tarball option.
The project is now capable of producing a CPAN distribution with the following caveats:
a local-lib environment has not yet been provisioned
The first time you run make with SYNTAX_CHECKING=on (the default)
Bootstrapper will install all of the dependencies it detected in
local/lib/perl5.
Tests were not run
Tests are disabled during import since dependencies have not yet been
installed. Running make the first time will install dependencies
and attempt to run your tests.
syntax checking was not done
Running make the first time will syntax check all of your files
after installing dependencies. .pm files are created and syntax
checked in an order that respects the intra-project module
dependencies.
linting was not performed
Your files may not conform to perltidy and they may not pass
perlcritic's critique.
running make the first time will perform the following actions:
perltidy if installedperlcritic if installed (severity level 5)So, your first task is to make sure the dependencies are installed
properly and the distribution builds. That means fixing tests as
necessary, specifying dependencies missed by the scanner, tidying
files or resolving perlcritic findings. If you are not interested
in linting yet, turn that off on your first make (make
LINT=off). To turn linting off permanently add LINT=off to your
config.mk file.
See "FAQ" for more troubleshooting information for broken builds.
Once your project produces a CPAN distribution and passes your tests there are some other actions you should consider.
buildspec.yml - verify the
module name, author, and resource links are correct Your project may want to package additional files that are installed
into the distribution's share directory. Move them into an appropriate
directory or the root of the project and add them to the
buildspec.yml file.
extra-files:
- ChangeLog
- share:
- config/some-file.ini
- my-app.json
make gitRun make tidy if you wish to format the imported source
make tidy
Perltidy uses the perltidy configuration file pointed to by PERLTIDYRC.
Dependency scanning may be disabled with:
make SCAN=OFF
Perltidy may be disabled independently with:
make PERLTIDY=""
Perlcritic may be disabled independently with:
make PERLCRITIC=""
Both lint tools may be disabled together with:
make LINT=OFF
To disable dependency scanning and both lint tools while retaining syntax checking, use:
make quick
Test installation
cpanm -n -v ./My-Script-1.0.0.tar.gz
--import cannot be used with --stub - they are mutually
exclusive ways to create the initial source.pm files
to determine where to place them under lib/. If the importer cannot
match the filename with a package declaration inside the file, it will
warn and skip that fileImported files are not tidied automatically.
Run make tidy after import if you want to format the imported source.
If PERLTIDYRC is configured, that profile is used. Otherwise
perltidy runs with its default configuration.
Inter-module dependencies are normally detected automatically.
The build generates deps.mk from dependencies between modules in the
distribution so prerequisite modules are built before syntax checking
modules that depend on them.
If a dependency cannot be inferred automatically, declare it explicitly
in project.mk:
lib/My/Script.pm: \
lib/My/Script/Role/Frobnicate.pm \
lib/My/Script/Role/List.pm
See "Inter-module dependencies" for details.
Suppose you have a project that used CLI::Simple as base class and
now want to use the CPAN::Maker::Bootstrapper framework.
The import-scaffold command is a convenience wrapper around
--import specifically designed to consume tarballs generated by
cli-simple -scaffold:
cmb import-scaffold my-script-roles.tar.gz --module My::Script --installdir .
The tarball is extracted to a temporary directory and fed to the importer automatically. See CLI::Simple for details on generating scaffold tarballs.
cmb reads configuration from an INI-format file. The configuration
file may be selected explicitly with --config, specified through
the CPAN_MAKER_CONFIG environment variable, or discovered
automatically.
Configuration values can be used to set defaults when scaffolding distributions and to configure installed extensions.
See "new" in CPAN::Maker::Bootstrapper::ConfigReader for the configuration file discovery order.
When --installdir is not supplied when you bootstrap a project, the
bootstrapper uses basedir from the configuration when one is
defined. Otherwise, it uses the current working directory as the base
directory for the new project.
An explicit --installdir always takes precedence.
If name or email is missing from the [user] section,
Bootstrapper attempts to obtain the missing values from
~/.gitconfig.
You can configure your name, email address, and GitHub identity with:
git config --global user.name <your-full-name>
git config --global user.email <your-email>
git config --global user.github <your-github-identity>
If you typically create projects in one directory, add the basedir
option:
git config --global cpan-maker.basedir $HOME/git
A Bootstrapper configuration file may contain entries such as:
[user]
email = your-email@somedomain
name = First Last
# use to construct GitHub resource URLs
github = github-user
[cpan-maker]
basedir = /home/myhome/git
# indicates the resources section of Makefile.PL should contain github references
resources = github
See CPAN::Maker::Bootstrapper::ConfigReader for a complete description of the configuration file.
Use the --config option to use your custom config.
You can generate a starter configuration with:
cmb create-config > ~/.cpan-makerrc
Then point cmb at it by setting the
CPAN_MAKER_CONFIG environment variable in your shell profile:
export CPAN_MAKER_CONFIG=$HOME/.cpan-makerrc
CPAN_MAKER_CONFIG
Path to an INI-format configuration file. Takes precedence over
automatically discovered configuration files but is overridden by
--config.
SCAN
Controls dependency scanning during make. Set to OFF to disable
distribution dependency scanning. The default is ON.
The following files are installed into the project directory:
Makefile - the complete build system. Derives project paths
and names from MODULE_NAME, the package name in a custom stub, or
the project directory name. See "THE PROJECT MAKEFILE".buildspec.yml - generated from the template, pre-populated
with your module name, git identity, GitHub username, and project URLs.lib/<Module/Path>.pm.in - stub module, populated from
either class-module.pm.tmpl or cli-module.pm.tmpl when
--stub cli is used.
Note: Files under lib/ and bin/ use .pm.in and .pl.in
as editable sources. The generated .pm and .pl files are derived
from them by the Makefile and will be overwritten by subsequent
builds.
t/00-<project-name>.t - minimal smoke test that calls
use_ok on your module.
.includes/ - the managed build system directory. Contains
all .mk files installed and maintained by the bootstrapper. These
files are write-protected and should never be edited directly. Updated
with make update.
.includes/bootstrap.mk - used internally by the bootstrapper
.includes/bash-completion.mk - make bash-completion target
.includes/build-init.mk - used to initialize build-time variables
.includes/modulino.mk - make modulino target
.includes/git.mk - make git target
.includes/help.mk - make help target
.includes/local.mk - vendors dependencies for syntax checking
.includes/perl.mk - pattern rules, syntax checking, tidy, critic
.includes/test.mk - recipes for the test targets; test, test-all, etc
.includes/update.mk - make update target
.includes/upgrade.mk - make upgrade/check-upgrade targets
.includes/version.mk - make release/minor/major targets
project.mk - your extension point for custom make rules,
inter-module dependencies, and project-specific variables. Never
touched by make update. See "CUSTOM BUILD TARGETS".
modulino.tmpl - template used by make modulino to
generate bash wrapper scripts for modulino-style modules.VERSION - contains the current version string in
major.minor.patch format. Managed by make release, make minor,
and make major.ChangeLog - empty placeholder, required by the distribution.config.mk - developer-maintained Make configuration for
persistent project build settings. This file is not managed by
make update.build-config.mk - generated Make configuration containing
resolved project values and discovered build helper commands. It is
created automatically as needed and should not be edited or committed.The Makefile serves as the orchestration layer for building and
maintaining a bootstrapped Perl project. It coordinates the recipes
required to generate source files, resolve dependencies, run tests,
build distributions, and perform other development and release
operations. The individual recipes are organized into Makefile
fragments under .includes/, which are included by the main
Makefile.
Before these recipes can run, Make needs to establish the project's
configuration. On the initial invocation, Make creates config.mk if
it does not exist, then invokes cmb create-build-config to generate
build-config.mk. The generated file contains the resolved project
settings and paths to the tools used by the build.
cmb create-build-config determines the primary module name from
MODULE_NAME, if supplied, or derives it from the project directory
name. It uses that name to construct values such as MODULE_PATH,
PROJECT_NAME, and UNIT_TEST_NAME. It also resolves configured
defaults and locates development tools such as Perl, Git, and the
dependency installers.
For example, a project with MODULE_NAME=My::New::Module produces:
MODULE_NAME := My::New::Module
MODULE_PATH := lib/My/New/Module.pm
PROJECT_NAME := My-New-Module
UNIT_TEST_NAME := t/00-my-new-module.t
build-config.mk is generated configuration derived from the
current build environment. It provides reasonable defaults by
discovering available tools, configuration files, and other
settings needed by the build. It is regenerated when a fresh
build environment is established and should not be edited or
committed.
config.mk provides persistent, project-specific overrides
for these discovered settings. Values defined in config.mk
take precedence over the corresponding defaults in
build-config.mk.
Because discovered settings may differ between development
machines and CI environments, projects should record their
intended build policy in config.mk and commit that file to
source control.
Changes to config.mk cause build-config.mk to be
regenerated so the build incorporates the updated settings.
make / make all
Builds the distribution tarball. When dependency scanning is enabled,
updates requires, test-requires, recommends, and suggests,
and generates README.md as prerequisites.
make bash-completion
Generates and installs a bash completion function for your project's
modulino, then prints the source line to enable it. The function is
produced by <modulino> -generate-completion (available to any
CLI::Simple-based modulino) and written to
~/.local/share/bash-completion/completions/<alias>.
make bash-completion
# then, as it instructs:
source ~/.local/share/bash-completion/completions/<alias>
The target depends on the modulino, so it will build bin/<alias>
first if needed. Completion is only available for modulinos that
subclass CLI::Simple.
make help
Lists the available build targets and commonly used build variables.
Project-specific targets in project.mk are included when their
target definition contains a ## description.
make requires / make test-requires
Scans source files with scandeps-static.pl and writes the dependency
files specified in the buildspec.yml file used by make-cpan-dist.pl.
Any change to your .pm.in files will trigger a rescan of your
modules for new dependencies. This can add a significant delay when
you have many modules and a large number of dependencies. You can
avoid the scan if you know that no new dependencies have been added by
setting the environment variable SCAN to OFF (case insensitive).
make SCAN=OFF
You can make scanning deliberate by adding SCAN=OFF to your
config.mk file. Then, to rescan:
make SCAN=ON
make recommends / make suggests
Companion targets to make requires. The dependency scanner classifies
each discovered module into one of three tiers: hard requires,
recommends (soft, non-eval conditional dependencies), and suggests
(eval-wrapped, optional dependencies).
These files are consumed by CPAN::Maker when it generates the
distribution metadata, including the corresponding dependency sections
in Makefile.PL. See "Dependencies Management".
DARKPAN_REQUIRES
Set DARKPAN_REQUIRES to a true value (1, yes, on, or
si) to generate dependency manifests for modules available from a
configured DarkPAN.
This is useful when a distribution published to CPAN has one or more runtime dependencies that are intentionally hosted on a separate CPAN-compatible repository. CPAN metadata can still declare those dependencies normally, but standard installers need additional information to locate distributions that should be obtained from the DarkPAN.
The generated DarkPAN manifests provide that information without duplicating
the dependency declarations maintained in requires. They are included in
the distribution as installation aids for the person or process installing
the module. They are not automatically consulted by Perl installers during a
normal installation; the installer must explicitly use the appropriate
manifest or configure the DarkPAN repository.
When enabled, DARKPAN_URL must specify the base URL of the
CPAN-compatible repository:
DARKPAN_REQUIRES = yes
DARKPAN_URL = https://cpan.example.com/repository
make examines requires and generates:
cpanfile.darkpan
cpanm.darkpan
When DARKPAN_REQUIRES is enabled, cpanfile.darkpan and cpanm.darkpan
are added to buildspec.yml as extra files and are therefore included
in the distribution. They are expected to be tracked by git unless the
developer explicitly adds them to extra-files.skip.
For each module listed in requires, the build checks whether the module
is available from the configured DarkPAN. Modules found there are added to
the generated DarkPAN manifests.
For modules available from the DarkPAN, the build also checks MetaCPAN. If a module is available from both CPAN and the DarkPAN, the DarkPAN version is preferred and the module remains in the generated manifests. This allows the DarkPAN to provide a version of a module that is also published on CPAN.
Modules that should not be obtained from the DarkPAN may be listed in
darkpan.skip, one module name per line. Those modules are omitted from
both generated DarkPAN manifests.
cpanfile.darkpan contains dependencies selected for resolution from
the DarkPAN in cpanfile syntax. cpanm.darkpan contains the same
dependencies in a form suitable for passing to
cpanm.
The configured DarkPAN must publish:
modules/02packages.details.txt.gz
under DARKPAN_URL.
make package
Runs the quality and dependency gates together (lint plus a
dependency scan) - a convenience for pre-release verification.
TARBALL_ORDER_ONLY_PREREQS
Additional order-only prerequisites for the distribution tarball.
Set this in project.mk when project-specific generated artifacts or
other preparation steps must complete before the tarball is built but
should not themselves determine whether the tarball is out of date.
TARBALL_ORDER_ONLY_PREREQS += prepare-assets
make release / make minor / make major
Bumps the patch, minor, or major version number in VERSION.
make clean
Removes build artifacts registered for cleaning. Does not affect
buildspec.yml, VERSION, or any *.in source files.
If your project needs a project-specific clean recipe, use the
clean-local target with a double-colon.
clean-local::
rm -rf workdir
make test
Runs the project's distribution unit tests under t/:
prove -I lib -I local/lib/perl5 -v t/
make test also runs any project-specific test-local:: recipes
defined in project.mk.
Projects may have tests that exercise development infrastructure,
external services, generated artifacts, or other behavior that should
not be included in the CPAN distribution. These can be added through
the test-local:: extension point:
test-local::
./bin/test-integration
The double-colon form allows project.mk to extend the managed
test-local target without replacing it.
make test also recognizes the conventional extended-test
directories xt/author/, xt/release/, and xt/smoke/. These
test suites are not run by default, but may be enabled through the
corresponding environment or make variables:
AUTHOR_TESTING=1 make test
RELEASE_TESTING=1 make test
AUTOMATED_TESTING=1 make test
When enabled, make test invokes the corresponding test target after
the normal t/ test suite and test-local:: recipes have completed.
make test-author
Runs tests under xt/author/:
prove -I lib -I local/lib/perl5 -r xt/author
The xt/author/ directory is created automatically if it does not
already exist.
This target uses a double-colon rule and may therefore be extended in
project.mk without replacing the managed target:
test-author::
./bin/check-generated-docs
make test-release
Runs tests under xt/release/:
prove -I lib -I local/lib/perl5 -r xt/release
The xt/release/ directory is created automatically if it does not
already exist.
The target may be extended in project.mk using test-release::.
make test-smoke
Runs tests under xt/smoke/:
prove -I lib -I local/lib/perl5 -r xt/smoke
The xt/smoke/ directory is created automatically if it does not
already exist.
The target may be extended in project.mk using test-smoke::.
make test-all
Runs the complete test suite: distribution tests under t/, any
project-specific test-local:: recipes, and the author, release, and
smoke test suites.
It is equivalent to running:
make test AUTHOR_TESTING=1 RELEASE_TESTING=1 AUTOMATED_TESTING=1
The extended test directories follow established Perl distribution
conventions. CPAN::Maker::Bootstrapper preserves those conventions
rather than requiring imported or existing projects to reorganize
their tests.
make tidy
Runs perltidy on all .pm.in and .pl.in source files.
If PERLTIDYRC is set, the named profile is used:
make tidy PERLTIDYRC=.perltidyrc
If PERLTIDYRC is not set, perltidy runs using its default
configuration.
If PERLTIDYRC names a file that does not exist, the target fails.
The target also performs syntax checking before modifying the source files.
make critic
Runs perlcritic on the project's Perl source files.
If PERLCRITICRC is set, the named profile is used:
make critic PERLCRITICRC=.perlcriticrc
If PERLCRITICRC is not set, perlcritic runs using its default
configuration.
If PERLCRITICRC names a file that does not exist, the target fails.
The target also honors:
PERLCRITIC_THEME
PERLCRITIC_SEVERITY
and performs syntax checking before running perlcritic.
make lint
Runs both linting targets:
make tidy
make critic
The perltidy and perlcritic configuration variables described above apply to their respective targets.
make git
Initializes a git repository, stages all recommended project files
including .includes/*, and makes an initial BigBang commit.
make quick
Builds the distribution tarball with distribution dependency scanning and perltidy/perlcritic disabled. Syntax checking remains enabled.
Useful during active development when you want fast iterative builds
without updating requires, test-requires, recommends, or
suggests.
make quick
Equivalent to:
make SCAN=OFF LINT=OFF
make workflow
Installs a CI build script (builder), its default environment file
(builder.env), and a GitHub Actions workflow
(.github/workflows/build.yml) into your project, templated with your
module and project name. Also merges any build-only dependencies
builder needs into build-requires.
make workflow
git add build-requires builder builder.env .github/workflows/build.yml
Commit these files - GitHub Actions will then run ./builder on
every push to main or dev. See "Continuous Integration" for
what builder does, how to customize its environment and build
lifecycle, and how to run it outside of GitHub Actions.
make build-ci
Runs builder locally inside Docker, against your current working tree,
to reproduce a CI build without pushing. Requires docker and a
builder script (run make workflow first if you don't have one).
make build-ci
See "Continuous Integration" for the variables that control this target.
The Makefile will automatically create a README.md from your
Perl module's pod. The stock buildspec.yml will include that
README.md in the distribution's share directory. If you want the
README.md to be included in the distribution but not installed,
edit the buildspec.yml file.
Before
extra-files:
- ChangeLog
- share:
- README.md
After extra-files: - ChangeLog - README.md
If you want to generate README.md from a custom source, create a
README.md.in file. That file will be filtered through
md-utils.pl (from Markdown::Render) to produce a .md file.
install (default)
Scaffolds a new project. This is the default command, so:
cmb -m My::Module
...is the same as:
cmb -m My::Module install
create-config
Outputs a stub configuration file to STDOUT. Create and edit a new
config to customize the behavior of cmb.
cmb create-config > ~/.cpan-makerrc
Then set CPAN_MAKER_CONFIG to point to it:
export CPAN_MAKER_CONFIG=$HOME/.cpan-makerrc
deps-filter
cmb deps-filter requires
Filters a dependency list so that modules already provided by another listed distribution are removed.
The command consults the public CPAN package index and any repositories
listed in build-mirrors. Repository indexes are cached under the
user's cache directory and conditionally refreshed on subsequent runs.
This command is normally invoked automatically by the generated
Makefile for requires, recommends, suggests, and
test-requires.
dist-file
cmb dist-file distribution-name filename
Copies a distribution file to STDOUT. Searches the root and share/
directories of the distribution for file. Throws an exception if
either the file is not found or the distribution is invalid.
Example:
cmb dist-file CPAN-Maker-Bootstrapper builder.env
extra-files
cmb extra-files path file1 file2 ...
Adds files to the distribution. Use . for files that should appear
at the root of the distribution tarball but not be installed into the
share directory. Use share for files that should be installed into
the distribution share directory.
NOTE: file should be the relative path within the project that points to the file.
Example:
cmb extra-files . README.md
cmb extra-files share share/config.json
Entries may be removed by editing buildspec.yml directly, which is
usually the clearest approach.
The cmb extra-files command also supports removing an entry by
prefixing the filename with -:
cmb extra-files . -README.md
This is primarily useful from scripts or other automated workflows.
create-deps
cmb create-deps [module.pm.in ...]
Emits GNU make dependency rules (to STDOUT) capturing the
inter-module dependencies within your distribution -- i.e. which of
your own .pm files use which others. Uses
Module::ScanDeps::Static to scan each source module, then prints
target: prerequisite lines (in deps.mk form) for the internal
packages only, so make rebuilds a dependent module when a module it
depends on changes. With no arguments every project module is scanned;
name one or more modules to restrict the output.
create-darkpan-requires
cmb create-darkpan-requires [--filter file] [requires-file]
Examines requires (or requires-file) and identifies dependencies
available from the configured DarkPAN. Each dependency is checked
against the DarkPAN 02packages.details.txt.gz index. Dependencies
found on the DarkPAN are included in the generated manifests.
For each dependency found on the DarkPAN, MetaCPAN is also checked. If the module is available from both CPAN and the DarkPAN, a warning is emitted and the DarkPAN is preferred.
The optional --filter argument names a file containing module names to
exclude from the generated manifests, one module per line:
cmb create-darkpan-requires --filter darkpan.skip requires
This is useful when a module is available from both CPAN and the DarkPAN but the distribution author wants that dependency to be resolved from CPAN.
When invoked through the generated Makefile, darkpan.skip is used
automatically when it exists.
The presence of darkpan.skip affects only which dependencies are
written to the manifests; it does not change the distribution or
source-control treatment of the generated files.
The generated files are intended as installation aids and are included with
the distribution. They do not alter normal Perl dependency resolution by
themselves and are not automatically consulted during installation. Instead,
they are intended to be consumed explicitly by your installation tool, such
as cpm or cpanm.
For distributions that include these files, the cpan-distfile utility
provided with DarkPAN::Resolver::SQLite can be used to retrieve them
directly from a CPAN distribution without manually downloading and unpacking
the tarball.
cpan-distfile Some::Module cpanm.darkpan > cpanm.darkpan
See DarkPAN::Resolver::SQLite for examples of using these manifests with
cpm and cpanm.
The command generates two representations of those dependencies:
cpanfile.darkpan
cpanm.darkpan
cpanfile.darkpan uses cpanfile syntax:
requires 'Amazon::API::CloudWatchLogs', '1.43.90';
cpanm.darkpan contains one cpanm module requirement per line:
Amazon::API::CloudWatchLogs~1.43.90
The version constraints are taken from requires; the DarkPAN index
is used only to determine whether a module is available from a DarkPAN
repository.
This command is normally invoked automatically by make when
DARKPAN_REQUIRES is enabled.
critique
cmb critique file ...
cmb critique --file-list manifest
Runs Perl::Critic over the given files (or a newline-delimited
--file-list). Defaults to the pbp theme at severity 5; override
with the PERLCRITIC_THEME, PERLCRITIC_SEVERITY, and
PERLCRITICRC environment variables. Requires Perl::Critic to be
installed.
import
Imports an existing Perl project using a YAML manifest.
cmb import import.yml
The manifest identifies the primary module, source directory, and files to import into the generated project.
Unlike the --import option, which discovers source files
automatically, the import command uses explicit file-selection
and destination rules.
Use --dry-run to preview the import plan:
cmb --dry-run import import.yml
Syntax checking is disabled during the initial import build. This allows projects with unresolved dependencies or specialized runtime requirements to be imported before those requirements are addressed.
See "IMPORTING FILES" for the manifest format and available file-selection options.
publish-to-cpan
cmb publish-to-cpan distribution.tar.gz [username [password]]
Uploads a distribution tarball to PAUSE.
The username and password may be supplied as arguments or through
PAUSE_USER and PAUSE_PASSWORD. Normally this command is invoked
by make publish, which rebuilds and tests the distribution before
uploading it.
resolve-vars
cmb resolve-vars [--vars-file FILE] [--no-strict] source-file
Filters source-file to STDOUT, substituting @TOKEN@ placeholders
with values drawn from the environment (or from a --vars-file). This
is the mechanism the generated Makefile uses to turn .pm.in and
.pl.in sources into their built .pm/.pl counterparts -- for
example filling 2.6.1 from the VERSION file or
@BUILD_DATE@ at build time.
A placeholder is required to have a value only when it appears in live
code. Placeholders that occur solely inside POD or # comments are
treated as references, not substitutions: they never trigger a "no
value present" error and are left untouched when no value is
available. This lets you document a token in your POD (e.g. mention
@BUILD_DATE@ in a description) without breaking the build.
For placeholders that do appear in code, behavior depends on
--strict (the default):
@TOKEN@) rather than being
substituted to an empty string. See "--vars-file" and "--strict, --no-strict".
--basedir|-b DIR
Base directory in which to create the project. Defaults to the
current working directory when --installdir and --basedir are not
provided. The directory must exist or the script will throw an
exception.
Note: If --installdir is provided it takes precedence and
--basedir is ignored.
default: pwd
--color|--no-color
default: color
To turn color off use --no-color.
--dry-run|-D
Previews an operation without completing it.
For import, displays the planned file mappings without creating
the project.
--config|-c configuration file
Explicitly selects an INI-format configuration file, overriding
CPAN_MAKER_CONFIG and automatic configuration discovery.
See "new" in CPAN::Maker::Bootstrapper::ConfigReader for the configuration file discovery order.
--email|-e EMAIL
Override the author email. Defaults to user.email from the
configuration file, your local .git/config or the global git
configuration.
--force|-f
Overwrite an existing project. Without this flag, the command dies if a
Makefile already exists in the target directory.
--github-user|-g USER
Override the GitHub username used to construct repository URLs in
buildspec.yml. Defaults to user.github from your configuration
file, your local .git/config or the global git configuration.
--import|-I path
A path that contains .pm or .pl files for importing into the
project. You can specify multiple paths. You cannot use --stub and
--import together.
Example:
cmb --module Foo::Bar -I ~/foo-bar/lib -I ~/foo-bar/bin
--module option. The corresponding
module file must exist beneath one of the import paths. For example,
Foo::Bar must be found as Foo/Bar.pm.The Makefile will automatically attempt to substitute the
token @PACKAGE_VERSION@ inside your .pl.in or .pm.in
files with the current semantic version in the VERSION file. If you
want to use that for versioning your scripts and modules add the token
as shown below:
C<our $VERSION = 'E<64>PACKAGE_VERSIONE<64>';>
--installdir|-i DIR
Directory in which to create the project. When supplied, this overrides
the configured or command-line basedir. The directory is created if
it does not exist.
Example:
cmb --installdir ~/git/My-Module
The install directory should include the project name.
Note: --installdir overrides --basedir.
--module|-m MODULE
The Perl module name for the new project, e.g. My::New::Module.
Used to derive the project directory name, source file path, and
tarball name.
You may omit this option when the module name can be determined from a custom stub file or from the project directory name.
--resources|-r github
Currently takes only a single value: 'github' that indicates that the
resources section of Makefile.PL should be populated with GitHub
URL references. Future versions may support additional providers.
--strict, --no-strict
Controls how resolve-vars treats an @TOKEN@ placeholder that
appears in code but has no value in the environment or --vars-file.
--strict (the default) makes this a fatal error. --no-strict
downgrades it to a warning and leaves the placeholder literal in the
output.
This affects code placeholders only. Placeholders that appear solely in
POD or # comments are always ignored by the missing-value check
regardless of this flag, so --no-strict is not needed merely to
document a token.
--stub|-s TYPE|PATH
Controls the module stub used to generate the initial .pm.in source
file. Three forms are accepted:
class-module.pm.tmpl).cli - uses the CLI stub (cli-module.pm.tmpl), which
inherits from CLI::Simple and includes a skeleton main, init,
and a placeholder command.--module option if
you supply your own stub file. See the explanation for the
--module option for details. When specifying a stub you cannot use the --import option.
--username|-u NAME
Override the author name used in the module stub and buildspec.yml.
Defaults to user.name, your local .git/config or the global git
configuration.
--vars-file
The path to a file containing template variable values used by
resolve-vars.
CPAN::Maker::Bootstrapper supports optional extensions that introduce
new cmb commands, command-line options, configuration settings, and
Makefile targets.
Extensions allow developers to add specialized workflows to their projects while retaining the build and release infrastructure provided by Bootstrapper. Each project can install and configure only the capabilities it needs.
An extension is packaged as a Perl distribution named
CPAN::Maker::Bootstrapper::Role::ExtensionName. It provides a
role.yml manifest, one or more Perl roles, and optionally a
role.mk file containing Makefile recipes.
Extensions must be installed before they can be added to a project.
Two extensions illustrate this approach:
CPAN::Maker::Bootstrapper::Role::LLM
Integrates large language models into development workflows, providing commands for tasks such as source code review and documentation analysis.
CPAN::Maker::Bootstrapper::Role::PAUSEUpload
Integrates distribution uploads to PAUSE (the Perl Authors Upload Server) into the project's release workflow.
Both extensions are distributed independently of Bootstrapper and can be installed and enabled on a per-project basis.
First, install the extension distribution using your preferred CPAN client. For example:
cpanm CPAN::Maker::Bootstrapper::Role::Example
Then, from the root of an existing Bootstrapper project, run:
make add-plugin PLUGIN=Example
The add-plugin target invokes cmb add-plugin, installs the
extension's project files, and runs make update.
The extension is registered beneath:
.roles/CPAN-Maker-Bootstrapper-Role-Example/
The directory contains a copy of the extension's role.yml manifest
and, when supplied, its role.mk file.
The extension's Perl modules remain installed in the Perl environment; they are not copied into the project.
If the extension is already registered, add-plugin refuses to
overwrite it. To replace the project-local extension files, use:
make add-plugin PLUGIN=Example FORCE=1
The existing files are backed up during installation and restored if validation fails.
The project-local .roles/ directory should be committed to version
control so that other developers receive the same extension
registration when checking out the project.
Those developers must also install the corresponding Perl extension distribution.
An extension can provide two complementary forms of functionality:
commands executed by cmb and Makefile targets executed by make.
role.ymlThe role.yml manifest is required. It declares the Perl roles and
commands supplied by the extension, along with any option specifications
and default option values.
The manifest is loaded by CLI::Simple, which integrates the
extension's commands and options with those provided by
CPAN::Maker::Bootstrapper.
When an extension is added to a project, add-plugin verifies that
role.yml exists and that its declared roles can be loaded.
Extension functionality is implemented in Perl roles composed using
Role::Tiny.
These roles are installed as part of the extension distribution.
CLI::Simple composes the roles associated with the selected command,
rather than loading every extension role for every invocation.
This allows extensions to introduce dependencies that are needed only when their commands are executed.
role.mkThe optional role.mk file supplies GNU Make recipes.
The generated project Makefile includes extension Makefiles using:
-include $(wildcard .roles/*/role.mk)
Consequently, targets defined in an extension's role.mk become
available to the project after the extension has been registered.
An extension that provides only cmb commands does not need a
role.mk file.
Run:
make help
to discover documented Makefile targets, including those contributed by extensions.
Extensions can participate in the project update lifecycle by adding
recipes to the pre-update and post-update targets.
The make update operation executes these phases in order:
pre-updateRuns extension or project recipes before Bootstrapper-managed files are refreshed.
Refreshes the project build infrastructure from the installed
CPAN::Maker::Bootstrapper distribution.
post-updateRuns extension or project recipes after the managed files have been updated.
An extension may augment either hook using GNU Make double-colon
rules in its role.mk:
pre-update::
@echo "Preparing extension update"
post-update::
@echo "Updating extension files"
Double-colon rules allow multiple extensions and the project itself to contribute recipes to the same targets without replacing one another's implementations.
Extensions are responsible for managing their own project files.
The presence of a role.mk file does not automatically make those
files part of Bootstrapper's managed-file inventory.
An extension can use post-update to install or refresh files
needed by its commands or Makefile targets.
Extension commands are registered through their role.yml
manifests. When cmb recognizes a command, CLI::Simple composes
the roles associated with that command using Role::Tiny.
Option specifications are registered globally across all loaded manifests. This is necessary because command-line arguments must be parsed before the selected command and its roles can be determined.
Extension authors should prefix option names with an identifier associated with their extension to avoid collisions with core options or options provided by other extensions.
For example, an extension named example might declare:
example-model=s
example-max-items=i
Option names and aliases must be unique across all registered
manifests. The prefix is a convention, not a requirement imposed by
CLI::Simple.
Extensions may define configuration settings in the Bootstrapper configuration file. Each extension should use a separate INI section corresponding to its option prefix.
For example:
[example]
model = standard
max-items = 25
identifier = "00123"
enabled = true
Bootstrapper combines the section name and configuration key to identify the corresponding option accessor, replacing hyphens with underscores.
The settings above map to the following accessors:
get_example_model() set_example_model()
get_example_max_items() set_example_max_items()
get_example_identifier() set_example_identifier()
get_example_enabled() set_example_enabled()
Configuration values are applied only when the corresponding setter exists and the option has not already been defined. Unrecognized settings are ignored.
Numeric values are converted to numbers, and the values true and
false are converted to boolean values. Numeric-looking strings can
be preserved by enclosing them in double quotes, as illustrated by
the identifier setting above. The surrounding quotes are removed
before the value is assigned.
The existing [cpan-maker] section is handled separately to preserve
its established configuration names and behavior.
Bootstrapper resolves option values according to the following precedence, from highest to lowest:
Values explicitly supplied on the command line take precedence over configuration settings and defaults.
Values defined in Bootstrapper's configuration file are applied when the corresponding option has not already been defined.
Default values declared in role.yml are applied to options that
remain undefined after initialization.
This precedence follows the CLI::Simple initialization lifecycle.
Bootstrapper loads configuration settings during init(), after
command-line parsing. CLI::Simple subsequently applies manifest
defaults only to options that remain undefined.
Consequently, extension authors can provide sensible defaults in
role.yml while allowing projects to override those defaults through
Bootstrapper's configuration file.
The installed Makefile and files under .includes/ are managed by
CPAN::Maker::Bootstrapper. They are intentionally write-protected and
may be replaced by make update when the bootstrapper is upgraded.
Project-specific build logic belongs in project.mk, which is always
writable and is never touched by make update. This provides an
upgrade-safe extension point for project-specific targets, variables,
and build ordering.
The managed include files live in the .includes/ directory,
where they are write-protected and clearly separated from project
files. The Makefile includes them automatically:
include .includes/bootstrap.mk
include .includes/perl.mk
include .includes/local.mk
include .includes/help.mk
include .includes/version.mk
include .includes/git.mk
include .includes/update.mk
include .includes/upgrade.mk
include .includes/bash-completion.mk
include .includes/modulino.mk
These files are included if they exist:
include config.mk
include project.mk
include extra-files.mk
The managed build system is deliberately immutable: the
Makefile, everything under .includes/, and the generated
.pm/.pl files are write-protected on purpose. This is a feature,
not a restriction. It lets make update replace those files with
newer, better versions without clobbering anything of yours, and it
guarantees that two projects on the same bootstrapper version use the
same managed build rules -- there is no per-project drift hiding in a
locally edited managed rule.
You can override any of it -- these are your files, and nothing stops you
from chmod +w and editing a generated module or a managed include. But
you should not need to, and if you do, make update will overwrite your
change. Every legitimate customization has a sanctioned hook that survives
make update:
Project-specific targets, rules, and build ordering --
project.mk (always writable; never touched by make update).
Reach for project.mk when your project needs to do something the
generic build system can't know about, for example:
.pm.in from a JSON/YAML schema, render documentation, compile
assets, or (as Amazon::API does) build a Storable data file consumed at
runtime.lib/Foo/Bar.pm: lib/Foo.pm when one module must be built before another.scp/upload/notify target that runs
after all.clean-local:: double-colon rule to remove
your own generated files, and CLEANFILES += for anything else. See "What Belongs in project.mk" for worked examples of each, and
"What Does NOT Belong in project.mk" for the line between your extensions
and the managed core.
Build-behavior toggles (dependency scanning, linting, syntax
checking, version-drift strictness) -- make variables set on the command
line or in config.mk (see "CONFIGURATION" and the variable list below).
TEMPLATE_VARS and let cmb resolve-vars fill them, rather than
hand-editing a generated .pm (see "Custom Template Tokens").buildspec.yml;
extra-files.mk wires them into the tarball automatically.+ prefix in
requires (see "Dependencies Management").If you find yourself wanting to edit a managed file, check this list first: the hook you need almost certainly exists, and using it keeps you on the upgrade path instead of forking the build system.
Why the generated .pm/.pl files are read-only: they are
regenerated from their .pm.in/.pl.in sources when its
prerequisites require regeneration, so any edit you make directly to a
generated .pm would be silently lost on the next make. The
chmod -w is there to stop you from making that mistake. Edit the
.pm.in source, not the generated .pm.
The installed Makefile is structured around a few key concepts:
lib/ as .pm.in and in bin/ as
.pl.in. The build generates the final .pm and .pl files from
these sources by substituting @PACKAGE_VERSION@ and other
tokens, running syntax checks, and optionally running perltidy and
perlcritic..checked records successful syntax
and validation checks, .tdy records successful perltidy processing,
and .crit records successful perlcritic processing. Each sentinel is
regenerated only when the source or the prerequisites for that gate
change.scandeps-static.pl scans your
source files and maintains the dependency files used by CPAN::Maker
when generating distribution metadata, including requires,
test-requires, recommends, and suggests. Controlled by
SCAN=ON|OFF.make.
It is built by make-cpan-dist.pl using buildspec.yml.deps.mk so make can build
modules in dependency order before syntax checking them. Dependencies
that cannot be inferred automatically may be added in project.mk.Key build variables you can override on the make command line or in
config.mk:
SCAN=OFF - skip distribution dependency scanningLINT=OFF - skip perltidy and perlcriticSYNTAX_CHECKING=OFF - skip perl -wc syntax checksMIN_PERL_VERSION=5.016 - minimum Perl version for Makefile.PLPERLTIDYRC=/path/to/rc - path to perltidy configurationPERLCRITICRC=/path/to/rc - path to perlcritic configurationSKIP_TESTS=1 - interpreted by CPAN::Maker; skips running
the test suite when building the distribution tarballPERLWC_SKIP="file1 file2" - space-separated list of files
to exclude from syntax and POD checksPOD=extract|remove - extract POD to a companion .pod file
or strip it entirely from the built .pmPERLINCLUDE="-I path" - additional include paths used during
the perl -wc syntax check. Defaults to -I lib -I local/lib/perl5
for hermetic checking; see "The local dependency library".CPAN_INSTALLER=cpm|carton - selects the installer used to
populate local/ for hermetic syntax checking. Auto-detected (cpm
preferred) if unset.Two further toggles, CMB_UPDATE_CHECK and CMB_VERSION_DRIFT, are set
in config.mk rather than on the command line; see "Automatic Drift and
Update Checks". config.mk is read on every invocation of make and is
the right place for durable, machine- or project-wide build settings such as
SYNTAX_CHECKING=OFF on a box without an installer.
project.mkCustom targets
Any target specific to your project - generating assets, running linters, deploying, sending notifications:
.PHONY: deploy
deploy: all ## deploy the distribution
scp $(TARBALL) user@myserver:/opt/cpan
Add ## followed by a description to a target definition to include
the target in the output from make help. Because project.mk is
included in MAKEFILE_LIST, project-specific targets are discovered
automatically:
make help
There is no separate help table to maintain.
Inter-module dependencies
If your modules have build-time dependencies on each other, declare them here rather than modifying the Makefile:
lib/Foo/Bar.pm: lib/Foo.pm
Additional file generation
If your project generates code or configuration from templates beyond what the standard Makefile handles:
lib/Foo/Generated.pm.in: schema/foo.json
perl bin/generate-module.pl $< > $@
Project-specific variables
DEPLOY_HOST = myserver.example.com
DEPLOY_PATH = /opt/cpan/incoming
Extending CLEANFILES
Add project-specific generated files to the cleanup target by
appending to CLEANFILES:
CLEANFILES += mygenerated.pm config/generated.yml
Extending the clean target
clean-local::
rm -rf workdir
Extending the test recipe
Projects may have development or integration tests that should not be
included in the CPAN distribution. Add them to make test by
extending test-local with a double-colon rule:
test-local::
prove -I lib -v xt/
project.mkall, clean, requiresReplacing managed variables such as DEPS or CLEANFILES.
Use documented extension points such as CLEANFILES += where
provided rather than redefining the managed value.
Anything that duplicates logic already in the managed Makefile
The build generates each .pm/.pl from its .pm.in/.pl.in source
by substituting @TOKEN@ placeholders through cmb resolve-vars.
The standard tokens (@PACKAGE_VERSION@, @MODULE_NAME@,
@GIT_SHA@, and the other git-metadata variables) are always
available, but the mechanism is extensible: a project can define its own
tokens without editing any managed file.
To add a token, declare its name in TEMPLATE_VARS (in
project.mk) and provide a value -- as a make variable, an exported
environment variable, or through the variables file passed to
resolve-vars. For example, to stamp a build timestamp:
# in project.mk
BUILD_DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
TEMPLATE_VARS += BUILD_DATE
# in a .pm.in source
our $BUILD_DATE = 'E<64>BUILD_DATEE<64>';
cmb resolve-vars then fills @BUILD_DATE@ from the value
when its prerequisites require regeneration. The token grammar is
uppercase-only (@[A-Z0-9_]+@), so placeholders never
collide with real Perl such as @_ or @ISA.
By default, substitution is fail-loud for placeholders that appear in live code: if a token has no value, the build stops and names the offending token rather than silently substituting an empty string.
Placeholders that occur only in POD or comments do not require values.
--no-strict may be used to downgrade a missing live-code value to a
warning and leave the placeholder unchanged.
The following targets manage the lifecycle of the build system itself:
make check-upgrade / make upgrade-check
Checks MetaCPAN to see if a newer version of
CPAN::Maker::Bootstrapper is available.
make publish
Builds the distribution tarball, unpacks it into a temporary directory,
runs its normal Makefile.PL, build, and test sequence, and uploads
the tarball to PAUSE if all checks succeed.
Set the PAUSE credentials with:
make publish PAUSE_USER=username PAUSE_PASSWORD=password
make upgrade
Checks MetaCPAN, installs the latest version via cpanm, then
automatically runs make update to refresh the managed project
files.
make update
Copies the managed files from the currently installed bootstrapper
distribution into your project directory. After running, use
git diff to review what changed.
The following files are managed and may be updated:
Makefile
.includes/bootstrap.mk
.includes/perl.mk
.includes/local.mk
.includes/git.mk
.includes/help.mk
.includes/update.mk
.includes/upgrade.mk
.includes/version.mk
.includes/bash-completion.mk
.includes/modulino.mk
.includes/publish.mk
Your project.mk, buildspec.yml, requires, VERSION, source
files and tests are never touched by make update.
make cpanm
Installs cpanminus if it is not already available on your
PATH. Required for make upgrade to work:
make cpanm && make upgrade
Every build runs two checks before proceeding, so you don't have to
remember to run make check-upgrade yourself:
CPAN::Maker::Bootstrapper published on CPAN than the
one installed on this machine?CPAN::Maker::Bootstrapper would produce?These are independent questions - your installed bootstrapper can be
fully current while a given project has still drifted from it (most
commonly because the project hasn't been through make update
since you last upgraded), or your bootstrapper itself can be behind
CPAN while every project stays perfectly in sync with it.
Drift can happen for either of two reasons: your installed
CPAN::Maker::Bootstrapper was upgraded since this project last ran
make update, or a managed file was hand-edited despite the
warnings not to (see "What You Should Never Modify"). make
doesn't try to tell these apart - the fix is the same either way:
make update
Two variables, set in config.mk, control how strict these checks
are:
CMB_UPDATE_CHECK (ON|OFF, default ON)
Set to OFF to skip the MetaCPAN lookup - useful in CI or offline
environments where the network call would just fail or slow things
down.
CMB_VERSION_DRIFT (FAIL|WARN|IGNORE, default FAIL)
Controls what happens when a project's managed files no longer match
the installed bootstrapper. FAIL stops the build until you run
make update; WARN prints a message and continues; IGNORE
skips the check entirely.
The files in .includes/ - perl.mk, git.mk, help.mk etc. -
are managed files that will be overwritten by make update. Do not
modify managed files directly. Use config.mk for documented build
variables and project.mk for project-specific targets, rules, and
build ordering.
The Makefile itself is also managed and will be overwritten by
make update. Use the documented project-level configuration and
extension files instead.
The build system scans .pm.in and .pl.in source files and
maintains the dependency files used by CPAN::Maker when generating
distribution metadata:
F<requires>
F<test-requires>
F<recommends>
F<suggests>
Distribution dependency scanning is controlled by SCAN. Set
SCAN=OFF to skip updates to requires, test-requires,
recommends, and suggests for a build; the default is ON.
To prevent an entry from being removed by a rescan, prefix the module
name with +. These entries are sticky and survive all subsequent
scans even if the scanner no longer detects them. To pin a specific
version, simply edit the version number in the requires file. If
the scanner subsequently detects a different version, the Makefile
will preserve your pinned version. Note that pinned versions are
never updated automatically - if you want to adopt a newer version
you must edit the file manually.
In your requires file:
+Foo::Bar 1.0 # sticky - survives all rescans
Baz::Qux 2.5 # version pinned - scanner won't override this version
Note: These two mechanisms are independent - + controls whether an entry
survives rescans, while the version number controls what version is
required.
Syntax checking is performed in a hermetic environment: each
generated .pm is compiled with perl -wc against lib and a
project-local library at local/lib/perl5 only, with PERL5LIB
explicitly cleared for the check. This ensures a module's dependencies
are actually declared and installed, rather than being satisfied by
chance from whatever happens to be in your PERL5LIB or system
@INC. A dependency that compiles on your machine but is missing
from the declared dependency set will fail the build here instead of
surprising you on a clean install or CI box.
To populate that library, the build installs your declared dependencies into
local/ using cpm (preferred) or carton:
make local # installs requires/recommends/suggests/test-requires into local/lib/perl5
This runs automatically as a prerequisite of the module build, so a
normal make installs dependencies first, then syntax-checks against
them.
cpm is preferred because it supports multiple resolvers directly
from build-mirrors. carton is also supported; see
"build-mirrors" for its mirror behavior.
The use of a build-mirrors file versus specifying the mirrors in
the dependency files determines the scope of their
use. build-mirrors (and cpanfile mirror declarations) set the
resolvers used for every dependency in the build. To route a
single module to a specific mirror, URL, or distribution -- without
affecting how anything else resolves -- annotate that module's entry
in requires with mirror=, url=, or dist= (see
"build-mirrors").
The + prefix has a second use beyond protecting mirror annotations.
Because the check is hermetic, a module reached only at runtime will not
be found unless it is declared. Static scanning (scandeps-static.pl)
cannot see a dependency loaded dynamically -- for example a module pulled
in through a method call rather than a use statement -- so it will never
add it to requires. Declare such modules explicitly with +, which
makes the entry sticky and survives every rescan:
+Log::Log4perl # loaded at runtime via a framework call; scanner can't see it
build-mirrorsWhen using the preferred CPAN installer (cpm), the build system
reads mirror URLs, one per line, from a build-mirrors file in the
project root and passes each as a resolver. This allows the build to
resolve dependencies against one or more configured CPAN-compatible
repositories, including private DarkPAN repositories.
When carton is used, because it does not support multiple mirrors
when setting the mirror using an environment variable, the build
system will use the first mirror in your build-mirrors file if
present. carton supports multiple mirrors only by specifying them
in the cpanfile.
Entries in requires, suggests, and recommends may carry optional
qualifiers after the version to control where a module resolves from:
+Foo::Bar 1.0 mirror=https://cpan.openbedrock.net/orepan2
+Baz::Qux 2.5 url=https://example.com/authors/id/D/DU/DUMMY/Baz-Qux-2.5.tar.gz
+Xyz::Abc 0.9 dist=Xyz-Distribution
Only dist=, url=, and mirror= are permitted after the version;
anything else is an error. Prefix such entries with the sticky + so the
scanner does not strip the annotation on a later rescan. See CPAN::Maker
for the full format.
A modulino is a Perl module that doubles as a runnable script by checking whether it was invoked directly or loaded as a library:
package Foo::Bar;
caller or __PACKAGE__->main;
sub main {
...
exit 0;
}
Modulinos are useful for CLI scripts because they encourage encapsulation, simplify unit testing, and keep logic organized in named methods rather than inline code.
The Makefile provides a modulino target that generates a wrapper
script for invoking your module. By default it uses MODULE_NAME,
producing a script named after the module:
make modulino
For a project named Foo::Bar this creates bin/foo-bar.in.
make then builds bin/foo-bar from that source file via a
pattern rule, and the executable ends up in the distribution.
To create a modulino wrapper for a module other than the primary
project module, override MODULE_NAME:
make modulino MODULE_NAME=Foo::Bar::Buz
This creates bin/foo-bar-buz.in invoking Foo::Bar::Buz.
To give the wrapper a short or memorable name independent of the
module name, set ALIAS:
make modulino MODULE_NAME=Foo::Bar::Buz ALIAS=fbb
This creates bin/fbb.in which still invokes Foo::Bar::Buz.
ALIAS accepts either a plain name (fbb) or a module-style
name (Foo::Bar::Buz) - colons are converted to hyphens and
the result is lowercased.
The generated wrapper scripts (without the .in suffix) are
automatically added to .gitignore since they are build artifacts.
The .in source files are tracked by git.
CPAN::Maker::Bootstrapper provides a clean-room build path that can be used locally or from a CI system.
The CI design separates source acquisition from project build responsibilities:
source acquisition caller or CI system
build environment builder
project build make
A CI system such as GitHub Actions is responsible for checking out the
project. builder then operates on that existing project directory,
installs the required build environment and dependencies, and runs the
project build.
make build-ci provides the corresponding local clean-room build. It
uses the current working tree as its source, copies that tree into a
disposable container build directory, and invokes builder there.
This separation keeps builder independent of repository hosting,
branch selection, and source-control workflow while allowing the same
build mechanism to be used both locally and in CI.
The build lifecycle is:
builder.env
|
v
builder-pre
|
v
make
|
v
builder-post
builder can run unmodified in GitHub Actions, in another CI runner,
or by hand from the command line.
builder operates on an existing project directory. Source acquisition
is deliberately outside its responsibility; the caller must clone,
check out, or otherwise provide the project before invoking builder.
Run it from the root of a project:
./builder
or pass the project directory explicitly:
/builder /path/to/project
The project directory defaults to the current working directory.
builder changes to that directory, loads the project CI environment,
resolves and installs the required build dependencies, and runs the
configured build lifecycle.
For the standard containerized clean-room build, use:
make build-ci
make build-ci copies the current working tree into a disposable build
environment and invokes builder there. Because it operates on the
current working tree rather than cloning the repository, the build may
include uncommitted and untracked files present on disk.
builder accepts environment variables that control dependency
installation and build behavior. These values may also be set in
builder.env.
INSTALLER
The command used to install Perl dependencies.
The default is:
cpm install -g --show-build-log-on-failure --verbose
SCAN
Controls dependency scanning during the project build.
The builder default is:
SCAN=on
SYNTAX_CHECKING
Controls Perl syntax checking during the project build.
The builder does not enable syntax checking by default. To use syntax
checking as a CI quality gate, add the following to builder.env:
SYNTAX_CHECKING=on
LINT
Controls the perltidy and perlcritic quality gates.
The builder does not enable linting by default. To enable configured lint tools during CI, add:
LINT=on
to builder.env.
PERLTIDYRC
Specifies the perltidy profile used when linting is enabled.
If PERLTIDYRC is not defined, builder looks for .perltidyrc
or perltidyrc in the project. An explicitly supplied value is not
overwritten by profile discovery.
When a profile is configured and LINT=on, builder installs
Perl::Tidy before running the project build.
If PERLTIDYRC names a file that does not exist, the build fails.
PERLCRITICRC
Specifies the perlcritic profile used when linting is enabled.
If PERLCRITICRC is not defined, builder looks for
.perlcriticrc or perlcriticrc in the project. An explicitly
supplied value is not overwritten by profile discovery.
When a profile is configured and LINT=on, builder installs
Perl::Critic and the supporting policy modules required by the
managed build.
If PERLCRITICRC names a file that does not exist, the build fails.
Setting LINT=on without either a perltidy or perlcritic profile is
treated as a configuration error.
NO_ECHO
Passed through to the generated Makefile when set.
CMB_VERSION_DRIFT
Controls how builder handles differences between the installed
CPAN::Maker::Bootstrapper version and the version expected by the
project.
The generated builder.env defaults this to:
CMB_VERSION_DRIFT=ignore
builder.envBefore installing project build dependencies or running the project
build, builder loads builder.env from the project root when that
file exists.
Variables defined there are exported to the build environment and are used when resolving the effective CI build policy.
A generated project includes:
CMB_VERSION_DRIFT=ignore
NO_ECHO=
The default builder performs a clean distribution build but does not
enable syntax checking or linting as CI phase gates. Projects that want
those checks may opt in through builder.env.
For example:
SYNTAX_CHECKING=on
LINT=on
When linting is enabled, provide a perltidyrc, .perltidyrc,
perlcriticrc, or .perlcriticrc for each lint tool the CI build
should run. The corresponding tool dependencies are installed by
builder before make is invoked.
builder.env therefore provides a project-local place to define CI
policy without modifying builder itself.
builder exposes two Makefile hooks around the main project build:
builder-pre
builder-post
The build lifecycle is:
builder.env
|
builder-pre
|
make
|
builder-post
builder-pre runs after builder.env has been loaded and before the
main make invocation.
builder-post runs only after the main build completes successfully.
Generated projects define both targets as empty double-colon targets:
builder-pre::
builder-post::
Projects may extend them in project.mk without modifying the managed
Makefile.
For example:
builder-pre::
./prepare-ci-environment
builder-post::
./collect-build-artifacts
These hooks are intended for project-specific CI setup and post-build work that should remain outside the managed build files.
make build-cimake build-ci runs the current project in a disposable containerized
build environment.
Unlike a CI workflow that clones the repository, build-ci uses the
current working tree as its source. The project is mounted read-only,
copied into the container build area, and then passed to builder.
This means the build reflects the files currently present on disk, including uncommitted changes and untracked files.
The source tree itself is not modified by the container build.
make build-ci accepts the following variables:
DOCKER_BUILD_IMAGE - container image used for the build
DOCKER_CPAN_INSTALLER
- dependency installer command used in the container
BUILD_LOG - path used for captured build output
MODULE_NAME - primary module name passed into the clean-room build
For example:
make build-ci
or:
make build-ci DOCKER_BUILD_IMAGE=debian:trixie
The command exits with the status of the container build even though
the output is also written to BUILD_LOG.
builder recognizes project files that supply additional build
requirements without modifying builder itself.
build-apt-deps
A whitespace-separated list of additional Debian packages required by the project build.
builder installs these packages in addition to its standard build
environment before installing Perl dependencies.
For example:
libxml2-dev
libpq-dev
build-mirrors
A list of CPAN mirror URLs, one per line.
Use this file when the build requires a DarkPAN or another additional CPAN-compatible repository.
For example:
https://cpan.openbedrock.net
https://cpan.metacpan.org
The configured mirrors are used when resolving project dependencies.
These files describe build inputs. For environment variables use
builder.env; for project-specific Makefile behavior use
project.mk.
perlcritic and perltidy GatesDuring a CI build, the build script enables PERLTIDY and
PERLCRITIC when it can find the corresponding configuration files
in the project.
For reproducible CI builds, keep the project's perltidyrc and
perlcriticrc aligned with the configuration used in your development
environment.
Many editors and IDEs run Perltidy or Perl::Critic automatically while
you work. If those tools use a personal configuration that differs from
the project's checked-in configuration, code that appears clean locally
may fail during build-ci.
When a project-local perltidyrc or perlcriticrc is present,
build-ci uses it to enforce the project's formatting and critic
policies in the clean build environment. If no project-local
configuration can be discovered, the corresponding check is disabled
rather than falling back to the tool's default configuration.
"make workflow", "make build-ci"
The following tool(s) must be on your PATH:
git - used to read global identity configmake - GNU make is required to build the projectcurl - used by make upgrade to query MetaCPANcpm or carton - installs declared dependencies into a
project-local library (local/lib/perl5) for hermetic syntax checkingYou can set make variables like SYNTAX_CHECKING in config.mk, which
is included on every invocation of make, to alter build behavior -- for
example SYNTAX_CHECKING=OFF to skip the check when neither installer is
present (undeclared dependencies then go undetected).
Note: neither cpm nor carton is a hard prerequisite of
CPAN::Maker::Bootstrapper itself; they are needed only to populate the
local library for hermetic checking. cpm is preferred for its
multi-mirror support (see "build-mirrors").
.pm and .pl Generation
Generated .pm and .pl files are derived from their
.pm.in/.pl.in sources through cmb resolve-vars and are
read-only. Always edit the .in source.
Use @PACKAGE_VERSION@ like this:
our $VERSION ='``@PACKAGE_VERSION@``';
The import feature cannot be used with --stub
git
git is used throughout the framework. make git initializes the
repository and creates the initial commit, and the Bootstrapper reads
user identity and related defaults from your configuration file,
your local .git/config or the global git configuration.
checking
There are several common causes.
One possible cause is an inter-module build-order dependency. The build
system normally detects dependencies between modules in the distribution
and writes them to deps.mk, allowing make to build prerequisite
modules before syntax-checking modules that depend on them.
For example, if lib/Foo/Bar.pm uses lib/Foo.pm, the generated
dependency rules ensure that Foo.pm is built first.
If the dependency cannot be inferred automatically, declare it explicitly
in project.mk:
lib/Foo/Bar.pm: lib/Foo.pm
See "Inter-module dependencies" for details.
Another cause is a real dependency that is not installed in
local/. Because syntax checking runs against local/lib/perl5
with PERL5LIB cleared, a dependency that is present elsewhere on
your system but not declared will fail here.
Confirm it is in requires (add it with a sticky + if the scanner
can't see it -- see "Dependencies Management"), then make local
to install it. This is the check working as intended: it catches a
missing declaration on your machine instead of on someone else's.
If the module genuinely cannot be loaded outside its runtime
environment (an Apache handler, a mod_perl module, etc.), add it to
PERLWC_SKIP in project.mk:
PERLWC_SKIP = lib/My/Apache/Handler.pm
Files listed in PERLWC_SKIP are excluded from the perl -wc
syntax-checking and POD-checking stages. They are still built and
included in the distribution; only those validation steps are skipped.
make quick
This disables distribution dependency scanning and all linting
(perltidy, perlcritic) for the current build. requires,
test-requires, recommends, and suggests are not updated.
Syntax checking remains enabled.
Use make without flags when you are
ready to do a full build before committing or releasing.
You can also disable individual features:
make SCAN=OFF # skip distribution dependency scanning only
make LINT=OFF # skip all linting only
make SYNTAX_CHECKING=OFF # skip syntax checking only
The bootstrapper generates build-config.mk using the tools
and configuration available in the current build environment.
This provides a usable starting configuration without requiring
the developer to specify every build setting.
However, discovered settings may differ between machines. For example, developers may have different perltidy or perlcritic configuration files, or different tools installed.
config.mk allows the project to override these discovered
settings and establish a consistent build policy.
For example:
SCAN = ON
LINT = ON
SYNTAX_CHECKING = ON
PERLTIDYRC = .perltidyrc
PERLCRITICRC = .perlcriticrc
Commit config.mk and any referenced configuration files
to source control so that developers and CI use the same
project-defined settings.
build-config.mk, on the other hand, is generated for the
current environment and should not be committed.
Create the source file with the .pm.in or .pl.in extension in
the appropriate directory:
lib/My/New/Module.pm.in
bin/my-script.pl.in
The build system discovers them automatically via find-files - no
changes to the Makefile are required. The next make will include
them in the dependency scan and the distribution.
Edit buildspec.yml and add entries to the extra-files section:
extra-files:
- ChangeLog
- README.md
- share:
- my-config-template.yml
- my-data-file.json
Files listed under share: are installed into the distribution's
share directory and can be accessed at runtime via
File::ShareDir.
The build verifies that files listed in extra-files are tracked by
git. This helps catch files that have been added to the distribution
but accidentally omitted from the project repository.
Some extra files are generated build artifacts and therefore should
not be committed to the repository. Add those files to
extra-files.skip, one file per line:
generated/service-data.dat
share/generated-index.json
Blank lines and lines beginning with # are ignored.
extra-files.skip only disables the git tracking check for those
files. The files remain part of the distribution and continue to be
included as dependencies when determining whether the distribution
tarball must be rebuilt.
When DARKPAN_REQUIRES is enabled, cpanfile.darkpan and
cpanm.darkpan are automatically added to buildspec.yml as extra
files. They are therefore subject to the normal git tracking check. If
the developer wants to include these generated manifests in the
distribution without tracking them in the repository, they may be
added explicitly to extra-files.skip. See "DARKPAN_REQUIRES".
Edit requires directly. Prefix the module name with + to make
the entry sticky - it will survive all subsequent rescans even if the
scanner no longer detects it:
+My::Required::Module 1.5
To pin a version without making the entry sticky, just set the version number. The scanner will preserve your version if it detects a different one on subsequent builds:
Some::Module 2.0
These two mechanisms are independent - + controls survivability,
the version number controls what version is required. See "Dependencies Management"
for full details.
Create a requires.skip file in the project root with one module
name per line:
My::Own::Module
Some::Transitive::Dep
The scanner will never add these to requires. Use
test-requires.skip for the same effect on test dependencies.
Note that on a clean first build neither skip file has any effect
since there is no prior requires file to compare against. The skip
list takes effect from the second build onward.
The .pm files in lib/ are generated from the .pm.in sources
and are write-protected. Always edit the .pm.in file - the .pm
is regenerated when its prerequisites require regeneration and your
changes will be lost.
If you are unsure which file to edit:
ls -l lib/My/Module.pm lib/My/Module.pm.in
The .pm.in file is the one you own.
This means your project's managed files (Makefile,
.includes/*.mk) no longer match what your currently installed
CPAN::Maker::Bootstrapper would generate. There are two ways this
happens - upgrading your bootstrapper (e.g. via cpanm
\--upgrade-all) instantly "drifts" every project you haven't yet
updated, or a managed file was edited by hand. Both are fixed the
same way:
make update
If you don't want a drifted project to fail the build outright, set
CMB_VERSION_DRIFT=WARN (or =IGNORE) in that project's
config.mk. See "Automatic Drift and Update Checks".
The managed files in .includes/ should never be edited directly.
Use config.mk, project.mk, or the other documented project-level
extension points instead.
This is why make git and committing your .includes/ directory is
strongly recommended - git is your safety net for the entire build
system.
make says nothing to do but my source changedThe most common cause is that the generated .pm file is newer than
the .pm.in source. This can happen if you accidentally edited the
.pm directly or if file timestamps got out of sync. Force a rebuild:
touch lib/My/Module.pm.in
Or do a clean rebuild:
make clean && make
make SCAN=OFF
This skips distribution dependency scanning for that run, so
requires, test-requires, recommends, and suggests are not
updated.
Inter-module dependency discovery for deps.mk is independent of
SCAN and may still run when syntax checking is enabled. The default
is SCAN=ON.
make SYNTAX_CHECKING=OFF
Similarly you can disable individual quality gates:
make PERLTIDY="" PERLCRITIC=""
make upgrade
This checks MetaCPAN for a newer version of
CPAN::Maker::Bootstrapper, installs it via cpanm, and
automatically refreshes the managed files in .includes/ with
make update. Review the changes with git diff.
If cpanm is not installed:
make cpanm && make upgrade
Create the script in bin/ with a .sh.in extension:
bin/my-script.sh.in
The build system will process it through the standard token
substitution (replacing @PACKAGE_VERSION@ and
@MODULE_NAME@), make it executable, and include it in the
distribution automatically.
If your script is more than a few lines of bash, consider writing it as a modulino instead - a Perl module that doubles as a runnable script. Modulinos are easier to test, encourage encapsulation, and give you the full power of Perl and CPAN. The build system has first-class support for them:
make modulino
This generates a bash wrapper in bin/ that invokes your module as
a script if it uses the modulino pattern:
caller or __PACKAGE__->main;
See "MODULINOS" for full details.
When you package your CPAN distribution you can strip the pod from
your modules or you can extract the pod and provide them as separate
.pod files. The POD make variable controls that behavior:
make POD=extract
extract will strip POD from your module and create a .pod file
containing the stripped POD that will be added to your distribution.
make POD=remove
remove will strip POD from your module. No POD will be included in
the distribution.
...use_log4perl() requires Log::Log4perl...
If you are using CLI::Simple with its built-in logging support based
on Log::Log4perl, you probably have something similar to this in
your code:
__PACKAGE__->use_log4perl({ level => 'info' });
This requires Log::Log4perl to be installed at runtime.
CPAN::Maker::Bootstrapper scans your project to discover
dependencies and installs them into an isolated build environment for
syntax checking. This prevents modules installed in the developer's
local environment from masking undeclared dependencies.
Because your code does not directly load Log::Log4perl, the
dependency scanner may not discover it. Furthermore, CLI::Simple
declares Log::Log4perl as an optional dependency, so it is not
automatically installed.
You can explicitly declare the dependency in either of two ways:
Add an appropriate dependency declaration near the call to
__PACKAGE__-use_log4perl()>
require Log::Log4perl;
or
use Log::Log4perl();
Add Log::Log4perl directly to your requires file:
+Log::Log4perl
The + prefix instructs CPAN::Maker::Bootstrapper to retain this
manually declared dependency when rescanning your project.
Note: The same approach applies to other required modules that the dependency scanner cannot discover. See "Dependencies Management".
First check the "FAQ" sections above - your issue may already be covered.
If you believe you have found a bug or want to request a feature, please open an issue on GitHub:
https://github.com/rlauer6/CPAN-Maker-Bootstrapper/issues
When reporting a bug please include:
CPAN::Maker::Bootstrapper (cmb --version
or perl -MCPAN::Maker::Bootstrapper -e 'print $CPAN::Maker::Bootstrapper::VERSION')make -n or make --debug=v if the issue is
build-relatedbuildspec.yml and project.mk if relevant (redact
any sensitive information)perl --version, make --version)Pull requests are welcome. The project follows the standard GitHub fork-and-PR workflow.
CPAN::Maker - the distribution builder driven by buildspec.yml
(includes make-cpan-dist.pl)
CLI::Simple - the CLI framework used by the bootstrapper itself and optionally by generated CLI module stubs
CPAN::Maker::Bootstrapper::ConfigReader - the config reader bundled with this distribution, available for use in your own tools.
Module::ScanDeps::Static - the static scanner used for CPAN
dependency discovery and for inter-module build-order discovery
through deps.mk.
The current runtime, build, test, recommended, and suggested dependencies are declared in the distribution metadata generated by CPAN::Maker.
See Makefile.PL, META.json, or META.yml in the distribution
for the authoritative dependency set.
Some optional features require additional dependencies only when those features are used.
This documentation refers to version 2.6.1
Rob Lauer - rlauer@treasurersbriefcase.com
Copyright 2026, Robert C. Lauer All right reserved.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.