/uudmap.h
/bitcount.h
+lib/inc/
lib/Win32.pm
lib/Win32API/
lib/Win32CORE.pm
cpan/MIME-Base64/t/unicode.t See whether MIME::Base64 works
cpan/MIME-Base64/t/warn.t See whether MIME::Base64 works
cpan/Module-Build/Changes Module::Build
+cpan/Module-Build/lib/inc/latest.pm Module::Build
+cpan/Module-Build/lib/inc/latest/private.pm Module::Build
cpan/Module-Build/lib/Module/Build/API.pod Module::Build
cpan/Module-Build/lib/Module/Build/Authoring.pod Module::Build
cpan/Module-Build/lib/Module/Build/Base.pm Module::Build
+cpan/Module-Build/lib/Module/Build/Bundling.pod Module::Build
cpan/Module-Build/lib/Module/Build/Compat.pm Module::Build
cpan/Module-Build/lib/Module/Build/Config.pm Module::Build
cpan/Module-Build/lib/Module/Build/Cookbook.pm Module::Build
cpan/Module-Build/lib/Module/Build/Version.pm Module::Build
cpan/Module-Build/lib/Module/Build/YAML.pm Module::Build
cpan/Module-Build/scripts/config_data Module::Build
+cpan/Module-Build/t/actions/installdeps.t Module::Build
cpan/Module-Build/t/add_property.t Module::Build
cpan/Module-Build/t/basic.t Module::Build
-cpan/Module-Build/t/bundled/Tie/CPHash.pm Module::Build.pm
+cpan/Module-Build/t/bundled/Tie/CPHash.pm Module::Build
+cpan/Module-Build/t/bundle_inc.t Module::Build
cpan/Module-Build/t/compat/exit.t Module::Build
cpan/Module-Build/t/compat.t Module::Build
-cpan/Module-Build/t/debug.t Module::Build tests
+cpan/Module-Build/t/debug.t Module::Build
cpan/Module-Build/t/destinations.t Module::Build
cpan/Module-Build/t/extend.t Module::Build
cpan/Module-Build/t/ext.t Module::Build
cpan/Module-Build/t/files.t Module::Build
cpan/Module-Build/t/help.t Module::Build
-cpan/Module-Build/t/install_extra_target.t Module::Build tests
+cpan/Module-Build/t/install_extra_target.t Module::Build
cpan/Module-Build/t/install.t Module::Build
cpan/Module-Build/t/lib/DistGen.pm Module::Build
cpan/Module-Build/t/lib/MBTest.pm Module::Build
cpan/Module-Build/t/manifypods.t Module::Build
-cpan/Module-Build/t/mbyaml.t Module::Build
cpan/Module-Build/t/metadata2.t Module::Build
cpan/Module-Build/t/metadata.t Module::Build
cpan/Module-Build/t/moduleinfo.t Module::Build
+cpan/Module-Build/t/mymeta.t Module::Build
cpan/Module-Build/t/new_from_context.t Module::Build
cpan/Module-Build/t/notes.t Module::Build
cpan/Module-Build/t/parents.t Module::Build
-cpan/Module-Build/t/PL_files.t Module::Build tests
+cpan/Module-Build/t/perl_mb_opt.t Module::Build
+cpan/Module-Build/t/PL_files.t Module::Build
cpan/Module-Build/t/pod_parser.t Module::Build
cpan/Module-Build/t/ppm.t Module::Build
+cpan/Module-Build/t/properties/module_name.t Module::Build
+cpan/Module-Build/t/properties/needs_compiler.t Module::Build
+cpan/Module-Build/t/properties/share_dir.t Module::Build
+cpan/Module-Build/t/README.pod Module::Build
+cpan/Module-Build/t/resume.t Module::Build
cpan/Module-Build/t/runthrough.t Module::Build
+cpan/Module-Build/t/sample.t Module::Build
cpan/Module-Build/t/script_dist.t Module::Build
cpan/Module-Build/t/test_file_exts.t Module::Build
cpan/Module-Build/t/test_types.t Module::Build
cpan/Module-Build/t/tilde.t Module::Build
cpan/Module-Build/t/use_tap_harness.t Module::Build
cpan/Module-Build/t/versions.t Module::Build
-cpan/Module-Build/t/write_default_maniskip.t Module::Build tests
+cpan/Module-Build/t/write_default_maniskip.t Module::Build
cpan/Module-Build/t/xs.t Module::Build
cpan/Module-Load-Conditional/lib/Module/Load/Conditional.pm Module::Conditional
cpan/Module-Load-Conditional/t/01_Module_Load_Conditional.t Module::Conditional tests
SIGNATURE THANKS TODO Todo VERSION WHATSNEW
);
-
+
# Each entry in the %Modules hash roughly represents a distribution,
# except in the case of CPAN=1, where it *exactly* represents a single
# CPAN distribution.
# Each key reprepresents a string prefix, with longest prefixes checked
# first. The first match causes that prefix to be replaced with the
# corresponding key. For example, with the following MAP:
-# {
+# {
# 'lib/' => 'lib/',
# '' => 'lib/Foo/',
# },
# these files are mapped as shown:
#
# README becomes lib/Foo/README
-# lib/Foo.pm becomes lib/Foo.pm
+# lib/Foo.pm becomes lib/Foo.pm
#
# The default is dependent on the type of module.
# For distributions which appear to be stored under ext/, it defaults to:
#
# otherwise, it's
#
-# {
+# {
# 'lib/' => 'lib/',
# '' => 'lib/Foo/Bar/',
# }
'Module::Build' =>
{
'MAINTAINER' => 'kwilliams',
- 'DISTRIBUTION' => 'DAGOLDEN/Module-Build-0.35.tar.gz',
+ 'DISTRIBUTION' => 'DAGOLDEN/Module-Build-0.35_08.tar.gz',
'FILES' => q[cpan/Module-Build],
- 'EXCLUDED' => [ qw{ t/par.t t/signature.t scripts/bundle.pl},
- qr!^contrib/! ],
+ 'EXCLUDED' => [ qw{ t/par.t t/signature.t },
+ qr!^contrib/!, qr!^devtools! ],
'CPAN' => 1,
'UPSTREAM' => 'cpan',
},
Revision history for Perl extension Module::Build.
+0.35_08 - Mon Nov 16 22:38:28 EST 2009
+
+ Bug fixes:
+
+ - Multiple tests were failing due to dependency problems. Author
+ dependencies have been largely removed from core 'requires' into
+ optional features. Feature prereq detection and messaging have been
+ expanded and bugs on older Perls have been removed.
+
+0.35_07 - Sat Nov 14 17:14:39 EST 2009
+
+ Bug fixes:
+
+ - Auto-detection of abstract and author fixed for mixed-case POD headers
+ (RT#51117) [David Wheeler]
+
+ - resume() was not restoring additions to @INC added in Build.PL
+ (RT#50145) [David Golden]
+
+ - When tarball paths are less than 100 characters, disables 'prefix'
+ mode of Archive::Tar for maximum compatibility (RT#50571) [David Golden]
+
+0.35_06 - Fri Nov 13 14:51:28 EST 2009
+
+ Enhancements:
+
+ - Added experimental inc/ bundling; see Module::Build::Bundling for
+ details. [David Golden and Eric Wilhelm]
+
+ - Clarified that 'apache' in the license attribute indicates the Apache
+ License 2.0 and added 'apache_1_1' for the older version of the license
+ (RT#50614) [David Golden]
+
+ Bug fixes:
+
+ - Merging 'requires' and 'build_requires' in Module::Build::Compat could
+ lead to duplicate PREREQ_PM entries; now the highest version is used
+ for PREREQ_PM. (RT#50948) [David Golden]
+
+ - Module::Build::Compat will now die with an error if advanced,
+ non-numeric prerequisites are given, as these are not supported by
+ ExtUtils::MakeMaker in PREREQ_PM [David Golden]
+
+ - Made MYMETA generation non-fatal if fields required for META.yml
+ are missing [David Golden]
+
+ - Added Pod::Simple to requirements for manpage support; avoids
+ problems if a user has a broken Pod::Man/Pod::Simple. (RT#50081)
+ [David Golden]
+
+ - Won't die if installed Pod::Readme is broken [David Golden]
+
+ Other:
+
+ - Fixed Module::Build::Notes POD [David Golden]
+
+ - Some commands had become silent by default, so added a few short status
+ messages so users know something actually happened [David Golden]
+
+ - Cleaned up Changes file formatting [David Golden]
+
+ - Removed most PERL_CORE customizations from test files due to
+ reorganization of dual-life modules in core (RT#49522) [David Golden]
+
+0.35_05 - Wed Oct 28 17:20:59 EDT 2009
+
+ Bug fixes:
+
+ - Fix test failure in t/actions/installdeps.t when $^X is not the default
+ perl [David Golden]
+
+ - Work around $VERSION numbers in ActiveState with multiple underscores
+ that prevent Module::Build from installing on Win32 [David Golden]
+
+ - Fix bug cleaning compatibility Makefile when older ExtUtils::Manifest is
+ installed [David Golden with help from David Cantrell]
+
+ Other:
+
+ - Suppressed more warnings from tests [David Golden]
+
+ - Add provisional support for 'package NAME VERSION' syntax added in
+ Perl 5.11.1 [David Golden]
+
+0.35_04 - Fri Oct 23 11:20:41 EDT 2009
+
+ Bug fixes:
+
+ - Fix test failure if IPC::Cmd isn't installed [David Golden]
+
+ Other:
+
+ - Suppressed warning messages from various tests [David Golden]
+
+0.35_03 - Wed Oct 21 21:20:59 EDT 2009
+
+ *** API CHANGE ***
+
+ - The prepare_metadata() method used to take a YAML::Node object as an
+ argument for modification. The method now takes no arguments and just
+ returns a hash reference of metadata. [David Golden]
+
+ Enhancements
+
+ - Command line options may be set via the PERL_MB_OPT environment
+ variable (similar to PERL_MM_OPT in ExtUtils::MakeMaker)
+
+ Bug fixes:
+
+ - Updated PPM generation to PPM v4 (RT#49600) [Olivier Mengue]
+
+ - When c_source is specified, the directory scan will include additional,
+ less-common C++ extensions (RT49298) [David Golden]
+
+ - When module_name is not supplied, no packlist was being written; fixed
+ by guessing module_name from dist_version_from or the directory name
+ (just like ExtUtils::Manifest does without NAME) [David Golden]
+
+ - Bumped IO::File prereq to fix binmode failures in PPMMaker on Perl
+ prior to 5.8.8 [David Golden]
+
+ Other:
+
+ - Replaced use of YAML.pm with YAML::Tiny; Module::Build::YAML is now
+ based on YAML::Tiny as well [David Golden]
+
+ - Reduced amount of console output under normal operation (use --verbose
+ to see all output) [David Golden]
+
+0.35_02 - Mon Sep 7 22:37:42 EDT 2009
+
+ Enhancements:
+
+ - Added 'needs_compiler' property. Defaults to true if XS or c_source
+ exist. If true, ExtUtils::CBuilder is also added to build_requires.
+ [David Golden]
+
+ - File::ShareDir automatically added to 'requires' if 'share_dir' is set
+ [David Golden]
+
+ - Added 'Build installdeps' action to install needed dependencies via
+ a user-configurable command line program. (Defaults to 'cpan'.)
+ [Eric Wilhelm]
+
+ Bug fixes:
+
+ - Failure to detect a compiler will now warn during Build.PL and be a
+ fatal error when trying to compile during Build. (RT#48918) [David
+ Golden]
+
+ - Fixed directory sorting failure in share_dir.t [David Golden]
+
+ - Property defaults that are data structures were being assigned as
+ references to new objects. Changed so that defaults are cloned instead.
+ (This mostly affects testing, which often creates multiple objects in the
+ same process) [David Golden]
+
+ - Simplified error message on exit under use_tap_harness [suggested by
+ David Wheeler]
+
+ - Fixed typemap search to use a dist-level typemap if a typemap is not
+ found in the directory with the *.xs file; (was manifesting as warnings
+ in Perl 5.6 tests) [David Golden]
+
+ Other:
+
+ - Replaced guts of new_from_context(). Build.PL is now executed in a
+ separate process before resume() is called. (This is generally only of
+ interest to Module::Build or toolchain developers) (RT#49350) [David
+ Golden, Eric Wilhelm, Ken Williams]
+
+ - Revised test helper classes to fix potential bugs and add new features
+ to make writing tests simpler and easier. Changes incorporated into
+ t/README.pod and t/sample.t as examples for new testing. [David Golden]
+
+0.35_01 - Mon Aug 31 12:11:10 EDT 2009
+
+ Enhancements:
+
+ - Generates MYMETA.yml during Build.PL (new standard protocol for
+ communicating configuration results between toolchain components)
+ [David Golden]
+
+ - Added 'share_dir' property to provide File::ShareDir support;
+ set automatically if a directory called 'share' exists
+ [David Golden]
+
+ Bug fixes:
+
+ - Fix the t/destinations.t fix. [David Golden, with thanks to Eric Wilhelm]
+
+ - Fix recursive test files in generated Makefile.PL (RT#49254) [Sawyer X]
+
+ - Guard against trying :utf8 when :utf8 isn't available
+
+ - The "test" action now dies when using the 'use_tap_harness'
+ option and tests fail, matching the behavior under Test::Harness.
+ (RT#49080) [initial patch from David Wheeler; revised by David Golden]
+
+ Other:
+
+ - Added t/README.pod and t/sample.t to guide developers writing new tests
+ [David Golden, with some code from Eric Wilhelm]
+
+ - Module::Build::Compat 'passthrough' style has been deprecated. Using
+ 'passthrough' will issue warnings on Makefile.PL generation. See
+ Module::Build::Compat documentation for rationale.
+
0.35 - Thu Aug 27 09:12:02 EDT 2009
Bug fixes:
- - Fix t/destinations.t segfault on 5.6.2
+
+ - Fix t/destinations.t segfault on 5.6.2 [David Golden]
0.34_06 - Sat Aug 22 21:58:26 EDT 2009
Bug fixes:
+
- Multiple test fixes for OS2 [Ilya Zakharevich]
+
- Generated.ppd files use :utf8 if possible (RT#48827) [Olivier Mengue]
+
- Fixed preservation of custom install_paths on resume (RT#41166)
[David Golden]
+
- Warn instead of crashing when Pod::Man tries to create files with
colons on vfat partitions on unix (RT#45544) [David Golden]
0.34_05 - Sun Aug 9 22:31:37 EDT 2009
Bug fixes:
- - When auto_configure_requires is true (the default), Module::Build will
+
+ - When auto_configure_requires is true (the default), Module::Build will
only add last 'major' version of Module:Build (e.g. 0.XX) to
configure_requires to avoid specifying a minor development release not
available on CPAN [David Golden]
0.34_04 - Sat Aug 8 11:02:24 EDT 2009
Other:
+
- Added documentation warning that 'get_options' should be capitalized
to avoid conflicting with future Module::Build options and changed
the examples accordingly.
0.34_03 - Sat Aug 8 07:39:16 EDT 2009
Bug fixes:
+
- Fixed failing xs.t if /tmp is mounted noexec (RT#47331) [David Golden]
+
- Fixed failing debug.t on VMS (RT#48362) [Craig Berry]
+
- Prevent par.t from dying on error in .zip extraction [David Golden]
+
- Fixed potential runthrough.t failure on 5.6.2 [David Golden]
Other:
+
- Archive::Tar changed from 'requires' to 'recommends' so non-authors
without IO::Zlib can still use Module::Build to install modules
[reported by Matt Trout, fix by David Golden]
0.340201 - Sun Aug 9 22:11:04 EDT 2009
Other:
+
- Version bump for Perl core for 5.10.1 release; no other changes
0.34_02 - Sun Jul 26 22:50:40 EDT 2009
Bug-fixes:
+
- Bundled Module::Build::Version updated to bring into sync with CPAN
version.pm 0.77 [John Peacock]
0.34_01 - Sat Jul 18 16:32:09 EDT 2009
Enhancements:
+
- Added --debug flag to trace Build action execution (RT#47933)
[David Golden]
Bug-fixes:
- - Bundled Module::Build::Version version code updated to fix unsafe use
+
+ - Bundled Module::Build::Version version code updated to fix unsafe use
of $@ (RT#47980) [John Peacock]
0.34 - Tue Jul 7 16:56:47 EDT 2009
0.33_06 - Sun Jul 5 10:11:40 EDT 2009
Bug-fixes:
+
- Bundled version code will use pure Perl on 5.10.0 to work around
a corner case involving eval and locale [John Peacock]
+
- Reversed VMS patch from 0.33_03 [Craig Berry]
+
- PL_files in Build.PL that are in the bin/scripts directory should not be
- installed as if they are scripts (fixed for case-tolerant systems).
+ installed as if they are scripts (fixed for case-tolerant systems).
[David Golden, reported by Craig Berry]
0.33_05 - Sun Jun 28 22:06:49 EDT 2009
Enhancements:
+
- New 'auto_configure_requires' parameter (default 1) controls
whether Module::Build should add itself to configure_requires
in META.yml if not specified in Build.PL [David Golden]
Bug-fixes:
+
- The default MANIFEST.SKIP created by the "manifest" action
was out of date. It will now use the installed MANIFEST.SKIP
and add some Module::Build and distribution specific items
to it. [Michael Schwern]
Other:
+
- configure_requires do not necessarily need to be in requires
or build_requires; warning to that effect has been removed
[David Golden]
0.33_04 - Fri Jun 26 07:09:06 EDT 2009
Bug-fixes:
+
- Don't try utf8 YAML I/O on Perl 5.6 [David Golden]
Other:
+
- configure_requires added to prereq report (RT#47254) [Curtis Jewell]
+
- updated Module::Build::Version to match forthcoming version.pm 0.77
(RT#47256) [John Peacock]
+
- skips xs.t and ppm.t when perl was not compiled with dynamic loading
- since Module::Buld does not support static linking (RT#46178)
+ since Module::Buld does not support static linking (RT#46178)
[David Golden]
+
- skip failing test in par.t if Archive::Zip is broken [David Golden]
+
- Added YAML utf8 patch in 0.33_03 changes list
+
- Added attribution for patches in 0.33_03 changes list
0.33_03 - Mon Jun 22 17:22:56 EDT 2009
Bug-fixes:
- - Removes Module::Build from its own configure/build_requires
+
+ - Removes Module::Build from its own configure/build_requires
[David Golden]
+
- ConfigData->feature() confirms that modules actually load successfully,
not just that they are present. (RT#43557) [David Golden]
- - Module::Build::Compat handling of INSTALL*LIB (RT#43827)
+
+ - Module::Build::Compat handling of INSTALL*LIB (RT#43827)
[Tony Payne, David Golden]
+
- Module::Build::Compat and recursive test files (RT#39171) [Dave Rolsky]
+
- Fixed bug linking non-standard XS names on Windows (RT#38065) ["snaury"]
- - Run PL files that don't generate any file (RT#39365)
+
+ - Run PL files that don't generate any file (RT#39365)
[Matisse Enzer, David Golden]
+
- HTML generation failure no longer fatal (RT#36660) [David Golden]
- - realclean might not delete Build.bat on Windows (RT#43863)
+
+ - realclean might not delete Build.bat on Windows (RT#43863)
[Roy Ivy, David Golden]
+
- include_dirs parameter now works correctly when given a single
string argument (RT#40177) [David Wheeler]
+
- Lots of spelling fixes in the POD (RT#45528r) [Lars Dieckow]
+
- On Unix-like systems, tilde expansion is more liberal in username
characters accepted (RT#33492) [Jon Jensen]
Other
+
- On MSWin32, bumped File::Spec prereq to 3.30 for a variety of fixes
+
- Add support for VMS in Unix compatibility mode (RT#42157)
[John E. Malmberg
- - Added a can_action($name) method (RT#45172) [brian d foy]
+
+ - Added a can_action($name) method (RT#45172) [brian d foy]
+
- Documented that subclass methods should not permanently change
current directory (RT#46919) [David Wheeler]
+
- META.yml encoded in UTF-8 (RT#43765) [Olivier Mengue]
0.33_02 - Mon Jun 15 12:23:55 EDT 2009
Bug-fixes:
+
- Fixed tests for bleadperl
0.33_01 - Sat Jun 13 20:24:42 EDT 2009
Bug-fixes:
+
- Fixed RT#42724: consolidated VMS fixes [patch by Craig Berry]
+
- Fixed RT#46338: passthrough Makefile.PL cleans Makefile during distclean
+
- Fixed RT#45700: t/compat.t for HP/UX make
Other:
- - Adds current Module::Build to configure_requires (and build_requires)
+
+ - Adds current Module::Build to configure_requires (and build_requires)
if no configure_requires is specified
+
- Always normalizes version number tuples in META.yml (e.g. 'v1.2.0')
- (Partially addresses RT#46150)
- - Normalizes a generated dist_version (e.g. from a .pm file) --
+ (Partially addresses RT#46150)
+
+ - Normalizes a generated dist_version (e.g. from a .pm file) --
dist_version set manually in Build.PL is not normalized
+
- Documentation update for create_license
+
- Minor POD cleanup
0.33 - Sun May 3 20:16:34 PDT 2009
Bug-fixes:
+
- Fixed RT#45462: Compat.pm needs to reference 'Build.com' on VMS
[patch from John Malmberg]
+
- Fixed RT#45461: ext.t on VMS [patch from John Malmberg]
+
- Fixed RT#43861: Module::Build::PPMMaker has broken PPD name
versioning for v5.10+
0.32_01 - Tue Apr 14 17:14:22 PDT 2009
Bug-fixes:
+
- Module::Build::Compat had stopped adding "PL_FILES => {}" when no
PL_files property was set in Build.PL; restored old behavior and fixed
tests and documentation related to this issue [David Golden]
- - Caches ExtUtils::CBuilder object in a temporary stash instead of properties
+
+ - Caches ExtUtils::CBuilder object in a temporary stash instead of properties
+
- Fixed undef resources->license in META.yml (RT #44453).
+
- Use $^X instead of 'perl' in t/ext.t [David Golden] (RT #43485)
Other:
+
- Generated META.yml will indicate version 1.4 of the specification
(RT #37478) [patch from Alexandr Ciornii]
+
- Archive::Tar now the default for generating tarballs on all platforms
(avoids problems with incompatible tar binaries)
+
- dist_dir() now uses dist_name() and dist_version() accessors rather
than using its properties directly. [brian d foy] (RT #45038)
-
+
0.32 - Wed Feb 25 17:40:02 PST 2009
No changes since 0.31_04.
0.31_04 - Fri Feb 20 11:04:59 PST 2009
Other
- - Bumped Test::Harness prereq to 3.16 for latest PERL5LIB fixes (solves
+
+- Bumped Test::Harness prereq to 3.16 for latest PERL5LIB fixes (solves
test failures when installing Module::Build using CPANPLUS::Dist::Build)
[David Golden]
0.31_03 - Sun Feb 8 14:54:01 PST 2009
Enhancements
+
- added a "prereq_data" action that prints a Perl data structure of
all prerequisites; can be loaded by external tools using eval()
[David Golden]
Bug-fixes
+
- 'fakeinstall' action warns and skips without ExtUtils::Install 1.32+
[David Golden, reported by Zefram]
+
- allows Module::Build version mismatch when installing self; works around
limitations in CPANPLUS::Dist::Build [David Golden]
0.31_02 - Tue Jan 27 09:16:43 PST 2009
Other
+
- tests now use File::Temp (added to build_requires); appears to fix
Win32 testing heisenbug on directory removal during high system loads
+
- use_tap_harness.t will skip unless a release version of TAP::Harness
is installed
+
- improved diagnostics to ensure_blib() tests in t/lib/MBTest.pm
Compat
+
- passthrough Makefile.PL will now play nice with cpantesters' on
exit(0) (RT#32018) [Eric Wilhelm]
Bug Fixes
+
- fix for doubling-up of --prefix (RT#19951)
0.31012 - Wed Jan 14 01:36:19 PST 2009
Bug Fixes
+
- t/tilde.t maybe actually fixed on MSWin32 now.
0.31011 - Mon Jan 12 21:57:04 PST 2009
Bug Fixes
+
- t/tilde.t had been failing on MSWin32 (RT#42349)
0.3101 - Mon Jan 12 13:52:36 PST 2009
Other
+
- added 'mirbsd' as a Unix-type OS [BinGOs]
+
- added 'haiku' as a Unix-type OS (backported from bleadperl)
+
- skips certain tests on VMS (backported from bleadperl)
+
- sets $^X to absolute path in tests (backported from bleadperl)
0.31 - Sat Dec 20 15:03:33 2008
Deprecations
+
- Use of attributes as class methods is deprecated (this was never a
documented feature and appears to only have worked accidentally.)
0.30_02 - Mon Dec 15 12:23:55 PST 2008
Bug Fixes
+
- make Software::License dependency "softer".
0.30_01 - Thu Dec 11 18:25:53 PST 2008
New Docs
+
- Added a recipe for writing a new action to the Cookbook
+
- Added a recipe for bundling Module::Build to the Cookbook.
Doc Fixes
+
- Clarified dist_abstract search procedure in API.pod (RT#41056) [Mario
Domgoergen]
Bug Fixes
+
- Workaround HARNESS_TIMER env issue in t/compat.t (RT#39635)
+
- Fix ~ expansion when $HOME is different from /etc/passwd as
when running sudo. [rt.cpan.org 39662]
+
- Fixed a small POD error in the Cookbook. [Damyan Ivanov]
+
- Unset group/other write permission bits when using Archive::Tar to
build the dist tarball. (RT#39804) [David Golden]
Enhancements
+
- We now support a 'create_license' parameter to new() that will
create a LICENSE file during the 'dist' phase with the full text of
the license. This requires Software::License on the author's
machine.
+
- Added lgpl2/lgpl3 entries to the supported licenses (RT#40532).
+
- Support for validating properties with a check subref. [David
Wheeler]
Test Fixes
+
- Defend against more stray environment variables interfering
with the tests.
Other
+
- Updated our embedded version.pm to 0.76, enhanced documentation on
dist_version_from. [John Peacock]
have stopped, but it didn't. Fixed. [Matthew Cast and David
Golden]
- - Module::Build::Compat adds "require 5.XXXXX" to Makefile.PL when
+ - Module::Build::Compat adds "require 5.XXXXX" to Makefile.PL when
'perl' is specified as a 'requires' prerequisite [David Golden]
- - Refactored t/compat.t for modularity and transparency; added
- labels for all tests; supressed subprocess output to
+ - Refactored t/compat.t for modularity and transparency; added
+ labels for all tests; supressed subprocess output to
STDOUT and STDERR [David Golden]
- Fixed bug in perl_version_to_float when version is already a float
- The synonyms 'scripts' and 'prereq' for 'script_files' and
'requires' were broken in a previous version (0.27_01, probably),
but now they're fixed. [David Golden]
-
+
- Previously, we assumed that any custom subclass of Module::Build
was located in _build/lib/. This is only true if the author used
the subclass() method, though. We now use %INC to find where the
- Added experimental code to build a .ppd file, in support of
ActiveState's "Perl Package Manager". [original patch by Dave
Rolsky]
-
+
- For authors who use Module::Signature to sign their distributions,
we now create the SIGNATURE file right in the distribution
directory, rather than creating it in the top-level directory and
copying it into place. This solves problems related to having
files get out of date with respect to their signatures.
-
+
- We now don't depend on Module::Info to scan for packages during the
'dist' action anymore, because it's way too aggressive about
loading other modules that you may not want loaded. We now just
- The distribution directory (e.g. Sample-Module-0.13/ ) will now be
deleted during the 'clean' or 'realclean' actions.
-
+
- During testing of modules, blib/lib and blib/arch are now added as
absolute paths, not relative. This helps tests that load the
modules at runtime and may change the current working directory
(like Module::Build itself does during testing).
-
+
- The $Config{cc} entry on some people's systems is something like
'ccache gcc', so we now split that string using split_like_shell().
[Richard Clamp]
- When compiling C code, we now respect 'pollute' and 'inc'
parameters. (XXX - needs docs) [Dave Rolsky]
-
+
- Made the creation of the "install map" more generic. (XXX - needs
documentation)
- Renamed module_name_to_file() to find_module_by_name(), and added
a parameter specifying the directories to search in. Previously
we searched in 'lib' and @INC, which wasn't correct in all
- situations.
+ situations.
- Patched the docs to change "Build test" to "./Build test"
[Elizabeth Mattijsen]
- For the 'Build dist' action, we'll use the 'tar' and 'gzip'
programs (as specified by Config.pm) on Unix platforms, otherwise
we'll use Archive::Tar and Compress::Zlib.
-
+
0.02 Wed Sep 5 00:53:04 CDT 2001
- Added POD documentation.
use vars qw($VERSION @ISA);
@ISA = qw(Module::Build::Base);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
# Okay, this is the brute-force method of finding out what kind of
'actions'. In this case the actions run are 'build' (the default
action), 'test', and 'install'. Other actions defined so far include:
- build manpages
- clean pardist
- code ppd
- config_data ppmdist
- diff prereq_data
- dist prereq_report
- distcheck pure_install
- distclean realclean
- distdir retest
- distmeta skipcheck
- distsign test
- disttest testall
- docs testcover
- fakeinstall testdb
- help testpod
- html testpodcoverage
- install versioninstall
- manifest
+ build manifest
+ clean manpages
+ code pardist
+ config_data ppd
+ diff ppmdist
+ dist prereq_data
+ distcheck prereq_report
+ distclean pure_install
+ distdir realclean
+ distmeta retest
+ distsign skipcheck
+ disttest test
+ docs testall
+ fakeinstall testcover
+ help testdb
+ html testpod
+ install testpodcoverage
+ installdeps versioninstall
You can run the 'help' action for a complete list of actions.
GZIP compression.
By default, this action will use the C<Archive::Tar> module. However, you can
-force it to use binary "tar" and "gzip" executables by supplying an explicit
+force it to use binary "tar" and "gzip" executables by supplying an explicit
C<tar> (and optional C<gzip>) parameter:
./Build dist --tar C:\path\to\tar.exe --gzip C:\path\to\zip.exe
distribution. The metadata includes the distribution name, version,
abstract, prerequisites, license, and various other data about the
distribution. This file is created as F<META.yml> in YAML format.
-It is recommended that the C<YAML> module be installed to create it.
-If the C<YAML> module is not installed, an internal module supplied
+It is recommended that the C<YAML::Tiny> module be installed to create it.
+If the C<YAML::Tiny> module is not installed, an internal module supplied
with Module::Build will be used to write the META.yml file, and this
will most likely be fine.
module from being present on your system, which can be a confusing
situation indeed.
+=item installdeps
+
+[version 0.36]
+
+This action will use the C<cpan_client> parameter as a command to install
+missing prerequisites. You will be prompted whether to install
+optional dependencies.
+
+The C<cpan_client> option defaults to 'cpan' but can be set as an option or in
+F<.modulebuildrc>. It must be a shell command that takes a list of modules to
+install as arguments (e.g. 'cpanp -i' for CPANPLUS). If the program part is a
+relative path (e.g. 'cpan' or 'cpanp'), it will be located relative to the perl
+program that executed Build.PL.
+
+ /opt/perl/5.8.9/bin/perl Build.PL
+ ./Build installdeps --cpan_client 'cpanp -i'
+ # installs to 5.8.9
+
=item manifest
[version 0.05]
[version 0.28]
This action is identical to the C<install> action. In the future,
-though, when C<install> starts writing to the file
+though, when C<install> starts writing to the file
F<$(INSTALLARCHLIB)/perllocal.pod>, C<pure_install> won't, and that
will be the only difference between them.
[version 0.25]
-This checks all the files described in the C<docs> action and
+This checks all the files described in the C<docs> action and
produces C<Test::Harness>-style output. If you are a module author,
this is useful to run before creating a new release.
[version 0.28]
-This checks the pod coverage of the distribution and
+This checks the pod coverage of the distribution and
produces C<Test::Harness>-style output. If you are a module author,
this is useful to run before creating a new release.
Suppress informative messages on output.
+=item verbose
+
+Display extra information about the Build on output.
+
+=item cpan_client
+
+Sets the C<cpan_client> command for use with the C<installdeps> action.
+See C<installdeps> for more details.
+
=item use_rcfile
Load the F<~/.modulebuildrc> option file. This option can be set to
false to prevent the custom resource file from being loaded.
-=item verbose
-
-Display extra information about the Build on output.
-
=item allow_mb_mismatch
Suppresses the check upon startup that the version of Module::Build
=back
-
=head2 Default Options File (F<.modulebuildrc>)
[version 0.28]
to all actions, and the key 'Build_PL' specifies options to be applied
when you invoke C<perl Build.PL>.
- * verbose=1 # global options
- diff flags=-u
- install --install_base /home/ken
- --install_path html=/home/ken/docs/html
+ * verbose=1 # global options
+ diff flags=-u
+ install --install_base /home/ken
+ --install_path html=/home/ken/docs/html
+ installdeps --cpan_client 'cpanp -i'
If you wish to locate your resource file in a different location, you
can set the environment variable C<MODULEBUILDRC> to the complete
absolute path of the file containing your options.
+=head2 Environment variables
+
+=over
+
+=item MODULEBUILDRC
+
+[version 0.28]
+
+Specifies an alternate location for a default options file as described above.
+
+=item PERL_MB_OPT
+
+[version 0.36]
+
+Command line options that are applied to Build.PL or any Build action. The
+string is split as the shell would (e.g. whitespace) and the result is
+prepended to any actual command-line arguments.
+
+=back
=head1 INSTALL PATHS
=head1 SEE ALSO
perl(1), L<Module::Build::Cookbook>, L<Module::Build::Authoring>,
-L<Module::Build::API>, L<ExtUtils::MakeMaker>, L<YAML>
+L<Module::Build::API>, L<ExtUtils::MakeMaker>, L<YAML::Tiny>
F<META.yml> Specification:
L<http://module-build.sourceforge.net/META-spec-current.html>
=item apache
-The distribution is licensed under the Apache Software License
-(L<http://opensource.org/licenses/apachepl.php>).
+The distribution is licensed under the Apache License, Version 2.0
+(L<http://apache.org/licenses/LICENSE-2.0>).
+
+=item apache_1_1
+
+The distribution is licensed under the Apache Software License, Version 1.1
+(L<http://apache.org/licenses/LICENSE-1.1>).
=item artistic
Setting C<module_name> won't override a C<dist_*> parameter you
specify explicitly.
+=item needs_compiler
+
+[version 0.36]
+
+The C<needs_compiler> parameter indicates whether a compiler is required to
+build the distsribution. The default is false, unless XS files are found or
+the C<c_source> parameter is set, in which case it is true. If true,
+L<ExtUtils::CBuilder> is automatically added to C<build_requires> if needed.
+
+For a distribution where a compiler is I<optional>, e.g. a dual XS/pure-Perl
+distribution, C<needs_compiler> should explicitly be set to a false value.
+
=item PL_files
[version 0.06]
instead of C<script_files>. Please consider this usage deprecated,
though it will continue to exist for several version releases.
+=item share_dir
+
+[version 0.36]
+
+An optional parameter specifying directories of static data files to
+be installed as read-only files for use with L<File::ShareDir>. The
+C<share_dir> property supports both distribution-level and
+module-level share files.
+
+The default when C<share_dir> is not set is for any files in a F<share>
+directory at the top level of the distribution to be installed in
+distribution-level share directory. Alternatively, C<share_dir> can be set to
+a directory name or an arrayref of directory names containing files to be
+installed in the distribution-level share directory.
+
+If C<share_dir> is a hashref, it may have C<dist> or C<module> keys
+providing full flexibility in defining share directories to install.
+
+ share_dir => {
+ dist => [ 'examples', 'more_examples' ],
+ module => {
+ Foo::Templates => ['share/html', 'share/text'],
+ Foo::Config => 'share/config',
+ }
+ }
+
+If C<share_dir> is set (manually or automatically), then File::ShareDir
+will automatically be added to the C<requires> hash.
+
=item sign
[version 0.16]
[version 0.28]
-When called from a directory containing a F<Build.PL> script and a
-F<META.yml> file (in other words, the base directory of a
-distribution), this method will run the F<Build.PL> and return the
-resulting C<Module::Build> object to the caller. Any key-value
-arguments given to C<new_from_context()> are essentially like
-command line arguments given to the F<Build.PL> script, so for example
-you could pass C<< verbose => 1 >> to this method to turn on
-verbosity.
+When called from a directory containing a F<Build.PL> script (in other words,
+the base directory of a distribution), this method will run the F<Build.PL> and
+call C<resume()> to return the resulting C<Module::Build> object to the caller.
+Any key-value arguments given to C<new_from_context()> are essentially like
+command line arguments given to the F<Build.PL> script, so for example you
+could pass C<< verbose => 1 >> to this method to turn on verbosity.
=item resume()
[version 0.03]
-You'll probably never call this method directly, it's only called from
-the auto-generated C<Build> script. The C<new()> method is only
-called once, when the user runs C<perl Build.PL>. Thereafter, when
-the user runs C<Build test> or another action, the C<Module::Build>
-object is created using the C<resume()> method to re-instantiate with
-the settings given earlier to C<new()>.
+You'll probably never call this method directly, it's only called from the
+auto-generated C<Build> script (and the C<new_from_context> method). The
+C<new()> method is only called once, when the user runs C<perl Build.PL>.
+Thereafter, when the user runs C<Build test> or another action, the
+C<Module::Build> object is created using the C<resume()> method to
+re-instantiate with the settings given earlier to C<new()>.
=item subclass()
=item prepare_metadata()
-[version 0.28]
+[version 0.36]
-This method is provided for authors to override to customize the
-fields of F<META.yml>. It is passed a YAML::Node node object which can
-be modified as desired and then returned. E.g.
+This method returns a hash reference of metadata that can be used to create a
+YAML datastream. It is provided for authors to override or customize the fields
+of F<META.yml>. E.g.
package My::Builder;
use base 'Module::Build';
sub prepare_metadata {
my $self = shift;
- my $node = $self->SUPER::prepare_metadata( shift );
- $node->{custom_field} = 'foo';
- return $node;
+ my $data = $self->SUPER::prepare_metadata();
+ $data->{custom_field} = 'foo';
+ return $data;
}
+Prior to version 0.36, this method took a YAML::Node as an argument to hold
+assembled metadata.
+
=item prereq_failures()
[version 0.11]
=item build_script()
+=item bundle_inc()
+
+=item bundle_inc_preload()
+
=item c_source()
=item config_dir()
=item conflicts()
+=item cpan_client()
+
=item create_license()
=item create_makefile_pl()
=item module_name()
+=item mymetafile()
+
+=item needs_compiler()
+
=item orig_dir()
=item perl()
=head1 SEE ALSO
perl(1), L<Module::Build>(3), L<Module::Build::Authoring>(3),
-L<Module::Build::Cookbook>(3), L<ExtUtils::MakeMaker>(3), L<YAML>(3)
+L<Module::Build::Cookbook>(3), L<ExtUtils::MakeMaker>(3), L<YAML::Tiny>(3)
F<META.yml> Specification:
L<http://module-build.sourceforge.net/META-spec-current.html>
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
BEGIN { require 5.00503 }
use Module::Build::ModuleInfo;
use Module::Build::Notes;
use Module::Build::Config;
+use Module::Build::Version;
#################### Constructors ###########################
$self->{invoked_action} = $self->{action} ||= 'Build_PL';
$self->cull_args(@ARGV);
-
+
die "Too early to specify a build action '$self->{action}'. Do 'Build $self->{action}' instead.\n"
if $self->{action} && $self->{action} ne 'Build_PL';
$self->check_manifest;
- $self->check_prereq;
- $self->check_autofeatures;
+ $self->auto_require;
+ if ( $self->check_prereq + $self->check_autofeatures != 2) {
+ $self->log_warn(<<EOF);
+
+ERRORS/WARNINGS FOUND IN PREREQUISITES. You may wish to install the versions
+of the modules indicated above before proceeding with this installation
+
+EOF
+ unless (
+ $self->dist_name eq 'Module-Build' ||
+ $ENV{PERL5_CPANPLUS_IS_RUNNING} || $ENV{PERL5_CPAN_IS_RUNNING}
+ ) {
+ $self->log_warn(
+ "Run 'Build installdeps' to install missing prerequisites.\n\n"
+ );
+ }
+ }
+
+ # record for later use in resume;
+ $self->{properties}{_added_to_INC} = [ $self->_added_to_INC ];
+
+ $self->set_bundle_inc;
$self->dist_name;
$self->dist_version;
+ $self->_guess_module_name unless $self->module_name;
$self->_find_nested_builds;
my $self = $package->_construct(@_);
$self->read_config;
+ unshift @INC, @{ $self->{properties}{_added_to_INC} || [] };
+
# If someone called Module::Build->current() or
# Module::Build->new_from_context() and the correct class to use is
# actually a *subclass* of Module::Build, we may need to load that
$self->log_warn(" * WARNING: Configuration was initially created with '$self->{properties}{perl}',\n".
" but we are now using '$perl'.\n");
}
-
+
$self->cull_args(@ARGV);
unless ($self->allow_mb_mismatch) {
" or use --allow_mb_mismatch 1 to skip this version check.\n")
if $mb_version ne $self->{properties}{mb_version};
}
-
+
$self->{invoked_action} = $self->{action} ||= 'build';
return $self;
sub new_from_context {
my ($package, %args) = @_;
-
- # XXX Read the META.yml and see whether we need to run the Build.PL?
-
- # Run the Build.PL. We use do() rather than run_perl_script() so
- # that it runs in this process rather than a subprocess, because we
- # need to make sure that the environment is the same during Build.PL
- # as it is during resume() (and thereafter).
- {
- local @ARGV = $package->unparse_args(\%args);
- do './Build.PL';
- die $@ if $@;
- }
+
+ $package->run_perl_script('Build.PL',[],[$package->unparse_args(\%args)]);
return $package->resume;
}
"in (@paths)\n";
}
+# Adapted from IPC::Cmd::can_run()
+sub find_command {
+ my ($self, $command) = @_;
+
+ if( File::Spec->file_name_is_absolute($command) ) {
+ return $self->_maybe_command($command);
+
+ } else {
+ for my $dir ( File::Spec->path ) {
+ my $abs = File::Spec->catfile($dir, $command);
+ return $abs if $abs = $self->_maybe_command($abs);
+ }
+ }
+}
+
+# Copied from ExtUtils::MM_Unix::maybe_command
+sub _maybe_command {
+ my($self,$file) = @_;
+ return $file if -x $file && ! -d $file;
+ return;
+}
+
sub _is_interactive {
return -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ; # Pipe?
}
}
if (my $info = $ph->{auto_features}->access($key)) {
- my $failures = $self->prereq_failures($info);
- my $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/,
- keys %$failures ) ? 1 : 0;
- return !$disabled;
+ my $disabled;
+ for my $type ( @{$self->prereq_action_types} ) {
+ next if $type eq 'description' || $type eq 'recommends' || ! exists $info->{$type};
+ my $prereqs = $info->{$type};
+ for my $modname ( sort keys %$prereqs ) {
+ my $spec = $prereqs->{$modname};
+ my $status = $self->check_installed_status($modname, $spec);
+ if ((!$status->{ok}) xor ($type =~ /conflicts$/)) { return 0; }
+ if ( ! eval "require $modname; 1" ) { return 0; }
+ }
+ }
+ return 1;
}
return $ph->{features}->access($key, @_);
sub _mb_feature {
my $self = shift;
-
+
if (($self->module_name || '') eq 'Module::Build') {
# We're building Module::Build itself, so ...::ConfigData isn't
# valid, but $self->features() should be.
}
}
+sub _warn_mb_feature_deps {
+ my $self = shift;
+ my $name = shift;
+ $self->log_warn(
+ "The '$name' feature is not available. Please install missing\n" .
+ "feature dependencies and try again.\n".
+ $self->_feature_deps_msg($name) . "\n"
+ );
+}
sub add_build_element {
my ($self, $elem) = @_;
sub ACTION_config_data {
my $self = shift;
return unless $self->has_config_data;
-
+
my $module_name = $self->module_name
or die "The config_data feature requires that 'module_name' be set";
my $notes_name = $module_name . '::ConfigData'; # TODO: Customize name ???
$self->config_file('features')
], $notes_pm);
- $self->log_info("Writing config notes to $notes_pm\n");
+ $self->log_verbose("Writing config notes to $notes_pm\n");
File::Path::mkpath(File::Basename::dirname($notes_pm));
Module::Build::Notes->write_config_data
}
########################################################################
-{ # enclosing these lexicals -- TODO
+{ # enclosing these lexicals -- TODO
my %valid_properties = ( __PACKAGE__, {} );
my %additive_properties;
sub valid_properties_defaults {
my %out;
- for (reverse shift->_mb_classes) {
- @out{ keys %{ $valid_properties{$_} } } = map {
+ for my $class (reverse shift->_mb_classes) {
+ @out{ keys %{ $valid_properties{$class} } } = map {
$_->()
- } values %{ $valid_properties{$_} };
+ } values %{ $valid_properties{$class} };
}
return \%out;
}
my %p = @_ == 1 ? ( default => shift ) : @_;
my $type = ref $p{default};
- $valid_properties{$class}{$property} = $type eq 'CODE'
- ? $p{default}
- : sub { $p{default} };
+ $valid_properties{$class}{$property} =
+ $type eq 'CODE' ? $p{default} :
+ $type eq 'HASH' ? sub { return { %{ $p{default} } } } :
+ $type eq 'ARRAY'? sub { return [ @{ $p{default} } ] } :
+ sub { return $p{default} } ;
push @{$additive_properties{$class}->{$type}}, $property
if $type;
__PACKAGE__->add_property(auto_configure_requires => 1);
__PACKAGE__->add_property(blib => 'blib');
__PACKAGE__->add_property(build_class => 'Module::Build');
-__PACKAGE__->add_property(build_elements => [qw(PL support pm xs pod script)]);
+__PACKAGE__->add_property(build_elements => [qw(PL support pm xs share_dir pod script)]);
__PACKAGE__->add_property(build_script => 'Build');
__PACKAGE__->add_property(build_bat => 0);
+__PACKAGE__->add_property(bundle_inc => []);
+__PACKAGE__->add_property(bundle_inc_preload => []);
__PACKAGE__->add_property(config_dir => '_build');
__PACKAGE__->add_property(include_dirs => []);
+__PACKAGE__->add_property(license => 'unknown');
__PACKAGE__->add_property(metafile => 'META.yml');
+__PACKAGE__->add_property(mymetafile => 'MYMETA.yml');
__PACKAGE__->add_property(recurse_into => []);
__PACKAGE__->add_property(use_rcfile => 1);
__PACKAGE__->add_property(create_packlist => 1);
__PACKAGE__->add_property(config => undef);
__PACKAGE__->add_property(test_file_exts => ['.t']);
__PACKAGE__->add_property(use_tap_harness => 0);
+__PACKAGE__->add_property(cpan_client => 'cpan');
__PACKAGE__->add_property(tap_harness_args => {});
__PACKAGE__->add_property(
'installdirs',
has_config_data
install_base
libdoc_dirs
- license
magic_number
mb_version
module_name
+ needs_compiler
orig_dir
perl
pm_files
recursive_test_files
script_files
scripts
+ share_dir
sign
test_files
verbose
$opts{code} ||= '';
$opts{class} ||= 'MyModuleBuilder';
-
+
my $filename = File::Spec->catfile($build_dir, 'lib', split '::', $opts{class}) . '.pm';
my $filedir = File::Basename::dirname($filename);
- $pack->log_info("Creating custom builder $filename in $filedir\n");
-
+ $pack->log_verbose("Creating custom builder $filename in $filedir\n");
+
File::Path::mkpath($filedir);
die "Can't create directory $filedir: $!" unless -d $filedir;
-
+
my $fh = IO::File->new("> $filename") or die "Can't create $filename: $!";
print $fh <<EOF;
package $opts{class};
1;
EOF
close $fh;
-
+
unshift @INC, File::Spec->catdir(File::Spec->rel2abs($build_dir), 'lib');
eval "use $opts{class}";
die $@ if $@;
return $opts{class};
}
+sub _guess_module_name {
+ my $self = shift;
+ my $p = $self->{properties};
+ return if $p->{module_name};
+ if ( $p->{dist_version_from} && -e $p->{dist_version_from} ) {
+ my $mi = Module::Build::ModuleInfo->new_from_file($self->dist_version_from);
+ $p->{module_name} = $mi->name;
+ }
+ else {
+ my $mod_path = my $mod_name = File::Basename::basename($self->base_dir);
+ $mod_name =~ s{-}{::}g;
+ $mod_path =~ s{-}{/}g;
+ $mod_path .= ".pm";
+ if ( -e $mod_path || -e File::Spec->catfile('lib', $mod_path) ) {
+ $p->{module_name} = $mod_name;
+ }
+ else {
+ $self->log_warn( << 'END_WARN' );
+No 'module_name' was provided and it could not be inferred
+from other properties. This will prevent a packlist from
+being written for this file. Please set either 'module_name'
+or 'dist_version_from' in Build.PL.
+END_WARN
+ }
+ }
+}
+
sub dist_name {
my $self = shift;
my $p = $self->{properties};
return $p->{dist_name} if defined $p->{dist_name};
-
+
die "Can't determine distribution name, must supply either 'dist_name' or 'module_name' parameter"
unless $self->module_name;
-
+
($p->{dist_name} = $self->module_name) =~ s/::/-/g;
-
+
return $p->{dist_name};
}
my $p = $self->{properties};
my $member = "dist_$part";
return $p->{$member} if defined $p->{$member};
-
+
my $docfile = $self->_main_docfile
or return;
my $fh = IO::File->new($docfile)
or return;
-
+
require Module::Build::PodParser;
my $parser = Module::Build::PodParser->new(fh => $fh);
my $method = "get_$part";
sub read_config {
my ($self) = @_;
-
+
my $file = $self->config_file('build_params')
or die "Can't find 'build_params' in " . $self->config_dir;
my $fh = IO::File->new($file) or die "Can't read '$file': $!";
sub _write_data {
my ($self, $filename, $data) = @_;
-
+
my $file = $self->config_file($filename);
my $fh = IO::File->new("> $file") or die "Can't create '$file': $!";
unless (ref($data)) { # e.g. magicnum
sub write_config {
my ($self) = @_;
-
+
File::Path::mkpath($self->{properties}{config_dir});
-d $self->{properties}{config_dir} or die "Can't mkdir $self->{properties}{config_dir}: $!";
-
+
my @items = @{ $self->prereq_action_types };
$self->_write_data('prereqs', { map { $_, $self->$_() } @items });
$self->_write_data('build_params', [$self->{args}, $self->{config}->values_set, $self->{properties}]);
$self->{phash}{$_}->write() foreach qw(notes cleanup features auto_features config_data runtime_params);
}
+{
+ # packfile map -- keys are guts of regular expressions; If they match,
+ # values are module names corresponding to the packlist
+ my %packlist_map = (
+ '^File::Spec' => 'Cwd',
+ '^Devel::AssertOS' => 'Devel::CheckOS',
+ );
+
+ sub _find_packlist {
+ my ($self, $inst, $mod) = @_;
+ my $lookup = $mod;
+ my $packlist = eval { $inst->packlist($lookup) };
+ if ( ! $packlist ) {
+ # try from packlist_map
+ while ( my ($re, $new_mod) = each %packlist_map ) {
+ if ( $mod =~ qr/$re/ ) {
+ $lookup = $new_mod;
+ $packlist = eval { $inst->packlist($lookup) };
+ last;
+ }
+ }
+ }
+ return $packlist ? $lookup : undef;
+ }
+
+ sub set_bundle_inc {
+ my $self = shift;
+
+ my $bundle_inc = $self->{properties}{bundle_inc};
+ my $bundle_inc_preload = $self->{properties}{bundle_inc_preload};
+ # We're in author mode if inc::latest is loaded, but not from cwd
+ return unless inc::latest->can('loaded_modules');
+ require ExtUtils::Installed;
+ # ExtUtils::Installed is buggy about finding additions to default @INC
+ my $inst = ExtUtils::Installed->new(extra_libs => [@INC]);
+ my @bundle_list = map { [ $_, 0 ] } inc::latest->loaded_modules;
+
+ # XXX TODO: Need to get ordering of prerequisites correct so they are
+ # are loaded in the right order. Use an actual tree?!
+
+ while( @bundle_list ) {
+ my ($mod, $prereq) = @{ shift @bundle_list };
+
+ # XXX TODO: Append prereqs to list
+ # skip if core or already in bundle or preload lists
+ # push @bundle_list, [$_, 1] for prereqs()
+
+ # Locate packlist for bundling
+ my $lookup = $self->_find_packlist($inst,$mod);
+ if ( ! $lookup ) {
+ # XXX Really needs a more helpful error message here
+ die << "NO_PACKLIST";
+Could not find a packlist for '$mod'. If it's a core module, try
+force installing it from CPAN.
+NO_PACKLIST
+ }
+ else {
+ push @{ $prereq ? $bundle_inc_preload : $bundle_inc }, $lookup;
+ }
+ }
+ } # sub check_bundling
+}
+
sub check_autofeatures {
my ($self) = @_;
my $features = $self->auto_features;
-
- return unless %$features;
- $self->log_info("Checking features:\n");
+ return 1 unless %$features;
# TODO refactor into ::Util
my $longest = sub {
};
my $max_name_len = length($longest->(keys %$features));
- while (my ($name, $info) = each %$features) {
- $self->log_info(" $name" . '.' x ($max_name_len - length($name) + 4));
+ my ($num_disabled, $log_text) = (0, "\nChecking optional features...\n");
+ for my $name ( sort keys %$features ) {
+ $log_text .= $self->_feature_deps_msg($name, $max_name_len);
+ }
+
+ $num_disabled = () = $log_text =~ /disabled/g;
+
+ # warn user if features disabled
+ if ( $num_disabled ) {
+ $self->log_warn( $log_text );
+ return 0;
+ }
+ else {
+ $self->log_verbose( $log_text );
+ return 1;
+ }
+}
+
+sub _feature_deps_msg {
+ my ($self, $name, $max_name_len) = @_;
+ $max_name_len ||= length $name;
+ my $features = $self->auto_features;
+ my $info = $features->{$name};
+ my $feature_text = "$name" . '.' x ($max_name_len - length($name) + 4);
+ my ($log_text, $disabled) = ('','');
if ( my $failures = $self->prereq_failures($info) ) {
- my $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/,
- keys %$failures ) ? 1 : 0;
- $self->log_info( $disabled ? "disabled\n" : "enabled\n" );
-
- my $log_text;
- while (my ($type, $prereqs) = each %$failures) {
- while (my ($module, $status) = each %$prereqs) {
- my $required =
- ($type =~ /^(?:\w+_)?(?:requires|conflicts)$/) ? 1 : 0;
- my $prefix = ($required) ? '-' : '*';
- $log_text .= " $prefix $status->{message}\n";
- }
+ $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/,
+ keys %$failures ) ? 1 : 0;
+ $feature_text .= $disabled ? "disabled\n" : "enabled\n";
+
+ for my $type ( @{ $self->prereq_action_types } ) {
+ next unless exists $failures->{$type};
+ $feature_text .= " $type:\n";
+ my $prereqs = $failures->{$type};
+ for my $module ( sort keys %$prereqs ) {
+ my $status = $prereqs->{$module};
+ my $required =
+ ($type =~ /^(?:\w+_)?(?:requires|conflicts)$/) ? 1 : 0;
+ my $prefix = ($required) ? '!' : '*';
+ $feature_text .= " $prefix $status->{message}\n";
+ }
}
- $self->log_warn("$log_text") unless $self->quiet;
} else {
- $self->log_info("enabled\n");
+ $feature_text .= "enabled\n";
+ }
+ $log_text .= $feature_text if $disabled || $self->verbose;
+ return $log_text;
+}
+
+# Automatically detect and add prerequisites based on configuration
+sub auto_require {
+ my ($self) = @_;
+ my $p = $self->{properties};
+
+ # add current Module::Build to configure_requires if there
+ # isn't one already specified (but not ourself, so we're not circular)
+ if ( $self->dist_name ne 'Module-Build'
+ && $self->auto_configure_requires
+ && ! exists $p->{configure_requires}{'Module::Build'}
+ ) {
+ (my $ver = $VERSION) =~ s/^(\d+\.\d\d).*$/$1/; # last major release only
+ $self->_add_prereq('configure_requires', 'Module::Build', $ver);
+ }
+
+ # if we're in author mode, add inc::latest modules to
+ # configure_requires if not already set. If we're not in author mode
+ # then configure_requires will have been satisfied, or we'll just
+ # live with what we've bundled
+ if ( inc::latest->can('loaded_module') ) {
+ for my $mod ( inc::latest->loaded_modules ) {
+ next if exists $p->{configure_requires}{$mod};
+ $self->_add_prereq('configure_requires', $mod, $mod->VERSION);
}
}
- $self->log_warn("\n") unless $self->quiet;
+ # If needs_compiler is not explictly set, automatically set it
+ # If set, we need ExtUtils::CBuilder (and a compiler)
+ my $xs_files = $self->find_xs_files;
+ if ( ! defined $p->{needs_compiler} ) {
+ $self->needs_compiler( keys %$xs_files || defined $self->c_source );
+ }
+ if ($self->needs_compiler) {
+ $self->_add_prereq('build_requires', 'ExtUtils::CBuilder', 0);
+ if ( ! $self->have_c_compiler ) {
+ $self->log_warn(<<'EOM');
+Warning: ExtUtils::CBuilder not installed or no compiler detected
+Proceeding with configuration, but compilation may fail during Build
+
+EOM
+ }
+ }
+
+ # If using share_dir, require File::ShareDir
+ if ( $self->share_dir ) {
+ $self->_add_prereq( 'requires', 'File::ShareDir', '1.00' );
+ }
+
+ return;
+}
+
+sub _add_prereq {
+ my ($self, $type, $module, $version) = @_;
+ my $p = $self->{properties};
+ $version = 0 unless defined $version;
+ if ( exists $p->{$type}{$module} ) {
+ return if $self->compare_versions( $version, '<=', $p->{$type}{$module} );
+ }
+ $self->log_verbose("Adding to $type\: $module => $version\n");
+ $p->{$type}{$module} = $version;
+ return 1;
}
sub prereq_failures {
foreach my $type (@types) {
my $prereqs = $info->{$type};
- while ( my ($modname, $spec) = each %$prereqs ) {
+ for my $modname ( keys %$prereqs ) {
+ my $spec = $prereqs->{$modname};
my $status = $self->check_installed_status($modname, $spec);
if ($type =~ /^(?:\w+_)?conflicts$/) {
} elsif ($type =~ /^(?:\w+_)?recommends$/) {
next if $status->{ok};
$status->{message} = (!ref($status->{have}) && $status->{have} eq '<none>'
- ? "Optional prerequisite $modname is not installed"
+ ? "$modname is not installed"
: "$modname ($status->{have}) is installed, but we prefer to have $spec");
} else {
next if $status->{ok};
sub check_prereq {
my $self = shift;
- # If we have XS files, make sure we can process them.
- my $xs_files = $self->find_xs_files;
- if (keys %$xs_files && !$self->_mb_feature('C_support')) {
- $self->log_warn("Warning: this distribution contains XS files, ".
- "but Module::Build is not configured with C_support. ".
- "Please install ExtUtils::CBuilder to enable C_support.\n");
- }
-
# Check to see if there are any prereqs to check
my $info = $self->_enum_prereqs;
return 1 unless $info;
- $self->log_info("Checking prerequisites...\n");
+ my $log_text = "Checking prerequisites...\n";
my $failures = $self->prereq_failures($info);
if ( $failures ) {
-
- while (my ($type, $prereqs) = each %$failures) {
- while (my ($module, $status) = each %$prereqs) {
- my $prefix = ($type =~ /^(?:\w+_)?recommends$/) ? '*' : '- ERROR:';
- $self->log_warn(" $prefix $status->{message}\n");
+ $self->log_warn($log_text);
+ for my $type ( @{ $self->prereq_action_types } ) {
+ my $prereqs = $failures->{$type};
+ $self->log_warn(" ${type}:\n") if keys %$prereqs;
+ for my $module ( sort keys %$prereqs ) {
+ my $status = $prereqs->{$module};
+ my $prefix = ($type =~ /^(?:\w+_)?recommends$/) ? "* " : "! ";
+ $self->log_warn(" $prefix $status->{message}\n");
}
}
-
- $self->log_warn(<<EOF);
-
-ERRORS/WARNINGS FOUND IN PREREQUISITES. You may wish to install the versions
-of the modules indicated above before proceeding with this installation
-
-EOF
return 0;
-
} else {
-
- $self->log_info("Looks good\n\n");
+ $self->log_verbose($log_text . "Looks good\n\n");
return 1;
-
}
}
sub check_installed_status {
my ($self, $modname, $spec) = @_;
my %status = (need => $spec);
-
+
if ($modname eq 'perl') {
$status{have} = $self->perl_version;
-
+
} elsif (eval { no strict; $status{have} = ${"${modname}::VERSION"} }) {
# Don't try to load if it's already loaded
-
+
} else {
my $pm_info = Module::Build::ModuleInfo->new_from_module( $modname );
unless (defined( $pm_info )) {
@status{ qw(have message) } = ('<none>', "$modname is not installed");
return \%status;
}
-
+
$status{have} = $pm_info->version();
if ($spec and !defined($status{have})) {
@status{ qw(have message) } = (undef, "Couldn't find a \$VERSION in prerequisite $modname");
return \%status;
}
}
-
+
my @conditions = $self->_parse_conditions($spec);
-
+
foreach (@conditions) {
my ($op, $version) = /^\s* (<=?|>=?|==|!=) \s* ([\w.]+) \s*$/x
or die "Invalid prerequisite condition '$_' for $modname";
-
+
$version = $self->perl_version_to_float($version)
if $modname eq 'perl';
-
+
next if $op eq '>=' and !$version; # Module doesn't have to actually define a $VERSION
-
+
unless ($self->compare_versions( $status{have}, $op, $version )) {
$status{message} = "$modname ($status{have}) is installed, but we need version $op $version";
return \%status;
}
}
-
+
$status{ok} = 1;
return \%status;
}
sub compare_versions {
my $self = shift;
my ($v1, $op, $v2) = @_;
- $v1 = Module::Build::Version->new($v1)
+ $v1 = Module::Build::Version->new($v1)
unless UNIVERSAL::isa($v1,'Module::Build::Version');
my $eval_str = "\$v1 $op \$v2";
# I wish I could set $! to a string, but I can't, so I use $@
sub check_installed_version {
my ($self, $modname, $spec) = @_;
-
+
my $status = $self->check_installed_status($modname, $spec);
-
+
if ($status->{ok}) {
return $status->{have} if $status->{have} and "$status->{have}" ne '<none>';
return '0 but true';
}
-
+
$@ = $status->{message};
return 0;
}
sub _default_INC {
my $self = shift;
return @default_inc if @default_inc;
-
+
local $ENV{PERL5LIB}; # this is not considered part of the default.
-
+
my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter;
-
+
my @inc = $self->_backticks($perl, '-le', 'print for @INC');
chomp @inc;
-
+
return @default_inc = @inc;
}
}
sub print_build_script {
my ($self, $fh) = @_;
-
+
my $build_package = $self->build_class;
-
+
my $closedata="";
my %q = map {$_, $self->$_()} qw(config_dir base_dir);
sub create_build_script {
my ($self) = @_;
$self->write_config;
-
+
+ # Create MYMETA.yml
+ my $mymetafile = $self->mymetafile;
+ if ( $self->delete_filetree($mymetafile) ) {
+ $self->log_verbose("Removed previous '$mymetafile'\n");
+ }
+ $self->log_info("Creating new '$mymetafile' with configuration results\n");
+ $self->write_metafile( $mymetafile, $self->prepare_metadata( fatal => 0 ) );
+
+ # Create Build
my ($build_script, $dist_name, $dist_version)
= map $self->$_(), qw(build_script dist_name dist_version);
-
+
if ( $self->delete_filetree($build_script) ) {
- $self->log_info("Removed previous script '$build_script'\n\n");
+ $self->log_verbose("Removed previous script '$build_script'\n");
}
$self->log_info("Creating new '$build_script' script for ",
- "'$dist_name' version '$dist_version'\n");
+ "'$dist_name' version '$dist_version'\n");
my $fh = IO::File->new(">$build_script") or die "Can't create '$build_script': $!";
$self->print_build_script($fh);
close $fh;
-
+
$self->make_executable($build_script);
return 1;
sub check_manifest {
my $self = shift;
return unless -e 'MANIFEST';
-
+
# Stolen nearly verbatim from MakeMaker. But ExtUtils::Manifest
# could easily be re-written into a modern Perl dialect.
require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean.
local ($^W, $ExtUtils::Manifest::Quiet) = (0,1);
-
- $self->log_info("Checking whether your kit is complete...\n");
+
+ $self->log_verbose("Checking whether your kit is complete...\n");
if (my @missed = ExtUtils::Manifest::manicheck()) {
$self->log_warn("WARNING: the following files are missing in your kit:\n",
"\t", join("\n\t", @missed), "\n",
"Please inform the author.\n\n");
} else {
- $self->log_info("Looks good\n\n");
+ $self->log_verbose("Looks good\n\n");
}
}
use_rcfile
use_tap_harness
tap_harness_args
+ cpan_client
); # normalize only selected option names
return $opt;
require Module::Build::Compat;
%args = (%args, Module::Build::Compat->makefile_to_build_macros);
}
-
+
return \%args, $action;
}
sub cull_args {
my $self = shift;
- my ($args, $action) = $self->read_args(@_);
+ my @arg_list = @_;
+ unshift @arg_list, $self->split_like_shell($ENV{PERL_MB_OPT})
+ if $ENV{PERL_MB_OPT};
+ my ($args, $action) = $self->read_args(@arg_list);
$self->merge_args($action, %$args);
$self->merge_modulebuildrc( $action, %$args );
}
my ($self, $class, $seen) = @_;
$class ||= ref($self) || $self;
$seen ||= {};
-
+
no strict 'refs';
my @super = grep {not $seen->{$_}++} $class, @{ $class . '::ISA' };
return @super, map {$self->super_classes($_,$seen)} @super;
my %actions;
no strict 'refs';
-
+
foreach my $class ($self->super_classes) {
foreach ( keys %{ $class . '::' } ) {
$actions{$1}++ if /^ACTION_(\w+)/;
$@ = "Couldn't find any docs for action '$action'";
return;
}
-
+
return join '', @docs;
}
sub ACTION_help {
my ($self) = @_;
my $actions = $self->known_actions;
-
+
if (@{$self->{args}{ARGV}}) {
my $msg = eval {$self->get_action_docs($self->{args}{ARGV}[0], $actions)};
print $@ ? "$@\n" : $msg;
Usage: $0 <action> arg1=value arg2=value ...
Example: $0 test verbose=1
-
+
Actions defined:
EOF
-
+
print $self->_action_listing($actions);
print "\nRun `Build help <action>` for details on an individual action.\n";
# Flow down columns, not across rows
my @actions = sort keys %$actions;
@actions = map $actions[($_ + ($_ % 2) * @actions) / 2], 0..$#actions;
-
+
my $out = '';
while (my ($one, $two) = splice @actions, 0, 2) {
$out .= sprintf(" %-12s %-12s\n", $one, $two||'');
sub ACTION_retest {
my ($self) = @_;
-
+
# Protect others against our @INC changes
local @INC = @INC;
my $p = $self->{properties};
my @types = (
- (exists($args{type}) ? $args{type} : ()),
+ (exists($args{type}) ? $args{type} : ()),
(exists($args{types}) ? @{$args{types}} : ()),
);
@types or croak "need some types of tests to check";
$self->do_tests;
}
+# Test::Harness dies on failure but TAP::Harness does not, so we must
+# die if running under TAP::Harness
sub do_tests {
my $self = shift;
if(@$tests) {
my $args = $self->tap_harness_args;
if($self->use_tap_harness or ($args and %$args)) {
- $self->run_tap_harness($tests);
+ my $aggregate = $self->run_tap_harness($tests);
+ if ( $aggregate->has_errors ) {
+ die "Errors in testing. Cannot continue.\n";
+ }
}
else {
$self->run_test_harness($tests);
# TODO allow the test @INC to be set via our API?
- TAP::Harness->new({
+ my $aggregate = TAP::Harness->new({
lib => [@INC],
verbosity => $self->{properties}{verbose},
switches => [ $self->harness_switches ],
%{ $self->tap_harness_args },
})->runtests(@$tests);
+
+ return $aggregate;
}
sub run_test_harness {
my $pm_files = $self->rscan_dir
(File::Spec->catdir($self->blib, 'lib'), file_qr('\.pm$') );
my $cover_files = $self->rscan_dir('cover_db', sub {-f $_ and not /\.html$/});
-
+
$self->do_system(qw(cover -delete))
unless $self->up_to_date($pm_files, $cover_files)
&& $self->up_to_date($self->test_files, $cover_files);
}
- local $Test::Harness::switches =
- local $Test::Harness::Switches =
+ local $Test::Harness::switches =
+ local $Test::Harness::Switches =
local $ENV{HARNESS_PERL_SWITCHES} = "-MDevel::Cover";
$self->depends_on('test');
sub ACTION_code {
my ($self) = @_;
-
+
# All installable stuff gets created in blib/ .
# Create blib/arch to keep blib.pm happy
my $blib = $self->blib;
$self->add_to_cleanup($blib);
File::Path::mkpath( File::Spec->catdir($blib, 'arch') );
-
+
if (my $split = $self->autosplit) {
$self->autosplit_file($_, $blib) for ref($split) ? @$split : ($split);
}
-
+
foreach my $element (@{$self->build_elements}) {
my $method = "process_${element}_files";
$method = "process_files_by_extension" unless $self->can($method);
sub ACTION_build {
my $self = shift;
+ $self->log_info("Building " . $self->dist_name . "\n");
$self->depends_on('code');
$self->depends_on('docs');
}
sub process_files_by_extension {
my ($self, $ext) = @_;
-
+
my $method = "find_${ext}_files";
my $files = $self->can($method) ? $self->$method() : $self->_find_file_by_type($ext, 'lib');
-
+
while (my ($file, $dest) = each %$files) {
$self->copy_if_modified(from => $file, to => File::Spec->catfile($self->blib, $dest) );
}
my $self = shift;
my $p = $self->{properties};
return unless $p->{c_source};
-
+
push @{$p->{include_dirs}}, $p->{c_source};
-
- my $files = $self->rscan_dir($p->{c_source}, file_qr('\.c(pp)?$'));
+
+ my $files = $self->rscan_dir($p->{c_source}, file_qr('\.c(c|p|pp|xx|\+\+)?$'));
foreach my $file (@$files) {
push @{$p->{objects}}, $self->compile_c($file);
}
}
+sub process_share_dir_files {
+ my $self = shift;
+ my $files = $self->_find_share_dir_files;
+ return unless $files;
+
+ # root for all File::ShareDir paths
+ my $share_prefix = File::Spec->catdir($self->blib, qw/lib auto share/);
+
+ # copy all share files to blib
+ while (my ($file, $dest) = each %$files) {
+ $self->copy_if_modified(
+ from => $file, to => File::Spec->catfile( $share_prefix, $dest )
+ );
+ }
+}
+
+sub _find_share_dir_files {
+ my $self = shift;
+ my $share_dir = $self->share_dir;
+ return unless $share_dir;
+
+ my @file_map;
+ if ( $share_dir->{dist} ) {
+ my $prefix = File::Spec->catdir( "dist", $self->dist_name );
+ push @file_map, $self->_share_dir_map( $prefix, $share_dir->{dist} );
+ }
+
+ if ( $share_dir->{module} ) {
+ for my $mod ( keys %{ $share_dir->{module} } ) {
+ (my $altmod = $mod) =~ s{::}{-}g;
+ my $prefix = File::Spec->catdir("module", $altmod);
+ push @file_map, $self->_share_dir_map($prefix, $share_dir->{module}{$mod});
+ }
+ }
+
+ return { @file_map };
+}
+
+sub _share_dir_map {
+ my ($self, $prefix, $list) = @_;
+ my %files;
+ for my $dir ( @$list ) {
+ for my $f ( @{ $self->rscan_dir( $dir, sub {-f} )} ) {
+ $files{File::Spec->canonpath($f)} = File::Spec->catfile(
+ $prefix, File::Spec->abs2rel( $f, $dir )
+ );
+ }
+ }
+ return %files;
+}
+
sub process_PL_files {
my ($self) = @_;
my $files = $self->find_PL_files;
-
+
while (my ($file, $to) = each %$files) {
unless ($self->up_to_date( $file, $to )) {
$self->run_perl_script($file, [], [@$to]) or die "$file failed";
my $script_dir = File::Spec->catdir($self->blib, 'script');
File::Path::mkpath( $script_dir );
-
+
foreach my $file (keys %$files) {
my $result = $self->copy_if_modified($file, $script_dir, 'flatten') or next;
$self->fix_shebang_line($result) unless $self->is_vmsish;
my $self = shift;
if (my $files = $self->{properties}{PL_files}) {
# 'PL_files' is given as a Unix file spec, so we localize_file_path().
-
+
if (UNIVERSAL::isa($files, 'ARRAY')) {
return { map {$_, [/^(.*)\.PL$/]}
map $self->localize_file_path($_),
die "'PL_files' must be a hash reference or array reference";
}
}
-
+
return unless -d 'lib';
return { map {$_, [/^(.*)\.PL$/i ]} @{ $self->rscan_dir('lib',
file_qr('\.PL$')) } };
# meaningless, but we preserve if present.
return { map {$self->localize_file_path($_), $files->{$_}} keys %$files };
}
-
+
# No default location for script files
return {};
}
$files = [map { -d $_ ? $self->expand_test_dir($_) : $_ }
map glob,
$self->split_like_shell($files)];
-
+
# Always given as a Unix file spec.
return [ map $self->localize_file_path($_), @$files ];
-
+
} else {
# Find all possible tests in t/ or test.pl
my @tests;
sub _find_file_by_type {
my ($self, $type, $dir) = @_;
-
+
if (my $files = $self->{properties}{"${type}_files"}) {
# Always given as a Unix file spec
return { map $self->localize_file_path($_), %$files };
}
-
+
return {} unless -d $dir;
return { map {$_, $_}
map $self->localize_file_path($_),
sub fix_shebang_line { # Adapted from fixin() in ExtUtils::MM_Unix 1.35
my ($self, @files) = @_;
my $c = ref($self) ? $self->{config} : 'Module::Build::Config';
-
+
my ($does_shbang) = $c->get('sharpbang') =~ /^\s*\#\!/;
for my $file (@files) {
my $FIXIN = IO::File->new($file) or die "Can't process '$file': $!";
local $/ = "\n";
chomp(my $line = <$FIXIN>);
next unless $line =~ s/^\s*\#!\s*//; # Not a shbang file.
-
+
my ($cmd, $arg) = (split(' ', $line, 2), '');
next unless $cmd =~ /perl/i;
my $interpreter = $self->{properties}{perl};
-
+
$self->log_verbose("Changing sharpbang in $file to $interpreter");
my $shb = '';
$shb .= $c->get('sharpbang')."$interpreter $arg\n" if $does_shbang;
-
+
# I'm not smart enough to know the ramifications of changing the
# embedded newlines here to \n, so I leave 'em in.
$shb .= qq{
eval 'exec $interpreter $arg -S \$0 \${1+"\$\@"}'
if 0; # not running under some shell
} unless $self->is_windowsish; # this won't work on win32, so don't
-
+
my $FIXOUT = IO::File->new(">$file.new")
or die "Can't create new $file: $!\n";
-
+
# Print out the new #! line (or equivalent).
local $\;
undef $/; # Was localized above
print $FIXOUT $shb, <$FIXIN>;
close $FIXIN;
close $FIXOUT;
-
+
rename($file, "$file.bak")
or die "Can't rename $file to $file.bak: $!";
-
+
rename("$file.new", $file)
or die "Can't rename $file.new to $file: $!";
-
+
$self->delete_filetree("$file.bak")
or $self->log_warn("Couldn't clean up $file.bak, leaving it there");
-
+
$self->do_system($c->get('eunicefix'), $file) if $c->get('eunicefix') ne ':';
}
}
sub ACTION_testpod {
my $self = shift;
$self->depends_on('docs');
-
+
eval q{use Test::Pod 0.95; 1}
or die "The 'testpod' action requires Test::Pod version 0.95";
my $self = shift;
$self->depends_on('docs');
-
+
eval q{use Test::Pod::Coverage 1.00; 1}
or die "The 'testpodcoverage' action requires ",
"Test::Pod::Coverage version 1.00";
$self->config( 'man1ext' );
my $outfile = File::Spec->catfile($mandir, $manpage);
next if $self->up_to_date( $file, $outfile );
- $self->log_info("Manifying $file -> $outfile\n");
+ $self->log_verbose("Manifying $file -> $outfile\n");
eval { $parser->parse_from_file( $file, $outfile ); 1 }
- or $self->log_warn("Error creating '$outfile': $@\n");
+ or $self->log_warn("Error creating '$outfile': $@\n");
$files->{$file} = $outfile;
}
}
$self->config( 'man3ext' );
my $outfile = File::Spec->catfile( $mandir, $manpage);
next if $self->up_to_date( $file, $outfile );
- $self->log_info("Manifying $file -> $outfile\n");
+ $self->log_verbose("Manifying $file -> $outfile\n");
eval { $parser->parse_from_file( $file, $outfile ); 1 }
- or $self->log_warn("Error creating '$outfile': $@\n");
+ or $self->log_warn("Error creating '$outfile': $@\n");
$files->{$file} = $outfile;
}
}
sub contains_pod {
my ($self, $file) = @_;
return '' unless -T $file; # Only look at text files
-
+
my $fh = IO::File->new( $file ) or die "Can't open $file: $!";
while (my $line = <$fh>) {
return 1 if $line =~ /^\=(?:head|pod|item)/;
}
-
+
return '';
}
foreach my $type ( qw(bin lib) ) {
my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
- exclude =>
+ exclude =>
[ file_qr('\.(?:bat|com|html)$') ] );
next unless %$files;
push( @opts, "--css=$path2root/" . $self->html_css) if $self->html_css;
}
- $self->log_info("HTMLifying $infile -> $outfile\n");
+ $self->log_verbose("HTMLifying $infile -> $outfile\n");
$self->log_verbose("pod2html @opts\n");
- eval { Pod::Html::pod2html(@opts); 1 }
+ eval { Pod::Html::pod2html(@opts); 1 }
or $self->log_warn("pod2html @opts failed: $@");
}
my $self = shift;
my ($vol, $dirs, $file) = File::Spec->splitpath( shift );
my @dirs = File::Spec->splitdir( File::Spec->canonpath($dirs) );
-
+
# Remove known exts from the base name
$file =~ s/\.p(?:od|m|l)\z//i;
-
+
return join( $self->manpage_separator, @dirs, $file );
}
my @flags = @{$self->{args}{ARGV}};
@flags = $self->split_like_shell($self->{args}{flags} || '') unless @flags;
-
+
my $installmap = $self->install_map;
delete $installmap->{read};
delete $installmap->{write};
while (my $localdir = each %$installmap) {
my @localparts = File::Spec->splitdir($localdir);
my $files = $self->rscan_dir($localdir, sub {-f});
-
+
foreach my $file (@$files) {
my @parts = File::Spec->splitdir($file);
@parts = @parts[@localparts .. $#parts]; # Get rid of blib/lib or similar
-
+
my $installed = Module::Build::ModuleInfo->find_module_by_name(
join('::', @parts), \@myINC );
if (not $installed) {
print "Only in lib: $file\n";
next;
}
-
+
my $status = File::Compare::compare($installed, $file);
next if $status == 0; # Files are the same
die "Can't compare $installed and $file: $!" if $status == -1;
-
+
if ($file =~ $text_suffix) {
$self->do_system('diff', @flags, $installed, $file);
} else {
my ($self) = @_;
require ExtUtils::Install;
$self->depends_on('build');
- ExtUtils::Install::install($self->install_map, !$self->quiet, 0, $self->{args}{uninst}||0);
+ ExtUtils::Install::install($self->install_map, $self->verbose, 0, $self->{args}{uninst}||0);
}
sub ACTION_fakeinstall {
sub ACTION_versioninstall {
my ($self) = @_;
-
+
die "You must have only.pm 0.25 or greater installed for this operation: $@\n"
unless eval { require only; 'only'->VERSION(0.25); 1 };
-
+
$self->depends_on('build');
-
+
my %onlyargs = map {exists($self->{args}{$_}) ? ($_ => $self->{args}{$_}) : ()}
qw(version versionlib);
only::install::install(%onlyargs);
}
+sub ACTION_installdeps {
+ my ($self) = @_;
+
+ # XXX include feature prerequisites as optional prereqs?
+
+ my $info = $self->_enum_prereqs;
+ if (! $info ) {
+ $self->log_info( "No prerequisites detected\n" );
+ return;
+ }
+
+ my $failures = $self->prereq_failures($info);
+ if ( ! $failures ) {
+ $self->log_info( "All prerequisites satisfied\n" );
+ return;
+ }
+
+ my @install;
+ while (my ($type, $prereqs) = each %$failures) {
+ if($type =~ m/^(?:\w+_)?requires$/) {
+ push(@install, keys %$prereqs);
+ next;
+ }
+ $self->log_info("Checking optional dependencies:\n");
+ while (my ($module, $status) = each %$prereqs) {
+ push(@install, $module) if($self->y_n("Install $module?", 'y'));
+ }
+ }
+
+ return unless @install;
+
+ my ($command, @opts) = $self->split_like_shell($self->cpan_client);
+
+ # relative command should be relative to our active Perl
+ # so we need to locate that command
+ if ( ! File::Spec->file_name_is_absolute( $command ) ) {
+ my @bindirs = File::Basename::dirname($self->perl);
+ push @bindirs, map {$self->config->{"install${_}bin"}} '','site','vendor';
+ for my $d ( @bindirs ) {
+ my $abs_cmd = $self->find_command(File::Spec->catfile( $d, $command ));
+ if ( defined $abs_cmd ) {
+ $command = $abs_cmd;
+ last;
+ }
+ }
+ }
+
+ if ( ! -x $command ) {
+ die "cpan_client '$command' is not executable\n";
+ }
+
+ $self->do_system($command, @opts, @install);
+}
+
sub ACTION_clean {
my ($self) = @_;
+ $self->log_info("Cleaning up build files\n");
foreach my $item (map glob($_), $self->cleanup) {
$self->delete_filetree($item);
}
sub ACTION_realclean {
my ($self) = @_;
$self->depends_on('clean');
- $self->delete_filetree($self->config_dir, $self->build_script);
+ $self->log_info("Cleaning up configuration files\n");
+ $self->delete_filetree(
+ $self->config_dir, $self->mymetafile, $self->build_script
+ );
}
sub ACTION_ppd {
my ($self) = @_;
+
require Module::Build::PPMMaker;
my $ppd = Module::Build::PPMMaker->new();
my $file = $ppd->make_ppd(%{$self->{args}}, build => $self);
);
return();
}
-
+
$self->depends_on( 'build' );
return PAR::Dist::blib_to_par(
sub ACTION_dist {
my ($self) = @_;
-
+
$self->depends_on('distdir');
-
+
my $dist_dir = $self->dist_dir;
-
+
$self->make_tarball($dist_dir);
$self->delete_filetree($dist_dir);
}
sub ACTION_distcheck {
my ($self) = @_;
+ $self->_check_manifest_skip;
+
require ExtUtils::Manifest;
local $^W; # ExtUtils::Manifest is not warnings clean.
my ($missing, $extra) = ExtUtils::Manifest::fullcheck();
}
}
+sub _check_mymeta_skip {
+ my $self = shift;
+ my $maniskip = shift || 'MANIFEST.SKIP';
+
+ require ExtUtils::Manifest;
+ local $^W; # ExtUtils::Manifest is not warnings clean.
+
+ # older ExtUtils::Manifest had a private _maniskip
+ my $skip_factory = ExtUtils::Manifest->can('maniskip')
+ || ExtUtils::Manifest->can('_maniskip');
+
+ my $mymetafile = $self->mymetafile;
+ # we can't check it, just add it anyway to be safe
+ unless ( $skip_factory && $skip_factory->($maniskip)->($mymetafile) ) {
+ $self->log_warn("File '$maniskip' does not include '$mymetafile'. Adding it now.\n");
+ $self->_append_maniskip("^$mymetafile\$", $maniskip);
+ }
+}
+
sub _add_to_manifest {
my ($self, $manifest, $lines) = @_;
$lines = [$lines] unless ref $lines;
my $mode = (stat $manifest)[2];
chmod($mode | oct(222), $manifest) or die "Can't make $manifest writable: $!";
-
+
my $fh = IO::File->new("< $manifest") or die "Can't read $manifest: $!";
my $last_line = (<$fh>)[-1] || "\n";
my $has_newline = $last_line =~ /\n$/;
close $fh;
chmod($mode, $manifest);
- $self->log_info(map "Added to $manifest: $_\n", @$lines);
+ $self->log_verbose(map "Added to $manifest: $_\n", @$lines);
}
sub _sign_dir {
$self->log_warn("Couldn't load Module::Signature for 'distsign' action:\n $@\n");
return;
}
-
+
# Add SIGNATURE to the MANIFEST
{
my $manifest = File::Spec->catfile($dir, 'MANIFEST');
die "Signing a distribution requires a MANIFEST file" unless -e $manifest;
$self->_add_to_manifest($manifest, "SIGNATURE Added here by Module::Build");
}
-
+
# Would be nice if Module::Signature took a directory argument.
-
+
$self->_do_in_dir($dir, sub {local $Module::Signature::Quiet = 1; Module::Signature::sign()});
}
sub ACTION_skipcheck {
my ($self) = @_;
-
+
require ExtUtils::Manifest;
local $^W; # ExtUtils::Manifest is not warnings clean.
ExtUtils::Manifest::skipcheck();
sub ACTION_distclean {
my ($self) = @_;
-
+
$self->depends_on('realclean');
$self->depends_on('distcheck');
}
my $self = shift;
$self->log_info("Creating LICENSE file\n");
+ if ( ! $self->_mb_feature('license_creation') ) {
+ $self->_warn_mb_feature_deps('license_creation');
+ die "Aborting.\n";
+ }
+
my $l = $self->license
or die "No license specified";
my $class = "Software::License::$key";
eval "use $class; 1"
- or die "Can't load Software::License to create LICENSE file: $@";
+ or die "Can't load Software::License::$key to create LICENSE file: $@";
$self->delete_filetree('LICENSE');
return;
}
- if ( eval {require Pod::Readme; 1} ) {
+ # work around some odd Pod::Readme->new() failures in test reports by
+ # confirming that new() is available
+ if ( eval {require Pod::Readme; Pod::Readme->can('new') } ) {
$self->log_info("Creating README using Pod::Readme\n");
my $parser = Pod::Readme->new;
}
}
+sub do_create_bundle_inc {
+ my $self = shift;
+ my $dist_inc = File::Spec->catdir( $self->dist_dir, 'inc' );
+ require inc::latest;
+ inc::latest->write($dist_inc, @{$self->bundle_inc_preload});
+ inc::latest->bundle_module($_, $dist_inc) for @{$self->bundle_inc};
+ return 1;
+}
+
sub ACTION_distdir {
my ($self) = @_;
+ if ( @{$self->bundle_inc} && ! $self->_mb_feature('inc_bundling_support') ) {
+ $self->_warn_mb_feature_deps('inc_bundling_support');
+ die "Aborting.\n";
+ }
+
$self->depends_on('distmeta');
+ # Must not include MYMETA
+ $self->_check_mymeta_skip('MANIFEST.SKIP');
+
my $dist_files = $self->_read_manifest('MANIFEST')
- or die "Can't create distdir without a MANIFEST file - run 'manifest' action first";
+ or die "Can't create distdir without a MANIFEST file - run 'manifest' action first.\n";
delete $dist_files->{SIGNATURE}; # Don't copy, create a fresh one
die "No files found in MANIFEST - try running 'manifest' action?\n"
unless ($dist_files and keys %$dist_files);
my $metafile = $self->metafile;
$self->log_warn("*** Did you forget to add $metafile to the MANIFEST?\n")
unless exists $dist_files->{$metafile};
-
+
my $dist_dir = $self->dist_dir;
$self->delete_filetree($dist_dir);
$self->log_info("Creating $dist_dir\n");
$self->add_to_cleanup($dist_dir);
-
+
foreach my $file (keys %$dist_files) {
my $new = $self->copy_if_modified(from => $file, to_dir => $dist_dir, verbose => 0);
}
-
+
+ $self->do_create_bundle_inc if @{$self->bundle_inc};
+
$self->_sign_dir($dist_dir) if $self->{properties}{sign};
}
my $self = shift;
require ExtUtils::Manifest;
- return ExtUtils::Manifest->VERSION >= 1.50 ? 1 : 0;
- return 0;
+ return eval { ExtUtils::Manifest->VERSION(1.50); 1 };
}
}
+
+sub _append_maniskip {
+ my $self = shift;
+ my $skip = shift;
+ my $file = shift || 'MANIFEST.SKIP';
+ return unless defined $skip && length $skip;
+ my $fh = IO::File->new(">> $file")
+ or die "Can't open $file: $!";
+
+ print $fh "$skip\n";
+ $fh->close();
+}
+
sub _write_default_maniskip {
my $self = shift;
my $file = shift || 'MANIFEST.SKIP';
: $self->_slurp( $self->_default_maniskip );
$content .= <<'EOF';
+# Avoid configuration metadata file
+^MYMETA\.$
# Avoid Module::Build generated and utility files.
\bBuild$
return;
}
-sub ACTION_manifest {
+sub _check_manifest_skip {
my ($self) = @_;
my $maniskip = 'MANIFEST.SKIP';
- unless ( -e 'MANIFEST' || -e $maniskip ) {
+
+ if ( ! -e $maniskip ) {
$self->log_warn("File '$maniskip' does not exist: Creating a default '$maniskip'\n");
$self->_write_default_maniskip($maniskip);
}
+ else {
+ # MYMETA must not be added to MANIFEST, so always confirm the skip
+ $self->_check_mymeta_skip( $maniskip );
+ }
+
+ return;
+}
+
+sub ACTION_manifest {
+ my ($self) = @_;
+
+ $self->_check_manifest_skip;
require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean.
local ($^W, $ExtUtils::Manifest::Quiet) = (0,1);
return @files;
}
+sub share_dir {
+ my $self = shift;
+ my $p = $self->{properties};
+
+ $p->{share_dir} = shift if @_;
+
+ # Always coerce to proper hash form
+ if ( ! defined $p->{share_dir} ) {
+ # not set -- use default 'share' dir if exists
+ $p->{share_dir} = { dist => [ 'share' ] } if -d 'share';
+ }
+ elsif ( ! ref $p->{share_dir} ) {
+ # scalar -- treat as a single 'dist' directory
+ $p->{share_dir} = { dist => [ $p->{share_dir} ] };
+ }
+ elsif ( ref $p->{share_dir} eq 'ARRAY' ) {
+ # array -- treat as a list of 'dist' directories
+ $p->{share_dir} = { dist => $p->{share_dir} };
+ }
+ elsif ( ref $p->{share_dir} eq 'HASH' ) {
+ # hash -- check structure
+ my $share_dir = $p->{share_dir};
+ # check dist key
+ if ( defined $share_dir->{dist} ) {
+ if ( ! ref $share_dir->{dist} ) {
+ # scalar, so upgrade to arrayref
+ $share_dir->{dist} = [ $share_dir->{dist} ];
+ }
+ elsif ( ref $share_dir->{dist} ne 'ARRAY' ) {
+ die "'dist' key in 'share_dir' must be scalar or arrayref";
+ }
+ }
+ # check module key
+ if ( defined $share_dir->{module} ) {
+ my $mod_hash = $share_dir->{module};
+ if ( ref $mod_hash eq 'HASH' ) {
+ for my $k ( keys %$mod_hash ) {
+ if ( ! ref $mod_hash->{$k} ) {
+ $mod_hash->{$k} = [ $mod_hash->{$k} ];
+ }
+ elsif( ref $mod_hash->{$k} ne 'ARRAY' ) {
+ die "modules in 'module' key of 'share_dir' must be scalar or arrayref";
+ }
+ }
+ }
+ else {
+ die "'module' key in 'share_dir' must be hashref";
+ }
+ }
+ }
+ else {
+ die "'share_dir' must be hashref, arrayref or string";
+ }
+
+ return $p->{share_dir};
+}
+
sub script_files {
my $self = shift;
}
my %pl_files = map {
- File::Spec->canonpath( File::Spec->case_tolerant ? uc $_ : $_ ) => 1
+ File::Spec->canonpath( File::Spec->case_tolerant ? uc $_ : $_ ) => 1
} keys %{ $self->PL_files || {} };
my @bin_files = $self->_files_in('bin');
my %bin_map = map {
- $_ => File::Spec->canonpath( File::Spec->case_tolerant ? uc $_ : $_ )
+ $_ => File::Spec->canonpath( File::Spec->case_tolerant ? uc $_ : $_ )
} @bin_files;
return $_ = { map {$_ => 1} grep !$pl_files{$bin_map{$_}}, @bin_files };
my %licenses = (
perl => 'Perl_5',
apache => 'Apache_2_0',
+ apache_1_1 => 'Apache_1_1',
artistic => 'Artistic_1_0',
artistic_2 => 'Artistic_2_0',
lgpl => 'LGPL_2_1',
my %license_urls = (
perl => 'http://dev.perl.org/licenses/',
apache => 'http://apache.org/licenses/LICENSE-2.0',
+ apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1',
artistic => 'http://opensource.org/licenses/artistic-license.php',
artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php',
lgpl => 'http://opensource.org/licenses/lgpl-license.php',
sub do_create_metafile {
my $self = shift;
return if $self->{wrote_metadata};
-
+
my $p = $self->{properties};
my $metafile = $self->metafile;
-
+
unless ($p->{license}) {
$self->log_warn("No license specified, setting license = 'unknown'\n");
$p->{license} = 'unknown';
push @INC, File::Spec->catdir($self->blib, 'lib');
}
- if ( $self->write_metafile( $self->metafile, $self->generate_metadata ) ) {
+ if ( $self->write_metafile( $self->metafile, $self->prepare_metadata( fatal => 1 ) ) ) {
$self->{wrote_metadata} = 1;
$self->_add_to_manifest('MANIFEST', $metafile);
}
return 1;
}
-sub generate_metadata {
- my $self = shift;
- my $node = {};
-
- if ($self->_mb_feature('YAML_support')) {
- require YAML;
- require YAML::Node;
- # We use YAML::Node to get the order nice in the YAML file.
- $self->prepare_metadata( $node = YAML::Node->new({}) );
- } else {
- require Module::Build::YAML;
- my @order_keys;
- $self->prepare_metadata($node, \@order_keys);
- $node->{_order} = \@order_keys;
- }
- return $node;
-}
-
sub write_metafile {
my $self = shift;
my ($metafile, $node) = @_;
+ my $yaml;
if ($self->_mb_feature('YAML_support')) {
# XXX this is probably redundant, but stick with it
- require YAML;
- require YAML::Node;
- delete $node->{_order}; # XXX also probably redundant, but for safety
- # YAML API changed after version 0.30
- my $yaml_sub = $YAML::VERSION le '0.30' ? \&YAML::StoreFile : \&YAML::DumpFile;
- $yaml_sub->( $metafile, $node );
+ require YAML::Tiny;
+ $yaml = YAML::Tiny->new($node);
} else {
- # XXX probably redundant
require Module::Build::YAML;
- &Module::Build::YAML::DumpFile($metafile, $node);
+ $yaml = Module::Build::YAML->new($node);
}
- return 1;
+ my $result = $yaml->write($metafile)
+ or $self->log_warn( "Error writing '$metafile': " . $yaml->errstr . "\n");
+ return $result;
}
sub normalize_version {
if ( $version =~ /[=<>!,]/ ) { # logic, not just version
# take as is without modification
}
- elsif ( ref $version eq 'version' ||
+ elsif ( ref $version eq 'version' ||
ref $version eq 'Module::Build::Version' ) { # version objects
$version = $version->is_qv ? $version->normal : $version->stringify;
}
}
sub prepare_metadata {
- my ($self, $node, $keys) = @_;
+ my ($self, %args) = @_;
+ my $fatal = $args{fatal} || 0;
my $p = $self->{properties};
+ my $node = {};
# A little helper sub
my $add_node = sub {
my ($name, $val) = @_;
$node->{$name} = $val;
- push @$keys, $name if $keys;
};
foreach (qw(dist_name dist_version dist_author dist_abstract license)) {
(my $name = $_) =~ s/^dist_//;
$add_node->($name, $self->$_());
- die "ERROR: Missing required field '$_' for META.yml\n"
- unless defined($node->{$name}) && length($node->{$name});
+ unless ( defined($node->{$name}) && length($node->{$name}) ) {
+ my $err = "ERROR: Missing required field '$_' for metafile\n";
+ if ( $fatal ) {
+ die $err;
+ }
+ else {
+ $self->log_warn($err);
+ }
+ }
}
- $node->{version} = $self->normalize_version($node->{version});
+ $node->{version} = $self->normalize_version($node->{version});
if (defined( my $l = $self->license )) {
- die "Unknown license string '$l'"
- unless exists $self->valid_licenses->{ $l };
+ unless ( exists $self->valid_licenses->{ $l } ) {
+ my $err = "Unknown license string '$l'";
+ if ( $fatal ) {
+ die $err;
+ }
+ else {
+ $self->log_warn($err);
+ }
+ }
if (my $key = $self->valid_licenses->{ $l }) {
my $class = "Software::License::$key";
- if (eval "use $class; 1") {
+ if (eval "require Software::License; require $class; 1") {
# S::L requires a 'holder' key
$node->{resources}{license} = $class->new({holder=>"nobody"})->url;
}
# copy prereq data structures so we can modify them before writing to META
my %prereq_types;
for my $type ( 'configure_requires', @{$self->prereq_action_types} ) {
- if (exists $p->{$type}) {
+ if (exists $p->{$type}) {
for my $mod ( keys %{ $p->{$type} } ) {
- $prereq_types{$type}{$mod} =
+ $prereq_types{$type}{$mod} =
$self->normalize_version($p->{$type}{$mod});
}
}
}
- # add current Module::Build to configure_requires if there
- # isn't one already specified (but not ourself, so we're not circular)
- if ( $self->dist_name ne 'Module-Build'
- && $self->auto_configure_requires
- && ! exists $prereq_types{'configure_requires'}{'Module::Build'}
- ) {
- (my $ver = $VERSION) =~ s/^(\d+\.\d\d).*$/$1/; # last major release only
- $prereq_types{configure_requires}{'Module::Build'} = $ver;
- }
-
for my $t ( keys %prereq_types ) {
$add_node->($t, $prereq_types{$t});
}
my $pkgs = eval { $self->find_dist_packages };
if ($@) {
$self->log_warn("$@\nWARNING: Possible missing or corrupt 'MANIFEST' file.\n" .
- "Nothing to enter for 'provides' field in META.yml\n");
+ "Nothing to enter for 'provides' field in metafile.\n");
} else {
$node->{provides} = $pkgs if %$pkgs;
}
$add_node->('generated_by', "Module::Build version $Module::Build::VERSION");
- $add_node->('meta-spec',
+ $add_node->('meta-spec',
{version => '1.4',
url => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
});
# private stock.
my $manifest = $self->_read_manifest('MANIFEST')
- or die "Can't find dist packages without a MANIFEST file - run 'manifest' action first";
+ or die "Can't find dist packages without a MANIFEST file\nRun 'Build manifest' to generate one\n";
# Localize
my %dist_files = map { $self->localize_file_path($_) => $_ }
keys %$manifest;
- my @pm_files = grep {exists $dist_files{$_}} keys %{ $self->find_pm_files };
+ my @pm_files = grep { $_ !~ m{^t} } # skip things in t/
+ grep {exists $dist_files{$_}}
+ keys %{ $self->find_pm_files };
+
+ return $self->find_packages_in_files(\@pm_files, \%dist_files);
+}
+
+sub find_packages_in_files {
+ my ($self, $file_list, $filename_map) = @_;
# First, we enumerate all packages & versions,
# separating into primary & alternative candidates
my( %prime, %alt );
- foreach my $file (@pm_files) {
- next if $dist_files{$file} =~ m{^t/}; # Skip things in t/
-
- my @path = split( /\//, $dist_files{$file} );
+ foreach my $file (@{$file_list}) {
+ my $mapped_filename = $filename_map->{$file};
+ my @path = split( /\//, $mapped_filename );
(my $prime_package = join( '::', @path[1..$#path] )) =~ s/\.pm$//;
my $pm_info = Module::Build::ModuleInfo->new_from_file( $file );
my $version = $pm_info->version( $package );
if ( $package eq $prime_package ) {
- if ( exists( $prime{$package} ) ) {
- # M::B::ModuleInfo will handle this conflict
- die "Unexpected conflict in '$package'; multiple versions found.\n";
- } else {
- $prime{$package}{file} = $dist_files{$file};
+ if ( exists( $prime{$package} ) ) {
+ # M::B::ModuleInfo will handle this conflict
+ die "Unexpected conflict in '$package'; multiple versions found.\n";
+ } else {
+ $prime{$package}{file} = $mapped_filename;
$prime{$package}{version} = $version if defined( $version );
}
} else {
- push( @{$alt{$package}}, {
- file => $dist_files{$file},
- version => $version,
- } );
+ push( @{$alt{$package}}, {
+ file => $mapped_filename,
+ version => $version,
+ } );
}
}
}
sub make_tarball {
my ($self, $dir, $file) = @_;
$file ||= $dir;
-
+
$self->log_info("Creating $file.tar.gz\n");
-
+
if ($self->{args}{tar}) {
my $tar_flags = $self->verbose ? 'cvf' : 'cf';
$self->do_system($self->split_like_shell($self->{args}{tar}), $tar_flags, "$file.tar", $dir);
$self->do_system($self->split_like_shell($self->{args}{gzip}), "$file.tar") if $self->{args}{gzip};
} else {
- eval { require Archive::Tar && Archive::Tar->VERSION(1.08); 1 }
- or die "You must install Archive::Tar to make a distribution tarball\n".
+ eval { require Archive::Tar && Archive::Tar->VERSION(1.09); 1 }
+ or die "You must install Archive::Tar 1.09+ to make a distribution tarball\n".
"or specify a binary tar program with the '--tar' option.\n".
"See the documentation for the 'dist' action.\n";
+ my $files = $self->rscan_dir($dir);
+
# Archive::Tar versions >= 1.09 use the following to enable a compatibility
# hack so that the resulting archive is compatible with older clients.
- $Archive::Tar::DO_NOT_USE_PREFIX = 0;
+ # If no file path is 100 chars or longer, we disable the prefix field
+ # for maximum compatibility. If there are any long file paths then we
+ # need the prefix field after all.
+ $Archive::Tar::DO_NOT_USE_PREFIX =
+ (grep { length($_) >= 100 } @$files) ? 0 : 1;
- my $files = $self->rscan_dir($dir);
my $tar = Archive::Tar->new;
$tar->add_files(@$files);
for my $f ($tar->get_files) {
# or original_prefix('lib' => $value);
my ($self, $key, $value) = @_;
# update property before merging with defaults
- if ( @_ == 3 && defined $key) {
+ if ( @_ == 3 && defined $key) {
# $value can be undef; will mask default
$self->{properties}{original_prefix}{$key} = $value;
}
}
}
}
-
+
$map{read} = ''; # To keep ExtUtils::Install quiet
return \%map;
!ref($pattern) || (ref $pattern eq 'Regexp') ? sub {push @result, $File::Find::name if /$pattern/} :
ref($pattern) eq 'CODE' ? sub {push @result, $File::Find::name if $pattern->()} :
die "Unknown pattern type";
-
+
File::Find::find({wanted => $subr, no_chdir => 1}, $dir);
return \@result;
}
my $deleted = 0;
foreach (@_) {
next unless -e $_;
- $self->log_info("Deleting $_\n");
+ $self->log_verbose("Deleting $_\n");
File::Path::rmtree($_, 0, 0);
die "Couldn't remove '$_': $!\n" if -e $_;
$deleted++;
sub have_c_compiler {
my ($self) = @_;
-
+
my $p = $self->{properties};
- return $p->{have_compiler} if defined $p->{have_compiler};
-
+ return $p->{_have_c_compiler} if defined $p->{_have_c_compiler};
+
$self->log_verbose("Checking if compiler tools configured... ");
my $b = eval { $self->cbuilder };
- my $have = $b && $b->have_compiler;
+ my $have = $b && eval { $b->have_compiler };
$self->log_verbose($have ? "ok.\n" : "failed.\n");
- return $p->{have_compiler} = $have;
+ return $p->{_have_c_compiler} = $have;
}
sub compile_c {
my ($self, $file, %args) = @_;
- my $b = $self->cbuilder;
+ if ( ! $self->have_c_compiler ) {
+ die "Error: no compiler detected to compile '$file'. Aborting\n";
+ }
+
+ my $b = $self->cbuilder;
my $obj_file = $b->object_file($file);
$self->add_to_cleanup($obj_file);
return $obj_file if $self->up_to_date($file, $obj_file);
sub compile_xs {
my ($self, $file, %args) = @_;
-
- $self->log_info("$file -> $args{outfile}\n");
+
+ $self->log_verbose("$file -> $args{outfile}\n");
if (eval {require ExtUtils::ParseXS; 1}) {
-
+
ExtUtils::ParseXS::process_file(
filename => $file,
prototypes => 0,
);
} else {
# Ok, I give up. Just use backticks.
-
+
my $xsubpp = Module::Build::ModuleInfo->find_module_by_name('ExtUtils::xsubpp')
or die "Can't find ExtUtils::xsubpp in INC (@INC)";
-
+
my @typemaps;
push @typemaps, Module::Build::ModuleInfo->find_module_by_name(
'ExtUtils::typemap', \@INC
);
my $lib_typemap = Module::Build::ModuleInfo->find_module_by_name(
- 'typemap', [File::Basename::dirname($file)]
+ 'typemap', [File::Basename::dirname($file), File::Spec->rel2abs('.')]
);
push @typemaps, $lib_typemap if $lib_typemap;
@typemaps = map {+'-typemap', $_} @typemaps;
my $cf = $self->{config};
my $perl = $self->{properties}{perl};
-
+
my @command = ($perl, "-I".$cf->get('installarchlib'), "-I".$cf->get('installprivlib'), $xsubpp, '-noprototypes',
@typemaps, $file);
-
+
$self->log_info("@command\n");
my $fh = IO::File->new("> $args{outfile}") or die "Couldn't write $args{outfile}: $!";
print {$fh} $self->_backticks(@command);
sub split_like_shell {
my ($self, $string) = @_;
-
+
return () unless defined($string);
return @$string if UNIVERSAL::isa($string, 'ARRAY');
$string =~ s/^\s+|\s+$//g;
return () unless length($string);
-
+
return Text::ParseWords::shellwords($string);
}
sub do_system {
my ($self, @cmd) = @_;
- $self->log_info("@cmd\n");
+ $self->log_verbose("@cmd\n");
# Some systems proliferate huge PERL5LIBs, try to ameliorate:
my %seen;
my $sep = $self->config('path_sep');
- local $ENV{PERL5LIB} =
+ local $ENV{PERL5LIB} =
( !exists($ENV{PERL5LIB}) ? '' :
length($ENV{PERL5LIB}) < 500
? $ENV{PERL5LIB}
unless (defined $file and length $file) {
die "No 'from' parameter given to copy_if_modified";
}
-
- # makes no sense to replicate an absolute path, so assume flatten
+
+ # makes no sense to replicate an absolute path, so assume flatten
$args{flatten} = 1 if File::Spec->file_name_is_absolute( $file );
my $to_path;
} else {
die "No 'to' or 'to_dir' parameter given to copy_if_modified";
}
-
+
return if $self->up_to_date($file, $to_path); # Already fresh
{
# Create parent directories
File::Path::mkpath(File::Basename::dirname($to_path), 0, oct(777));
-
- $self->log_info("Copying $file -> $to_path\n") if $args{verbose};
-
+
+ $self->log_verbose("Copying $file -> $to_path\n");
+
if ($^O eq 'os2') {# copy will not overwrite; 0x1 = overwrite
chmod 0666, $to_path;
File::Copy::syscopy($file, $to_path, 0x1) or die "Can't copy('$file', '$to_path'): $!";
}
$most_recent_source = -M _ if -M _ < $most_recent_source;
}
-
+
foreach my $derived (@$derived) {
return 0 if -M $derived > $most_recent_source;
}
my ($self, $first, $second) = @_;
# File::Spec doesn't have an easy way to check whether one directory
# is inside another, unfortunately.
-
+
($first, $second) = map File::Spec->canonpath($_), ($first, $second);
my @first_dirs = File::Spec->splitdir($first);
my @second_dirs = File::Spec->splitdir($second);
return 0 if @second_dirs < @first_dirs;
-
+
my $is_same = ( File::Spec->case_tolerant
? sub {lc(shift()) eq lc(shift())}
: sub {shift() eq shift()} );
-
+
while (@first_dirs) {
return 0 unless $is_same->(shift @first_dirs, shift @second_dirs);
}
-
+
return 1;
}
--- /dev/null
+=head1 NAME
+
+Module::Build::Bundling - How to bundle Module::Build with a distribution
+
+=head1 SYNOPSIS
+
+ # Build.PL
+ use lib '.';
+ use inc::latest 'Module::Build';
+
+ Module::Build->new(
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ )->create_build_script;
+
+=head1 DESCRIPTION
+
+B<WARNING -- THIS IS AN EXPERIMENTAL FEATURE>
+
+In order to install a distribution using Module::Build, users must
+have Module::Build available on their systems. There are two ways
+to do this. The first way is to include Module::Build in the
+C<configure_requires> metadata field. This field is supported by
+recent versions L<CPAN> and L<CPANPLUS> and is a standard feature
+in the Perl core as of Perl 5.10.1. Module::Build now adds itself
+to C<configure_requires> by default.
+
+The second way supports older Perls that have not upgraded CPAN or
+CPANPLUS and involves bundling an entire copy of Module::Build
+into the distribution's C<inc/> directory. This is the same approach
+used by L<Module::Install>, a modern wrapper around ExtUtils::MakeMaker
+for Makefile.PL based distributions.
+
+The "trick" to making this work for Module::Build is making sure the
+highest version Module::Build is used, whether this is in C<inc/> or
+already installed on the user's system. This ensures that all necessary
+features are available as well as any new bug fixes. This is done using
+the new L<inc::latest> module.
+
+A "normal" Build.PL looks like this (with only the minimum required
+fields):
+
+ use Module::Build;
+
+ Module::Build->new(
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ )->create_build_script;
+
+A "bundling" Build.PL replaces the initial "use" line with a nearly
+transparent replacement:
+
+ use lib '.';
+ use inc::latest 'Module::Build';
+
+ Module::Build->new(
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ )->create_build_script;
+
+The inital C<lib> line ensures that the top-level distribution directory is
+added to C<@INC> so that C<inc::latest> loads from C<./inc/latest.pm>. You
+SHOULD NOT add 'inc' to C<@INC> (unless you have other special reasons for
+doing so) -- that's not how C<inc::latest> works.
+
+For I<authors>, when "Build dist" is run, Module::Build will be
+automatically bundled into C<inc> according to the rules for
+L<inc::latest>.
+
+For I<users>, inc::latest will load the latest Module::Build, whether
+installed or bundled in C<inc/>.
+
+=head1 BUNDLING OTHER CONFIGURATION DEPENDENCIES
+
+The same approach works for other configuration dependencies -- modules
+that I<must> be available for Build.PL to run. All other dependencies can
+be specified as usual in the Build.PL and CPAN or CPANPLUS will install
+them after Build.PL finishes.
+
+For example, to bundle the L<Devel::AssertOS::Unix> module (which ensures a
+"Unix-like" operating system), one could do this:
+
+ use inc::latest 'Devel::AssertOS::Unix';
+ use inc::latest 'Module::Build';
+
+ Module::Build->new(
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ )->create_build_script;
+
+The C<inc::latest> module creates bundled directories based on the packlist
+file of an installed distribution. Even though C<inc::latest> takes module
+name arguments, it is better to think of it as bundling and making
+available entire I<distributions>. When a module is loaded through
+C<inc::latest>, it looks in all bundled distributions in C<inc/> for a
+newer module than can be found in the existing C<@INC> array.
+
+Thus, the module-name provided should usually be the "top-level" module
+name of a distribution, though this is not strictly required. For example,
+L<Module::Build> has a number of heuristics to map module names to
+packlists, allowing users to do things like this:
+
+ use inc::latest 'Devel::AssertOS::Unix';
+
+even though Devel::AssertOS::Unix is contained within the Devel-CheckOS
+distribution.
+
+At the current time, packlists are required. Thus, bundling dual-core
+modules, I<including Module::Build>, may require a 'forced install' over
+versions in the latest version of perl in order to create the necessary
+packlist for bundling. This limitation will hopefully be addressed in a
+future version of Module::Build.
+
+=head2 WARNING -- How to Manage Dependency Chains
+
+Before bundling a distribution you must ensure that all prerequisites are
+also bundled and load in the correct order. For Module::Build itself, this
+should not be necessary, but it is necessary for any other distribution.
+(A future release of Module::Build will hopefully address this deficiency.)
+
+For example, if you need C<Wibble>, but C<Wibble> depends on C<Wobble>,
+your Build.PL might look like this:
+
+ use inc::latest 'Wobble';
+ use inc::latest 'Wibble';
+ use inc::latest 'Module::Build';
+
+ Module::Build->new(
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ )->create_build_script;
+
+Authors are strongly suggested to limit the bundling of additional
+dependencies if at all possible and to carefully test their distribution
+tarballs on older versions of Perl before uploading to CPAN.
+
+=head1 AUTHOR
+
+David Golden <dagolden@cpan.org>
+
+Development questions, bug reports, and patches should be sent to the
+Module-Build mailing list at <module-build@perl.org>.
+
+Bug reports are also welcome at
+<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Module-Build>.
+
+=head1 SEE ALSO
+
+perl(1), L<inc::latest>, L<Module::Build>(3), L<Module::Build::API>(3),
+L<Module::Build::Cookbook>(3),
+
+=cut
+
+# vim: tw=75
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
use File::Basename ();
use File::Spec;
VENDOR => 'vendor',
);
-my %makefile_to_build =
+my %makefile_to_build =
(
TEST_VERBOSE => 'verbose',
VERBINST => 'verbose',
# "LIB=foo make" is not the same as "perl Makefile.PL LIB=foo"
delete $macro_to_build{LIB};
+sub _simple_prereq {
+ return $_[0] =~ /^[0-9_]+\.?[0-9_]*$/; # crudly, a decimal literal
+}
+
+sub _merge_prereq {
+ my ($req, $breq) = @_;
+ $req ||= {};
+ $breq ||= {};
+
+ # validate formats
+ for my $p ( $req, $breq ) {
+ for my $k (keys %$p) {
+ die "Prereq '$p->{$k}' for '$k' is not supported by Module::Build::Compat\n"
+ unless _simple_prereq($p->{$k});
+ }
+ }
+ # merge
+ my $merge = { %$req };
+ for my $k ( keys %$breq ) {
+ my $v1 = $merge->{$k} || 0;
+ my $v2 = $breq->{$k};
+ $merge->{$k} = $v1 > $v2 ? $v1 : $v2;
+ }
+ return %$merge;
+}
+
sub create_makefile_pl {
my ($package, $type, $build, %args) = @_;
-
+
die "Don't know how to build Makefile.PL of type '$type'"
unless $type =~ /^(small|passthrough|traditional)$/;
+ if ($type eq 'passthrough') {
+ $build->log_warn(<<"HERE");
+
+IMPORTANT NOTE: The '$type' style of Makefile.PL is deprecated and
+may be removed in a future version of Module::Build in favor of the
+'configure_requires' property. See Module::Build::Compat
+documentation for details.
+
+HERE
+ }
+
my $fh;
if ($args{fh}) {
$fh = $args{fh};
print {$fh} "# Note: this file was auto-generated by ", __PACKAGE__, " version $VERSION\n";
- # Minimum perl version should be specified as "require 5.XXXXXX" in
+ # Minimum perl version should be specified as "require 5.XXXXXX" in
# Makefile.PL
my $requires = $build->requires;
if ( my $minimum_perl = $requires->{perl} ) {
} elsif ($type eq 'passthrough') {
printf {$fh} <<'EOF', $subclass_load, ref($build), ref($build);
-
+
unless (eval "use Module::Build::Compat 0.02; 1" ) {
print "This module requires Module::Build to install itself.\n";
-
+
require ExtUtils::MakeMaker;
my $yn = ExtUtils::MakeMaker::prompt
(' Install Module::Build now from CPAN?', 'y');
-
+
unless ($yn =~ /^y/i) {
die " *** Cannot install without Module::Build. Exiting ...\n";
}
-
+
require Cwd;
require File::Spec;
require CPAN;
-
+
# Save this 'cause CPAN will chdir all over the place.
my $cwd = Cwd::cwd();
-
+
CPAN::Shell->install('Module::Build::Compat');
CPAN::Shell->expand("Module", "Module::Build::Compat")->uptodate
or die "Couldn't install Module::Build, giving up.\n";
-
+
chdir $cwd or die "Cannot chdir() back to $cwd: $!";
}
eval "use Module::Build::Compat 0.02; 1" or die $@;
%s
Module::Build::Compat->run_build_pl(args => \@ARGV);
- my $build_script = 'Build';
+ my $build_script = 'Build';
$build_script .= '.com' if $^O eq 'VMS';
exit(0) unless(-e $build_script); # cpantesters convention
require %s;
Module::Build::Compat->write_makefile(build_class => '%s');
EOF
-
+
} elsif ($type eq 'traditional') {
my (%MM_Args, %prereq);
tie %MM_Args, 'Tie::IxHash'; # Don't care if it fails here
tie %prereq, 'Tie::IxHash'; # Don't care if it fails here
}
-
+
my %name = ($build->module_name
? (NAME => $build->module_name)
: (DISTNAME => $build->dist_name));
-
+
my %version = ($build->dist_version_from
? (VERSION_FROM => $build->dist_version_from)
: (VERSION => $build->dist_version)
);
%MM_Args = (%name, %version);
-
- %prereq = ( %{$build->requires}, %{$build->build_requires} );
+
+ %prereq = _merge_prereq( $build->requires, $build->build_requires );
%prereq = map {$_, $prereq{$_}} sort keys %prereq;
-
+
delete $prereq{perl};
$MM_Args{PREREQ_PM} = \%prereq;
-
+
$MM_Args{INSTALLDIRS} = $build->installdirs eq 'core' ? 'perl' : $build->installdirs;
-
+
$MM_Args{EXE_FILES} = [ sort keys %{$build->script_files} ] if $build->script_files;
-
+
$MM_Args{PL_FILES} = $build->PL_files || {};
if ($build->recursive_test_files) {
- $MM_Args{TESTS} = join q{ }, $package->_test_globs($build);
+ $MM_Args{test} = { TESTS => join q{ }, $package->_test_globs($build) };
}
local $Data::Dumper::Terse = 1;
my $args = Data::Dumper::Dumper(\%MM_Args);
$args =~ s/\{(.*)\}/($1)/s;
-
+
print $fh <<"EOF";
use ExtUtils::MakeMaker;
WriteMakefile
sub subclass_dir {
my ($self, $build) = @_;
-
+
return (Module::Build::ModuleInfo->find_module_dir_by_name(ref $build)
|| File::Spec->catdir($build->config_dir, 'lib'));
}
my @out;
foreach my $arg (@_) {
next if $arg eq '';
-
+
my ($key, $val) = ($arg =~ /^(\w+)=(.+)/ ? ($1, $2) :
die "Malformed argument '$arg'");
}
}
}
- push @out, (config => \%config) if %config;
+ push @out, (config => \%config) if %config;
return @out;
}
$perl $Build $action
EOF
}
-
+
if ($self->_is_vms_mms) {
# Roll our own .EXPORT as MMS/MMK don't honor that directive.
- $maketext .= "\n.FIRST\n\t\@ $noop\n";
+ $maketext .= "\n.FIRST\n\t\@ $noop\n";
for my $macro (keys %macro_to_build) {
$maketext .= ".IFDEF $macro\n\tDEFINE $macro \"\$($macro)\"\n.ENDIF\n";
}
- $maketext .= "\n";
+ $maketext .= "\n";
}
else {
$maketext .= "\n.EXPORT : " . join(' ', keys %macro_to_build) . "\n\n";
}
-
+
return $maketext;
}
my $fh = IO::File->new("< $file") or die "Can't read $file: $!";
my $prereqs = eval do {local $/; <$fh>};
close $fh;
-
+
+ my %merged = _merge_prereq( $prereqs->{requires}, $prereqs->{build_requires} );
my @prereq;
- foreach my $section (qw/build_requires requires/) {
- foreach (keys %{$prereqs->{$section}}) {
- next if $_ eq 'perl';
- push @prereq, "$_=>q[$prereqs->{$section}{$_}]";
- }
+ foreach (sort keys %merged) {
+ next if $_ eq 'perl';
+ push @prereq, "$_=>q[$merged{$_}]";
}
-
return unless @prereq;
return "# PREREQ_PM => { " . join(", ", @prereq) . " }\n\n";
}
my $build = Module::Build->new
( module_name => 'Foo::Bar',
license => 'perl',
- create_makefile_pl => 'passthrough' );
+ create_makefile_pl => 'traditional' );
...
=over 4
+=item traditional
+
+A F<Makefile.PL> will be created in the "traditional" style, i.e. it will
+use C<ExtUtils::MakeMaker> and won't rely on C<Module::Build> at all.
+In order to create the F<Makefile.PL>, we'll include the C<requires> and
+C<build_requires> dependencies as the C<PREREQ_PM> parameter.
+
+You don't want to use this style if during the C<perl Build.PL> stage
+you ask the user questions, or do some auto-sensing about the user's
+environment, or if you subclass C<Module::Build> to do some
+customization, because the vanilla F<Makefile.PL> won't do any of that.
+
=item small
A small F<Makefile.PL> will be created that passes all functionality
already have C<Module::Build> installed in order to use this, or else
they'll get a module-not-found error.
-=item passthrough
+=item passthrough (DEPRECATED)
This is just like the C<small> option above, but if C<Module::Build> is
not already installed on the user's system, the script will offer to
use C<CPAN.pm> to download it and install it before continuing with
the build.
-=item traditional
-
-A F<Makefile.PL> will be created in the "traditional" style, i.e. it will
-use C<ExtUtils::MakeMaker> and won't rely on C<Module::Build> at all.
-In order to create the F<Makefile.PL>, we'll include the C<requires> and
-C<build_requires> dependencies as the C<PREREQ_PM> parameter.
+This option has been deprecated and may be removed in a future version
+of Module::Build. Modern CPAN.pm and CPANPLUS will recognize the
+C<configure_requires> metadata property and install Module::Build before
+running Build.PL if Module::Build is listed and Module::Build now
+adds itself to configure_requires by default.
-You don't want to use this style if during the C<perl Build.PL> stage
-you ask the user questions, or do some auto-sensing about the user's
-environment, or if you subclass C<Module::Build> to do some
-customization, because the vanilla F<Makefile.PL> won't do any of that.
+Perl 5.10.1 includes C<configure_requires> support. In the future, when
+C<configure_requires> support is deemed sufficiently widespread, the
+C<passthrough> style will be removed.
=back
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Config;
package Module::Build::Cookbook;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
=head1 NAME
# Find out what version of Module::Build is installed or fail quietly.
# This should be cross-platform.
- my $Installed_MB =
+ my $Installed_MB =
`$^X -e "eval q{require Module::Build; print Module::Build->VERSION} or exit 1";
# some operating systems put a newline at the end of every print.
package Module::Build::Dumper;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
# This is just a split-out of a wrapper function to do Data::Dumper
# stuff "the right way". See:
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use File::Spec;
use IO::File;
use Module::Build::Version;
+my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
my $PKG_REGEXP = qr{ # match a package declaration
^[\s\{;]* # intro chars on a line
\s+ # whitespace
([\w:]+) # a package name
\s* # optional whitespace
+ ($V_NUM_REGEXP)? # optional version number
+ \s* # optional whitesapce
; # semicolon line terminator
}x;
$self->_parse_version_expression( $line );
if ( $line =~ $PKG_REGEXP ) {
- $pkg = $1;
- push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs );
- $vers{$pkg} = undef unless exists( $vers{$pkg} );
- $need_vers = 1;
+ $pkg = $1;
+ push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs );
+ $vers{$pkg} = (defined $2 ? $2 : undef) unless exists( $vers{$pkg} );
+ $need_vers = defined $2 ? 0 : 1;
# VERSION defined with full package spec, i.e. $Module::VERSION
} elsif ( $vers_fullname && $vers_pkg ) {
$need_vers = 0 if $vers_pkg eq $pkg;
unless ( defined $vers{$vers_pkg} && length $vers{$vers_pkg} ) {
- $vers{$vers_pkg} =
+ $vers{$vers_pkg} =
$self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
} else {
# Warn unless the user is using the "$VERSION = eval
(ref($vsub) eq 'CODE') or
die "failed to build version sub for $self->{filename}";
my $result = eval { $vsub->() };
+ die "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
+ if $@;
- die "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n" if $@;
+ # Activestate apparently creates custom versions like '1.23_45_01', which
+ # cause M::B::Version to think it's an invalid alpha. So check for that
+ # and strip them
+ my $num_dots = () = $result =~ m{\.}g;
+ my $num_unders = () = $result =~ m{_}g;
+ if ( substr($result,0,1) ne 'v' && $num_dots < 2 && $num_unders > 1 ) {
+ $result =~ s{_}{}g;
+ }
# Bless it into our own version class
- $result = Module::Build::Version->new($result);
+ eval { $result = Module::Build::Version->new($result) };
+ die "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
+ if $@;
return $result;
}
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Data::Dumper;
use IO::File;
sub access {
my $self = shift;
return $self->read() unless @_;
-
+
my $key = shift;
return $self->read($key) unless @_;
-
+
my $value = shift;
$self->write({ $key => $value });
return $self->read($key);
return $self->{new}{$key} if exists $self->{new}{$key};
return $self->{disk}{$key};
}
-
+
# Return all data
my $out = (keys %{$self->{new}}
? {%{$self->{disk}}, %{$self->{new}}}
sub write {
my ($self, $href) = @_;
$href ||= {};
-
+
@{$self->{new}}{ keys %$href } = values %$href; # Merge
# Do some optimization to avoid unnecessary writes
next if ref $self->{disk}{$key} or !exists $self->{disk}{$key};
delete $self->{new}{$key} if $self->_same($self->{new}{$key}, $self->{disk}{$key});
}
-
+
if (my $file = $self->{file}) {
my ($vol, $dir, $base) = File::Spec->splitpath($file);
$dir = File::Spec->catpath($vol, $dir, '');
return unless -e $dir && -d $dir; # The user needs to arrange for this
return if -e $file and !keys %{ $self->{new} }; # Nothing to do
-
- @{$self->{disk}}{ keys %{$self->{new}} } = values %{$self->{new}}; # Merge
+
+ @{$self->{disk}}{ keys %{$self->{new}} } = values %{$self->{new}}; # Merge
$self->_dump($file, $self->{disk});
-
+
$self->{new} = {};
}
return $self->read;
sub _dump {
my ($self, $file, $data) = @_;
-
+
my $fh = IO::File->new("> $file") or die "Can't create '$file': $!";
print {$fh} Module::Build::Dumper->_data_dump($data);
}
+my $orig_template = do { local $/; <DATA> };
+close DATA;
+
sub write_config_data {
my ($self, %args) = @_;
+ my $template = $orig_template;
+ $template =~ s/NOTES_NAME/$args{config_module}/g;
+ $template =~ s/MODULE_NAME/$args{module}/g;
+ $template =~ s/=begin private\n//;
+ $template =~ s/=end private/=cut/;
+
+ # strip out private POD markers we use to keep pod from being
+ # recognized for *this* source file
+ $template =~ s{$_\n}{} for '=begin private', '=end private';
+
my $fh = IO::File->new("> $args{file}") or die "Can't create '$args{file}': $!";
+ print {$fh} $template;
+ print {$fh} "\n__DATA__\n";
+ print {$fh} Module::Build::Dumper->_data_dump([$args{config_data}, $args{feature}, $args{auto_features}]);
+
+}
+
+1;
+
+
+=head1 NAME
+
+Module::Build::Notes - Create persistent distribution configuration modules
+
+=head1 DESCRIPTION
+
+This module is used internally by Module::Build to create persistent
+configuration files that can be installed with a distribution. See
+L<Module::Build::ConfigData> for an example.
+
+=head1 AUTHOR
- printf $fh <<'EOF', $args{config_module};
-package %s;
+Ken Williams <kwilliams@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001-2006 Ken Williams. All rights reserved.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+perl(1), L<Module::Build>(3)
+
+=cut
+
+__DATA__
+package NOTES_NAME;
use strict;
my $arrayref = eval do {local $/; <DATA>}
or die "Couldn't load ConfigData data: $@";
sub set_config { $config->{$_[1]} = $_[2] }
sub set_feature { $features->{$_[1]} = 0+!!$_[2] } # Constrain to 1 or 0
-sub auto_feature_names { grep !exists $features->{$_}, keys %%$auto_features }
+sub auto_feature_names { grep !exists $features->{$_}, keys %$auto_features }
sub feature_names {
- my @features = (keys %%$features, auto_feature_names());
+ my @features = (keys %$features, auto_feature_names());
@features;
}
-sub config_names { keys %%$config }
+sub config_names { keys %$config }
sub write {
my $me = __FILE__;
sub feature {
my ($package, $key) = @_;
return $features->{$key} if exists $features->{$key};
-
+
my $info = $auto_features->{$key} or return 0;
-
- # Under perl 5.005, each(%%$foo) isn't working correctly when $foo
+
+ # Under perl 5.005, each(%$foo) isn't working correctly when $foo
# was reanimated with Data::Dumper and eval(). Not sure why, but
# copying to a new hash seems to solve it.
- my %%info = %%$info;
-
+ my %info = %$info;
+
require Module::Build; # XXX should get rid of this
- while (my ($type, $prereqs) = each %%info) {
+ while (my ($type, $prereqs) = each %info) {
next if $type eq 'description' || $type eq 'recommends';
-
- my %%p = %%$prereqs; # Ditto here.
- while (my ($modname, $spec) = each %%p) {
+
+ my %p = %$prereqs; # Ditto here.
+ while (my ($modname, $spec) = each %p) {
my $status = Module::Build->check_installed_status($modname, $spec);
if ((!$status->{ok}) xor ($type =~ /conflicts$/)) { return 0; }
if ( ! eval "require $modname; 1" ) { return 0; }
return 1;
}
-EOF
-
- my ($module_name, $notes_name) = ($args{module}, $args{config_module});
- printf $fh <<"EOF", $notes_name, $module_name;
+=begin private
=head1 NAME
-$notes_name - Configuration for $module_name
-
+NOTES_NAME - Configuration for MODULE_NAME
=head1 SYNOPSIS
- use $notes_name;
- \$value = $notes_name->config('foo');
- \$value = $notes_name->feature('bar');
-
- \@names = $notes_name->config_names;
- \@names = $notes_name->feature_names;
-
- $notes_name->set_config(foo => \$new_value);
- $notes_name->set_feature(bar => \$new_value);
- $notes_name->write; # Save changes
+ use NOTES_NAME;
+ $value = NOTES_NAME->config('foo');
+ $value = NOTES_NAME->feature('bar');
+
+ @names = NOTES_NAME->config_names;
+ @names = NOTES_NAME->feature_names;
+
+ NOTES_NAME->set_config(foo => $new_value);
+ NOTES_NAME->set_feature(bar => $new_value);
+ NOTES_NAME->write; # Save changes
=head1 DESCRIPTION
-This module holds the configuration data for the C<$module_name>
+This module holds the configuration data for the C<MODULE_NAME>
module. It also provides a programmatic interface for getting or
setting that configuration data. Note that in order to actually make
-changes, you'll have to have write access to the C<$notes_name>
+changes, you'll have to have write access to the C<NOTES_NAME>
module, and you should attempt to understand the repercussions of your
actions.
=over 4
-=item config(\$name)
+=item config($name)
Given a string argument, returns the value of the configuration item
by that name, or C<undef> if no such item exists.
-=item feature(\$name)
+=item feature($name)
Given a string argument, returns the value of the feature by that
name, or C<undef> if no such feature exists.
-=item set_config(\$name, \$value)
+=item set_config($name, $value)
Sets the configuration item with the given name to the given value.
The value may be any Perl scalar that will serialize correctly using
complex data structures. It probably does not include transient
things like filehandles or sockets.
-=item set_feature(\$name, \$value)
+=item set_feature($name, $value)
Sets the feature with the given name to the given boolean value. The
value will be converted to 0 or 1 automatically.
=item config_names()
Returns a list of all the names of config items currently defined in
-C<$notes_name>, or in scalar context the number of items.
+C<NOTES_NAME>, or in scalar context the number of items.
=item feature_names()
Returns a list of all the names of features currently defined in
-C<$notes_name>, or in scalar context the number of features.
+C<NOTES_NAME>, or in scalar context the number of features.
=item auto_feature_names()
=item write()
Commits any changes from C<set_config()> and C<set_feature()> to disk.
-Requires write access to the C<$notes_name> module.
+Requires write access to the C<NOTES_NAME> module.
=back
=head1 AUTHOR
-C<$notes_name> was automatically created using C<Module::Build>.
+C<NOTES_NAME> was automatically created using C<Module::Build>.
C<Module::Build> was written by Ken Williams, but he holds no
-authorship claim or copyright claim to the contents of C<$notes_name>.
-
-=cut
+authorship claim or copyright claim to the contents of C<NOTES_NAME>.
-__DATA__
-
-EOF
-
- print {$fh} Module::Build::Dumper->_data_dump([$args{config_data}, $args{feature}, $args{auto_features}]);
-}
+=end private
-1;
package Module::Build::PPMMaker;
use strict;
+use Config;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
# This code is mostly borrowed from ExtUtils::MM_Unix 6.10_03, with a
my $method = "dist_$info";
$dist{$info} = $build->$method() or die "Can't determine distribution's $info\n";
}
- $dist{version} = $self->_ppd_version($dist{version});
$self->_simple_xml_escape($_) foreach $dist{abstract}, @{$dist{author}};
# various licenses
my $ppd = <<"PPD";
<SOFTPKG NAME=\"$dist{name}\" VERSION=\"$dist{version}\">
- <TITLE>$dist{name}</TITLE>
<ABSTRACT>$dist{abstract}</ABSTRACT>
@{[ join "\n", map " <AUTHOR>$_</AUTHOR>", @{$dist{author}} ]}
<IMPLEMENTATION>
PPD
- # TODO: We could set <IMPLTYPE VALUE="PERL" /> or maybe
- # <IMPLTYPE VALUE="PERL/XS" /> ???
-
# We don't include recommended dependencies because PPD has no way
# to distinguish them from normal dependencies. We don't include
# build_requires dependencies because the PPM installer doesn't
# build or test before installing. And obviously we don't include
# conflicts either.
-
+
foreach my $type (qw(requires)) {
my $prereq = $build->$type();
while (my ($modname, $spec) = each %$prereq) {
}
}
- # Another hack - dependencies are on modules, but PPD expects
- # them to be on distributions (I think).
- $modname =~ s/::/-/g;
-
- $ppd .= sprintf(<<'EOF', $modname, $self->_ppd_version($min_version));
- <DEPENDENCY NAME="%s" VERSION="%s" />
-EOF
+ # PPM4 spec requires a '::' for top level modules
+ $modname .= '::' unless $modname =~ /::/;
+ $ppd .= qq! <REQUIRE NAME="$modname" VERSION="$min_version" />\n!;
}
}
# We only include these tags if this module involves XS, on the
- # assumption that pure Perl modules will work on any OS. PERLCORE,
- # unfortunately, seems to indicate that a module works with _only_
- # that version of Perl, and so is only appropriate when a module
- # uses XS.
+ # assumption that pure Perl modules will work on any OS.
if (keys %{$build->find_xs_files}) {
my $perl_version = $self->_ppd_version($build->perl_version);
- $ppd .= sprintf(<<'EOF', $perl_version, $^O, $self->_varchname($build->config) );
- <PERLCORE VERSION="%s" />
- <OS NAME="%s" />
+ $ppd .= sprintf(<<'EOF', $self->_varchname($build->config) );
<ARCHITECTURE NAME="%s" />
EOF
}
my $ppd_file = "$dist{name}.ppd";
my $fh = IO::File->new(">$ppd_file")
or die "Cannot write to $ppd_file: $!";
- $fh->binmode(":utf8") if $fh->can("binmode");
+
+ $fh->binmode(":utf8")
+ if $fh->can('binmode') && $] >= 5.008 && $Config{useperlio};
print $fh $ppd;
close $fh;
'<' => '<',
);
my $rx = join '|', keys %escapes;
-
+
sub _simple_xml_escape {
$_[1] =~ s/($rx)/$escapes{$1}/go;
}
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use vars qw(@ISA);
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
-
+
# $Config{sitelib} and $Config{sitearch} are, unfortunately, missing.
foreach ('sitelib', 'sitearch') {
$self->config($_ => $self->config("install$_"))
unless $self->config($_);
}
-
+
# For some reason $Config{startperl} is filled with a bunch of crap.
(my $sp = $self->config('startperl')) =~ s/.*Exit \{Status\}\s//;
$self->config(startperl => $sp);
-
+
return $self;
}
if( !@_ and !@ARGV ) {
require MacPerl;
-
+
# What comes first in the action list.
my @action_list = qw(build test install);
my %actions = map {+($_, 1)} $self->known_actions;
foreach (@action_list) {
$_ .= ' *' if $toolserver{$_};
}
-
+
my $cmd = MacPerl::Pick("What build command? ('*' requires ToolServer)", @action_list);
return unless defined $cmd;
$cmd =~ s/ \*$//;
$ARGV[0] = ($cmd);
-
+
my $args = MacPerl::Ask('Any extra arguments? (ie. verbose=1)', '');
return unless defined $args;
push @ARGV, $self->split_like_shell($args);
}
-
+
$self->SUPER::dispatch(@_);
}
sub ACTION_install {
my $self = shift;
-
+
return $self->SUPER::ACTION_install(@_)
if eval {ExtUtils::Install->VERSION('1.30'); 1};
-
+
local $^W = 0; # Avoid a 'redefine' warning
local *ExtUtils::Install::find = sub {
my ($code, @dirs) = @_;
return File::Find::find($code, @dirs);
};
-
+
return $self->SUPER::ACTION_install(@_);
}
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
# or if we get a single arg that is an array reference, quote the
# elements of it and return the reference.
my ($self, @args) = @_;
- my $got_arrayref = (scalar(@args) == 1
- && UNIVERSAL::isa($args[0], 'ARRAY'))
- ? 1
+ my $got_arrayref = (scalar(@args) == 1
+ && UNIVERSAL::isa($args[0], 'ARRAY'))
+ ? 1
: 0;
# Do not quote qualifiers that begin with '/'.
- map { if (!/^\//) {
+ map { if (!/^\//) {
$_ =~ s/\"/""/g; # escape C<"> by doubling
$_ = q(").$_.q(");
}
}
- ($got_arrayref ? @{$args[0]}
+ ($got_arrayref ? @{$args[0]}
: @args
);
- return $got_arrayref ? $args[0]
+ return $got_arrayref ? $args[0]
: join(' ', @args);
}
return `$cmd $args`;
}
+=item find_command
+
+Local an executable program
+
+=cut
+
+sub find_command {
+ my ($self, $command) = @_;
+
+ # a lot of VMS executables have a symbol defined
+ # check those first
+ if ( $^O eq 'VMS' ) {
+ require VMS::DCLsym;
+ my $syms = VMS::DCLsym->new;
+ return $command if scalar $syms->getsym( uc $command );
+ }
+
+ $self->SUPER::find_command($command);
+}
+
+# _maybe_command copied from ExtUtils::MM_VMS::maybe_command
+
+=item _maybe_command (override)
+
+Follows VMS naming conventions for executable files.
+If the name passed in doesn't exactly match an executable file,
+appends F<.Exe> (or equivalent) to check for executable image, and F<.Com>
+to check for DCL procedure. If this fails, checks directories in DCL$PATH
+and finally F<Sys$System:> for an executable file having the name specified,
+with or without the F<.Exe>-equivalent suffix.
+
+=cut
+
+sub _maybe_command {
+ my($self,$file) = @_;
+ return $file if -x $file && ! -d _;
+ my(@dirs) = ('');
+ my(@exts) = ('',$Config{'exe_ext'},'.exe','.com');
+
+ if ($file !~ m![/:>\]]!) {
+ for (my $i = 0; defined $ENV{"DCL\$PATH;$i"}; $i++) {
+ my $dir = $ENV{"DCL\$PATH;$i"};
+ $dir .= ':' unless $dir =~ m%[\]:]$%;
+ push(@dirs,$dir);
+ }
+ push(@dirs,'Sys$System:');
+ foreach my $dir (@dirs) {
+ my $sysfile = "$dir$file";
+ foreach my $ext (@exts) {
+ return $file if -x "$sysfile$ext" && ! -d _;
+ }
+ }
+ }
+ return;
+}
+
=item do_system
Override to ensure that we quote the arguments but not the command.
sub do_system {
# The command must not be quoted but the arguments to it must be.
my ($self, @cmd) = @_;
- $self->log_info("@cmd\n");
+ $self->log_verbose("@cmd\n");
my $cmd = shift @cmd;
my $args = $self->_quote_args(@cmd);
return !system("$cmd $args");
=item _infer_xs_spec
-Inherit the standard version but tweak the library file name to be
+Inherit the standard version but tweak the library file name to be
something Dynaloader can find.
=cut
=item dist_dir
-Inherit the standard version but replace embedded dots with underscores because
+Inherit the standard version but replace embedded dots with underscores because
a dot is the directory delimiter on VMS.
=cut
=item man3page_name
-Inherit the standard version but chop the extra manpage delimiter off the front if
+Inherit the standard version but chop the extra manpage delimiter off the front if
there is one. The VMS version of splitdir('[.foo]') returns '', 'foo'.
=cut
$newdirs = File::Spec::Unix->catdir(@hdirs, @backup, @dirs);
}
-
+
# Now put the two cases back together
$arg = File::Spec::Unix->catpath($hvol, $newdirs, $file);
$unix_rpt = VMS::Feature::current("filename_unix_report");
} else {
my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
- $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
+ $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
}
return $unix_rpt;
}
$efs = VMS::Feature::current("efs_charset");
} else {
my $env_efs = $ENV{'DECC$EFS_CHARSET'} || '';
- $efs = $env_efs =~ /^[ET1]/i;
+ $efs = $env_efs =~ /^[ET1]/i;
}
return $efs;
}
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Base;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Config;
if ( lc $basename eq lc $self->build_script ) {
if ( $self->build_bat ) {
- $self->log_info("Deleting $basename.bat\n");
+ $self->log_verbose("Deleting $basename.bat\n");
my $full_progname = $0;
$full_progname =~ s/(?:\.bat)?$/.bat/i;
# into words. The algorithm below was bashed out by Randy and Ken
# (mostly Randy), and there are a lot of regression tests, so we
# should feel free to adjust if desired.
-
+
(my $self, local $_) = @_;
-
+
return @$_ if defined() && UNIVERSAL::isa($_, 'ARRAY');
-
+
my @argv;
return @argv unless defined() && length();
-
+
my $arg = '';
my( $i, $quote_mode ) = ( 0, 0 );
-
+
while ( $i < length() ) {
-
+
my $ch = substr( $_, $i , 1 );
my $next_ch = substr( $_, $i+1, 1 );
-
+
if ( $ch eq '\\' && $next_ch eq '"' ) {
$arg .= '"';
$i++;
} else {
$arg .= $ch;
}
-
+
$i++;
}
-
+
push( @argv, $arg ) if defined( $arg ) && length( $arg );
return @argv;
}
return !$status;
}
+# Copied from ExtUtils::MM_Win32
+sub _maybe_command {
+ my($self,$file) = @_;
+ my @e = exists($ENV{'PATHEXT'})
+ ? split(/;/, $ENV{PATHEXT})
+ : qw(.com .exe .bat .cmd);
+ my $e = '';
+ for (@e) { $e .= "\Q$_\E|" }
+ chop $e;
+ # see if file ends in one of the known extensions
+ if ($file =~ /($e)$/i) {
+ return $file if -e $file;
+ }
+ else {
+ for (@e) {
+ return "$file$_" if -e "$file$_";
+ }
+ }
+ return;
+}
+
1;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Platform::Unix;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Platform::Unix;
'.'
}
+# Copied from ExtUtils::MM_Cygwin::maybe_command()
+# If our path begins with F</cygdrive/> then we use C<ExtUtils::MM_Win32>
+# to determine if it may be a command. Otherwise we use the tests
+# from C<ExtUtils::MM_Unix>.
+
+sub _maybe_command {
+ my ($self, $file) = @_;
+
+ if ($file =~ m{^/cygdrive/}i) {
+ require Module::Build::Platform::Win32;
+ return Module::Build::Platform::Win32->_maybe_command($file);
+ }
+
+ return $self->SUPER::_maybe_command($file);
+}
+
1;
__END__
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Platform::Unix;
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use Module::Build::Platform::Unix;
sub have_forkpipe { 0 }
+# Copied from ExtUtils::MM_OS2::maybe_command
+sub _maybe_command {
+ my($self,$file) = @_;
+ $file =~ s,[/\\]+,/,g;
+ return $file if -x $file && ! -d _;
+ return "$file.exe" if -x "$file.exe" && ! -d _;
+ return "$file.cmd" if -x "$file.cmd" && ! -d _;
+ return;
+}
+
1;
__END__
use strict;
use vars qw($VERSION);
-$VERSION = '0.35';
+$VERSION = '0.35_08';
$VERSION = eval $VERSION;
use vars qw(@ISA);
sub _myparse_from_filehandle {
my ($self, $fh) = @_;
-
+
local $_;
while (<$fh>) {
next unless /^=(?!cut)/ .. /^=cut/; # in POD
last if ($self->{abstract}) = /^ (?: [a-z:]+ \s+ - \s+ ) (.*\S) /ix;
}
-
+
my @author;
while (<$fh>) {
- next unless /^=head1\s+AUTHORS?/ ... /^=/;
+ next unless /^=head1\s+AUTHORS?/i ... /^=/;
next if /^=/;
push @author, $_ if /\@/;
}
return unless @author;
s/^\s+|\s+$//g foreach @author;
-
+
$self->{author} = \@author;
-
+
return;
}
sub get_abstract {
my $self = shift;
return $self->{abstract} if defined $self->{abstract};
-
+
$self->parse_from_filehandle($self->{fh});
return $self->{abstract};
sub get_author {
my $self = shift;
return $self->{author} if defined $self->{author};
-
+
$self->parse_from_filehandle($self->{fh});
return $self->{author} || [];
my ($self, $text) = @_;
$text =~ s/^\s+//;
$text =~ s/\s+$//;
- if ($self->{_head} eq 'NAME') {
+ if (uc $self->{_head} eq 'NAME') {
my ($name, $abstract) = split( /\s+-\s+/, $text, 2 );
$self->{abstract} = $abstract;
- } elsif ($self->{_head} =~ /^AUTHORS?$/) {
+ } elsif ($self->{_head} =~ /^AUTHORS?$/i) {
push @{$self->{author}}, $text if $text =~ /\@/;
}
}
map { $args{$_} = 1 } @_
}
else { # no parameters at all on use line
- %args =
+ args =
(
qv => 1,
'UNIVERSAL::VERSION' => 1,
}
my $callpkg = caller();
-
+
if (exists($args{declare})) {
- *{$callpkg."::declare"} =
+ *{$callpkg."::declare"} =
sub {return $class->declare(shift) }
unless defined(&{$callpkg.'::declare'});
}
{
my ($class, $value) = @_;
my $self = bless ({}, ref ($class) || $class);
-
+
if ( ref($value) && eval('$value->isa("version")') ) {
# Can copy the elements directly
$self->{version} = [ @{$value->{version} } ];
$value = sprintf("%.9f",$value);
$value =~ s/(0+)$//; # trim trailing zeros
}
-
+
# This is not very efficient, but it is morally equivalent
# to the XS code (as that is the reference implementation).
# See vutil/vutil.c for details
}
$start = $last = $pos = $s;
-
+
# pre-scan the input string to check for decimals/underbars
while ( substr($value,$pos,1) =~ /[._\d,]/ ) {
if ( substr($value,$pos,1) eq '.' ) {
$orev = $rev;
$rev += substr($value,$s,1) * $mult;
$mult /= 10;
- if ( abs($orev) > abs($rev)
+ if ( abs($orev) > abs($rev)
|| abs($rev) > abs($VERSION_MAX) ) {
if ( warnings::enabled("overflow") ) {
require Carp;
$orev = $rev;
$rev += substr($value,$end,1) * $mult;
$mult *= 10;
- if ( abs($orev) > abs($rev)
+ if ( abs($orev) > abs($rev)
|| abs($rev) > abs($VERSION_MAX) ) {
if ( warnings::enabled("overflow") ) {
require Carp;
# Append revision
push @{$self->{version}}, $rev;
- if ( substr($value,$pos,1) eq '.'
+ if ( substr($value,$pos,1) eq '.'
&& substr($value,$pos+1,1) =~ /\d/ ) {
$s = ++$pos;
}
- elsif ( substr($value,$pos,1) eq '_'
+ elsif ( substr($value,$pos,1) eq '_'
&& substr($value,$pos+1,1) =~ /\d/ ) {
$s = ++$pos;
}
- elsif ( substr($value,$pos,1) eq ','
+ elsif ( substr($value,$pos,1) eq ','
&& substr($value,$pos+1,1) =~ /\d/ ) {
$s = ++$pos;
}
*parse = \&new;
-sub numify
+sub numify
{
my ($self) = @_;
unless (_verify($self)) {
return $string;
}
-sub normal
+sub normal
{
my ($self) = @_;
unless (_verify($self)) {
require Carp;
Carp::croak("Invalid version object");
}
- return exists $self->{original}
- ? $self->{original}
- : exists $self->{qv}
+ return exists $self->{original}
+ ? $self->{original}
+ : exists $self->{qv}
? $self->normal
: $self->numify;
}
}
# tiebreaker for alpha with identical terms
- if ( $retval == 0
- && $l == $r
+ if ( $retval == 0
+ && $l == $r
&& $left->{version}[$m] == $right->{version}[$m]
&& ( $lalpha || $ralpha ) ) {
}
}
- return $retval;
+ return $retval;
}
sub vbool {
return vcmp($self,$self->new("0"),1);
}
-sub vnoop {
- require Carp;
+sub vnoop {
+ require Carp;
Carp::croak("operation not supported with version object");
}
if ( defined $req ) {
unless ( defined $version ) {
require Carp;
- my $msg = $] < 5.006
+ my $msg = $] < 5.006
? "$class version $req required--this is only version "
: "$class does not define \$$class\::VERSION"
."--version check failed";
if ( $req > $version ) {
require Carp;
if ( $req->is_qv ) {
- Carp::croak(
+ Carp::croak(
sprintf ("%s version %s required--".
"this is only version %s", $class,
$req->normal, $version->normal)
);
}
else {
- Carp::croak(
+ Carp::croak(
sprintf ("%s version %s required--".
"this is only version %s", $class,
$req->stringify, $version->stringify)
+# Adapted from YAML::Tiny 1.40
package Module::Build::YAML;
use strict;
-use vars qw($VERSION @EXPORT @EXPORT_OK);
-$VERSION = "0.50";
-@EXPORT = ();
-@EXPORT_OK = qw(Dump Load DumpFile LoadFile);
+use Carp 'croak';
+# UTF Support?
+sub HAVE_UTF8 () { $] >= 5.007003 }
+BEGIN {
+ if ( HAVE_UTF8 ) {
+ # The string eval helps hide this from Test::MinimumVersion
+ eval "require utf8;";
+ die "Failed to load UTF-8 support" if $@;
+ }
+
+ # Class structure
+ require 5.004;
+
+ $Module::Build::YAML::VERSION = '1.40';
+
+ # Error storage
+ $Module::Build::YAML::errstr = '';
+}
+
+# The character class of all characters we need to escape
+# NOTE: Inlined, since it's only used once
+# my $RE_ESCAPE = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f\"\n]';
+
+# Printed form of the unprintable characters in the lowest range
+# of ASCII characters, listed by ASCII ordinal position.
+my @UNPRINTABLE = qw(
+ z x01 x02 x03 x04 x05 x06 a
+ x08 t n v f r x0e x0f
+ x10 x11 x12 x13 x14 x15 x16 x17
+ x18 x19 x1a e x1c x1d x1e x1f
+);
+
+# Printable characters for escapes
+my %UNESCAPES = (
+ z => "\x00", a => "\x07", t => "\x09",
+ n => "\x0a", v => "\x0b", f => "\x0c",
+ r => "\x0d", e => "\x1b", '\\' => '\\',
+);
+
+# Special magic boolean words
+my %QUOTE = map { $_ => 1 } qw{
+ null Null NULL
+ y Y yes Yes YES n N no No NO
+ true True TRUE false False FALSE
+ on On ON off Off OFF
+};
+
+#####################################################################
+# Implementation
+
+# Create an empty Module::Build::YAML object
sub new {
- my $this = shift;
- my $class = ref($this) || $this;
- my $self = {};
- bless $self, $class;
- return($self);
+ my $class = shift;
+ bless [ @_ ], $class;
}
-sub Dump {
- shift if ($_[0] eq __PACKAGE__ || ref($_[0]) eq __PACKAGE__);
- my $yaml = "";
- foreach my $item (@_) {
- $yaml .= "---\n";
- $yaml .= &_yaml_chunk("", $item);
- }
- return $yaml;
+# Create an object from a file
+sub read {
+ my $class = ref $_[0] ? ref shift : shift;
+
+ # Check the file
+ my $file = shift or return $class->_error( 'You did not specify a file name' );
+ return $class->_error( "File '$file' does not exist" ) unless -e $file;
+ return $class->_error( "'$file' is a directory, not a file" ) unless -f _;
+ return $class->_error( "Insufficient permissions to read '$file'" ) unless -r _;
+
+ # Slurp in the file
+ local $/ = undef;
+ local *CFG;
+ unless ( open(CFG, $file) ) {
+ return $class->_error("Failed to open file '$file': $!");
+ }
+ my $contents = <CFG>;
+ unless ( close(CFG) ) {
+ return $class->_error("Failed to close file '$file': $!");
+ }
+
+ $class->read_string( $contents );
}
-sub Load {
- shift if ($_[0] eq __PACKAGE__ || ref($_[0]) eq __PACKAGE__);
- die "not yet implemented";
+# Create an object from a string
+sub read_string {
+ my $class = ref $_[0] ? ref shift : shift;
+ my $self = bless [], $class;
+ my $string = $_[0];
+ unless ( defined $string ) {
+ return $self->_error("Did not provide a string to load");
+ }
+
+ # Byte order marks
+ # NOTE: Keeping this here to educate maintainers
+ # my %BOM = (
+ # "\357\273\277" => 'UTF-8',
+ # "\376\377" => 'UTF-16BE',
+ # "\377\376" => 'UTF-16LE',
+ # "\377\376\0\0" => 'UTF-32LE'
+ # "\0\0\376\377" => 'UTF-32BE',
+ # );
+ if ( $string =~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {
+ return $self->_error("Stream has a non UTF-8 BOM");
+ } else {
+ # Strip UTF-8 bom if found, we'll just ignore it
+ $string =~ s/^\357\273\277//;
+ }
+
+ # Try to decode as utf8
+ utf8::decode($string) if HAVE_UTF8;
+
+ # Check for some special cases
+ return $self unless length $string;
+ unless ( $string =~ /[\012\015]+\z/ ) {
+ return $self->_error("Stream does not end with newline character");
+ }
+
+ # Split the file into lines
+ my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
+ split /(?:\015{1,2}\012|\015|\012)/, $string;
+
+ # Strip the initial YAML header
+ @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;
+
+ # A nibbling parser
+ while ( @lines ) {
+ # Do we have a document header?
+ if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
+ # Handle scalar documents
+ shift @lines;
+ if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
+ push @$self, $self->_read_scalar( "$1", [ undef ], \@lines );
+ next;
+ }
+ }
+
+ if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
+ # A naked document
+ push @$self, undef;
+ while ( @lines and $lines[0] !~ /^---/ ) {
+ shift @lines;
+ }
+
+ } elsif ( $lines[0] =~ /^\s*\-/ ) {
+ # An array at the root
+ my $document = [ ];
+ push @$self, $document;
+ $self->_read_array( $document, [ 0 ], \@lines );
+
+ } elsif ( $lines[0] =~ /^(\s*)\S/ ) {
+ # A hash at the root
+ my $document = { };
+ push @$self, $document;
+ $self->_read_hash( $document, [ length($1) ], \@lines );
+
+ } else {
+ croak("Module::Build::YAML failed to classify the line '$lines[0]'");
+ }
+ }
+
+ $self;
}
-# This is basically copied out of YAML.pm and simplified a little.
-sub DumpFile {
- shift if ($_[0] eq __PACKAGE__ || ref($_[0]) eq __PACKAGE__);
- my $filename = shift;
- local $/ = "\n"; # reset special to "sane"
- my $mode = '>';
- if ($filename =~ /^\s*(>{1,2})\s*(.*)$/) {
- ($mode, $filename) = ($1, $2);
- }
- open my $OUT, "$mode $filename"
- or die "Can't open $filename for writing: $!";
- binmode($OUT, ':utf8') if $] >= 5.008;
- print $OUT Dump(@_);
- close $OUT;
-}
-
-# This is basically copied out of YAML.pm and simplified a little.
-sub LoadFile {
- shift if ($_[0] eq __PACKAGE__ || ref($_[0]) eq __PACKAGE__);
- my $filename = shift;
- open my $IN, $filename
- or die "Can't open $filename for reading: $!";
- binmode($IN, ':utf8') if $] >= 5.008;
- return Load(do { local $/; <$IN> });
- close $IN;
-}
-
-sub _yaml_chunk {
- my ($indent, $values) = @_;
- my $yaml_chunk = "";
- my $ref = ref($values);
- my ($value, @allkeys, %keyseen);
- if (!$ref) { # a scalar
- $yaml_chunk .= &_yaml_value($values) . "\n";
- }
- elsif ($ref eq "ARRAY") {
- foreach $value (@$values) {
- $yaml_chunk .= "$indent-";
- $ref = ref($value);
- if (!$ref) {
- $yaml_chunk .= " " . &_yaml_value($value) . "\n";
- }
- else {
- $yaml_chunk .= "\n";
- $yaml_chunk .= &_yaml_chunk("$indent ", $value);
- }
- }
- }
- else { # assume "HASH"
- if ($values->{_order} && ref($values->{_order}) eq "ARRAY") {
- @allkeys = @{$values->{_order}};
- $values = { %$values };
- delete $values->{_order};
- }
- push(@allkeys, sort keys %$values);
- foreach my $key (@allkeys) {
- next if (!defined $key || $key eq "" || $keyseen{$key});
- $keyseen{$key} = 1;
- $yaml_chunk .= "$indent$key:";
- $value = $values->{$key};
- $ref = ref($value);
- if (!$ref) {
- $yaml_chunk .= " " . &_yaml_value($value) . "\n";
- }
- else {
- $yaml_chunk .= "\n";
- $yaml_chunk .= &_yaml_chunk("$indent ", $value);
- }
- }
- }
- return($yaml_chunk);
-}
-
-sub _yaml_value {
- my ($value) = @_;
- # undefs become ~
- return '~' if not defined $value;
-
- # empty strings will become empty strings
- return '""' if $value eq '';
-
- # allow simple scalars (without embedded quote chars) to be unquoted
- # (includes $%_+=-\;:,./)
- return $value if $value !~ /["'`~\n!\@\#^\&\*\(\)\{\}\[\]\|<>\?]/;
-
- # quote and escape strings with special values
- return "'$value'"
- if $value !~ /['`~\n!\#^\&\*\(\)\{\}\[\]\|\?]/; # nothing but " or @ or < or > (email addresses)
-
- $value =~ s/\n/\\n/g; # handle embedded newlines
- $value =~ s/"/\\"/g; # handle embedded quotes
- return qq{"$value"};
+# Deparse a scalar string to the actual scalar
+sub _read_scalar {
+ my ($self, $string, $indent, $lines) = @_;
+
+ # Trim trailing whitespace
+ $string =~ s/\s*\z//;
+
+ # Explitic null/undef
+ return undef if $string eq '~';
+
+ # Quotes
+ if ( $string =~ /^\'(.*?)\'\z/ ) {
+ return '' unless defined $1;
+ $string = $1;
+ $string =~ s/\'\'/\'/g;
+ return $string;
+ }
+ if ( $string =~ /^\"((?:\\.|[^\"])*)\"\z/ ) {
+ # Reusing the variable is a little ugly,
+ # but avoids a new variable and a string copy.
+ $string = $1;
+ $string =~ s/\\"/"/g;
+ $string =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
+ return $string;
+ }
+
+ # Special cases
+ if ( $string =~ /^[\'\"!&]/ ) {
+ croak("Module::Build::YAML does not support a feature in line '$lines->[0]'");
+ }
+ return {} if $string eq '{}';
+ return [] if $string eq '[]';
+
+ # Regular unquoted string
+ return $string unless $string =~ /^[>|]/;
+
+ # Error
+ croak("Module::Build::YAML failed to find multi-line scalar content") unless @$lines;
+
+ # Check the indent depth
+ $lines->[0] =~ /^(\s*)/;
+ $indent->[-1] = length("$1");
+ if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
+ croak("Module::Build::YAML found bad indenting in line '$lines->[0]'");
+ }
+
+ # Pull the lines
+ my @multiline = ();
+ while ( @$lines ) {
+ $lines->[0] =~ /^(\s*)/;
+ last unless length($1) >= $indent->[-1];
+ push @multiline, substr(shift(@$lines), length($1));
+ }
+
+ my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
+ my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
+ return join( $j, @multiline ) . $t;
}
-1;
+# Parse an array
+sub _read_array {
+ my ($self, $array, $indent, $lines) = @_;
-__END__
+ while ( @$lines ) {
+ # Check for a new document
+ if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
+ while ( @$lines and $lines->[0] !~ /^---/ ) {
+ shift @$lines;
+ }
+ return 1;
+ }
+
+ # Check the indent level
+ $lines->[0] =~ /^(\s*)/;
+ if ( length($1) < $indent->[-1] ) {
+ return 1;
+ } elsif ( length($1) > $indent->[-1] ) {
+ croak("Module::Build::YAML found bad indenting in line '$lines->[0]'");
+ }
+
+ if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
+ # Inline nested hash
+ my $indent2 = length("$1");
+ $lines->[0] =~ s/-/ /;
+ push @$array, { };
+ $self->_read_hash( $array->[-1], [ @$indent, $indent2 ], $lines );
+
+ } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
+ # Array entry with a value
+ shift @$lines;
+ push @$array, $self->_read_scalar( "$2", [ @$indent, undef ], $lines );
-=head1 NAME
+ } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
+ shift @$lines;
+ unless ( @$lines ) {
+ push @$array, undef;
+ return 1;
+ }
+ if ( $lines->[0] =~ /^(\s*)\-/ ) {
+ my $indent2 = length("$1");
+ if ( $indent->[-1] == $indent2 ) {
+ # Null array entry
+ push @$array, undef;
+ } else {
+ # Naked indenter
+ push @$array, [ ];
+ $self->_read_array( $array->[-1], [ @$indent, $indent2 ], $lines );
+ }
-Module::Build::YAML - Provides just enough YAML support so that Module::Build works even if YAML.pm is not installed
+ } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
+ push @$array, { };
+ $self->_read_hash( $array->[-1], [ @$indent, length("$1") ], $lines );
-=head1 SYNOPSIS
+ } else {
+ croak("Module::Build::YAML failed to classify line '$lines->[0]'");
+ }
- use Module::Build::YAML;
+ } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
+ # This is probably a structure like the following...
+ # ---
+ # foo:
+ # - list
+ # bar: value
+ #
+ # ... so lets return and let the hash parser handle it
+ return 1;
- ...
+ } else {
+ croak("Module::Build::YAML failed to classify line '$lines->[0]'");
+ }
+ }
-=head1 DESCRIPTION
+ return 1;
+}
+
+# Parse an array
+sub _read_hash {
+ my ($self, $hash, $indent, $lines) = @_;
+
+ while ( @$lines ) {
+ # Check for a new document
+ if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
+ while ( @$lines and $lines->[0] !~ /^---/ ) {
+ shift @$lines;
+ }
+ return 1;
+ }
-Provides just enough YAML support so that Module::Build works even if YAML.pm is not installed.
+ # Check the indent level
+ $lines->[0] =~ /^(\s*)/;
+ if ( length($1) < $indent->[-1] ) {
+ return 1;
+ } elsif ( length($1) > $indent->[-1] ) {
+ croak("Module::Build::YAML found bad indenting in line '$lines->[0]'");
+ }
-Currently, this amounts to the ability to write META.yml files when C<perl Build distmeta>
-is executed via the Dump() and DumpFile() functions/methods.
+ # Get the key
+ unless ( $lines->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+|$)// ) {
+ if ( $lines->[0] =~ /^\s*[?\'\"]/ ) {
+ croak("Module::Build::YAML does not support a feature in line '$lines->[0]'");
+ }
+ croak("Module::Build::YAML failed to classify line '$lines->[0]'");
+ }
+ my $key = $1;
-=head1 AUTHOR
+ # Do we have a value?
+ if ( length $lines->[0] ) {
+ # Yes
+ $hash->{$key} = $self->_read_scalar( shift(@$lines), [ @$indent, undef ], $lines );
+ } else {
+ # An indent
+ shift @$lines;
+ unless ( @$lines ) {
+ $hash->{$key} = undef;
+ return 1;
+ }
+ if ( $lines->[0] =~ /^(\s*)-/ ) {
+ $hash->{$key} = [];
+ $self->_read_array( $hash->{$key}, [ @$indent, length($1) ], $lines );
+ } elsif ( $lines->[0] =~ /^(\s*)./ ) {
+ my $indent2 = length("$1");
+ if ( $indent->[-1] >= $indent2 ) {
+ # Null hash entry
+ $hash->{$key} = undef;
+ } else {
+ $hash->{$key} = {};
+ $self->_read_hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
+ }
+ }
+ }
+ }
-Stephen Adkins <spadkins@gmail.com>
+ return 1;
+}
+
+# Save an object to a file
+sub write {
+ my $self = shift;
+ my $file = shift or return $self->_error('No file name provided');
-=head1 COPYRIGHT
+ # Write it to the file
+ open( CFG, '>' . $file ) or return $self->_error(
+ "Failed to open file '$file' for writing: $!"
+ );
+ print CFG $self->write_string;
+ close CFG;
-Copyright (c) 2006. Stephen Adkins. All rights reserved.
+ return 1;
+}
-This program is free software; you can redistribute it and/or modify it
-under the same terms as Perl itself.
+# Save an object to a string
+sub write_string {
+ my $self = shift;
+ return '' unless @$self;
-See L<http://www.perl.com/perl/misc/Artistic.html>
+ # Iterate over the documents
+ my $indent = 0;
+ my @lines = ();
+ foreach my $cursor ( @$self ) {
+ push @lines, '---';
-=cut
+ # An empty document
+ if ( ! defined $cursor ) {
+ # Do nothing
+
+ # A scalar document
+ } elsif ( ! ref $cursor ) {
+ $lines[-1] .= ' ' . $self->_write_scalar( $cursor, $indent );
+
+ # A list at the root
+ } elsif ( ref $cursor eq 'ARRAY' ) {
+ unless ( @$cursor ) {
+ $lines[-1] .= ' []';
+ next;
+ }
+ push @lines, $self->_write_array( $cursor, $indent, {} );
+
+ # A hash at the root
+ } elsif ( ref $cursor eq 'HASH' ) {
+ unless ( %$cursor ) {
+ $lines[-1] .= ' {}';
+ next;
+ }
+ push @lines, $self->_write_hash( $cursor, $indent, {} );
+
+ } else {
+ croak("Cannot serialize " . ref($cursor));
+ }
+ }
+
+ join '', map { "$_\n" } @lines;
+}
+
+sub _write_scalar {
+ my $string = $_[1];
+ return '~' unless defined $string;
+ return "''" unless length $string;
+ if ( $string =~ /[\x00-\x08\x0b-\x0d\x0e-\x1f\"\'\n]/ ) {
+ $string =~ s/\\/\\\\/g;
+ $string =~ s/"/\\"/g;
+ $string =~ s/\n/\\n/g;
+ $string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g;
+ return qq|"$string"|;
+ }
+ if ( $string =~ /(?:^\W|\s)/ or $QUOTE{$string} ) {
+ return "'$string'";
+ }
+ return $string;
+}
+
+sub _write_array {
+ my ($self, $array, $indent, $seen) = @_;
+ if ( $seen->{refaddr($array)}++ ) {
+ die "Module::Build::YAML does not support circular references";
+ }
+ my @lines = ();
+ foreach my $el ( @$array ) {
+ my $line = (' ' x $indent) . '-';
+ my $type = ref $el;
+ if ( ! $type ) {
+ $line .= ' ' . $self->_write_scalar( $el, $indent + 1 );
+ push @lines, $line;
+
+ } elsif ( $type eq 'ARRAY' ) {
+ if ( @$el ) {
+ push @lines, $line;
+ push @lines, $self->_write_array( $el, $indent + 1, $seen );
+ } else {
+ $line .= ' []';
+ push @lines, $line;
+ }
+
+ } elsif ( $type eq 'HASH' ) {
+ if ( keys %$el ) {
+ push @lines, $line;
+ push @lines, $self->_write_hash( $el, $indent + 1, $seen );
+ } else {
+ $line .= ' {}';
+ push @lines, $line;
+ }
+
+ } else {
+ die "Module::Build::YAML does not support $type references";
+ }
+ }
+
+ @lines;
+}
+
+sub _write_hash {
+ my ($self, $hash, $indent, $seen) = @_;
+ if ( $seen->{refaddr($hash)}++ ) {
+ die "Module::Build::YAML does not support circular references";
+ }
+ my @lines = ();
+ foreach my $name ( sort keys %$hash ) {
+ my $el = $hash->{$name};
+ my $line = (' ' x $indent) . "$name:";
+ my $type = ref $el;
+ if ( ! $type ) {
+ $line .= ' ' . $self->_write_scalar( $el, $indent + 1 );
+ push @lines, $line;
+
+ } elsif ( $type eq 'ARRAY' ) {
+ if ( @$el ) {
+ push @lines, $line;
+ push @lines, $self->_write_array( $el, $indent + 1, $seen );
+ } else {
+ $line .= ' []';
+ push @lines, $line;
+ }
+
+ } elsif ( $type eq 'HASH' ) {
+ if ( keys %$el ) {
+ push @lines, $line;
+ push @lines, $self->_write_hash( $el, $indent + 1, $seen );
+ } else {
+ $line .= ' {}';
+ push @lines, $line;
+ }
+
+ } else {
+ die "Module::Build::YAML does not support $type references";
+ }
+ }
+
+ @lines;
+}
+
+# Set error
+sub _error {
+ $Module::Build::YAML::errstr = $_[1];
+ undef;
+}
+
+# Retrieve error
+sub errstr {
+ $Module::Build::YAML::errstr;
+}
+
+#####################################################################
+# YAML Compatibility
+
+sub Dump {
+ Module::Build::YAML->new(@_)->write_string;
+}
+
+sub Load {
+ my $self = Module::Build::YAML->read_string(@_);
+ unless ( $self ) {
+ croak("Failed to load YAML document from string");
+ }
+ if ( wantarray ) {
+ return @$self;
+ } else {
+ # To match YAML.pm, return the last document
+ return $self->[-1];
+ }
+}
+
+BEGIN {
+ *freeze = *Dump;
+ *thaw = *Load;
+}
+
+sub DumpFile {
+ my $file = shift;
+ Module::Build::YAML->new(@_)->write($file);
+}
+
+sub LoadFile {
+ my $self = Module::Build::YAML->read($_[0]);
+ unless ( $self ) {
+ croak("Failed to load YAML document from '" . ($_[0] || '') . "'");
+ }
+ if ( wantarray ) {
+ return @$self;
+ } else {
+ # Return only the last document to match YAML.pm,
+ return $self->[-1];
+ }
+}
+
+#####################################################################
+# Use Scalar::Util if possible, otherwise emulate it
+
+BEGIN {
+ eval {
+ require Scalar::Util;
+ };
+ if ( $@ ) {
+ # Failed to load Scalar::Util
+ eval <<'END_PERL';
+sub refaddr {
+ my $pkg = ref($_[0]) or return undef;
+ if (!!UNIVERSAL::can($_[0], 'can')) {
+ bless $_[0], 'Scalar::Util::Fake';
+ } else {
+ $pkg = undef;
+ }
+ "$_[0]" =~ /0x(\w+)/;
+ my $i = do { local $^W; hex $1 };
+ bless $_[0], $pkg if defined $pkg;
+ $i;
+}
+END_PERL
+ } else {
+ Scalar::Util->import('refaddr');
+ }
+}
+
+1;
+
+__END__
--- /dev/null
+package inc::latest;
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.35_08';
+$VERSION = eval $VERSION;
+
+use Carp;
+use File::Basename ();
+use File::Spec ();
+use File::Path ();
+use IO::File ();
+use File::Copy ();
+
+# track and return modules loaded by inc::latest
+my @loaded_modules;
+sub loaded_modules {@loaded_modules}
+
+# must ultimately "goto" the import routine of the module to be loaded
+# so that the calling package is correct when $mod->import() runs.
+sub import {
+ my ($package, $mod, @args) = @_;
+ return unless(defined $mod);
+
+ my $inc_path = './inc/latest.pm';
+ my $private_path = './inc/latest/private.pm';
+ if(-e $inc_path) {
+ # delete our methods
+ delete $inc::latest::{$_} for(keys %inc::latest::);
+ # load the bundled module
+ require $inc_path;
+ require $private_path;
+ my $import = inc::latest->can('import');
+ goto $import;
+ }
+
+ # author mode - just record and load the modules
+ push(@loaded_modules, $mod);
+ require inc::latest::private;
+ goto \&inc::latest::private::_load_module;
+}
+
+sub write {
+ my $package = shift;
+ my ($where, @preload) = @_;
+
+ warn "should really be writing in inc/" unless $where =~ /inc$/;
+
+ # write inc/latest.pm
+ File::Path::mkpath( $where );
+ my $fh = IO::File->new( File::Spec->catfile($where,'latest.pm'), "w" );
+ print {$fh} "# This stub created by inc::latest $VERSION\n";
+ print {$fh} <<'HERE';
+package inc::latest;
+use strict;
+use vars '@ISA';
+require inc::latest::private;
+@ISA = qw/inc::latest::private/;
+HERE
+ if (@preload) {
+ print {$fh} "\npackage inc::latest::preload;\n";
+ for my $mod (@preload) {
+ print {$fh} "inc::latest->import('$mod');\n";
+ }
+ }
+ print {$fh} "\n1;\n";
+ close $fh;
+
+ # write inc/latest/private;
+ require inc::latest::private;
+ File::Path::mkpath( File::Spec->catdir( $where, 'latest' ) );
+ my $from = $INC{'inc/latest/private.pm'};
+ my $to = File::Spec->catfile($where,'latest','private.pm');
+ File::Copy::copy( $from, $to ) or die "Couldn't copy '$from' to '$to': $!";
+
+ return 1;
+}
+
+sub bundle_module {
+ my ($package, $module, $where) = @_;
+
+ # create inc/inc_$foo
+ (my $dist = $module) =~ s{::}{-}g;
+ my $inc_lib = File::Spec->catdir($where,"inc_$dist");
+ File::Path::mkpath $inc_lib;
+
+ # get list of files to copy
+ require ExtUtils::Installed;
+ # workaround buggy EU::Installed check of @INC
+ my $inst = ExtUtils::Installed->new(extra_libs => [@INC]);
+ my $packlist = $inst->packlist( $module ) or die "Couldn't find packlist";
+ my @files = grep { /\.pm$/ } keys %$packlist;
+
+
+ # figure out prefix
+ my $mod_path = quotemeta $package->_mod2path( $module );
+ my ($prefix) = grep { /$mod_path$/ } @files;
+ $prefix =~ s{$mod_path$}{};
+
+ # copy files
+ for my $from ( @files ) {
+ next unless $from =~ /\.pm$/;
+ (my $mod_path = $from) =~ s{^\Q$prefix\E}{};
+ my $to = File::Spec->catfile( $inc_lib, $mod_path );
+ File::Path::mkpath(File::Basename::dirname($to));
+ File::Copy::copy( $from, $to ) or die "Couldn't copy '$from' to '$to': $!";
+ }
+ return 1;
+}
+
+# Translate a module name into a directory/file.pm to search for in @INC
+sub _mod2path {
+ my ($self, $mod) = @_;
+ my @parts = split /::/, $mod;
+ $parts[-1] .= '.pm';
+ return $parts[0] if @parts == 1;
+ return File::Spec->catfile(@parts);
+}
+
+1;
+
+
+=head1 NAME
+
+inc::latest - use modules bundled in inc/ if they are newer than installed ones
+
+=head1 SYNOPSIS
+
+ # in Build.PL
+ use inc::latest 'Module::Build';
+
+=head1 DESCRIPTION
+
+The C<inc::latest> module helps bootstrap configure-time dependencies for CPAN
+distributions. These dependencies get bundled into the C<inc> directory within
+a distribution and are used by Build.PL (or Makefile.PL).
+
+Arguments to C<inc::latest> are module names that are checked against both the
+current C<@INC> array and against specially-named directories in C<inc>. If
+the bundled verison is newer than the installed one (or the module isn't
+installed, then, the bundled directory is added to the start of <@INC> and the
+module is loaded from there.
+
+There are actually two variations of C<inc::latest> -- one for authors and one
+for the C<inc> directory. For distribution authors, the C<inc::latest>
+installed in the system will record modules loaded via C<inc::latest> and can
+be used to create the bundled files in C<inc>, including writing the second
+variation as C<inc/latest.pm>.
+
+This second C<inc::latest> is the one that is loaded in a distribution being
+installed (e.g. from Build.PL). This bundled C<inc::latest> is the one
+that determines which module to load.
+
+=head2 Special notes on bundling
+
+The C<inc::latest> module creates bundled directories based on the packlist
+file of an installed distribution. Even though C<inc::latest> takes module
+name arguments, it is better to think of it as bundling and making available
+entire I<distributions>. When a module is loaded through C<inc::latest>,
+it looks in all bundled distributions in C<inc/> for a newer module than
+can be found in the existing C<@INC> array.
+
+Thus, the module-name provided should usually be the "top-level" module name of
+a distribution, though this is not strictly required. For example,
+L<Module::Build> has a number of heuristics to map module names to packlists,
+allowing users to do things like this:
+
+ use inc::latest 'Devel::AssertOS::Unix';
+
+even though Devel::AssertOS::Unix is contained within the Devel-CheckOS
+distribution.
+
+At the current time, packlists are required. Thus, bundling dual-core modules
+may require a 'forced install' over versions in the latest version of perl
+in order to create the necessary packlist for bundling.
+
+=head1 USAGE
+
+When calling C<use>, the bundled C<inc::latest> takes a single module name and
+optional arguments to pass to that module's own import method.
+
+ use 'inc::latest' 'Foo::Bar' qw/foo bar baz/;
+
+=head2 Author-mode
+
+You are in author-mode inc::latest if any of the Author-mode methods are
+available. For example:
+
+ if ( inc::latest->can('write') ) {
+ inc::latest->write('inc');
+ }
+
+=over 4
+
+=item loaded_modules()
+
+ my @list = inc::latest->loaded_modules;
+
+This takes no arguments and always returns a list of module names requested for
+loading via "use inc::latest 'MODULE'", regardless of wether the load was
+successful or not.
+
+=item write()
+
+ inc::latest->write( 'inc' );
+
+This writes the bundled version of inc::latest to the directory name given as an
+argument. It almost all cases, it should be 'C<inc>'.
+
+=item bundle_module()
+
+ for my $mod ( inc::latest->loaded_modules ) {
+ inc::latest->bundle_module($mod, $dir);
+ }
+
+If $mod corresponds to a packlist, then this function creates a specially-named
+directory in $dir and copies all .pm files from the modlist to the new
+directory (which almost always should just be 'inc'). For example, if Foo::Bar
+is the name of the module, and $dir is 'inc', then the directory would be
+'inc/inc_Foo-Bar' and contain files like this:
+
+ inc/inc_Foo-Bar/Foo/Bar.pm
+
+Currently, $mod B<must> have a packlist. If this is not the case (e.g. for a
+dual-core module), then the bundling will fail. You may be able to create a
+packlist by forced installing the module on top of the version that came with
+core Perl.
+
+=back
+
+=head2 As bundled in inc/
+
+All methods are private. Only the C<import> method is public.
+
+=head1 AUTHOR
+
+Eric Wilhelm <ewilhelm@cpan.org>, David Golden <dagolden@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2009 by Eric Wilhelm and David Golden
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Module::Build>
+
+=cut
+
--- /dev/null
+package inc::latest::private;
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.35_08';
+$VERSION = eval $VERSION;
+
+use File::Spec;
+use IO::File;
+
+# must ultimately "goto" the import routine of the module to be loaded
+# so that the calling package is correct when $mod->import() runs.
+sub import {
+ my ($package, $mod, @args) = @_;
+ my $file = $package->_mod2path($mod);
+
+ if ($INC{$file}) {
+ # Already loaded, but let _load_module handle import args
+ goto \&_load_module;
+ }
+
+ # A bundled copy must be present
+ my ($bundled, $bundled_dir) = $package->_search_bundled($file)
+ or die "No bundled copy of $mod found";
+
+ my $from_inc = $package->_search_INC($file);
+ unless ($from_inc) {
+ # Only bundled is available
+ unshift(@INC, $bundled_dir);
+ goto \&_load_module;
+ }
+
+ if (_version($from_inc) >= _version($bundled)) {
+ # Ignore the bundled copy
+ goto \&_load_module;
+ }
+
+ # Load the bundled copy
+ unshift(@INC, $bundled_dir);
+ goto \&_load_module;
+}
+
+sub _version {
+ require ExtUtils::MakeMaker;
+ return ExtUtils::MM->parse_version(shift);
+}
+
+# use "goto" for import to preserve caller
+sub _load_module {
+ my $package = shift; # remaining @_ is ready for goto
+ my ($mod, @args) = @_;
+ eval "require $mod; 1" or die $@;
+ if ( my $import = $mod->can('import') ) {
+ goto $import;
+ }
+ return 1;
+}
+
+sub _search_bundled {
+ my ($self, $file) = @_;
+
+ my $mypath = 'inc';
+
+ local *DH; # Maintain 5.005 compatibility
+ opendir DH, $mypath or die "Can't open directory $mypath: $!";
+
+ while (defined(my $e = readdir DH)) {
+ next unless $e =~ /^inc_/;
+ my $try = File::Spec->catfile($mypath, $e, $file);
+
+ return($try, File::Spec->catdir($mypath, $e)) if -e $try;
+ }
+ return;
+}
+
+# Look for the given path in @INC.
+sub _search_INC {
+ # TODO: doesn't handle coderefs or arrayrefs or objects in @INC, but
+ # it probably should
+ my ($self, $file) = @_;
+
+ foreach my $dir (@INC) {
+ next if ref $dir;
+ my $try = File::Spec->catfile($dir, $file);
+ return $try if -e $try;
+ }
+
+ return;
+}
+
+# Translate a module name into a directory/file.pm to search for in @INC
+sub _mod2path {
+ my ($self, $mod) = @_;
+ my @parts = split /::/, $mod;
+ $parts[-1] .= '.pm';
+ return $parts[0] if @parts == 1;
+ return File::Spec->catfile(@parts);
+}
+
+1;
+
+
use lib 't/lib';
use MBTest tests => 8;
use DistGen;
-use Module::Build;
+blib_load('Module::Build');
my $dist;
--- /dev/null
+=head1 A GUIDE TO WRITING TESTS FOR MODULE::BUILD
+
+This document provides tips on writing new tests for Module::Build. Please
+note that many existing tests were written prior to these guidelines and
+have many different styles. Please don't copy/paste old tests by rote without
+considering better ways to test. See C<sample.t> for a starter test file.
+
+=head1 TEST FILE PREAMBLE
+
+Every Module::Build test should begin with the same preamble to ensure that the
+test library is set properly and that the correct version of Module::Build is
+being tested.
+
+ use strict;
+ use lib 't/lib';
+ use MBTest tests => 2; # or 'no_plan'
+
+ blib_load('Module::Build');
+
+The C<MBTest> module is in C<t/lib/> and subclasses Test::More. When loaded
+it cleans up several environment variables that could cause problems,
+tweaks C<@INC> and exports several helper functions. See that module for
+details.
+
+=head1 CREATING A TEST DISTRIBUTION
+
+The C<DistGen> module in C<t/lib/> should be used to create sample
+distributions for testing. It provides numerous helpful methods to
+create a skeleton distribution, add files, change files, and so on.
+Run C<perldoc> on C<t/lib/DistGen.pm> to see the documentation.
+
+ # CREATE A TEST DISTRIBUTION
+
+ use DistGen;
+
+ # create dist object in a temp directory
+ my $dist = DistGen->new;
+
+ # enter the test distribution directory before further testing
+ $dist->chdir_in;
+
+ # generate the skeleton files
+ $dist->regen;
+
+
+=head1 GETTING A MODULE::BUILD OBJECT
+
+From inside the test distribution, you can get the Module::Build object
+configured in Build.PL using the C<new_from_context> method on the
+dist object. This is just like Module::Build's C<new_from_context> except
+it passes C<< quiet => 1 >> to avoid sending output to the terminal.
+Use the Module::Build object to test the programmatic API.
+
+ my $mb = $dist->new_from_context( quiet => 1 );
+ isa_ok( $mb, "Module::Build" );
+ is( $mb->dist_name, "Simple", "dist_name is 'Simple'" );
+
+=head1 TESTING THE COMMAND LINE API
+
+The command line API is tested by running subprocesses, not via a Module::Build
+object. The C<DistGen> object has helper methods for running C<Build.PL> and
+C<Build> and passing arguments on the command line.
+
+ $dist->run_build_pl( '--quiet' );
+ $dist->run_build( 'test' );
+
+=head1 TYPICAL TESTING CYCLE
+
+The typical testing cycle is to generate or modify a test distribution, either
+through the C<DistGen> object or directly in the filesystem, then regenerate
+the distribution and test it (or run command line tests and observe the
+result.)
+
+ # Modify the distribution
+
+ $dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'artistic',
+ }
+ );
+ $dist->regen;
+
+ # Get a new build object and test it
+
+ $mb = $dist->new_from_context;
+ is( $mb->license, "artistic", "saw 'artistic' license" );
+
+
+=head1 COPYRIGHT
+
+This documentation is Copyright (C) 2009 by David Golden. You can redistribute
+it and/or modify it under the same terms as Perl 5.10.0.
+
--- /dev/null
+use strict;
+use lib 't/lib';
+use MBTest;
+use DistGen;
+
+plan tests => 7;
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+
+# create dist object in a temp directory
+# enter the directory and generate the skeleton files
+my $dist = DistGen->new->chdir_in;
+
+$dist->change_build_pl(
+ module_name => $dist->name,
+ requires => {
+ 'File::Spec' => 9999,
+ },
+ build_requires => {
+ 'Getopt::Long' => 9998,
+ },
+ cpan_client => $^X . ' -le print($_)for($^X,@ARGV)',
+)->regen;
+
+# get a Module::Build object and test with it
+my $mb;
+stdout_stderr_of( sub { $mb = $dist->new_from_context('verbose' => 1) } );
+isa_ok( $mb, "Module::Build" );
+like( $mb->cpan_client, qr/^\Q$^X\E/, "cpan_client is mocked with perl" );
+
+my $out = stdout_of( sub {
+ $dist->run_build('installdeps')
+});
+ok( length($out), "ran mocked Build installdeps");
+my $expected = quotemeta(Module::Build->find_command($^X));
+like( $out, qr/$expected/i, "relative cpan_client resolved relative to \$^X" );
+like( $out, qr/File::Spec/, "saw File::Spec prereq" );
+like( $out, qr/Getopt::Long/, "saw Getopt::Long prereq" );
+
+$out = stdout_stderr_of( sub {
+ $dist->run_build('installdeps', '--cpan_client', 'ADLKASJDFLASDJ')
+});
+like( $out, qr/cpan_client .* is not executable/,
+ "Build installdeps with bad cpan_client dies"
+);
+
+# vim:ts=2:sw=2:et:sta:sts=2
use strict;
use lib 't/lib';
-use MBTest tests => 29;
+use MBTest tests => 27;
#use MBTest 'no_plan';
use DistGen;
-BEGIN { use_ok 'Module::Build' or die; }
-ensure_blib 'Module::Build';
+blib_load 'Module::Build';
my $tmp = MBTest->tmpdir;
my $dist = DistGen->new( dir => $tmp );
use strict;
use lib 't/lib';
-use MBTest tests => 60;
+use MBTest tests => 58;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
$mb = Module::Build->new( dist_name => $dist->name, dist_version => 7 );
ok $mb;
- ok ! $mb->module_name; # Make sure it's defined
+ ok $mb->module_name; # Set via heuristics
is $mb->dist_name, $dist->name;
}
is $args{foo}, 1;
# revert test distribution to pristine state because we modified a file
- $dist->remove;
- $dist = DistGen->new( dir => $tmp );
- $dist->regen;
- $dist->chdir_in;
+ $dist->regen( clean => 1 );
}
# Test author stuff
is_deeply $mb->include_dirs, ['/foo'], 'Should have single include dir';
}
-# cleanup
-$dist->remove;
--- /dev/null
+# sample.t -- a sample test file for Module::Build
+
+use strict;
+use lib 't/lib';
+use MBTest; # or 'no_plan'
+use DistGen;
+use Config;
+use IO::File;
+use File::Spec;
+use ExtUtils::Packlist;
+use File::Path;
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
+
+if ( Module::Build::ConfigData->feature('inc_bundling_support') ) {
+ plan tests => 18;
+} else {
+ plan skip_all => 'inc_bundling_support feature is not enabled';
+}
+
+# need to do a temp install of M::B being tested to ensure a packlist
+# is available for bundling
+
+my $current_mb = Module::Build->resume();
+my $temp_install = MBTest->tmpdir();
+my $arch = $Config{archname};
+my $lib_path = File::Spec->catdir($temp_install,qw/lib perl5/);
+my $arch_path = File::Spec->catdir( $lib_path, $arch );
+mkpath ( $arch_path );
+ok( -d $arch_path, "created temporary M::B pseudo-install directory");
+
+unshift @INC, $lib_path, $arch_path;
+local $ENV{PERL5LIB} = join( $Config{path_sep},
+ $lib_path, $arch_path, ($ENV{PERL5LIB} ? $ENV{PERL5LIB} : () )
+);
+
+stdout_of( sub { $current_mb->dispatch('install', install_base => $temp_install) } );
+
+# create dist object in a temp directory
+# enter the directory and generate the skeleton files
+my $dist = DistGen->new( inc => 1 )->chdir_in->regen;
+
+# get a Module::Build object and test with it
+my $mb = $dist->new_from_context(); # quiet by default
+isa_ok( $mb, "Module::Build" );
+is( $mb->dist_name, "Simple", "dist_name is 'Simple'" );
+is_deeply( $mb->bundle_inc, [ 'Module::Build' ],
+ "Module::Build is flagged for bundling"
+);
+
+# see what gets bundled
+stdout_stderr_of( sub { $mb->dispatch('distdir') } );
+
+my $dist_inc = File::Spec->catdir($mb->dist_dir, 'inc');
+ok( -e File::Spec->catfile( $dist_inc, 'latest.pm' ),
+ "./inc/latest.pm created"
+);
+
+ok( -d File::Spec->catdir( $dist_inc, 'inc_Module-Build' ),
+ "dist_dir/inc/inc_Module_Build created"
+);
+
+my $mb_file =
+ File::Spec->catfile( $dist_inc, qw/inc_Module-Build Module Build.pm/ );
+
+ok( -e $mb_file,
+ "dist_dir/inc/inc_Module_Build/Module/Build.pm created"
+);
+
+ok( -e File::Spec->catfile( $dist_inc, qw/inc_Module-Build Module Build Base.pm/ ),
+ "dist_dir/inc/inc_Module_Build/Module/Build/Base.pm created"
+);
+
+# Force bundled M::B to a higher version so it gets loaded
+
+my $fh = IO::File->new($mb_file, "+<") or die "Could not open $mb_file: $!";
+my $mb_code = do { local $/; <$fh> };
+$mb_code =~ s{\$VERSION\s+=\s+\S+}{\$VERSION = 9999;};
+$fh->seek(0,0);
+print {$fh} $mb_code;
+$fh->close;
+
+# test the bundling in dist_dir
+chdir $mb->dist_dir;
+
+stdout_of( sub { Module::Build->run_perl_script('Build.PL',[],[]) } );
+
+my $meta = IO::File->new('MYMETA.yml');
+ok( $meta, "found MYMETA.yml" );
+ok( scalar( grep { /generated_by:.*9999/ } <$meta> ),
+ "dist_dir Build.PL loaded bundled Module::Build"
+);
+
+#--------------------------------------------------------------------------#
+# test identification of dependencies
+#--------------------------------------------------------------------------#
+
+$dist->chdir_in;
+
+$dist->add_file( 'mylib/Foo.pm', << 'HERE' );
+package Foo;
+our $VERSION = 1;
+1;
+HERE
+
+$dist->add_file( 'mylib/Bar.pm', << 'HERE' );
+package Bar;
+use Foo;
+our $VERSION = 42;
+1;
+HERE
+
+$dist->change_file( 'Build.PL', << "HERE" );
+use inc::latest 'Module::Build';
+use inc::latest 'Foo';
+
+Module::Build->new(
+ module_name => '$dist->{name}',
+ license => 'perl',
+)->create_build_script;
+HERE
+
+$dist->regen( clean => 1 );
+
+make_packlist($_,'mylib') for qw/Foo Bar/;
+
+# get a Module::Build object and test with it
+my $abs_mylib = File::Spec->rel2abs('mylib');
+
+
+unshift @INC, $abs_mylib;
+$mb = $dist->new_from_context(); # quiet by default
+isa_ok( $mb, "Module::Build" );
+is_deeply( [sort @{$mb->bundle_inc}], [ 'Foo', 'Module::Build' ],
+ "Module::Build and Foo are flagged for bundling"
+);
+
+my $output = stdout_stderr_of( sub { $mb->dispatch('distdir') } );
+
+ok( -e File::Spec->catfile( $dist_inc, 'latest.pm' ),
+ "./inc/latest.pm created"
+);
+
+ok( -d File::Spec->catdir( $dist_inc, 'inc_Foo' ),
+ "dist_dir/inc/inc_Foo created"
+);
+
+$dist->change_file( 'Build.PL', << "HERE" );
+use inc::latest 'Module::Build';
+use inc::latest 'Bar';
+
+Module::Build->new(
+ module_name => '$dist->{name}',
+ license => 'perl',
+)->create_build_script;
+HERE
+
+$dist->regen( clean => 1 );
+make_packlist($_,'mylib') for qw/Foo Bar/;
+
+$mb = $dist->new_from_context(); # quiet by default
+isa_ok( $mb, "Module::Build" );
+is_deeply( [sort @{$mb->bundle_inc}], [ 'Bar', 'Module::Build' ],
+ "Module::Build and Bar are flagged for bundling"
+);
+
+$output = stdout_stderr_of( sub { $mb->dispatch('distdir') } );
+
+ok( -e File::Spec->catfile( $dist_inc, 'latest.pm' ),
+ "./inc/latest.pm created"
+);
+
+ok( -d File::Spec->catdir( $dist_inc, 'inc_Bar' ),
+ "dist_dir/inc/inc_Bar created"
+);
+
+
+
+sub make_packlist {
+ my ($mod, $lib) = @_;
+ my $arch = $Config{archname};
+ (my $mod_path = $mod) =~ s{::}{/}g;
+ my $mod_file = File::Spec->catfile( $lib, "$mod_path\.pm" );
+ my $abs = File::Spec->rel2abs($mod_file);
+ my $packlist_path = File::Spec->catdir($lib, $arch, 'auto', $mod_path);
+ mkpath $packlist_path;
+ my $packlist = ExtUtils::Packlist->new;
+ $packlist->{$abs}++;
+ $packlist->write( File::Spec->catfile( $packlist_path, '.packlist' ));
+}
+
+# vim:ts=2:sw=2:et:sta:sts=2
my $is_vms_mms = ($^O eq 'VMS') && ($Config{make} =~ /MM[SK]/i);
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
+blib_load('Module::Build::Version');
#########################
#########################
-use Module::Build;
-use Module::Build::Compat;
+blib_load('Module::Build');
+blib_load('Module::Build::Compat');
use Carp; $SIG{__WARN__} = \&Carp::cluck;
license => 'perl',
requires => {
'perl' => $],
- 'File::Spec' => 0,
+ 'File::Spec' => 0.2,
},
- build_requires => {
- 'Test::More' => 0,
+ build_requires => {
+ 'Test::More' => 0,
+ 'File::Spec' => 0,
},
PL_files => { 'foo.PL' => 'foo' },
});
test_makefile_types(
requires => {
'perl' => $],
- 'File::Spec' => 0,
+ 'File::Spec' => 0.2,
+ },
+ build_requires => {
'Test::More' => 0,
+ 'File::Spec' => 0,
},
PL_files => {
'foo.PL' => 'foo',
# Create M::B instance but don't pollute STDOUT
my $mb;
-stdout_of( sub {
+stdout_stderr_of( sub {
$mb = Module::Build->new_from_context;
});
ok $mb, "Module::Build->new_from_context";
# Makefile.PL - make sure it fails in the right way here.
local @Foo::Builder::ISA = qw(Module::Build);
my $foo_builder;
- stdout_of( sub {
+ stdout_stderr_of( sub {
$foo_builder = Foo::Builder->new_from_context;
});
foreach my $style ('passthrough', 'small') {
# Now make sure it can actually work.
my $bar_builder;
- stdout_of( sub {
+ stdout_stderr_of( sub {
$bar_builder = Module::Build->subclass( class => 'Bar::Builder' )->new_from_context;
});
foreach my $style ('passthrough', 'small') {
create_makefile_pl($style, $bar_builder);
my $result;
- stdout_of( sub {
+ stdout_stderr_of( sub {
$result = $mb->run_perl_script('Makefile.PL');
});
ok $result, "Makefile.PL ran without error";
my $libdir = File::Spec->catdir( $tmp, 'libdir' );
my $result;
- stdout_of( sub {
+ stdout_stderr_of( sub {
$result = $mb->run_perl_script('Makefile.PL', [],
[
"LIB=$libdir",
# Make sure those switches actually had an effect
my ($ran_ok, $output);
- $output = stdout_of( sub { $ran_ok = $new_build->do_system(@make, 'test') } );
+ $output = stdout_stderr_of( sub { $ran_ok = $new_build->do_system(@make, 'test') } );
ok $ran_ok, "make test ran without error";
$output =~ s/^/# /gm; # Don't confuse our own test output
like $output, qr/(?:# ok \d+\s+)+/, 'Should be verbose';
$make_macro = '/macro=("' . $make_macro . '")';
}
- $output = stdout_of( sub {
+ $output = stdout_stderr_of( sub {
local $ENV{HARNESS_TIMER}; # RT#39635 - timer messes with output
$ran_ok = $mb->do_system(@make, 'test', $make_macro)
} );
}
}
- stdout_of( sub { $mb->do_system(@make, 'realclean'); } );
+ stdout_stderr_of( sub { $mb->do_system(@make, 'realclean'); } );
ok ! -e $makefile, "$makefile shouldn't exist";
1 while unlink 'Makefile.PL';
create_makefile_pl('passthrough', $mb);
- stdout_of( sub {
+ stdout_stderr_of( sub {
$mb->run_perl_script('Makefile.PL', [], ['INSTALL_BASE=~/foo']);
});
my $b2 = Module::Build->current;
ok $b2->install_base, "install_base set";
unlike $b2->install_base, qr/^~/, "Tildes should be expanded";
- stdout_of( sub { $mb->do_system(@make, 'realclean'); } );
+ stdout_stderr_of( sub { $mb->do_system(@make, 'realclean'); } );
ok ! -e $makefile, "$makefile shouldn't exist";
1 while unlink 'Makefile.PL';
$dist->regen;
my $mb;
- stdout_of( sub {
+ stdout_stderr_of( sub {
$mb = Module::Build->new_from_context( recursive_test_files => 1 );
});
create_makefile_pl('traditional', $mb);
my $args = extract_writemakefile_args() || {};
- is $args->{TESTS},
- join( q{ },
- File::Spec->catfile(qw(t *.t)),
- File::Spec->catfile(qw(t deep *.t))
- ),
- 'Makefile.PL has correct TESTS line for recursive test files';
-}
-# cleanup
-$dist->remove;
+ if ( exists $args->{test}->{TESTS} ) {
+ is $args->{test}->{TESTS},
+ join( q{ },
+ File::Spec->catfile(qw(t *.t)),
+ File::Spec->catfile(qw(t deep *.t))
+ ),
+ 'Makefile.PL has correct TESTS line for recursive test files';
+ } else {
+ ok( ! exists $args->{TESTS}, 'Not using incorrect recursive tests key' );
+ }
+
+}
#########################################################
+sub _merge_prereqs {
+ my ($first, $second) = @_;
+ my $new = { %$first };
+ for my $k (keys %$second) {
+ if ( exists $new->{$k} ) {
+ my ($v1,$v2) = ($new->{$k},$second->{$k});
+ $new->{$k} = ($v1 > $v2 ? $v1 : $v2);
+ }
+ else {
+ $new->{$k} = $second->{$k};
+ }
+ }
+ return $new;
+}
+
sub test_makefile_types {
my %opts = @_;
$opts{requires} ||= {};
+ $opts{build_requires} ||= {};
$opts{PL_files} ||= {};
foreach my $type (@makefile_types) {
# Create M::B instance
my $mb;
- stdout_of( sub {
+ stdout_stderr_of( sub {
$mb = Module::Build->new_from_context;
});
ok $mb, "Module::Build->new_from_context";
test_makefile_pl_requires_perl( $opts{requires}{perl} );
test_makefile_creation($mb);
- test_makefile_prereq_pm( $opts{requires} );
+ test_makefile_prereq_pm( _merge_prereqs($opts{requires}, $opts{build_requires}) );
test_makefile_pl_files( $opts{PL_files} ) if $type eq 'traditional';
my ($output,$success);
# Capture output to keep our STDOUT clean
- $output = stdout_of( sub {
+ $output = stdout_stderr_of( sub {
$success = $mb->do_system(@make);
});
ok $success, "make ran without error";
}
# Can't let 'test' STDOUT go to our STDOUT, or it'll confuse Test::Harness.
- $output = stdout_of( sub {
+ $output = stdout_stderr_of( sub {
$success = $mb->do_system(@make, 'test');
});
ok $success, "make test ran without error";
like uc $output, qr{DONE\.|SUCCESS}, "make test output indicated success";
- $output = stdout_of( sub {
+ $output = stdout_stderr_of( sub {
$success = $mb->do_system(@make, 'realclean');
});
ok $success, "make realclean ran without error";
my ($output, $result);
# capture output to avoid polluting our test output
- $output = stdout_of( sub {
+ $output = stdout_stderr_of( sub {
$result = $build->run_perl_script('Makefile.PL', $preargs, $postargs);
});
my $label = "Makefile.PL ran without error";
}
sub create_makefile_pl {
- Module::Build::Compat->create_makefile_pl(@_);
+ my @args = @_;
+ stdout_stderr_of( sub { Module::Build::Compat->create_makefile_pl(@args) } );
my $ok = ok -e 'Makefile.PL', "$_[0] Makefile.PL created";
# Some really conservative make's, like HP/UX, assume files with the same
use strict;
use lib 't/lib';
-use MBTest tests => 5;
+use MBTest tests => 3;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
#########################
my $mb; stdout_of(sub{ $mb = Module::Build->new_from_context});
-use Module::Build::Compat;
+blib_load('Module::Build::Compat');
$dist->regen;
-Module::Build::Compat->create_makefile_pl('passthrough', $mb);
+stdout_stderr_of(
+ sub{ Module::Build::Compat->create_makefile_pl('passthrough', $mb); }
+);
# as silly as all of this exit(0) business is, that is what the cpan
# testers have instructed everybody to do so...
use strict;
use lib 't/lib';
-use MBTest tests => 3;
+use MBTest tests => 1;
-require_ok('Module::Build');
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
use DistGen;
my $dist = DistGen->new( dir => $tmp );
$dist->regen;
-END{ $dist->remove }
-
$dist->chdir_in;
#########################
);
}
-#########################
-
-# cleanup
use strict;
use lib 't/lib';
-use MBTest tests => 115;
+use MBTest tests => 113;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
}
# Poke at the innards of MB to change the default install locations.
- my $old = $mb->install_sets->{site} = \%test_config;
+ my $old = $mb->install_sets->{site};
+ $mb->install_sets->{site} = \%test_config;
$mb->config(siteprefixexp => catdir(File::Spec->rootdir,
'wierd', 'prefix'));
}
}
-
-$dist->remove;
use lib 't/lib';
use MBTest;
-use Module::Build;
-
my @unix_splits =
(
{ q{one t'wo th'ree f"o\"ur " "five" } => [ 'one', 'two three', 'fo"ur ', 'five' ] },
{ 'a " b " c' => [ 'a', ' b ', 'c' ] },
);
-plan tests => 10 + 4*@unix_splits + 4*@win_splits;
+plan tests => 9 + 4*@unix_splits + 4*@win_splits;
-ensure_blib('Module::Build');
+blib_load('Module::Build');
+blib_load('Module::Build::Platform::Unix');
+blib_load('Module::Build::Platform::Windows');
#########################
# I think 3.24 isn't actually the majik version, my 3.23 seems to pass...
my $low_TPW_version = Text::ParseWords->VERSION < 3.24;
-use Module::Build::Platform::Unix;
foreach my $test (@unix_splits) {
# Text::ParseWords bug:
local $TODO = $low_TPW_version && ((keys %$test)[0] =~ m{\\\n});
do_split_tests('Module::Build::Platform::Unix', $test);
}
-use Module::Build::Platform::Windows;
foreach my $test (@win_splits) {
do_split_tests('Module::Build::Platform::Windows', $test);
}
use strict;
use lib 't/lib';
-use MBTest tests => 66;
+use MBTest tests => 64;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
$mb->add_build_element('foo');
$mb->add_build_element('foo');
- is_deeply $mb->build_elements, [qw(PL support pm xs pod script foo)],
+ is_deeply $mb->build_elements, [qw(PL support pm xs share_dir pod script foo)],
'The foo element should be in build_elements only once';
$mb->dispatch('build');
meta_add => {foo => 'bar'},
conflicts => {'Foo::Barxx' => 0},
);
- my %data;
- $mb->prepare_metadata( \%data );
- is $data{foo}, 'bar';
+ my $data = $mb->prepare_metadata;
+ is $data->{foo}, 'bar';
$mb->meta_merge(foo => 'baz');
- $mb->prepare_metadata( \%data );
- is $data{foo}, 'baz';
+ $data = $mb->prepare_metadata;
+ is $data->{foo}, 'baz';
$mb->meta_merge(conflicts => {'Foo::Fooxx' => 0});
- $mb->prepare_metadata( \%data );
- is_deeply $data{conflicts}, {'Foo::Barxx' => 0, 'Foo::Fooxx' => 0};
+ $data = $mb->prepare_metadata;
+ is_deeply $data->{conflicts}, {'Foo::Barxx' => 0, 'Foo::Fooxx' => 0};
$mb->meta_add(conflicts => {'Foo::Bazxx' => 0});
- $mb->prepare_metadata( \%data );
- is_deeply $data{conflicts}, {'Foo::Bazxx' => 0, 'Foo::Fooxx' => 0};
+ $data = $mb->prepare_metadata;
+ is_deeply $data->{conflicts}, {'Foo::Bazxx' => 0, 'Foo::Fooxx' => 0};
}
{
}
-# cleanup
-$dist->remove;
use strict;
use lib 't/lib';
-use MBTest tests => 6;
+use MBTest tests => 4;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
use IO::File;
my $tmp = MBTest->tmpdir;
ok( Module::Build->dir_contains($first, $second) );
}
-# cleanup
-$dist->remove;
use strict;
use lib 't/lib';
-use MBTest tests => 25;
+use MBTest tests => 23;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
-
-use Cwd ();
-use File::Path ();
-
-my $cwd = Cwd::cwd();
-my $tmp = MBTest->tmpdir;
+blib_load('Module::Build');
use DistGen;
-my $dist = DistGen->new(dir => $tmp);
-
-
+my $dist = DistGen->new;
$dist->regen;
+$dist->chdir_in;
my $restart = sub {
- $dist->clean();
- DistGen::chdir_all( $cwd );
- File::Path::rmtree( $tmp );
# we're redefining the same package as we go, so...
delete($::{'MyModuleBuilder::'});
delete($INC{'MyModuleBuilder.pm'});
- $dist->regen;
- chdir($dist->dirname) or
- die "Can't chdir to '@{[$dist->dirname]}': $!";
+ $dist->regen( clean => 1 );
};
-chdir($dist->dirname) or die "Can't chdir to '@{[$dist->dirname]}': $!";
-
########################################################################
{ # check the =item style
my $mb = Module::Build->subclass(
# cleanup
$dist->clean();
-DistGen::chdir_all($cwd);
-File::Path::rmtree( $tmp );
# vim:ts=2:sw=2:et:sta
use strict;
use lib 't/lib';
-use MBTest tests => 36;
+use MBTest tests => 34;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
use Config;
use Cwd ();
is keys %$pms, 0;
# revert to pristine state
- $dist->remove;
- $dist = DistGen->new( dir => $tmp );
- $dist->regen;
- $dist->chdir_in;
+ $dist->regen( clean => 1 );
}
sub strip_volume {
ok -e $file or diag("Expected $file to exist, but it doesn't");
}
-
-# cleanup
-$dist->remove;
use strict;
use lib 't/lib';
-use MBTest tests => 8;
+use MBTest tests => 6;
-require_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
use File::Spec::Functions qw( catdir );
}
#Copy share files to blib
-sub process_share_files
+sub process_shared_files
{
my $self = shift;
- $self->copy_files("share");
+ $self->copy_files("shared");
}
1;
);
$build->add_build_element('etc');
-$build->add_build_element('share');
+$build->add_build_element('shared');
my $distdir = lc $build->dist_name();
foreach my $id ('core', 'site', 'vendor') {
#Where to install these build types when using prefix symantics
- $build->prefix_relpaths($id, 'share' => "share/$distdir");
+ $build->prefix_relpaths($id, 'shared' => "shared/$distdir");
$build->prefix_relpaths($id, 'etc' => "etc/$distdir");
#Where to install these build types when using default symantics
my $set = $build->install_sets($id);
- $set->{'share'} = '/usr/'.($id eq 'site' ? 'local/':'')."share/$distdir";
+ $set->{'shared'} = '/usr/'.($id eq 'site' ? 'local/':'')."shared/$distdir";
$set->{'etc'} = ($id eq 'site' ? '/usr/local/etc/':'/etc/').$distdir;
}
#Where to install these types when using install_base symantics
-$build->install_base_relpaths('share' => "share/$distdir");
+$build->install_base_relpaths('shared' => "shared/$distdir");
$build->install_base_relpaths('etc' => "etc/$distdir");
$build->create_build_script();
===EOF===
-$dist->add_file("share/data", <<'===EOF===');
+$dist->add_file("shared/data", <<'===EOF===');
7 * 9 = 42?
===EOF===
-$dist->add_file("share/html/index.html", <<'===EOF===');
+$dist->add_file("shared/html/index.html", <<'===EOF===');
<HTML>
<BODY>
<H1>Hello World!</H1>
my $error;
$error++ unless ok(-e "blib/etc/config", "Built etc/config");
-$error++ unless ok(-e "blib/share/data", "Built share/data");
-$error++ unless ok(-e "blib/share/html/index.html", "Built share/html");
+$error++ unless ok(-e "blib/shared/data", "Built shared/data");
+$error++ unless ok(-e "blib/shared/html/index.html", "Built shared/html");
diag "OUTPUT:\n$output" if $error;
$output = stdout_of sub { $dist->run_build('install') };
$error = 0;
$error++ unless ok(-e "$installdest/etc/simple/config", "installed etc/config");
-$error++ unless ok(-e "$installdest/share/simple/data", "installed share/data");
-$error++ unless ok(-e "$installdest/share/simple/html/index.html", "installed share/html");
+$error++ unless ok(-e "$installdest/shared/simple/data", "installed shared/data");
+$error++ unless ok(-e "$installdest/shared/simple/html/index.html", "installed shared/html");
diag "OUTPUT:\n$output" if $error;
-$dist->remove();
$VERSION = '0.01';
$VERBOSE = 0;
-
use Carp;
+use MBTest ();
use Cwd ();
use File::Basename ();
use File::Find ();
$vms_efs_case = VMS::Feature::current("efs_case_preserve");
} else {
my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
- $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
+ $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
my $efs_case = $ENV{'DECC$EFS_CASE_PRESERVE'} || '';
$vms_efs_case = $efs_case =~ /^[ET1]/i;
}
}
sub chdir_all ($) {
- # OS/2 has "current directory per disk", undeletable;
+ # OS/2 has "current directory per disk", undeletable;
# doing chdir() to another disk won't change cur-dir of initial disk...
chdir('/') if $^O eq 'os2';
chdir shift;
}
+
########################################################################
+END { chdir_all(MBTest->original_cwd); }
+
sub new {
- my $package = shift;
+ my $self = bless {}, shift;
+ $self->reset(@_);
+}
+
+sub reset {
+ my $self = shift;
my %options = @_;
$options{name} ||= 'Simple';
- $options{dir} ||= Cwd::cwd();
+ $options{dir} = File::Spec->rel2abs(
+ defined $options{dir} ? $options{dir} : MBTest->tmpdir
+ );
my %data = (
no_manifest => 0,
xs => 0,
+ inc => 0,
%options,
);
- my $self = bless( \%data, $package );
-
- # So we can clean up later even if the caller chdir()s
- $self->{dir} = File::Spec->rel2abs($self->{dir});
+ %$self = %data;
tie %{$self->{filedata}}, 'Tie::CPHash';
tie %{$self->{pending}{change}}, 'Tie::CPHash';
+ # start with a fresh, empty directory
if ( -d $self->dirname ) {
warn "Warning: Removing existing directory '@{[$self->dirname]}'\n";
- $self->remove;
+ File::Path::rmtree( $self->dirname );
}
+ File::Path::mkpath( $self->dirname );
$self->_gen_default_filedata();
return $self;
}
+sub remove {
+ my $self = shift;
+ $self->chdir_original if($self->did_chdir);
+ File::Path::rmtree( $self->dirname );
+ return $self;
+}
+
+sub revert {
+ my ($self, $file) = @_;
+ if ( defined $file ) {
+ delete $self->{filedata}{$file};
+ delete $self->{pending}{$_}{$file} for qw/change remove/;
+ }
+ else {
+ delete $self->{filedata}{$_} for keys %{ $self->{filedata} };
+ for my $pend ( qw/change remove/ ) {
+ delete $self->{pending}{$pend}{$_} for keys %{ $self->{pending}{$pend} };
+ }
+ }
+ $self->_gen_default_filedata;
+}
+
sub _gen_default_filedata {
my $self = shift;
$self->add_file($member, $data) unless($self->{filedata}{$member});
};
- $self->$add_unless('Build.PL', undent(<<" ---"));
- use strict;
- use Module::Build;
+ if ( ! $self->{inc} ) {
+ $self->$add_unless('Build.PL', undent(<<" ---"));
+ use strict;
+ use Module::Build;
- my \$builder = Module::Build->new(
- module_name => '$self->{name}',
- license => 'perl',
- );
+ my \$builder = Module::Build->new(
+ module_name => '$self->{name}',
+ license => 'perl',
+ );
- \$builder->create_build_script();
- ---
+ \$builder->create_build_script();
+ ---
+ }
+ else {
+ $self->$add_unless('Build.PL', undent(<<" ---"));
+ use strict;
+ use inc::latest 'Module::Build';
+
+ my \$builder = Module::Build->new(
+ module_name => '$self->{name}',
+ license => 'perl',
+ );
+
+ \$builder->create_build_script();
+ ---
+ }
my $module_filename =
join( '/', ('lib', split(/::/, $self->{name})) ) . '.pm';
# 5.6 is missing const char * in its typemap
$self->$add_unless('typemap', undent(<<" ---"));
- const char * T_PV
+ const char *\tT_PV
---
$self->$add_unless('t/basic.t', undent(<<" ---"));
my $manifest = shift;
my $fh = IO::File->new( ">$manifest" ) or do {
- $self->remove();
die "Can't write '$manifest'\n";
};
my $dirname = File::Basename::dirname( $fullname );
unless ( -d $dirname ) {
File::Path::mkpath( $dirname ) or do {
- $self->remove();
die "Can't create '$dirname'\n";
};
}
}
my $fh = IO::File->new(">$fullname") or do {
- $self->remove();
die "Can't write '$fullname'\n";
};
print $fh $self->{filedata}{$file};
}
$self->_gen_manifest( $manifest );
}
+ return $self;
}
sub clean {
}, ($^O eq 'VMS' ? './' : File::Spec->curdir) );
chdir_all( $here );
-}
-
-sub remove {
- my $self = shift;
- croak("invalid usage -- remove()") if(@_);
- $self->chdir_original if($self->did_chdir);
- File::Path::rmtree( $self->dirname );
- # might as well check
- croak("\nthis test should have used chdir_in()") unless(Cwd::getcwd);
-}
-
-sub revert {
- my $self = shift;
- die "Unimplemented.\n";
+ return $self;
}
sub add_file {
}
delete( $self->{filedata}{$file} );
$self->{pending}{remove}{$file} = 1;
+ return $self;
}
sub change_build_pl {
- my ($self, $opts) = @_;
+ my ($self, @opts) = @_;
+
+ my $opts = ref $opts[0] eq 'HASH' ? $opts[0] : { @opts };
local $Data::Dumper::Terse = 1;
(my $args = Dumper($opts)) =~ s/^\s*\{|\}\s*$//g;
use strict;
use Module::Build;
my \$b = Module::Build->new(
- # Some CPANPLUS::Dist::Build versions need to allow mismatches
+ # Some CPANPLUS::Dist::Build versions need to allow mismatches
# On logic: thanks to Module::Install, CPAN.pm must set both keys, but
# CPANPLUS sets only the one
- allow_mb_mismatch => (
+ allow_mb_mismatch => (
\$ENV{PERL5_CPANPLUS_IS_RUNNING} && ! \$ENV{PERL5_CPAN_IS_RUNNING} ? 1 : 0
),
$args
);
\$b->create_build_script();
---
+ return $self;
}
sub change_file {
my $data = shift;
$self->{filedata}{$file} = $data;
$self->{pending}{change}{$file} = 1;
+ return $self;
}
sub get_file {
sub chdir_in {
my $self = shift;
-
- $self->{original_dir} ||= Cwd::cwd; # only once
+ $self->{original_dir} ||= Cwd::cwd; # only once!
my $dir = $self->dirname;
chdir($dir) or die "Can't chdir to '$dir': $!";
+ return $self;
}
########################################################################
-sub did_chdir {
- my $self = shift;
+sub did_chdir { exists shift()->{original_dir} }
- return exists($self->{original_dir});
-}
########################################################################
sub chdir_original {
my $self = shift;
- croak("never called chdir_in()") unless($self->{original_dir});
- my $dir = $self->{original_dir};
+ my $dir = delete $self->{original_dir};
chdir_all($dir) or die "Can't chdir to '$dir': $!";
+ return $self;
}
########################################################################
+sub new_from_context {
+ my ($self, @args) = @_;
+ require Module::Build;
+ return Module::Build->new_from_context( quiet => 1, @args );
+}
+
sub run_build_pl {
my ($self, @args) = @_;
require Module::Build;
- Module::Build->run_perl_script('Build.PL', [], [@args])
+ return Module::Build->run_perl_script('Build.PL', [], [@args])
}
sub run_build {
my ($self, @args) = @_;
require Module::Build;
my $build_script = $^O eq 'VMS' ? 'Build.com' : 'Build';
- Module::Build->run_perl_script($build_script, [], [@args])
+ return Module::Build->run_perl_script($build_script, [], [@args])
}
1;
use DistGen;
# create distribution and prepare to test
- my $dist = DistGen->new(name => 'Foo::Bar', dir => $tmp);
- $dist->regen;
+ my $dist = DistGen->new(name => 'Foo::Bar');
$dist->chdir_in;
# change distribution files
$dist->remove_file('t/some_test.t');
$dist->regen;
- # clean up extraneous files
+ # undo changes and clean up extraneous files
+ $dist->revert;
$dist->clean;
# exercise the command-line interface
$dist->run_build_pl();
$dist->run_build('test');
- # finish testing and clean up
- $dist->chdir_original;
- $dist->remove;
+ # start over as a new distribution
+ $dist->reset( name => 'Foo::Bar', xs => 1 );
+ $dist->chdir_in;
=head1 USAGE
A DistGen object manages a set of files in a distribution directory.
-The constructor and some methods only define the target state of the
-distribution. They do B<not> make any changes to the filesystem:
+The C<new()> constructor initializes the object and creates an empty
+directory for the distribution. It does not create files or chdir into
+the directory. The C<reset()> method re-initializes the object in a
+new directory with new parameters. It also does not create files or change
+the current directory.
+
+Some methods only define the target state of the distribution. They do B<not>
+make any changes to the filesystem:
- new
add_file
change_file
change_build_pl
remove_file
+ revert
Other methods then change the filesystem to match the target state of
-the distribution (or to remove it entirely):
+the distribution:
- regen
clean
+ regen
remove
Other methods are provided for a convenience during testing. The
-most important are ones that manage the current directory:
+most important is the one to enter the distribution directory:
chdir_in
- chdir_original
Additional methods portably encapsulate running Build.PL and Build:
=head1 API
-=head2 Constructor
+=head2 Constructors
=head3 new()
-Create a new object. Does not write its contents (see L</regen()>.)
+Create a new object and an empty directory to hold the distribution's files.
+If no C<dir> option is provided, it defaults to MBTest->tmpdir, which sets
+a different temp directory for Perl core testing and CPAN testing.
+
+The C<new> method does not write any files -- see L</regen()> below.
- my $tmp = MBTest->tmpdir;
my $dist = DistGen->new(
name => 'Foo::Bar',
- dir => $tmp,
+ dir => MBTest->tmpdir,
xs => 1,
no_manifest => 0,
);
=item dir
-The (parent) directory in which to create the distribution directory.
-The default is File::Spec->curdir. The distribution will be created
-under this according to the "dist" form of C<name> (e.g. "Foo-Bar".)
+The (parent) directory in which to create the distribution directory. The
+distribution will be created under this according to the "dist" form of C<name>
+(e.g. "Foo-Bar".) Defaults to a temporary directory.
+
+ $dist = DistGen->new( dir => '/tmp/MB-test' );
+ $dist->regen;
+
+ # distribution files have been created in /tmp/MB-test/Simple
=item xs
typemap
lib/Simple.xs # based on name parameter
+=head3 reset()
+
+The C<reset> method re-initializes the object as if it were generated
+from a fresh call to C<new>. It takes the same optional parameters as C<new>.
+
+ $dist->reset( name => 'Foo::Bar', xs => 0 );
+
=head2 Adding and editing files
Note that C<$filename> should always be specified with unix-style paths,
$dist->remove_file( $filename );
+=head3 revert()
+
+Returns the object to its initial state, or given a $filename it returns that
+file to its initial state if it is one of the built-in files.
+
+ $dist->revert;
+ $dist->revert($filename);
+
=head2 Changing the distribution directory
These methods immediately affect the filesystem.
$dist->regen(clean => 1);
-If the optional C<clean> argument is given, it also removes any
-extraneous files that do not belong to the distribution.
+If the optional C<clean> argument is given, it also calls C<clean>. These
+can also be chained like this, instead:
+
+ $dist->clean->regen;
=head3 clean()
$dist->clean;
-=begin TODO
-
-=head3 revert()
-
-[Unimplemented] Returns the object to its initial state, or given a
-$filename it returns that file to it's initial state if it is one of
-the built-in files.
+=head3 remove()
- $dist->revert;
- $dist->revert($filename);
+Changes back to the original directory and removes the distribution
+directory (but not the temporary directory set during C<new()>).
-=end TODO
+ $dist = DistGen->new->chdir->regen;
+ # ... do some testing ...
-=head3 remove()
+ $dist->remove->chdir_in->regen;
+ # ... do more testing ...
-Removes the entire distribution directory.
+This is like a more aggressive form of C<clean>. Generally, calling C<clean>
+and C<regen> should be sufficient.
=head2 Changing directories
my @delete_env_keys = qw(
DEVEL_COVER_OPTIONS
MODULEBUILDRC
+ PERL_MB_OPT
HARNESS_TIMER
HARNESS_OPTIONS
HARNESS_VERBOSE
# In case the test wants to use our other bundled
# modules, make sure they can be loaded.
- push @INC, File::Spec->catdir('t', 'bundled');
+ my $t_lib = File::Spec->catdir('t', 'bundled');
+ push @INC, $t_lib; # Let user's installed version override
+
+ if ($ENV{PERL_CORE}) {
+ # We change directories, so expand @INC and $^X to absolute paths
+ # Also add .
+ @INC = (map(File::Spec->rel2abs($_), @INC), ".");
+ $^X = File::Spec->rel2abs($^X);
+ }
}
use Exporter;
find_in_path
check_compiler
have_module
- ensure_blib
+ blib_load
);
push @EXPORT, @extra_exports;
__PACKAGE__->export(scalar caller, @extra_exports);
# always return to the current directory
{
- my $cwd = Cwd::cwd;
+ my $cwd = File::Spec->rel2abs(Cwd::cwd);
+
+ sub original_cwd { return $cwd }
END {
# Go back to where you came from!
}
########################################################################
-# Setup a temp directory
-sub tmpdir {
- my ($self, $usr_tmp) = @_;
- return File::Temp::tempdir( 'MB-XXXXXXXX',
- CLEANUP => 1, DIR => $ENV{PERL_CORE} ? Cwd::cwd :
- $usr_tmp ? $usr_tmp : File::Spec->tmpdir
- );
+# Setup a temp directory
+sub tmpdir {
+ my ($self, @args) = @_;
+ my $dir = $ENV{PERL_CORE} ? MBTest->original_cwd : File::Spec->tmpdir;
+ return File::Temp::tempdir('MB-XXXXXXXX', CLEANUP => 1, DIR => $dir, @args);
}
sub save_handle {
$stdout = stdout_of ( sub {
$stderr = stderr_of( $subr )
});
- return ($stdout, $stderr);
+ return wantarray ? ($stdout, $stderr) : $stdout . $stderr;
}
sub slurp {
sub find_in_path {
my $thing = shift;
-
- my @path = split $Config{path_sep}, $ENV{PATH};
+
my @exe_ext = exe_exts();
- foreach (@path) {
- my $fullpath = File::Spec->catfile($_, $thing);
+ if ( File::Spec->file_name_is_absolute( $thing ) ) {
foreach my $ext ( '', @exe_ext ) {
- return "$fullpath$ext" if -e "$fullpath$ext";
+ return "$thing$ext" if -e "$thing$ext";
+ }
+ }
+ else {
+ my @path = split $Config{path_sep}, $ENV{PATH};
+ foreach (@path) {
+ my $fullpath = File::Spec->catfile($_, $thing);
+ foreach my $ext ( '', @exe_ext ) {
+ return "$fullpath$ext" if -e "$fullpath$ext";
+ }
}
}
return;
local $SIG{__WARN__} = sub {};
+ blib_load('Module::Build');
my $mb = Module::Build->current;
$mb->verbose( 0 );
sub have_module {
my $module = shift;
- return eval "use $module; 1";
+ return eval "require $module; 1";
}
-sub ensure_blib {
- # Make sure the given module was loaded from blib/, not the larger system
+sub blib_load {
+ # Load the given module and ensure it came from blib/, not the larger system
my $mod = shift;
+ have_module($mod) or die "Error loading $mod\: $@\n";
+
(my $path = $mod) =~ s{::}{/}g;
-
- local $Test::Builder::Level = $Test::Builder::Level + 1;
- SKIP: {
- skip "no blib in core", 1 if $ENV{PERL_CORE};
- like $INC{"$path.pm"}, qr/\bblib\b/, "Make sure $mod was loaded from blib/"
- or diag "PERL5LIB: " . ($ENV{PERL5LIB} || '') . "\n" .
- "PERL5OPT: " . ($ENV{PERL5OPT} || '') . "\n" .
- "\@INC contains:\n " . join("\n ", @INC) . "\n";
+ $path .= ".pm";
+ my ($pkg, $file, $line) = caller;
+ unless($ENV{PERL_CORE}) {
+ unless($INC{$path} =~ m/\bblib\b/) {
+ (my $load_from = $INC{$path}) =~ s{$path$}{};
+ die "$mod loaded from '$load_from'\nIt should have been loaded from blib. \@INC contains:\n ",
+ join("\n ", @INC) . "\nFatal error occured in blib_load() at $file, line $line.\n";
+ }
}
}
use strict;
use lib 't/lib';
use MBTest;
-use Module::Build;
-use Module::Build::ConfigData;
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
if ( Module::Build::ConfigData->feature('manpage_support') ) {
- plan tests => 22;
+ plan tests => 21;
} else {
plan skip_all => 'manpage_support feature is not enabled';
}
-ensure_blib('Module::Build');
#########################
# revert to a pristine state
-$dist->remove;
-$dist = DistGen->new( dir => $tmp );
-$dist->regen;
-$dist->chdir_in;
-
+$dist->regen( clean => 1 );
my $mb2 = Module::Build->new(
module_name => $dist->name,
unlike $docs, qr/\n=/, $docs;
}
-
-# cleanup
-$dist->remove;
+++ /dev/null
-#!/usr/local/bin/perl -w
-
-use strict;
-use lib 't/lib';
-use MBTest 'no_plan';
-
-use_ok 'Module::Build::YAML';
-ensure_blib('Module::Build::YAML');
-
-my ($dir);
-$dir = ".";
-$dir = "t" if (-d "t");
-
-{
- my ($expected, $got, $var);
- ##########################################################
- # Test a typical-looking Module::Build structure (alphabetized)
- ##########################################################
- $var = {
- 'resources' => {
- 'license' => 'http://opensource.org/licenses/artistic-license.php'
- },
- 'meta-spec' => {
- 'version' => '1.2',
- 'url' => 'http://module-build.sourceforge.net/META-spec-v1.2.html'
- },
- 'generated_by' => 'Module::Build version 0.2709',
- 'version' => '0.13',
- 'name' => 'js-app',
- 'dynamic_config' => '1',
- 'author' => [
- '"Stephen Adkins" <spadkins@gmail.com>'
- ],
- 'license' => 'lgpl',
- 'build_requires' => {
- 'App::Build' => '0',
- 'File::Spec' => '0',
- 'Module::Build' => '0'
- },
- 'provides' => {
- 'JavaScript::App' => {
- 'version' => '0',
- 'file' => 'lib/JavaScript/App.pm'
- }
- },
- 'requires' => {
- 'App::Options' => '0'
- },
- 'abstract' => 'A framework for building dynamic widgets or full applications in Javascript'
- };
- $expected = <<'EOF';
----
-abstract: A framework for building dynamic widgets or full applications in Javascript
-author:
- - '"Stephen Adkins" <spadkins@gmail.com>'
-build_requires:
- App::Build: 0
- File::Spec: 0
- Module::Build: 0
-dynamic_config: 1
-generated_by: Module::Build version 0.2709
-license: lgpl
-meta-spec:
- url: http://module-build.sourceforge.net/META-spec-v1.2.html
- version: 1.2
-name: js-app
-provides:
- JavaScript::App:
- file: lib/JavaScript/App.pm
- version: 0
-requires:
- App::Options: 0
-resources:
- license: http://opensource.org/licenses/artistic-license.php
-version: 0.13
-EOF
- $got = &Module::Build::YAML::Dump($var);
- is($got, $expected, "Dump(): single deep hash");
-
- ##########################################################
- # Test a typical-looking Module::Build structure (ordered)
- ##########################################################
- $expected = <<'EOF';
----
-name: js-app
-version: 0.13
-author:
- - '"Stephen Adkins" <spadkins@gmail.com>'
-abstract: A framework for building dynamic widgets or full applications in Javascript
-license: lgpl
-resources:
- license: http://opensource.org/licenses/artistic-license.php
-requires:
- App::Options: 0
-build_requires:
- App::Build: 0
- File::Spec: 0
- Module::Build: 0
-dynamic_config: 1
-provides:
- JavaScript::App:
- file: lib/JavaScript/App.pm
- version: 0
-generated_by: Module::Build version 0.2709
-meta-spec:
- url: http://module-build.sourceforge.net/META-spec-v1.2.html
- version: 1.2
-EOF
- $var->{_order} = [qw(name version author abstract license resources requires build_requires dynamic_config provides)];
- $got = &Module::Build::YAML::Dump($var);
- is($got, $expected, "Dump(): single deep hash, ordered");
-
- ##########################################################
- # Test that an array turns into multiple documents
- ##########################################################
- $var = [
- "e",
- 2.71828,
- [ "pi", "is", 3.1416 ],
- { fun => "under_sun", 6 => undef, "more", undef },
- ];
- $expected = <<'EOF';
----
-e
----
-2.71828
----
-- pi
-- is
-- 3.1416
----
-6: ~
-fun: under_sun
-more: ~
-EOF
- $got = &Module::Build::YAML::Dump(@$var);
- is($got, $expected, "Dump(): multiple, various");
-
- ##########################################################
- # Test that a single array ref turns into one document
- ##########################################################
- $expected = <<'EOF';
----
-- e
-- 2.71828
--
- - pi
- - is
- - 3.1416
--
- 6: ~
- fun: under_sun
- more: ~
-EOF
- $got = &Module::Build::YAML::Dump($var);
- is($got, $expected, "Dump(): single array of various");
-
- ##########################################################
- # Test Object-Oriented Flavor of the API
- ##########################################################
- my $y = Module::Build::YAML->new();
- $got = $y->Dump($var);
- is($got, $expected, "Dump(): single array of various (OO)");
-
- ##########################################################
- # Test Quoting Conditions (newlines, quotes, tildas, undefs)
- ##########################################################
- $var = {
- 'foo01' => '`~!@#$%^&*()_+-={}|[]\\;\':",./?<>
-<nl>',
- 'foo02' => '~!@#$%^&*()_+-={}|[]\\;:,./<>?',
- 'foo03' => undef,
- 'foo04' => '~',
- };
- $expected = <<'EOF';
----
-foo01: "`~!@#$%^&*()_+-={}|[]\;':\",./?<>\n<nl>"
-foo02: "~!@#$%^&*()_+-={}|[]\;:,./<>?"
-foo03: ~
-foo04: "~"
-EOF
- $got = &Module::Build::YAML::Dump($var);
- is($got, $expected, "Dump(): tricky embedded characters");
-
- $var = {
- 'foo10' => undef,
- 'foo40' => '!',
- 'foo41' => '@',
- 'foo42' => '#',
- 'foo43' => '$',
- 'foo44' => '%',
- 'foo45' => '^',
- 'foo47' => '&',
- 'foo48' => '*',
- 'foo49' => '(',
- 'foo50' => ')',
- 'foo51' => '_',
- 'foo52' => '+',
- 'foo53' => '-',
- 'foo54' => '=',
- 'foo55' => '{',
- 'foo56' => '}',
- 'foo57' => '|',
- 'foo58' => '[',
- 'foo59' => ']',
- 'foo60' => '\\',
- 'foo61' => ';',
- 'foo62' => ':',
- 'foo63' => ',',
- 'foo64' => '.',
- 'foo65' => '/',
- 'foo66' => '<',
- 'foo67' => '>',
- 'foo68' => '?',
- 'foo69' => '\'',
- 'foo70' => '"',
- 'foo71' => '`',
- 'foo72' => '
-',
- };
- $expected = <<'EOF';
----
-foo10: ~
-foo40: "!"
-foo41: '@'
-foo42: "#"
-foo43: $
-foo44: %
-foo45: "^"
-foo47: "&"
-foo48: "*"
-foo49: "("
-foo50: ")"
-foo51: _
-foo52: +
-foo53: -
-foo54: =
-foo55: "{"
-foo56: "}"
-foo57: "|"
-foo58: "["
-foo59: "]"
-foo60: \
-foo61: ;
-foo62: :
-foo63: ,
-foo64: .
-foo65: /
-foo66: '<'
-foo67: '>'
-foo68: "?"
-foo69: "'"
-foo70: '"'
-foo71: "`"
-foo72: "\n"
-EOF
- $got = &Module::Build::YAML::Dump($var);
- is($got, $expected, "Dump(): tricky embedded characters (singles)");
-
-}
-
-
use strict;
use lib 't/lib';
-use MBTest tests => 53;
+use MBTest tests => 51;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
my $tmp = MBTest->tmpdir;
-use Module::Build::ConfigData;
-
my %metadata =
(
module_name => 'Simple',
$dist->chdir_in;
-use Module::Build;
my $mb = Module::Build->new_from_context;
##################################################
my $mb_config_req = {
'Module::Build' => int($Module::Build::VERSION * 100)/100
};
- my $node = $mb->prepare_metadata( {} );
+ my $node = $mb->prepare_metadata( );
# exists() doesn't seem to work here
is $node->{name}, $metadata{module_name};
{
my $mb_prereq = { 'Module::Build' => 0 };
$mb->configure_requires( $mb_prereq );
- my $node = $mb->prepare_metadata( {} );
+ my $node = $mb->prepare_metadata( );
# exists() doesn't seem to work here
$VERSION = '2.34';
---
$dist->regen( clean => 1 );
-$mb = new_build();
+stderr_of( sub { $mb = new_build(); } );
$err = stderr_of( sub { $provides = $mb->find_dist_packages } );
is_deeply($provides,
{'Simple' => { file => $simple_file,
$VERSION = '2.34';
---
$dist->regen( clean => 1 );
-$mb = new_build();
+stderr_of( sub { $mb = new_build(); } );
$err = stderr_of( sub { $provides = $mb->find_dist_packages } );
# XXX Should 'Foo' exist ??? Can't predict values for file & version
ok( exists( $provides->{Foo} ) );
$mb = new_build();
is_deeply($mb->find_dist_packages, {});
-############################################################
-# cleanup
-$dist->remove;
use strict;
use lib 't/lib';
-use MBTest tests => 20;
+use MBTest tests => 18;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
-my $tmp = MBTest->tmpdir;
-
-use Module::Build::ConfigData;
use DistGen;
skip( 'YAML_support feature is not enabled', 4 )
unless Module::Build::ConfigData->feature('YAML_support');
- my $dist = DistGen->new( dir => $tmp, no_manifest => 1 );
- $dist->regen;
-
- $dist->chdir_in;
+ my $dist = DistGen->new( no_manifest => 1 )->chdir_in->regen;
ok ! -e 'MANIFEST';
- my $mb = Module::Build->new_from_context;
+ my $mb;
+ stderr_of( sub { $mb = Module::Build->new_from_context } );
my $out;
$out = eval { stderr_of(sub{$mb->dispatch('distmeta')}) };
ok -e 'META.yml';
- $dist->remove;
}
=cut
---
-my $dist = DistGen->new( dir => $tmp );
+my $dist = DistGen->new->chdir_in;
$dist->change_build_pl
({
license => 'perl',
create_readme => 1,
});
-$dist->regen;
-
-$dist->chdir_in;
-
# .pm File with pod
#
is( $mb->dist_abstract, "A simple module",
"Extracting abstract from .pod over .pm");
-
-############################################################
-# cleanup
-$dist->remove;
use strict;
use lib 't/lib';
-use MBTest tests => 82;
-
-use_ok 'Module::Build::ModuleInfo';
-ensure_blib('Module::Build::ModuleInfo');
-
-my $tmp = MBTest->tmpdir;
-
-use DistGen;
-my $dist = DistGen->new( dir => $tmp );
-$dist->regen;
-
-$dist->chdir_in;
-
-#########################
-
-# class method C<find_module_by_name>
-my $module = Module::Build::ModuleInfo->find_module_by_name(
- 'Module::Build::ModuleInfo' );
-ok( -e $module, 'find_module_by_name() succeeds' );
-
-
-# fail on invalid module name
-my $pm_info = Module::Build::ModuleInfo->new_from_module(
- 'Foo::Bar', inc => [] );
-ok( !defined( $pm_info ), 'fail if can\'t find module by module name' );
-
-
-# fail on invalid filename
-my $file = File::Spec->catfile( 'Foo', 'Bar.pm' );
-$pm_info = Module::Build::ModuleInfo->new_from_file( $file, inc => [] );
-ok( !defined( $pm_info ), 'fail if can\'t find module by file name' );
-
-
-# construct from module filename
-$file = File::Spec->catfile( 'lib', split( /::/, $dist->name ) ) . '.pm';
-$pm_info = Module::Build::ModuleInfo->new_from_file( $file );
-ok( defined( $pm_info ), 'new_from_file() succeeds' );
-
-# construct from module name, using custom include path
-$pm_info = Module::Build::ModuleInfo->new_from_module(
- $dist->name, inc => [ 'lib', @INC ] );
-ok( defined( $pm_info ), 'new_from_module() succeeds' );
-
+use MBTest;
# parse various module $VERSION lines
+# these will be reversed later to create %modules
my @modules = (
- <<'---', # declared & defined on same line with 'our'
+ '1.23' => <<'---', # declared & defined on same line with 'our'
package Simple;
our $VERSION = '1.23';
---
- <<'---', # declared & defined on separate lines with 'our'
+ '1.23' => <<'---', # declared & defined on separate lines with 'our'
package Simple;
our $VERSION;
$VERSION = '1.23';
---
- <<'---', # use vars
+ '1.23' => <<'---', # use vars
package Simple;
use vars qw( $VERSION );
$VERSION = '1.23';
---
- <<'---', # choose the right default package based on package/file name
+ '1.23' => <<'---', # choose the right default package based on package/file name
package Simple::_private;
$VERSION = '0';
package Simple;
$VERSION = '1.23'; # this should be chosen for version
---
- <<'---', # just read the first $VERSION line
+ '1.23' => <<'---', # just read the first $VERSION line
package Simple;
$VERSION = '1.23'; # we should see this line
$VERSION = eval $VERSION; # and ignore this one
---
- <<'---', # just read the first $VERSION line in reopened package (1)
+ '1.23' => <<'---', # just read the first $VERSION line in reopened package (1)
package Simple;
$VERSION = '1.23';
package Error::Simple;
$VERSION = '2.34';
package Simple;
---
- <<'---', # just read the first $VERSION line in reopened package (2)
+ '1.23' => <<'---', # just read the first $VERSION line in reopened package (2)
package Simple;
package Error::Simple;
$VERSION = '2.34';
package Simple;
$VERSION = '1.23';
---
- <<'---', # mentions another module's $VERSION
+ '1.23' => <<'---', # mentions another module's $VERSION
package Simple;
$VERSION = '1.23';
if ( $Other::VERSION ) {
# whatever
}
---
- <<'---', # mentions another module's $VERSION in a different package
+ '1.23' => <<'---', # mentions another module's $VERSION in a different package
package Simple;
$VERSION = '1.23';
package Simple2;
# whatever
}
---
- <<'---', # $VERSION checked only in assignments, not regexp ops
+ '1.23' => <<'---', # $VERSION checked only in assignments, not regexp ops
package Simple;
$VERSION = '1.23';
if ( $VERSION =~ /1\.23/ ) {
# whatever
}
---
- <<'---', # $VERSION checked only in assignments, not relational ops
+ '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
package Simple;
$VERSION = '1.23';
if ( $VERSION == 3.45 ) {
# whatever
}
---
- <<'---', # $VERSION checked only in assignments, not relational ops
+ '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
package Simple;
$VERSION = '1.23';
package Simple2;
# whatever
}
---
- <<'---', # Fully qualified $VERSION declared in package
+ '1.23' => <<'---', # Fully qualified $VERSION declared in package
package Simple;
$Simple::VERSION = 1.23;
---
- <<'---', # Differentiate fully qualified $VERSION in a package
+ '1.23' => <<'---', # Differentiate fully qualified $VERSION in a package
package Simple;
$Simple2::VERSION = '999';
$Simple::VERSION = 1.23;
---
- <<'---', # Differentiate fully qualified $VERSION and unqualified
+ '1.23' => <<'---', # Differentiate fully qualified $VERSION and unqualified
package Simple;
$Simple2::VERSION = '999';
$VERSION = 1.23;
---
- <<'---', # $VERSION declared as package variable from within 'main' package
+ '1.23' => <<'---', # $VERSION declared as package variable from within 'main' package
$Simple::VERSION = '1.23';
{
package Simple;
$x = $y, $cats = $dogs;
}
---
- <<'---', # $VERSION wrapped in parens - space inside
+ '1.23' => <<'---', # $VERSION wrapped in parens - space inside
package Simple;
( $VERSION ) = '1.23';
---
- <<'---', # $VERSION wrapped in parens - no space inside
+ '1.23' => <<'---', # $VERSION wrapped in parens - no space inside
package Simple;
($VERSION) = '1.23';
---
- <<'---', # $VERSION follows a spurious 'package' in a quoted construct
+ '1.23' => <<'---', # $VERSION follows a spurious 'package' in a quoted construct
package Simple;
__PACKAGE__->mk_accessors(qw(
program socket proc
our $VERSION = "1.23";
---
- <<'---', # $VERSION using version.pm
+ '1.23' => <<'---', # $VERSION using version.pm
package Simple;
use version; our $VERSION = version->new('1.23');
---
- <<'---', # $VERSION using version.pm and qv()
+ '1.23' => <<'---', # $VERSION using version.pm and qv()
package Simple;
use version; our $VERSION = qv('1.230');
---
- <<'---', # Two version assignments, should ignore second one
+ '1.23' => <<'---', # Two version assignments, should ignore second one
$Simple::VERSION = '1.230';
$Simple::VERSION = eval $Simple::VERSION;
---
+ '1.23' => <<'---', # declared & defined on same line with 'our'
+package Simple;
+our $VERSION = '1.23_00_00';
+---
+ '1.23' => <<'---', # package NAME VERSION
+ package Simple 1.23;
+---
+ '1.23_01' => <<'---', # package NAME VERSION
+ package Simple 1.23_01;
+---
+ 'v1.2.3' => <<'---', # package NAME VERSION
+ package Simple v1.2.3;
+---
+ 'v1.2_3' => <<'---', # package NAME VERSION
+ package Simple v1.2_3;
+---
);
+my %modules = reverse @modules;
+
+plan tests => 36 + 2 * keys( %modules );
-my( $i, $n ) = ( 1, scalar( @modules ) );
-foreach my $module ( @modules ) {
+blib_load('Module::Build::ModuleInfo');
+
+my $tmp = MBTest->tmpdir;
+
+use DistGen;
+my $dist = DistGen->new( dir => $tmp );
+$dist->regen;
+
+$dist->chdir_in;
+
+#########################
+
+# class method C<find_module_by_name>
+my $module = Module::Build::ModuleInfo->find_module_by_name(
+ 'Module::Build::ModuleInfo' );
+ok( -e $module, 'find_module_by_name() succeeds' );
+
+
+# fail on invalid module name
+my $pm_info = Module::Build::ModuleInfo->new_from_module(
+ 'Foo::Bar', inc => [] );
+ok( !defined( $pm_info ), 'fail if can\'t find module by module name' );
+
+
+# fail on invalid filename
+my $file = File::Spec->catfile( 'Foo', 'Bar.pm' );
+$pm_info = Module::Build::ModuleInfo->new_from_file( $file, inc => [] );
+ok( !defined( $pm_info ), 'fail if can\'t find module by file name' );
+
+
+# construct from module filename
+$file = File::Spec->catfile( 'lib', split( /::/, $dist->name ) ) . '.pm';
+$pm_info = Module::Build::ModuleInfo->new_from_file( $file );
+ok( defined( $pm_info ), 'new_from_file() succeeds' );
+
+# construct from module name, using custom include path
+$pm_info = Module::Build::ModuleInfo->new_from_module(
+ $dist->name, inc => [ 'lib', @INC ] );
+ok( defined( $pm_info ), 'new_from_module() succeeds' );
+
+
+foreach my $module ( sort keys %modules ) {
+ my $expected = $modules{$module};
SKIP: {
skip( "No our() support until perl 5.6", 2 )
- if $] < 5.006 && $module =~ /\bour\b/;
+ if $] < 5.006 && $module =~ /\bour\b/;
+ skip( "No package NAME VERSION support until perl 5.11.1", 2 )
+ if $] < 5.011001 && $module =~ /package\s+[\w\:\']+\s+v?[0-9._]+/;
$dist->change_file( 'lib/Simple.pm', $module );
$dist->regen;
my $pm_info = Module::Build::ModuleInfo->new_from_file( $file );
# Test::Builder will prematurely numify objects, so use this form
- ok( $pm_info->version eq '1.23',
- "correct module version ($i of $n)" );
- is( $warnings, '', 'no warnings from parsing' );
- $i++;
+ my $errs;
+ ok( $pm_info->version eq $expected,
+ "correct module version (expected '$expected')" )
+ or $errs++;
+ is( $warnings, '', 'no warnings from parsing' ) or $errs++;
+ diag "Got: '@{[$pm_info->version]}'\nModule contents:\n$module" if $errs;
}
}
# revert to pristine state
-$dist->remove;
-$dist = DistGen->new( dir => $tmp );
-$dist->regen;
-$dist->chdir_in;
-
+$dist->regen( clean => 1 );
# Find each package only once
$dist->change_file( 'lib/Simple.pm', <<'---' );
is( $pm_info->version, '1.23_01', 'alpha version reported');
# NOTE the following test has be done this way because Test::Builder is
-# too smart for our own good and tries to see if the version object is a
+# too smart for our own good and tries to see if the version object is a
# dual-var, which breaks with alpha versions:
# Argument "1.23_0100" isn't numeric in addition (+) at
-# /usr/lib/perl5/5.8.7/Test/Builder.pm line 505.
+# /usr/lib/perl5/5.8.7/Test/Builder.pm line 505.
ok( $pm_info->version > 1.23, 'alpha version greater than non');
# revert to pristine state
-$dist->remove;
-$dist = DistGen->new( dir => $tmp );
-$dist->regen;
-$dist->chdir_in;
-
+$dist->regen( clean => 1 );
# parse $VERSION lines scripts for package main
my @scripts = (
---
);
-( $i, $n ) = ( 1, scalar( @scripts ) );
+my ( $i, $n ) = ( 1, scalar( @scripts ) );
foreach my $script ( @scripts ) {
$dist->change_file( 'bin/simple.plx', $script );
$dist->regen;
is( $pm_info->name, 'Simple', 'found default package' );
is( $pm_info->version, '0.01', 'version for default package' );
my @packages = $pm_info->packages_inside;
- is_deeply(\@packages, ['Simple']);
+ is_deeply(\@packages, ['Simple'], 'packages inside');
}
{
is( $pm_info->name, 'Simple', 'found default package' );
is( $pm_info->version, '0.60.128', 'version for default package' );
my @packages = $pm_info->packages_inside;
- is_deeply([sort @packages], ['Simple', 'Simple::Simon']);
+ is_deeply([sort @packages], ['Simple', 'Simple::Simon'], 'packages inside');
is( $pm_info->version('Simple::Simon'), '0.61.129', 'version for embedded package' );
}
-
-# cleanup
-$dist->remove;
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use lib 't/lib';
+use MBTest tests => 3;
+
+blib_load('Module::Build');
+
+my $tmp = MBTest->tmpdir;
+
+use DistGen;
+my $dist = DistGen->new( dir => $tmp );
+$dist->regen;
+$dist->chdir_in;
+
+#########################
+
+# Test MYMETA generation
+{
+ ok( ! -e "MYMETA.yml", "MYMETA.yml doesn't exist before Build.PL runs" );
+ my $output;
+ $output = stdout_of sub { $dist->run_build_pl };
+ like($output, qr/Creating new 'MYMETA.yml' with configuration results/,
+ "Saw MYMETA.yml creation message"
+ );
+ ok( -e "MYMETA.yml", "MYMETA.yml exists" );
+}
+
+#########################
+
use strict;
use lib 't/lib';
-use MBTest tests => 4;
+use MBTest tests => 2;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
use IO::File;
my $tmp = MBTest->tmpdir;
ok(! $@, 'dodged the bullet') or die;
ok($mb);
-# cleanup
-$dist->remove;
-
# vim:ts=2:sw=2:et:sta
use strict;
use lib 't/lib';
-use MBTest tests => 13;
+use MBTest tests => 11;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
ok $mb;
is $mb->notes('foo'), 'bar';
-
-# cleanup
-$dist->remove;
use strict;
use lib 't/lib';
-use MBTest tests => 28;
+use MBTest tests => 26;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
#########################
--- /dev/null
+# sample.t -- a sample test file for Module::Build
+
+use strict;
+use lib 't/lib';
+use MBTest;
+use DistGen;
+
+plan tests => 8; # or 'no_plan'
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+
+# create dist object in a temp directory
+# enter the directory and generate the skeleton files
+my $dist = DistGen->new->chdir_in->regen;
+
+$dist->add_file('t/subtest/foo.t', <<'END_T');
+use strict;
+use Test::More tests => 1;
+ok(1, "this is a recursive test");
+END_T
+
+$dist->regen;
+
+# get a Module::Build object and test with it
+my $mb = $dist->new_from_context(); # quiet by default
+isa_ok( $mb, "Module::Build" );
+is( $mb->dist_name, "Simple", "dist_name is 'Simple'" );
+ok( ! $mb->recursive_test_files, "set for no recursive testing" );
+
+# set for recursive testing using PERL_MB_OPT
+{
+ local $ENV{PERL_MB_OPT} = "--verbose --recursive_test_files 1";
+
+ my $out = stdout_stderr_of( sub {
+ $dist->run_build('test');
+ });
+ like( $out, qr/this is a recursive test/,
+ "recursive tests run via PERL_MB_OPT"
+ );
+}
+
+# set Build.PL opts using PERL_MB_OPT
+{
+ local $ENV{PERL_MB_OPT} = "--verbose --recursive_test_files 1";
+ my $mb = $dist->new_from_context(); # quiet by default
+ ok( $mb->recursive_test_files, "PERL_MB_OPT set recusive tests in Build.PL" );
+ ok( $mb->verbose, "PERL_MB_OPT set verbose in Build.PL" );
+}
+
+# verify settings preserved during 'Build test'
+{
+ ok( !$ENV{PERL_MB_OPT}, "PERL_MB_OPT cleared" );
+ my $out = stdout_stderr_of( sub {
+ $dist->run_build('test');
+ });
+ like( $out, qr/this is a recursive test/,
+ "recursive tests run via Build object"
+ );
+}
+
+# vim:ts=2:sw=2:et:sta:sts=2
use strict;
use lib 't/lib';
-use MBTest tests => 8;
+use MBTest tests => 9;
-use_ok 'Module::Build::PodParser';
-ensure_blib('Module::Build::PodParser');
+blib_load('Module::Build::PodParser');
#########################
}
+{
+ # Try again with mixed-case =head1s.
+ untie *FH;
+ tie *FH, 'IO::StringBased', <<'EOF';
+=head1 Name
+
+Foo::Bar - Perl extension for blah blah blah
+
+=head1 Author
+
+C<Foo::Bar> was written by Engelbert Humperdinck I<E<lt>eh@example.comE<gt>> in 2004.
+
+Home page: http://example.com/~eh/
+
+=cut
+EOF
+
+ my $pp = Module::Build::PodParser->new(fh => \*FH);
+ ok $pp, 'object created';
+
+ is $pp->get_author->[0], 'C<Foo::Bar> was written by Engelbert Humperdinck I<E<lt>eh@example.comE<gt>> in 2004.', 'author';
+ is $pp->get_abstract, 'Perl extension for blah blah blah', 'abstract';
+}
use strict;
use lib 't/lib';
use MBTest;
-
-use Module::Build;
-use Module::Build::ConfigData;
use Config;
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
my $manpage_support = Module::Build::ConfigData->feature('manpage_support');
my $HTML_support = Module::Build::ConfigData->feature('HTML_support');
} elsif ( $^O eq 'VMS' ) {
plan skip_all => "Needs porting work on VMS";
} else {
- plan tests => 13;
+ plan tests => 12;
}
}
-ensure_blib('Module::Build');
my $tmp = MBTest->tmpdir;
-
use DistGen;
my $dist = DistGen->new( dir => $tmp, xs => 1 );
$dist->add_file( 'hello', <<'---' );
use File::Spec::Functions qw(catdir);
-use Module::Build;
my @installstyle = qw(lib perl5);
my $mb = Module::Build->new_from_context(
verbose => 0,
# do a strict string comparison, but absent an XML parser it's the
# best we can do.
is $ppd, <<"---";
-<SOFTPKG NAME="$dist_filename" VERSION="0,01,0,0">
- <TITLE>@{[$dist->name]}</TITLE>
+<SOFTPKG NAME="$dist_filename" VERSION="0.01">
<ABSTRACT>Perl extension for blah blah blah</ABSTRACT>
<AUTHOR>A. U. Thor, a.u.thor\@a.galaxy.far.far.away</AUTHOR>
<IMPLEMENTATION>
- <PERLCORE VERSION="$perl_version" />
- <OS NAME="$^O" />
<ARCHITECTURE NAME="$varchname" />
<CODEBASE HREF="/path/to/codebase-xs" />
</IMPLEMENTATION>
}
-$dist->remove;
-
-
########################################
sub exists_ok {
--- /dev/null
+# sample.t -- a sample test file for Module::Build
+
+use strict;
+use lib 't/lib';
+use MBTest;
+use DistGen;
+
+plan tests => 4;
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+
+# enter the directory and generate the skeleton files
+my $dist = DistGen->new( name => "Not::So::Simple" )->chdir_in;
+
+#--------------------------------------------------------------------------#
+# try getting module_name from dist directory name
+#--------------------------------------------------------------------------#
+
+$dist->change_build_pl(
+ dist_name => 'Random-Name',
+ dist_version => 1,
+)->regen;
+
+my $mb = $dist->new_from_context();
+isa_ok( $mb, "Module::Build" );
+is( $mb->module_name, "Not::So::Simple",
+ "module_name guessed from directory name"
+);
+
+#--------------------------------------------------------------------------#
+# Try getting module_name from dist_version_from
+#--------------------------------------------------------------------------#
+
+$dist->add_file( 'lib/Simple/Name.pm', << 'END_PACKAGE' );
+package Simple::Name;
+our $VERSION = 1.23;
+1;
+END_PACKAGE
+
+$dist->change_build_pl(
+ dist_name => 'Random-Name',
+ dist_version_from => 'lib/Simple/Name.pm',
+ dist_abstract => "Don't complain about missing abstract",
+)->regen( clean => 1 );
+
+$mb = $dist->new_from_context();
+isa_ok( $mb, "Module::Build" );
+is( $mb->module_name, "Simple::Name",
+ "module_name guessed from dist_version_from"
+);
+
+# vim:ts=2:sw=2:et:sta:sts=2
--- /dev/null
+# sample.t -- a sample test file for Module::Build
+
+use strict;
+use lib 't/lib';
+use MBTest;
+use DistGen;
+
+plan tests => 19;
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+
+my $dist = DistGen->new->regen->chdir_in;
+
+# get a Module::Build object and test with it
+my $mb;
+stderr_of(sub {
+ ok( $mb = $dist->new_from_context, "Default Build.PL" );
+});
+
+ok( ! $mb->needs_compiler, "needs_compiler is false" );
+ok( ! exists $mb->{properties}{build_requires}{'ExtUtils::CBuilder'},
+ "ExtUtils::CBuilder is not in build_requires"
+);
+
+#--------------------------------------------------------------------------#
+# try with c_source
+#--------------------------------------------------------------------------#
+$dist->change_build_pl({
+ module_name => $dist->name,
+ license => 'perl',
+ c_source => 'src',
+});
+$dist->regen;
+stderr_of(sub {
+ ok( $mb = $dist->new_from_context,
+ "Build.PL with c_source"
+ );
+});
+is( $mb->c_source, 'src', "c_source is set" );
+ok( $mb->needs_compiler, "needs_compiler is true" );
+ok( exists $mb->{properties}{build_requires}{'ExtUtils::CBuilder'},
+ "ExtUtils::CBuilder was added to build_requires"
+);
+
+#--------------------------------------------------------------------------#
+# try with xs files
+#--------------------------------------------------------------------------#
+$dist = DistGen->new(dir => 'MBTest', xs => 1);
+$dist->regen;
+$dist->chdir_in;
+
+stderr_of(sub {
+ ok( $mb = $dist->new_from_context,
+ "Build.PL with xs files"
+ );
+});
+ok( $mb->needs_compiler, "needs_compiler is true" );
+ok( exists $mb->{properties}{build_requires}{'ExtUtils::CBuilder'},
+ "ExtUtils::CBuilder was added to build_requires"
+);
+
+#--------------------------------------------------------------------------#
+# force needs_compiler off, despite xs modules
+#--------------------------------------------------------------------------#
+
+$dist->change_build_pl({
+ module_name => $dist->name,
+ license => 'perl',
+ needs_compiler => 0,
+});
+$dist->regen;
+
+stderr_of(sub {
+ ok( $mb = $dist->new_from_context ,
+ "Build.PL with xs files, but needs_compiler => 0"
+ );
+});
+is( $mb->needs_compiler, 0, "needs_compiler is false" );
+ok( ! exists $mb->{properties}{build_requires}{'ExtUtils::CBuilder'},
+ "ExtUtils::CBuilder is not in build_requires"
+);
+
+#--------------------------------------------------------------------------#
+# don't override specific EU::CBuilder build_requires
+#--------------------------------------------------------------------------#
+
+$dist->change_build_pl({
+ module_name => $dist->name,
+ license => 'perl',
+ build_requires => { 'ExtUtils::CBuilder' => 0.2 },
+});
+$dist->regen;
+
+stderr_of(sub {
+ ok( $mb = $dist->new_from_context ,
+ "Build.PL with xs files, build_requires EU::CB 0.2"
+ );
+});
+ok( $mb->needs_compiler, "needs_compiler is true" );
+is( $mb->build_requires->{'ExtUtils::CBuilder'}, 0.2,
+ "build_requires for ExtUtils::CBuilder is correct version"
+);
+
+#--------------------------------------------------------------------------#
+# falsify compiler and test error handling
+#--------------------------------------------------------------------------#
+
+my $err = stderr_of( sub {
+ $mb = $dist->new_from_context( config => { cc => "adfasdfadjdjk" } )
+});
+ok( $mb, "Build.PL while hiding compiler" );
+like( $err, qr/no compiler detected/,
+ "hidden compiler resulted in warning message during Build.PL"
+);
+eval { $mb->dispatch('build') };
+like( $@, qr/no compiler detected/,
+ "hidden compiler resulted in fatal message during Build"
+);
+
+
+# vim:ts=2:sw=2:et:sta:sts=2
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use lib 't/lib';
+use MBTest;
+use File::Spec::Functions qw/catdir catfile/;
+
+#--------------------------------------------------------------------------#
+# Begin testing
+#--------------------------------------------------------------------------#
+
+plan tests => 21;
+
+blib_load('Module::Build');
+
+#--------------------------------------------------------------------------#
+# Create test distribution
+#--------------------------------------------------------------------------#
+
+my $tmp = MBTest->tmpdir;
+
+use DistGen;
+my $dist = DistGen->new( dir => $tmp, name => 'Simple::Share' );
+$dist->regen;
+$dist->chdir_in;
+
+#--------------------------------------------------------------------------#
+# Test setting 'share_dir'
+#--------------------------------------------------------------------------#
+
+my $mb = $dist->new_from_context;
+
+# Test without a 'share' dir
+ok( $mb, "Created Module::Build object" );
+is( $mb->share_dir, undef,
+ "default share undef if no 'share' dir exists"
+);
+ok( ! exists $mb->{properties}{requires}{'File::ShareDir'},
+ "File::ShareDir not added to 'requires'"
+);
+
+# Add 'share' dir and an 'other' dir and content
+$dist->add_file('share/foo.txt',<< '---');
+This is foo.txt
+---
+$dist->add_file('other/share/bar.txt',<< '---');
+This is bar.txt
+---
+$dist->regen;
+ok( -e catfile(qw/share foo.txt/), "Created 'share' directory" );
+ok( -e catfile(qw/other share bar.txt/), "Created 'other/share' directory" );
+
+# Check default when share_dir is not given
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir, { dist => [ 'share' ] },
+ "Default share_dir set as dist-type share"
+);
+is( $mb->{properties}{requires}{'File::ShareDir'}, '1.00',
+ "File::ShareDir 1.00 added to 'requires'"
+);
+
+# share_dir set to scalar
+$dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'perl',
+ share_dir => 'share',
+ }
+);
+$dist->regen;
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir, { dist => [ 'share' ] },
+ "Scalar share_dir set as dist-type share"
+);
+
+# share_dir set to arrayref
+$dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'perl',
+ share_dir => [ 'share' ],
+ }
+);
+$dist->regen;
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir, { dist => [ 'share' ] },
+ "Arrayref share_dir set as dist-type share"
+);
+
+# share_dir set to hashref w scalar
+$dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'perl',
+ share_dir => { dist => 'share' },
+ }
+);
+$dist->regen;
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir, { dist => [ 'share' ] },
+ "Hashref share_dir w/ scalar dist set as dist-type share"
+);
+
+# share_dir set to hashref w array
+$dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'perl',
+ share_dir => { dist => [ 'share' ] },
+ }
+);
+$dist->regen;
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir, { dist => [ 'share' ] },
+ "Hashref share_dir w/ arrayref dist set as dist-type share"
+);
+
+# Generate a module sharedir (scalar)
+$dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'perl',
+ share_dir => {
+ dist => 'share',
+ module => { $dist->name => 'other/share' },
+ },
+ }
+);
+$dist->regen;
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir,
+ { dist => [ 'share' ],
+ module => { $dist->name => ['other/share'] },
+ },
+ "Hashref share_dir w/ both dist and module shares (scalar-form)"
+);
+
+# Generate a module sharedir (array)
+$dist->change_build_pl(
+ {
+ module_name => $dist->name,
+ license => 'perl',
+ share_dir => {
+ dist => [ 'share' ],
+ module => { $dist->name => ['other/share'] },
+ },
+ }
+);
+$dist->regen;
+stdout_stderr_of( sub { $mb = $dist->new_from_context });
+is_deeply( $mb->share_dir,
+ { dist => [ 'share' ],
+ module => { $dist->name => ['other/share'] },
+ },
+ "Hashref share_dir w/ both dist and module shares (array-form)"
+);
+
+#--------------------------------------------------------------------------#
+# test constructing to/from mapping
+#--------------------------------------------------------------------------#
+
+is_deeply( $mb->_find_share_dir_files,
+ {
+ catfile(qw/share foo.txt/) => catfile(qw/dist Simple-Share foo.txt/),
+ catfile(qw/other share bar.txt/) => catfile(qw/module Simple-Share bar.txt/),
+ },
+ "share_dir filemap for copying to lib complete"
+);
+
+#--------------------------------------------------------------------------#
+# test moving files to blib
+#--------------------------------------------------------------------------#
+
+$mb->dispatch('build');
+
+ok( -d 'blib', "Build ran and blib exists" );
+ok( -d 'blib/lib/auto/share', "blib/lib/auto/share exists" );
+
+my $share_list = Module::Build->rscan_dir('blib/lib/auto/share', sub {-f});
+
+is_deeply(
+ [ sort @$share_list ], [
+ 'blib/lib/auto/share/dist/Simple-Share/foo.txt',
+ 'blib/lib/auto/share/module/Simple-Share/bar.txt',
+ ],
+ "share_dir files copied to blib"
+);
+
+#--------------------------------------------------------------------------#
+# test installing
+#--------------------------------------------------------------------------#
+
+my $temp_install = 'temp_install';
+mkdir $temp_install;
+ok( -d $temp_install, "temp install dir created" );
+
+$mb->install_base($temp_install);
+stdout_of( sub { $mb->dispatch('install') } );
+
+$share_list = Module::Build->rscan_dir(
+ "$temp_install/lib/perl5/auto/share", sub {-f}
+);
+
+is_deeply(
+ [ sort @$share_list ], [
+ "$temp_install/lib/perl5/auto/share/dist/Simple-Share/foo.txt",
+ "$temp_install/lib/perl5/auto/share/module/Simple-Share/bar.txt",
+ ],
+ "share_dir files correctly installed"
+);
+
+#--------------------------------------------------------------------------#
+# test with File::ShareDir
+#--------------------------------------------------------------------------#
+
+SKIP: {
+ eval { require File::ShareDir; File::ShareDir->VERSION(1.00) };
+ skip "needs File::ShareDir 1.00", 2 if $@;
+
+ unshift @INC, File::Spec->catdir($temp_install, qw/lib perl5/);
+ require Simple::Share;
+
+ eval {File::ShareDir::dist_file('Simple-Share','foo.txt') };
+ is( $@, q{}, "Found shared dist file" );
+
+ eval {File::ShareDir::module_file('Simple::Share','bar.txt') };
+ is( $@, q{}, "Found shared module file" );
+}
--- /dev/null
+use strict;
+use lib 't/lib';
+use MBTest;
+plan tests => 3; # or 'no_plan'
+use DistGen;
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+
+# create dist object in a temp directory
+# enter the directory and generate the skeleton files
+my $dist = DistGen->new->chdir_in;
+$dist->add_file('mylib/MBUtil.pm', << "---");
+package MBUtil;
+sub foo { 42 }
+1;
+---
+
+$dist->add_file('Build.PL', << "---");
+use strict;
+use lib 'mylib';
+use MBUtil;
+use Module::Build;
+
+die unless MBUtil::foo() == 42;
+
+my \$builder = Module::Build->new(
+module_name => '$dist->{name}',
+license => 'perl',
+);
+
+\$builder->create_build_script();
+---
+
+$dist->regen;
+
+# get a Module::Build object and test with it
+my $mb = $dist->new_from_context(); # quiet by default
+isa_ok( $mb, "Module::Build" );
+is( $mb->dist_name, "Simple", "dist_name is 'Simple'" );
+ok( ( grep { /mylib/ } @INC ), "resume added \@INC addition to \@INC");
+
+# vim:ts=2:sw=2:et:sta:sts=2
use strict;
use lib 't/lib';
-use MBTest tests => 32;
+use MBTest tests => 30;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
-
-use Module::Build::ConfigData;
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
my $have_yaml = Module::Build::ConfigData->feature('YAML_support');
#########################
requires => { 'File::Spec' => 0 },
});
+$dist->add_file( 'MANIFEST.SKIP', <<'---' );
+^MYMETA.yml$
+---
$dist->add_file( 'script', <<'---' );
#!perl -w
print "Hello, World!\n";
# do a strict string comparison, but absent an XML parser it's the
# best we can do.
is $ppd, <<'EOF';
-<SOFTPKG NAME="Simple" VERSION="0,01,0,0">
- <TITLE>Simple</TITLE>
+<SOFTPKG NAME="Simple" VERSION="0.01">
<ABSTRACT>Perl extension for blah blah blah</ABSTRACT>
<AUTHOR>A. U. Thor, a.u.thor@a.galaxy.far.far.away</AUTHOR>
<IMPLEMENTATION>
- <DEPENDENCY NAME="File-Spec" VERSION="0,0,0,0" />
+ <REQUIRE NAME="File::Spec" VERSION="0" />
<CODEBASE HREF="/path/to/codebase" />
</IMPLEMENTATION>
</SOFTPKG>
ok ! -e $mb->config_dir;
ok ! -e $mb->dist_dir;
-$dist->remove;
-
SKIP: {
skip( 'Windows-only test', 4 ) unless $^O =~ /^MSWin/;
my $out = slurp( $script_file );
is $out, $script_data, ' unmodified by pl2bat';
- $dist->remove;
}
-# cleanup
-$dist->remove;
--- /dev/null
+# sample.t -- a sample test file for Module::Build
+
+use strict;
+use lib 't/lib';
+use MBTest tests => 2; # or 'no_plan'
+use DistGen;
+
+# Ensure any Module::Build modules are loaded from correct directory
+blib_load('Module::Build');
+
+# create dist object in a temp directory
+# enter the directory and generate the skeleton files
+my $dist = DistGen->new->chdir_in->regen;
+
+# get a Module::Build object and test with it
+my $mb = $dist->new_from_context(); # quiet by default
+isa_ok( $mb, "Module::Build" );
+is( $mb->dist_name, "Simple", "dist_name is 'Simple'" );
+
+# vim:ts=2:sw=2:et:sta:sts=2
use DistGen qw(undent);
-use Module::Build;
+blib_load('Module::Build');
+blib_load('Module::Build::ConfigData');
# XXX DistGen shouldn't be assuming module-ness?
my $dist = DistGen->new(dir => MBTest->tmpdir);
['A. U. Thor, a.u.thor@a.galaxy.far.far.away']);
ok $mb->dispatch('distmeta');
-use Module::Build::ConfigData;
SKIP: {
skip( 'YAML_support feature is not enabled', 1 )
unless Module::Build::ConfigData->feature('YAML_support');
- require YAML;
- my $yml = YAML::LoadFile('META.yml');
+ require YAML::Tiny;
+ my $yml = YAML::Tiny::LoadFile('META.yml');
is_deeply($yml->{provides}, \%meta_provides);
}
$dist->chdir_original if $dist->did_chdir;
use strict;
use lib 't/lib';
-use MBTest tests => 5;
+use MBTest tests => 3;
use DistGen;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
my $dist = DistGen->new( dir => $tmp );
like $out, qr/^OK 1 - FIRST MYTEST[.]S/m, 'Should see first test output';
like $out, qr/^OK 2 - SECOND MYTEST[.]S/m, 'Should see second test output';
-# Cleanup.
-$dist->remove;
-
# vim:ts=4:sw=4:et:sta
use strict;
use lib 't/lib';
-use MBTest tests => 9;
+use MBTest tests => 7;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
is($::x, 3, "called a third time");
-$dist->remove;
-
# vim:ts=4:sw=4:et:sta
use strict;
use lib 't/lib';
-use MBTest tests => 15 + 12;
+use MBTest tests => 25;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
-
-my $tmp = MBTest->tmpdir;
+blib_load('Module::Build');
use DistGen;
-my $dist = DistGen->new(dir => $tmp);
+my $dist = DistGen->new()->chdir_in;
$dist->add_file('t/special_ext.st', <<'---');
#!perl
---
$dist->regen;
-$dist->chdir_in;
#########################
my $mb = Module::Build->subclass(
is(scalar(@{[$all_output =~ m/OK/mg]}), 8 );
is(scalar(@{[$all_output =~ m/ALL TESTS SUCCESSFUL\./mg]}), 1);
-$dist->remove;
-
{ # once-again
+$dist->revert;
+
$dist->add_file('t/foo/special.st', <<'---');
#!perl
use Test::More tests => 2;
ok 1;
---
$dist->regen;
-$dist->chdir_in;
my $mb = Module::Build->subclass(
code => q#
is(scalar(@{[$all_output =~ m/(OK 1)/mg]}), 5 );
is(scalar(@{[$all_output =~ m/(OK)/mg]}), 13 );
-$dist->remove;
} # end once-again
# vim:ts=4:sw=4:et:sta
use strict;
use lib 't/lib';
-use MBTest tests => 18;
+use MBTest tests => 16;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
like( run_sample( $p => "~$me/foo")->$p(), qr($expected)i );
}
-
-# cleanup
-$dist->remove;
use Test::More;
use lib 't/lib';
if (eval { require TAP::Harness } && TAP::Harness->VERSION >= 3) {
- plan tests => 8;
+ plan tests => 9;
} else {
plan skip_all => 'TAP::Harness 3+ not installed'
}
use MBTest;
use DistGen;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
my $dist = DistGen->new( dir => $tmp );
$dist->regen;
-
$dist->chdir_in;
+
#########################
# Make sure that TAP::Harness properly does its thing.
-ok my $mb = Module::Build->new(
+$dist->change_build_pl(
module_name => $dist->name,
use_tap_harness => 1,
quiet => 1,
-), 'Construct build object with test_file_exts parameter';
+);
+$dist->regen;
+
+ok my $mb = $dist->new_from_context,
+ 'Construct build object with test_file_exts parameter';
$mb->add_to_cleanup('save_out');
# Use uc() so we don't confuse the current test output
#########################
# Make sure that arguments are passed through to TAP::Harness.
-ok $mb = Module::Build->new(
+$dist->change_build_pl(
module_name => $dist->name,
use_tap_harness => 1,
tap_harness_args => { verbosity => 0 },
quiet => 1,
-), 'Construct build object with test_file_exts parameter';
+);
+$dist->regen;
+
+ok $mb = $dist->new_from_context,
+ 'Construct build object with test_file_exts parameter';
$mb->add_to_cleanup('save_out');
# Use uc() so we don't confuse the current test output
unlike $out, qr/^OK 1/m, 'Should not see first test output';
like $out, qr/^ALL TESTS SUCCESSFUL/m, 'Should see test success message';
-$dist->remove;
+#--------------------------------------------------------------------------#
+# test that a failing test dies
+#--------------------------------------------------------------------------#
+
+$dist->change_build_pl(
+ module_name => $dist->name,
+ use_tap_harness => 1,
+ tap_harness_args => { verbosity => 1 },
+ quiet => 1,
+);
+$dist->change_file('t/basic.t',<<"---");
+use Test::More tests => 1;
+use strict;
+
+use $dist->{name};
+ok 0;
+---
+$dist->regen;
+
+ok $mb = $dist->new_from_context,
+ 'Construct build object after setting tests to fail';
+# Use uc() so we don't confuse the current test output
+$out = stdout_stderr_of( sub { $dist->run_build('test')} );
+ok( $?, "'Build test' had non-zero exit code" );
+like( $out, qr{Errors in testing\. Cannot continue\.},
+ "Saw emulated Test::Harness die() message"
+);
# vim:ts=4:sw=4:et:sta
use strict;
use lib 't/lib';
-use MBTest tests => 4;
+use MBTest tests => 2;
-use_ok 'Module::Build';
-ensure_blib('Module::Build');
+blib_load('Module::Build');
my $tmp = MBTest->tmpdir;
is( Module::Build->version_from_file( $file ), '0.01', 'version_from_file' );
ok( Module::Build->compare_versions( '1.01_01', '>', '1.01' ), 'compare: 1.0_01 > 1.0' );
-
-
-# cleanup
-$dist->remove;
use DistGen;
use Cwd;
-use_ok 'Module::Build';
-ensure_blib 'Module::Build';
+blib_load('Module::Build');
{
my $cwd = Cwd::cwd;
chdir MBTest->tmpdir();
my $build = Module::Build->new(
+ module_name => "Foo::Bar",
dist_name => "Foo-Bar",
dist_version => '1.23',
);
use strict;
use lib 't/lib';
use MBTest;
-use Module::Build;
use Config;
my $tmp;
+blib_load('Module::Build');
+
{
my ($have_c_compiler, $C_support_feature, $tmp_exec) = check_compiler();
} elsif ( !$Config{usedl} ) {
plan skip_all => 'Perl not compiled for dynamic loading'
} else {
- plan tests => 23;
+ plan tests => 20;
}
require Cwd;
$tmp = MBTest->tmpdir( $tmp_exec ? undef : Cwd::cwd );
}
-ensure_blib('Module::Build');
#########################
use DistGen;
-my $dist = DistGen->new( dir => $tmp, xs => 1 );
-$dist->regen;
-
-$dist->chdir_in;
-my $mb = Module::Build->new_from_context;
+my $dist = DistGen->new( dir => $tmp, xs => 1 )->chdir_in->regen;
+my $mb = $dist->new_from_context;
eval {$mb->dispatch('clean')};
is $@, '';
# We can't be verbose in the sub-test, because Test::Harness will
# think that the output is for the top-level test.
-eval {$mb->dispatch('test')};
+stdout_stderr_of( sub { eval {$mb->dispatch('test')} });
is $@, '';
eval {$mb->dispatch('clean')};
# Make sure blib/ is gone after 'realclean'
ok ! -e 'blib';
-
-# cleanup
-$dist->remove;
-
-
########################################
# Try a XS distro with a deep namespace
-$dist = DistGen->new( name => 'Simple::With::Deep::Name',
- dir => $tmp, xs => 1 );
-$dist->regen;
-$dist->chdir_in;
-$mb = Module::Build->new_from_context;
-is $@, '';
+$dist->reset( name => 'Simple::With::Deep::Name', dir => $tmp, xs => 1 );
+$dist->chdir_in->regen;
-$mb->dispatch('build');
-is $@, '';
+$mb = $dist->new_from_context;
-$mb->dispatch('test');
+eval { $mb->dispatch('build') };
is $@, '';
-$mb->dispatch('realclean');
+stdout_stderr_of( sub { eval { $mb->dispatch('test') } } );
is $@, '';
-# cleanup
-$dist->remove;
-
+eval { $mb->dispatch('realclean') };
+is $@, '';
########################################
# Try a XS distro using a flat directory structure
# and a 'dist_name' instead of a 'module_name'
-$dist = DistGen->new( name => 'Dist-Name', dir => $tmp, xs => 1 );
+$dist->reset( name => 'Dist-Name', dir => $tmp, xs => 1 )->chdir_in;
$dist->remove_file('lib/Dist-Name.pm');
$dist->remove_file('lib/Dist-Name.xs');
---
$dist->regen;
-$dist->chdir_in;
-
-$mb = Module::Build->new_from_context;
-is $@, '';
+$mb = $dist->new_from_context;
-$mb->dispatch('build');
+eval { $mb->dispatch('build') };
is $@, '';
-$mb->dispatch('test');
+stdout_of( sub { eval { $mb->dispatch('test') } } );
is $@, '';
-$mb->dispatch('realclean');
+eval { $mb->dispatch('realclean') };
is $@, '';
-# cleanup
-$dist->remove;