summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMario Fetka <[email protected]>2017-10-31 13:38:28 (GMT)
committer Mario Fetka <[email protected]>2017-10-31 13:38:28 (GMT)
commitae1fc8494fd8671c00f626315a4d148075932bca (patch)
tree362d9538ea84707d1108d8254ebb665db137f804
Imported Upstream version 1.12upstream/1.12upstream
-rwxr-xr-xBuild.PL254
-rw-r--r--CHANGES418
-rw-r--r--LICENSE910
-rw-r--r--MANIFEST157
-rw-r--r--META.json221
-rw-r--r--META.yml150
-rw-r--r--README148
-rw-r--r--config/common.cfg34
-rw-r--r--config/glassfish.cfg69
-rw-r--r--config/jboss.cfg111
-rw-r--r--config/jboss7.cfg203
-rw-r--r--config/jetty.cfg191
-rw-r--r--config/memory.cfg200
-rw-r--r--config/metrics.cfg43
-rw-r--r--config/threads.cfg37
-rw-r--r--config/tomcat.cfg245
-rw-r--r--config/weblogic.cfg95
-rw-r--r--config/websphere.cfg30
-rw-r--r--config/websphere/appstate.cfg13
-rw-r--r--config/websphere/http.cfg129
-rw-r--r--config/websphere/jca.cfg43
-rw-r--r--config/websphere/jdbc.cfg88
-rw-r--r--config/websphere/jms.cfg44
-rw-r--r--config/websphere/threads.cfg45
-rw-r--r--config/wildfly.cfg134
-rw-r--r--docker/Dockerfile42
-rw-r--r--docker/README.md43
-rwxr-xr-xexamples/jsr77.pl158
-rw-r--r--examples/memory.pl11
-rw-r--r--examples/memory.sh13
-rw-r--r--examples/remote.pl31
-rwxr-xr-xexamples/threadDump.pl102
-rw-r--r--inc/Module-Build/Module/Build.pm1098
-rw-r--r--inc/Module-Build/Module/Build/API.pod1927
-rw-r--r--inc/Module-Build/Module/Build/Authoring.pod323
-rw-r--r--inc/Module-Build/Module/Build/Base.pm4653
-rw-r--r--inc/Module-Build/Module/Build/Compat.pm578
-rw-r--r--inc/Module-Build/Module/Build/Config.pm59
-rw-r--r--inc/Module-Build/Module/Build/ConfigData.pm201
-rw-r--r--inc/Module-Build/Module/Build/Cookbook.pm529
-rw-r--r--inc/Module-Build/Module/Build/Dumper.pm19
-rw-r--r--inc/Module-Build/Module/Build/ModuleInfo.pm471
-rw-r--r--inc/Module-Build/Module/Build/Notes.pm296
-rw-r--r--inc/Module-Build/Module/Build/PPMMaker.pm196
-rw-r--r--inc/Module-Build/Module/Build/Platform/Amiga.pm34
-rw-r--r--inc/Module-Build/Module/Build/Platform/Default.pm33
-rw-r--r--inc/Module-Build/Module/Build/Platform/EBCDIC.pm34
-rw-r--r--inc/Module-Build/Module/Build/Platform/MPEiX.pm34
-rw-r--r--inc/Module-Build/Module/Build/Platform/MacOS.pm152
-rw-r--r--inc/Module-Build/Module/Build/Platform/RiscOS.pm34
-rw-r--r--inc/Module-Build/Module/Build/Platform/Unix.pm73
-rw-r--r--inc/Module-Build/Module/Build/Platform/VMS.pm482
-rw-r--r--inc/Module-Build/Module/Build/Platform/VOS.pm34
-rw-r--r--inc/Module-Build/Module/Build/Platform/Windows.pm299
-rw-r--r--inc/Module-Build/Module/Build/Platform/aix.pm40
-rw-r--r--inc/Module-Build/Module/Build/Platform/cygwin.pm39
-rw-r--r--inc/Module-Build/Module/Build/Platform/darwin.pm40
-rw-r--r--inc/Module-Build/Module/Build/Platform/os2.pm39
-rw-r--r--inc/Module-Build/Module/Build/PodParser.pm106
-rw-r--r--inc/Module-Build/Module/Build/Version.pm686
-rw-r--r--inc/Module-Build/Module/Build/YAML.pm161
-rw-r--r--it/check_jmx4perl/base.cfg19
-rw-r--r--it/check_jmx4perl/base.pl50
-rw-r--r--it/check_jmx4perl/checks.cfg195
-rw-r--r--it/check_jmx4perl/multi_check.cfg64
-rwxr-xr-xit/it.pl62
-rw-r--r--it/t/01_version.t29
-rw-r--r--it/t/02_http_header.t14
-rw-r--r--it/t/10_base.t25
-rw-r--r--it/t/30_naming.t59
-rw-r--r--it/t/40_alias.t20
-rw-r--r--it/t/50_check_base.t40
-rw-r--r--it/t/51_check_relative.t37
-rw-r--r--it/t/52_check_operation.t45
-rw-r--r--it/t/53_check_non_numeric.t59
-rw-r--r--it/t/54_check_unit.t50
-rw-r--r--it/t/55_check_incremental.t47
-rw-r--r--it/t/56_check_value.t32
-rw-r--r--it/t/57_check_config.t113
-rw-r--r--it/t/58_check_multi_config.t104
-rw-r--r--it/t/59_check_timeout.t22
-rw-r--r--it/t/60_bulk_request.t22
-rw-r--r--it/t/64_check_perfdata.t59
-rw-r--r--it/t/70_overloaded_method.t33
-rw-r--r--it/t/80_read.t95
-rw-r--r--it/t/83_write.t26
-rw-r--r--it/t/84_exec.t24
-rw-r--r--it/t/85_path_escaping.t38
-rw-r--r--it/t/90_search.t18
-rw-r--r--it/t/95_cors.t90
-rw-r--r--it/t/99_discovery.t35
-rw-r--r--lib/JMX/Jmx4Perl.pm1243
-rw-r--r--lib/JMX/Jmx4Perl/Agent.pm486
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/ArtifactHandler.pm322
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/DownloadAgent.pm150
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Logger.pm167
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Meta.pm379
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier.pm162
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/ChecksumVerifier.pm89
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/GnuPGVerifier.pm170
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/MD5Verifier.pm60
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/OpenPGPVerifier.pm114
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/PGPKey.pm35
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/SHA1Verifier.pm58
-rw-r--r--lib/JMX/Jmx4Perl/Agent/Jolokia/WebXmlHandler.pm452
-rw-r--r--lib/JMX/Jmx4Perl/Agent/UserAgent.pm167
-rw-r--r--lib/JMX/Jmx4Perl/Alias.pm328
-rw-r--r--lib/JMX/Jmx4Perl/Alias/Object.pm100
-rw-r--r--lib/JMX/Jmx4Perl/Config.pm223
-rw-r--r--lib/JMX/Jmx4Perl/J4psh.pm327
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/Command.pm294
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/Command/Global.pm128
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/Command/MBean.pm721
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/Command/Server.pm125
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/CommandHandler.pm307
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/CompletionHandler.pm220
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/ServerHandler.pm207
-rw-r--r--lib/JMX/Jmx4Perl/J4psh/Shell.pm284
-rw-r--r--lib/JMX/Jmx4Perl/Manual.pod465
-rw-r--r--lib/JMX/Jmx4Perl/Nagios/CactiJmx4Perl.pm96
-rw-r--r--lib/JMX/Jmx4Perl/Nagios/CheckJmx4Perl.pm979
-rw-r--r--lib/JMX/Jmx4Perl/Nagios/MessageHandler.pm60
-rw-r--r--lib/JMX/Jmx4Perl/Nagios/SingleCheck.pm821
-rw-r--r--lib/JMX/Jmx4Perl/Product/ActiveMQ.pm79
-rw-r--r--lib/JMX/Jmx4Perl/Product/BaseHandler.pm647
-rw-r--r--lib/JMX/Jmx4Perl/Product/Geronimo.pm90
-rw-r--r--lib/JMX/Jmx4Perl/Product/Glassfish.pm99
-rw-r--r--lib/JMX/Jmx4Perl/Product/Hadoop.pm78
-rw-r--r--lib/JMX/Jmx4Perl/Product/JBoss.pm87
-rw-r--r--lib/JMX/Jmx4Perl/Product/Jetty.pm106
-rw-r--r--lib/JMX/Jmx4Perl/Product/Jonas.pm89
-rw-r--r--lib/JMX/Jmx4Perl/Product/Resin.pm92
-rw-r--r--lib/JMX/Jmx4Perl/Product/SpringDM.pm87
-rw-r--r--lib/JMX/Jmx4Perl/Product/Terracotta.pm83
-rw-r--r--lib/JMX/Jmx4Perl/Product/Tomcat.pm96
-rw-r--r--lib/JMX/Jmx4Perl/Product/Unknown.pm89
-rw-r--r--lib/JMX/Jmx4Perl/Product/Weblogic.pm131
-rw-r--r--lib/JMX/Jmx4Perl/Product/Websphere.pm88
-rw-r--r--lib/JMX/Jmx4Perl/Request.pm426
-rw-r--r--lib/JMX/Jmx4Perl/Response.pm180
-rw-r--r--lib/JMX/Jmx4Perl/Util.pm132
-rwxr-xr-xscripts/cacti_jmx4perl110
-rwxr-xr-xscripts/check_jmx4perl1911
-rwxr-xr-xscripts/j4psh144
-rwxr-xr-xscripts/jmx4perl779
-rwxr-xr-xscripts/jolokia647
-rw-r--r--t/10_handler.t35
-rw-r--r--t/20_alias.t22
-rw-r--r--t/30_request.t75
-rw-r--r--t/40_check_jmx4perl.t72
-rw-r--r--t/50_config.t23
-rw-r--r--t/60_parse_name.t29
-rw-r--r--t/70_pod_syntax.t13
-rw-r--r--t/j4p_test.cfg19
-rw-r--r--t/lib/It.pm53
-rw-r--r--t/lib/ProductTest/Test1Handler.pm31
-rw-r--r--t/lib/ProductTest/Test2Handler.pm27
157 files changed, 35016 insertions, 0 deletions
diff --git a/Build.PL b/Build.PL
new file mode 100755
index 0000000..539e0a5
--- /dev/null
+++ b/Build.PL
@@ -0,0 +1,254 @@
+#!/usr/bin/perl
+
+# Check for Module::Build at the right version or use or own bundled one
+# if the available one does not fit.
+my $Minimal_MB = 0.34;
+
+my $Installed_MB =
+ `$^X -e "eval q{require Module::Build; print Module::Build->VERSION} or exit 1"`;
+chomp $Installed_MB;
+
+$Installed_MB = 0 if $?;
+
+# Use our bundled copy of Module::Build if it's newer than the installed.
+unshift @INC, "inc/Module-Build" if $Minimal_MB > $Installed_MB;
+
+require Module::Build;
+use strict;
+use Data::Dumper;
+
+my %REQS = (
+ "JSON" => "2.12",
+ "LWP::UserAgent" => 0,
+ "URI" => "1.35",
+ "Data::Dumper" => 0,
+ "Getopt::Long" => 0,
+ "Carp" => 0,
+ "Module::Find" => 0,
+ "Scalar::Util" => 0,
+ "base" => 0,
+ "Sys::SigAction" => 0,
+ "IO::Socket::Multicast" => 0 # opt
+ );
+
+my %SCRIPTS = ();
+
+# Ask for various installation options:
+
+print <<EOT;
+
+Jmx4Perl comes with a set of supporting scripts, which
+are not necessarily required for using JMX::Jmx4Perl
+programmatically.
+EOT
+
+my $msg = <<EOT;
+
+jmx4perl
+========
+
+jmx4perl is a command line utility for accessing Jolokia agents
+(www.jolokia.org). It can be used for script based exploration
+and easy inspection of the JMX space.
+
+Install 'jmx4perl' ? (y/n)
+EOT
+
+chomp $msg;
+my $answer = y_n($msg,"y");
+if ($answer) {
+ add_reqs(
+ "Crypt::Blowfish_PP" => 0 # opt
+ );
+ add_script("scripts/jmx4perl" => 1);
+}
+
+my $msg = <<EOT;
+
+check_jmx4perl
+==============
+
+check_jmx4perl is a full featured Nagios Plugin (www.nagios.org) for
+monitoring JEE and other Java-servers.
+
+Install 'check_jmx4perl' ? (y/n)
+EOT
+
+chomp $msg;
+my $answer = y_n($msg,"y");
+if ($answer) {
+ add_reqs(
+ "Monitoring::Plugin" => "0.37", # req
+ "Text::ParseWords" => 0, # req
+ "Time::HiRes" => 0, # req
+ "Config::General" => "2.34",# req
+ "Pod::Usage" => 0, # opt
+ "Crypt::Blowfish_PP" => 0 # opt
+ );
+ add_script("scripts/check_jmx4perl" => 1);
+}
+
+$msg = <<EOT;
+
+cacti_jmx4perl
+==============
+
+cacti_jmx4perl is a script which can be used as a Cacti
+(www.cacti.net) plugin.
+
+Install 'cacti_jmx4perl' ? (y/n)
+EOT
+chomp $msg;
+$answer = y_n($msg,"y");
+if ($answer) {
+ add_reqs(
+ "Monitoring::Plugin" => "0.37", # req
+ "Text::ParseWords" => 0, # req
+ "Config::General" => "2.34",# req
+ "Pod::Usage" => 0, # opt
+ "Crypt::Blowfish_PP" => 0 # opt
+ );
+ add_script("scripts/cacti_jmx4perl" => 1);
+}
+
+$msg = <<EOT;
+
+j4psh
+=====
+
+j4psh is an interactive JMX shell with context sensitive command line
+completion. It uses JMX::Jmx4Perl for connecting to the JMX backend
+and has quite some Perl module dependencies.
+
+Install 'j4psh' ? (y/n)
+EOT
+chomp $msg;
+$answer = y_n($msg,"y");
+
+if ($answer) {
+ add_reqs(
+ "Getopt::Long" => 0, # req, GetOptionsFromArray must be exported
+ "Term::ShellUI" => 0, # req
+ "Term::Clui" => 0, # req
+ "Term::Size" => "0.207", # opt
+ "Config::General" => "2.34",# opt
+ "File::SearchPath" => 0, # opt
+ "Crypt::Blowfish_PP" => 0 # opt
+ );
+ add_script("scripts/j4psh" => 1);
+ # check for Term::ReadLine::Gnu
+ my $has_gnu_readline = eval "require Term::ReadLine; require Term::ReadLine::Gnu; 1";
+ my $has_perl_readline = eval "require Term::ReadLine::Perl; 1";
+ if (!$has_gnu_readline) {
+ $msg = <<EOT;
+
+Term::ReadLine::Gnu is the recommended readline module, but it is not
+necessarily required. It needs a development variant of libreadline
+installed along with header files.
+
+Use Term::ReadLine::Gnu ? (y/n)
+EOT
+ chomp $msg;
+ $answer = y_n($msg,"n");
+ if ($answer) {
+ add_reqs("Term::ReadLine::Gnu" => 0);
+ } elsif (!$has_perl_readline) {
+ add_reqs("Term::ReadLine::Perl" => 0,
+ "Term::ReadKey" => 0);
+ }
+ }
+}
+
+$msg = <<EOT;
+
+jolokia
+=======
+
+jolokia is an utility which helps in downloading
+and managing the Jolokia agents (www.jolokia.org), which
+are required on the server side for using jmx4perl.
+
+Install 'jolokia' ? (y/n)
+EOT
+chomp $msg;
+$answer = y_n($msg,"y");
+if ($answer) {
+ add_reqs(
+ "Archive::Zip" => 0, # req
+ "XML::LibXML" => 0, # req
+ "File::Temp" => 0, # req
+ "Digest::MD5" => 0, # opt
+ "Digest::SHA1" => 0, # opt
+ "XML::Twig" => 0, # opt
+ "Term::ProgressBar" => 0 # opt
+ );
+ add_script("scripts/jolokia" => 1);
+ my $has_openpgp = eval "require Crypt::OpenPGP; 1";
+ if (!$has_openpgp) {
+ my $check = `gpg --version`;
+ if ($?) {
+ $check = `gpg2 --version`;
+ if ($?) {
+ $msg = <<EOT;
+
+jolokia uses PGP verification for the files downloaded, but neither
+Crypt::OpenPGP nor GnuPG is installed. It is highly recommended to
+install at least one of them. Installing Crypt::OpenPGP however can
+be a pain due to its large set of dependencies.
+
+Use Crypt::OpenPGP ? (y/n)
+EOT
+ chomp $msg;
+ $answer = y_n($msg,"y");
+ if ($answer) {
+ add_reqs("Crypt::OpenPGP" => 0);
+ }
+ }
+ }
+ }
+}
+
+# Add extra requirements
+sub add_reqs {
+ my %to_add = @_;
+ for my $k (keys %to_add) {
+ $REQS{$k} = $to_add{$k};
+ }
+}
+
+sub add_script {
+ my $script = shift;
+ $SCRIPTS{$script} = 1;
+}
+
+sub y_n {
+ Module::Build->y_n(@_);
+}
+
+# ================================================================================
+
+my $build = Module::Build->new
+ (
+ dist_name => "jmx4perl",
+ dist_version_from => "lib/JMX/Jmx4Perl.pm",
+ dist_author => 'Roland Huss ([email protected])',
+ dist_abstract => 'Easy JMX access to Java EE applications',
+ #sign => 1,
+ installdirs => 'site',
+ license => 'gpl',
+
+ requires => \%REQS,
+ script_files => \%SCRIPTS,
+
+ build_requires => {
+ "Module::Build" => "0.34",
+ "Test::More" => "0",
+ },
+ configure_requires => { 'Module::Build' => 0.34 },
+ keywords => [ "JMX", "JEE", "Management", "Nagios", "Java", "Jolokia", "OSGi", "Mule" ],
+ );
+
+$build->create_build_script;
+
+# ===================================================================================
+
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..94a1cd9
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,418 @@
+1.12 (2015-07-28)
+
+ - Configuration can be also a directory in wich case <dir>/jmx4perl.cfg is tried
+ (e.g. ~/.j4p/jmx4perl.cfg)
+ - Added Docker build
+ - Fix boolean values to be strings "true"/"false" when deserialized.
+ - Changed from "Nagios::Plugin" to "Monitoring::Plugin"
+
+1.11 (2014-11-22)
+
+ - When within a MultiCheck a single check causes an exception, the
+ other checks are now still present in the output of check_jmx4perl
+ and the overall check has the status UNKNWON (#40)
+ - Minor fixes on the WebSphere Checks
+ - Fixed issue when calling check_jmx4perl for an operation without argument (RT##98166)
+
+1.10 (2014-06-30)
+
+ - Added WebSphere checks
+
+1.08 (2014-06-30)
+ - Fixed warning when using MBeanName (#31)
+ - Fixed BaseMBean and formatting
+ - Fixed relative checks when using MBean patterns
+ - Added formatter '%q' to include a ratio of value to base without
+ multiplying by 100 like for '%r'
+ - Disabled OpenPGPVerifier since it doesn't support the new digest
+ algorithms used for signing the Jolokia artefacts (#32)
+ - Don't set ssl_opts if on LWP < 6 (#28)
+ - Fixed BaseMBean and BaseAttribute config directives (#25)
+ - Fixed regexp for squeezing trailing slashes (RT#89108)
+ - Fixed check definition for 'wls_channel_connections (RT#89107)
+ - Changed check 'memory_gc_time' to be a relative check to measure the
+ relative amount taken for the GC. If you use this check (or a sub-check of this)
+ YOU NEED TO UPDATE YOUR THRESHOLDS (and regenerate the pn4p graphics) if you use this
+ check directly
+ - Fixed bug when using 0 thresholds in checks using parent checks (#38)
+ - Added support for '*' wildcard when navigation with cd for j4psh
+ - Fixed bug with check inheritance and check parameters which contain parantheses
+ - Added an option "MultiCheckPrefix" for "Checks" in order to specify the prefix
+ for multi checks
+ - Added config options "SummaryOk" and "SummaryFailure" for allowing to fine tune
+ multi check output (#24)
+ - "Argument" can be used in "Operation" config checks for providing arguments to
+ Nagios checks which are based on operations (#27)
+
+1.07 (2013-04-16)
+ - Added more robust timeout for the Jmx4Perl Agent (requires Sys::SigAction)
+ - SSL Host key verification switched off when connecting via SSL
+ - Fixed issue with quoting in j4psh (#14)
+ - 'cat' in j4psh is now caseinsensitive when using wildcards (#18)
+ - Added BaseMBean, BaseAttribute and BasePath as alternative to Base f
+ or check_jmx4perl (#16)
+ - "jolokia" can do 'repack' and 'info' also when not being connected
+ to the internet (#20)
+ - Multi-Checks can now reference other Multi-Checks either via <MultiCheck>
+ or <Check> (#19)
+ - Added new option '--perfdata true|false' (PerData false in configuration) for
+ switching of performance data. Also, for string checks performance data is
+ switched off always. (#22)
+ - Added %y and %z as placeholder for configured CRITICAL and WARNING thresholds
+ for the output provided with "Label" in check_jmx4perl (#13)
+
+1.06 (2012-10-13)
+ - A a scripting mode to check_jmx4perl which allows putting in arbitraty
+ Perl code for extracting the value to match against
+ - weblogic specific Nagios checks added
+ - Added name as optional parameter for Nagios checks in tomcat.cfg (thanks Wolfgang)
+ - When a multi checks fails, then the name of the check is added instead of its definition
+ key. This allows for better direct usage of predefined checks in own multi checks.
+ - If neither a CRITICAL nor a WARNING threshold is provided, then
+ the check always returns OK. This is especially useful when the
+ motivation is to only collect performance data.
+
+1.05 (2012-04-22)
+ - Added Time::HiRes as dependency to check_jmx4perl
+ - Replaced XML::Tidy with XML::Twig and relaxed version number
+ requirement on Module::Build
+ - RT#72413: Fixed configuration in threads.cfg
+ - Updated documentation for 'jmx4perl' (--method and --legacy-escape
+ explained)
+ - Bundled Module::Build 0.34 in order to improve the installation
+ experience
+ - j4psh: Added 'pwd' command
+ - j4psh: Added options -a (attributes) and -o (operatiosn) to the 'ls'
+ command which now also supports wildcards for filtering
+
+1.04 (2011-11-27)
+ - Fixed serious (and stupid) bug for jmx4perl and j4psh when printing
+ out scalar values.
+
+1.03 (2011-11-23)
+ - Fixed stupid last minute bug.
+
+1.02 (2011-11-23)
+ - Fix for threshold with 0 value in check_jmx4perl
+ - Fix automatic detection of the largest version number for Jolokia
+ agents in with the format 1.0.1 when downloading with 'jolokia'
+ - Fixed printing of boolean values for jmx4perl and j4psh for
+ complex data structures (finally)
+ - Added option '--option key=val' to jmx4perl and j4psh for tuning the
+ output format of these tools (known keys: format,booleans,indent)
+ - Added '--target' to j4psh so that it can operate against a JSR-160
+ proxy
+
+1.01 (2011-10-25)
+ - Fixed 'jolokia' to load the new renamed jvm agent.
+ - Fixed issue when printing boolean values with jmx4perl
+ - Fixed issue with LWP as old as 5.805
+ - Bumped required version of Module::Build to 0.38 in order to cope
+ with messed up version number of XML::Tidy.
+ - j4psh works now with Getopt::Long before 2.38
+
+1.00 (2011-10-3)
+ - Changed escaping as introduced by Jolokia 1.0. If talking with
+ Jolokia < 1.0, use the option '--legacy-escape' must be used if
+ using GET requests with MBeans containing / in the
+ name. JMX::Jmx4Perl knows this option as well
+ ('legacy-escape'). j4psh does the detection automatically,
+ jmx4perl, check_jmx4perl and cacti_jmx4perl know about the new
+ configuration option.
+ - That's 1.0
+
+0.95 (2011-8-21)
+ - Fixed Cacti output when labels contains spaces
+ - Tuned ancient Perl coding style (thanks, datamuc)
+ - Fixed problem with jolokia and PGP verification in non-english
+ environments.
+ - Fixed 'search' command which now really returns undef if nothing
+ is found (and not a ref to an empty array). That will also fix some
+ detectors when the 'info' command is used.
+
+0.92 (2011-5-9)
+ - Fixed bug in pack specification (encryption) which is not available for
+ Perl 5.8 (and which broke Jmx4Perl for Perl 5.8)
+
+0.91 (2011-5-6)
+ - Added --unknown-is-critical option to map all UNKNOWN to CRITICAL values (RT#67899)
+ - Added jmx4perl back to the build process, which was forgotten in 0.90
+ - Fixed bug RT#67815 which was caused by an invalid replacement of placeholder
+ for certain cases (i.e. is during parent check definition resolving
+ ($0,$1) needs to be replaced by ($1,$2) which ended up falsely as ($2,$2)).
+ - Implemented --timeout option for check_jmx4perl, which is a pure HTTP timeout
+ for the communication between the Nagios checks and the Jolokia agent (RT#67821)
+ - Added a possibility to store encrypted passwords in the configuration file.
+ Please note, that this is *not* secure and only prevents casual attacks, since
+ the password needs to be symmetrically decrypted before passing it to the server.
+ In order to create an encrypted password, use 'jmx4perl encrypt <passwd>'.
+ - Fixed RT#67772 which prevented the proper count of failed checks for non-relative
+ checks within multi checks
+
+0.90 (2011-4-11)
+ - Tuned Build.PL so that scripts can be added conditionally.
+ - Fixed normalization issue with negative delta check values.
+ - Support for new JSON serialization style of Jolokia 0.90 added.
+ (i.e numbers and booleans are not returned as plain strings anymore
+ but as Long, Double, true/false. Null is returned as JSON-null.
+ If you have trouble with boolean checks in check_jmx4perl, please
+ update to this jmx4perl version.
+ - Added 'jolokia' for downloading and managing Jolokia agents
+ - Removed jmx4perl Java agent source and agent since jmx4perl now uses
+ Jolokia as agents (www.jolokia.org)
+ - Added 'cacti_jmx4perl', a tool for gathering Cacti data (www.cacti.net)
+ It is heavily based on 'check_jmx4perl' (without threshold handling).
+
+0.75 (2011-2-4)
+ - Fixed typo in POD documentation which prevented a successful
+ build in some situations
+
+0.74 (2011-1-16)
+ - Fixed problem with multichecks including operation-checks with
+ arguments. Specifying them in a configuration has been falsely
+ ignored.
+ - Added '--method' command line option and 'Method' check
+ configuration option to check_jmx4perl for selecting the prefered
+ HTTP request method.
+ - Fixed normalization of time values (RT #63545)
+ - Improved default check_jmx4perl configuration (in hopefully a backward
+ compatible way)
+ - Multi check service summary now contains the name of failed services
+ - Fixed problems with a single '/' argument (RT #62915)
+
+0.73 (2010-11-03)
+ - Fixed RT #61903 which occurs when the same check is referenced
+ multiple times (with potentially different parameters) in a
+ multicheck scenario.
+ - Fixed RT #62342: Perl warning when using operations and not --name
+ in check_jmx4perl
+ - Added --method to jmx4perl, config option 'method' for JMX::Jmx4Perl
+ in order to allow a default HTTP Method to use.
+ - Changed request command constant 'VERSION' to 'AGENT_VERSION' in
+ order to avoid conflicts with the usual versioning conventions for
+ Perl Modules. This is an API change, so in case you are using requests
+ with the constant VERSION you should change this to AGENT_VERSION
+ - Fixed issues when browsing with less in j4psh
+ - Extended config handling in j4psh to allow includes
+
+0.72 (2010-9-24)
+ - Fixed problem with quotes in the config when using "Value"
+ and/or "Base".
+ - Adapted tomcat.cfg to be more flexible (e.g. replaced 'Catalina'
+ domain part by a wildcard).
+ - Fixed bug for merged MBeanServers when using multiple attributes
+ and/or MBean patterns for a READ request
+ - Fixed broken --target, --target-user and --target-password for
+ check_jmx4perl (same for --proxy and co.)
+ - Tuned output of complex data in j4psh
+ - Unwrap an MBeanException to use the target exception for an error
+ message
+ - Agent tested with Mule 3.0
+
+0.71 (2010-8-16)
+ - Added 'ns' as unit (CpuThreadTime returns nano seconds)
+ - Fixed quoting of performance data in so far to let
+ Nagios::Plugin the complete control
+ - Fixed '--color' option and UseColor config directive
+ (section: <Shell>) to j4psh
+ - Added detection of a suitable pager for j4psh
+ - Fixed bug in server configuration when using old style syntax
+
+0.70 (2010-7-10)
+ - Extended configuration syntax for check_jmx4perl as an alternative to
+ command line options
+ + Parameterized checks
+ + Default values for parameters
+ + Multichecks (one HTTP request, many JMX requests)
+ + Check-Inheritance
+ + Predefined checks for certain environments (as sample configuration files)
+ + Added null value check, can be tuned with --null
+ - Added '--value' as a shortcut for --mbean/--attribute/--value
+ - Better documentation for check_jmx4perl (30 extra pages)
+ - <Server> sections are now named blocks, taking the server name as block
+ name (similar to <Check>). The old syntax with an "Name" argument is
+ still support but must not be mixed with the new syntax.
+ - Path elements containing '/' can now be escaped with '\/'
+ - j4p-osgi-bundle including pax-web-bundle so only a single bundle
+ is needed for deploying (when no OSGi HttpService is installed)
+ - Relaxed version requirements on core and compendium OSGi classes
+ for j4p-osgi bundle.
+ - Changed access restrictions (j4p-access.xml):
+ + <allow> and <deny>
+ + Wildcards (*) for attribute and operation names
+ + WARNING: Semantics of MBean specification has changed. Please
+ read the comments in j4p-access.xml.template
+ + Add logging (level info) for printing out which security policy
+ is used
+ - Started to add a java client library
+ - j4psh beta version added
+ - Agent:
+ + Switched from JUnit to TestNG for testing because of
+ support of testing groups
+ + New servlet init parameter option 'mbeanQualifier' to allow
+ multiple j4p-servlet in a single application server
+
+0.65 (2010-3-30)
+ - A JDK 6 java agent added for exporting the j4p protocol via
+ HTTP/JSON.
+ - Extended READ operation to support MBean patternames and multiple
+ attributes with a single request
+ - Renamed 'max_depth', 'max_list_size','max_objects' as processing
+ configuration parameters to 'maxDepth', 'maxCollectionSize' and
+ 'maxObjects' respectively for consistencies sake.
+ - Bug fix: POST request respect these parameters as well now
+ - Added 'ignoreErrors' request option in order to allow a bulk read
+ to succeed even if single read fails. In this case, the valu will
+ - 'search' returns properly escaped MBean Names if unsafe characters
+ are used.
+ - For GET request, instead of pathinfo a query with parameter 'p'
+ can be used as alternative. This works around certain issues with
+ special path handling with certain app-servers (e.g. Tomcat).
+ - JMX::Jmx4Perl::Request and JMX::Jmx4Perl::Agent hardened in order
+ to be more smart with unsafe MBean Names and detect automatically
+ the most convenient HTTP Request method (if not explicitely set)
+ - Added more unit and integration tests.
+ - Added VERSION command to JMX::Jmx4Perl to get to agent and
+ protocol version
+ - Fixed error handling for bulk requests. Now each request object
+ will return an associated response object even in the error case.
+ - Fixed JMX::Jmx4Perl::info for IBM JVMs
+ - Added JMX::Jmx4Perl->parse_name() for splitting up a given MBean
+ object name into its parts
+
+0.60 (2009-02-28)
+ - OSGi bundle (including dependencies) for exposing JSON export via
+ the OSGi HTTP-Service. It's in agent/modules/j4p-osgi.
+ - Refined error handling
+ - Removed legacy JDK 1.4 support. 0.36 is the one and only version
+ for which the JDK 1.4 backport has been tested to some amount.
+ - Added support for overloaded JMX operations for 'list' and 'exec'
+ - 'read' operation can now be used without attribute name in which
+ case the value of all attributes is returned. This can be used
+ directly with JMX::Jmx4Perl and the frontend jmx4perl.
+ - Support for Resin 3.1 added
+ - 'exec' operation can now deal with simple array arguments. Within
+ the perl modules, give an array ref for an array argument. This
+ gets translated to a comma separated list of values in the
+ string. For string array this works only with simle content
+ (i.e. no element containing a ',')
+
+0.51 (2009-12-30)
+ - Quickfix for a badly packaged agent/j4p.war
+
+0.50 (2009-12-24)
+ - Protocol of j4p.war has been extended to enable proxy mode
+ - Added '--target' to check_jmx4perl for using proxy mode
+ - Added '--target' to jmx4perl
+ - Added Mule agent. Use maven to build it in agent/modules/j4p-mule
+ - 'get_war' and 'get_mule_agent' as actions for Build.PL for
+ fetching java artifacts from the labs.consol.de maven repository.
+ - Cleaned up and updated Manual.pod
+
+0.40 (2009-11-14)
+ - Extended protocol to allow for JSON requests via POST in addition
+ to pure URL based requests via GET
+ - Implemented bulk requests: JMX::Jmx4Perl->request() can now take a
+ list of JMX::Jmx4Perl::Request objects in which case it will
+ return a list of JMX::Jmx4Perl::Response objects (instead of a
+ single, scalar, response when used with a single request)
+ - Support for Glassfish V3 Preview, Jonas 5.1 and Jetty 7.0.0
+
+0.36 (2009-10-30)
+ - Added <remote> to j4p-access.xml for restricting
+ access to certain hosts or subnets only.
+ - Added support for a JDK 1.4 agent war. The feature base for
+ this agent is frozen. It might even vanish in the future.
+ You need a JDK 1.4 agent for running within Weblogic 8.1
+ - Cleaned up j4p agent with help of sonar and associated
+ metric checkers like PMD, check_style and FindBugs.
+ - Added support for config files in jmx4perl and JMX::Jmx4Perl
+ which allows for shortcuts for agent URL as well as storing
+ user and credentials information.
+ - Fixed some bugs
+
+0.35 (2009-08-15)
+ - Added example 'threadDump.pl'
+ - Fixed bug when serializing floats and doubles.
+ - check_jmx4perl:
+ * Added support for checking string and boolean values
+ * Escaping performance data
+ * Include units-of-measurement in the plugin output
+ * Custom labeling of plugin output
+ * Perfdata contains always absolute values, even when
+ used with --base
+
+0.30 (2009-07-31)
+ - Fixed permission issue while running 'Build dist'
+ - Fixed URL generation for Websphere
+ - Added support for generic Bean serialization
+ - Added 'search' command to jmx4perl
+ - Fixed bug when using pathes with multiple components
+ - Added additional parameters 'max_depth', 'max_list_size' and
+ 'max_objects' to restrict the size of the JSON answer. Protocol
+ has changed as well a bit.
+ - jmx4perl: URL now as first argument for easier workflow when using
+ bash history for repeated usage.
+ - Added support for restricting MBean access via a policy file
+ (j4p-access.xml)
+
+0.21 (2009-07-03)
+ - Added '--proxy' for check_jmx4perl and jmx4perl
+ - check_jmx4perl:
+ + Refactored to work within the embedded Nagios Perl interpreter
+ (ePN)
+ + use relative values in the range from 0 to 100%
+ (for --critical and --warning) instead of [0..1]
+ + Renamed '--base-value' to '--base' since it can take now
+ absolute values (numbers) or "mbean/alias/path" tuples as an
+ argument in addition to alias names.
+ + Added '--operation' which allows for using return values of
+ operations as check values
+ + Added ~ 50 integration tests
+
+0.20 (2009-06-28)
+ - Support for writing attributes and executing operations
+ - Documentation fixes
+ - Tested for WebLogic 9. New initial support for Websphere 6.1 and
+ 7.0
+ - New "version" command to j4p-agent
+ - New "search" j4p-agent command for querying for MBean names
+ - Added '--base-alias' to check_jmx4perl for using relative
+ thresholds
+ - Added '--delta' to check_jmx4perl for using an incremental
+ mode
+ - Cleaned up check_jmx4perl perfdata output
+ - Added own j4p-agent MBean for configuration management
+ (history tracking and debugging info)
+ - JMX::Jmx4Perl has new request short-cuts 'set_attribute' and
+ 'execute'
+ - Renamed j4p-agent.war to j4p.war
+ - Started integration test suite below "it/" and "agent/modules/j4p-it"
+ for installing some test beans
+ - Cleaned up maven integration for the agent servlet
+ - Moved repository to git://github.com/rhuss/jmx4perl.git
+
+0.16
+ - Switched off debugging in agent servlet
+ - Fixed syntax error when using 'jmx4perl -v attributes'
+ - Fixed Jetty Handler.
+
+0.15
+ - Aliasing
+ - Autodetection
+ - Command line tool "jmx4perl"
+ * reading of attributes
+ * listing of all availabel attributes and operations.
+ * listing of all attribute values
+ * print server info
+ * print all available aliases
+ - Bug Fixes:
+ * Correct URL encoding for request URL
+ * Slash '/' needs to be custom encoded, since URI encoding doesn't
+ work for JBoss 4/5 due to a bug
+ - Tested to work on JBoss 4 & 5, Oracle WebLogic 10, Jonas 4, Geronimo 2,
+ Glassfish 2, Tomcat 4-6 and Jetty 5 & 6
+
+0.1
+ - Initial release
+ - check_jmx4perl
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c3c9516
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,910 @@
+jmx4perl is released under the GNU General Public License, Version 2
+or later (see below).
+
+Module::Build included for the best installation experience is released under the
+same terms as Perl itself, i.e. GPL V1 or later or the Artistic License. The full
+license for Module::Build is appended below.
+
+==============================================================================
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+===========================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+===========================================================================
+License for Module::Build:
+--------------------------
+
+This software is copyright (c) 2009 by Ken Williams <[email protected]> & Development questions, bug reports, and patches should be sent to the
+Module-Build mailing list at <[email protected]>..
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as perl itself.
+
+Terms of Perl itself
+
+a) the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any
+ later version, or
+b) the "Artistic License"
+
+--- The GNU General Public License, Version 1, February 1989 ---
+
+This software is Copyright (c) 2009 by Ken Williams <[email protected]> & Development questions, bug reports, and patches should be sent to the
+Module-Build mailing list at <[email protected]>..
+
+This is free software, licensed under:
+
+ The GNU General Public License, Version 1, February 1989
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The license agreements of most software companies try to keep users
+at the mercy of those companies. By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must tell them their rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License. The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications. Each
+licensee is addressed as "you".
+
+ 1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program. You may charge a fee for the physical act of
+transferring a copy.
+
+ 2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+ a) cause the modified files to carry prominent notices stating that
+ you changed the files and the date of any change; and
+
+ b) cause the whole of any work that you distribute or publish, that
+ in whole or in part contains the Program or any part thereof, either
+ with or without modifications, to be licensed at no charge to all
+ third parties under the terms of this General Public License (except
+ that you may choose to grant warranty protection to some or all
+ third parties, at your option).
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use
+ in the simplest and most usual way, to print or display an
+ announcement including an appropriate copyright notice and a notice
+ that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this General
+ Public License.
+
+ d) You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+
+ 3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+ a) accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ b) accompany it with a written offer, valid for at least three
+ years, to give any third party free (except for a nominal charge
+ for the cost of distribution) a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ c) accompany it with the information you received as to where the
+ corresponding source code may be obtained. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it. For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+ 4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License. However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+ 5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+
+ 7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+ 8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+ To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19xx name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ program `Gnomovision' (a program to direct compilers to make passes
+ at assemblers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+--- The Artistic License 1.0 ---
+
+This software is Copyright (c) 2009 by Ken Williams <[email protected]> & Development questions, bug reports, and patches should be sent to the
+Module-Build mailing list at <[email protected]>..
+
+This is free software, licensed under:
+
+ The Artistic License 1.0
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of
+the package the right to use and distribute the Package in a more-or-less
+customary fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+ - "Package" refers to the collection of files distributed by the Copyright
+ Holder, and derivatives of that collection of files created through
+ textual modification.
+ - "Standard Version" refers to such a Package if it has not been modified,
+ or has been modified in accordance with the wishes of the Copyright
+ Holder.
+ - "Copyright Holder" is whoever is named in the copyright or copyrights for
+ the package.
+ - "You" is you, if you're thinking about copying or distributing this Package.
+ - "Reasonable copying fee" is whatever you can justify on the basis of media
+ cost, duplication charges, time of people involved, and so on. (You will
+ not be required to justify it to the Copyright Holder, but only to the
+ computing community at large as a market that must bear the fee.)
+ - "Freely Available" means that no fee is charged for the item itself, though
+ there may be fees involved in handling the item. It also means that
+ recipients of the item may redistribute it under the same conditions they
+ received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived
+from the Public Domain or from the Copyright Holder. A Package modified in such
+a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided that
+you insert a prominent notice in each changed file stating how and when you
+changed that file, and provided that you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or an
+ equivalent medium, or placing the modifications on a major archive site
+ such as ftp.uu.net, or by allowing the Copyright Holder to include your
+ modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict with
+ standard executables, which must also be provided, and provide a separate
+ manual page for each non-standard executable that clearly documents how it
+ differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where to
+ get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of the Package
+ with your modifications.
+
+ c) accompany any non-standard executables with their corresponding Standard
+ Version executables, giving the non-standard executables non-standard
+ names, and clearly documenting the differences in manual pages (or
+ equivalent), together with instructions on where to get the Standard
+ Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this Package. You
+may not charge a fee for this Package itself. However, you may distribute this
+Package in aggregate with other (possibly commercial) programs as part of a
+larger (possibly commercial) software distribution provided that you do not
+advertise this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output
+from the programs of this Package do not automatically fall under the copyright
+of this Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+The End
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..396b958
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,157 @@
+Build.PL
+CHANGES
+config/common.cfg
+config/jboss.cfg
+config/jboss7.cfg
+config/jetty.cfg
+config/memory.cfg
+config/threads.cfg
+config/tomcat.cfg
+config/weblogic.cfg
+config/glassfish.cfg
+config/metrics.cfg
+config/wildfly.cfg
+config/websphere.cfg
+config/websphere/appstate.cfg
+config/websphere/http.cfg
+config/websphere/jca.cfg
+config/websphere/jdbc.cfg
+config/websphere/jms.cfg
+config/websphere/threads.cfg
+examples/jsr77.pl
+examples/memory.pl
+examples/memory.sh
+examples/remote.pl
+examples/threadDump.pl
+inc/Module-Build/Module/Build.pm
+inc/Module-Build/Module/Build/API.pod
+inc/Module-Build/Module/Build/Authoring.pod
+inc/Module-Build/Module/Build/Base.pm
+inc/Module-Build/Module/Build/Compat.pm
+inc/Module-Build/Module/Build/Config.pm
+inc/Module-Build/Module/Build/ConfigData.pm
+inc/Module-Build/Module/Build/Cookbook.pm
+inc/Module-Build/Module/Build/Dumper.pm
+inc/Module-Build/Module/Build/ModuleInfo.pm
+inc/Module-Build/Module/Build/Notes.pm
+inc/Module-Build/Module/Build/Platform/aix.pm
+inc/Module-Build/Module/Build/Platform/Amiga.pm
+inc/Module-Build/Module/Build/Platform/cygwin.pm
+inc/Module-Build/Module/Build/Platform/darwin.pm
+inc/Module-Build/Module/Build/Platform/Default.pm
+inc/Module-Build/Module/Build/Platform/EBCDIC.pm
+inc/Module-Build/Module/Build/Platform/MacOS.pm
+inc/Module-Build/Module/Build/Platform/MPEiX.pm
+inc/Module-Build/Module/Build/Platform/os2.pm
+inc/Module-Build/Module/Build/Platform/RiscOS.pm
+inc/Module-Build/Module/Build/Platform/Unix.pm
+inc/Module-Build/Module/Build/Platform/VMS.pm
+inc/Module-Build/Module/Build/Platform/VOS.pm
+inc/Module-Build/Module/Build/Platform/Windows.pm
+inc/Module-Build/Module/Build/PodParser.pm
+inc/Module-Build/Module/Build/PPMMaker.pm
+inc/Module-Build/Module/Build/Version.pm
+inc/Module-Build/Module/Build/YAML.pm
+it/check_jmx4perl/base.cfg
+it/check_jmx4perl/base.pl
+it/check_jmx4perl/checks.cfg
+it/check_jmx4perl/multi_check.cfg
+it/it.pl
+it/t/01_version.t
+it/t/02_http_header.t
+it/t/10_base.t
+it/t/30_naming.t
+it/t/40_alias.t
+it/t/50_check_base.t
+it/t/51_check_relative.t
+it/t/52_check_operation.t
+it/t/53_check_non_numeric.t
+it/t/54_check_unit.t
+it/t/55_check_incremental.t
+it/t/56_check_value.t
+it/t/57_check_config.t
+it/t/58_check_multi_config.t
+it/t/59_check_timeout.t
+it/t/60_bulk_request.t
+it/t/70_overloaded_method.t
+it/t/80_read.t
+it/t/85_path_escaping.t
+it/t/90_search.t
+it/t/95_cors.t
+it/t/83_write.t
+it/t/84_exec.t
+it/t/64_check_perfdata.t
+lib/JMX/Jmx4Perl.pm
+lib/JMX/Jmx4Perl/Agent.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/ArtifactHandler.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/DownloadAgent.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Logger.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Meta.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/ChecksumVerifier.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/GnuPGVerifier.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/MD5Verifier.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/OpenPGPVerifier.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/PGPKey.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/SHA1Verifier.pm
+lib/JMX/Jmx4Perl/Agent/Jolokia/WebXmlHandler.pm
+lib/JMX/Jmx4Perl/Agent/UserAgent.pm
+lib/JMX/Jmx4Perl/Alias.pm
+lib/JMX/Jmx4Perl/Alias/Object.pm
+lib/JMX/Jmx4Perl/Config.pm
+lib/JMX/Jmx4Perl/J4psh.pm
+lib/JMX/Jmx4Perl/J4psh/Command.pm
+lib/JMX/Jmx4Perl/J4psh/Command/Global.pm
+lib/JMX/Jmx4Perl/J4psh/Command/MBean.pm
+lib/JMX/Jmx4Perl/J4psh/Command/Server.pm
+lib/JMX/Jmx4Perl/J4psh/CommandHandler.pm
+lib/JMX/Jmx4Perl/J4psh/CompletionHandler.pm
+lib/JMX/Jmx4Perl/J4psh/ServerHandler.pm
+lib/JMX/Jmx4Perl/J4psh/Shell.pm
+lib/JMX/Jmx4Perl/Manual.pod
+lib/JMX/Jmx4Perl/Nagios/CactiJmx4Perl.pm
+lib/JMX/Jmx4Perl/Nagios/CheckJmx4Perl.pm
+lib/JMX/Jmx4Perl/Nagios/SingleCheck.pm
+lib/JMX/Jmx4Perl/Nagios/MessageHandler.pm
+lib/JMX/Jmx4Perl/Product/ActiveMQ.pm
+lib/JMX/Jmx4Perl/Product/BaseHandler.pm
+lib/JMX/Jmx4Perl/Product/Geronimo.pm
+lib/JMX/Jmx4Perl/Product/Glassfish.pm
+lib/JMX/Jmx4Perl/Product/Hadoop.pm
+lib/JMX/Jmx4Perl/Product/JBoss.pm
+lib/JMX/Jmx4Perl/Product/Jetty.pm
+lib/JMX/Jmx4Perl/Product/Jonas.pm
+lib/JMX/Jmx4Perl/Product/Resin.pm
+lib/JMX/Jmx4Perl/Product/SpringDM.pm
+lib/JMX/Jmx4Perl/Product/Terracotta.pm
+lib/JMX/Jmx4Perl/Product/Tomcat.pm
+lib/JMX/Jmx4Perl/Product/Unknown.pm
+lib/JMX/Jmx4Perl/Product/Weblogic.pm
+lib/JMX/Jmx4Perl/Product/Websphere.pm
+lib/JMX/Jmx4Perl/Request.pm
+lib/JMX/Jmx4Perl/Response.pm
+lib/JMX/Jmx4Perl/Util.pm
+LICENSE
+MANIFEST This list of files
+META.json
+META.yml
+README
+scripts/cacti_jmx4perl
+scripts/check_jmx4perl
+scripts/j4psh
+scripts/jmx4perl
+scripts/jolokia
+t/10_handler.t
+t/20_alias.t
+t/30_request.t
+t/40_check_jmx4perl.t
+t/50_config.t
+t/60_parse_name.t
+t/70_pod_syntax.t
+it/t/99_discovery.t
+t/j4p_test.cfg
+t/lib/It.pm
+t/lib/ProductTest/Test1Handler.pm
+t/lib/ProductTest/Test2Handler.pm
+docker/Dockerfile
+docker/README.md
diff --git a/META.json b/META.json
new file mode 100644
index 0000000..1afc208
--- /dev/null
+++ b/META.json
@@ -0,0 +1,221 @@
+{
+ "abstract" : "Easy JMX access to Java EE applications",
+ "author" : [
+ "Roland Huss ([email protected])"
+ ],
+ "dynamic_config" : 1,
+ "generated_by" : "Module::Build version 0.4211",
+ "license" : [
+ "gpl_1"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : "2"
+ },
+ "name" : "jmx4perl",
+ "prereqs" : {
+ "build" : {
+ "requires" : {
+ "Module::Build" : "0.34",
+ "Test::More" : "0"
+ }
+ },
+ "configure" : {
+ "requires" : {
+ "Module::Build" : "0.34"
+ }
+ },
+ "runtime" : {
+ "requires" : {
+ "Archive::Zip" : "0",
+ "Carp" : "0",
+ "Config::General" : "2.34",
+ "Crypt::Blowfish_PP" : "0",
+ "Data::Dumper" : "0",
+ "Digest::MD5" : "0",
+ "Digest::SHA1" : "0",
+ "File::SearchPath" : "0",
+ "File::Temp" : "0",
+ "Getopt::Long" : "0",
+ "IO::Socket::Multicast" : "0",
+ "JSON" : "2.12",
+ "LWP::UserAgent" : "0",
+ "Module::Find" : "0",
+ "Monitoring::Plugin" : "0.37",
+ "Pod::Usage" : "0",
+ "Scalar::Util" : "0",
+ "Sys::SigAction" : "0",
+ "Term::Clui" : "0",
+ "Term::ProgressBar" : "0",
+ "Term::ShellUI" : "0",
+ "Term::Size" : "0.207",
+ "Text::ParseWords" : "0",
+ "Time::HiRes" : "0",
+ "URI" : "1.35",
+ "XML::LibXML" : "0",
+ "XML::Twig" : "0",
+ "base" : "0"
+ }
+ }
+ },
+ "provides" : {
+ "JMX::Jmx4Perl" : {
+ "file" : "lib/JMX/Jmx4Perl.pm",
+ "version" : "1.12"
+ },
+ "JMX::Jmx4Perl::Agent" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::ArtifactHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/ArtifactHandler.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::DownloadAgent" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/DownloadAgent.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Logger" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Logger.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Logger::None" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Logger.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Meta" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Meta.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier::ChecksumVerifier" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/ChecksumVerifier.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier::GnuPGVerifier" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/GnuPGVerifier.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier::MD5Verifier" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/MD5Verifier.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier::OpenPGPVerifier" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/OpenPGPVerifier.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier::PGPKey" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/PGPKey.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::Verifier::SHA1Verifier" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/SHA1Verifier.pm"
+ },
+ "JMX::Jmx4Perl::Agent::Jolokia::WebXmlHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/Jolokia/WebXmlHandler.pm"
+ },
+ "JMX::Jmx4Perl::Agent::UserAgent" : {
+ "file" : "lib/JMX/Jmx4Perl/Agent/UserAgent.pm"
+ },
+ "JMX::Jmx4Perl::Alias" : {
+ "file" : "lib/JMX/Jmx4Perl/Alias.pm"
+ },
+ "JMX::Jmx4Perl::Alias::Object" : {
+ "file" : "lib/JMX/Jmx4Perl/Alias/Object.pm"
+ },
+ "JMX::Jmx4Perl::Config" : {
+ "file" : "lib/JMX/Jmx4Perl/Config.pm"
+ },
+ "JMX::Jmx4Perl::J4psh" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::Command" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/Command.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::Command::Global" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/Command/Global.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::Command::MBean" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/Command/MBean.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::Command::Server" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/Command/Server.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::CommandHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/CommandHandler.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::CompletionHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/CompletionHandler.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::ServerHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/ServerHandler.pm"
+ },
+ "JMX::Jmx4Perl::J4psh::Shell" : {
+ "file" : "lib/JMX/Jmx4Perl/J4psh/Shell.pm"
+ },
+ "JMX::Jmx4Perl::Nagios::CactiJmx4Perl" : {
+ "file" : "lib/JMX/Jmx4Perl/Nagios/CactiJmx4Perl.pm"
+ },
+ "JMX::Jmx4Perl::Nagios::CheckJmx4Perl" : {
+ "file" : "lib/JMX/Jmx4Perl/Nagios/CheckJmx4Perl.pm"
+ },
+ "JMX::Jmx4Perl::Nagios::MessageHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/Nagios/MessageHandler.pm"
+ },
+ "JMX::Jmx4Perl::Nagios::SingleCheck" : {
+ "file" : "lib/JMX/Jmx4Perl/Nagios/SingleCheck.pm"
+ },
+ "JMX::Jmx4Perl::Product::ActiveMQ" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/ActiveMQ.pm"
+ },
+ "JMX::Jmx4Perl::Product::BaseHandler" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/BaseHandler.pm"
+ },
+ "JMX::Jmx4Perl::Product::Geronimo" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Geronimo.pm"
+ },
+ "JMX::Jmx4Perl::Product::Glassfish" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Glassfish.pm"
+ },
+ "JMX::Jmx4Perl::Product::Hadoop" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Hadoop.pm"
+ },
+ "JMX::Jmx4Perl::Product::JBoss" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/JBoss.pm"
+ },
+ "JMX::Jmx4Perl::Product::Jetty" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Jetty.pm"
+ },
+ "JMX::Jmx4Perl::Product::Jonas" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Jonas.pm"
+ },
+ "JMX::Jmx4Perl::Product::Resin" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Resin.pm"
+ },
+ "JMX::Jmx4Perl::Product::SpringDM" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/SpringDM.pm"
+ },
+ "JMX::Jmx4Perl::Product::Terracotta" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Terracotta.pm"
+ },
+ "JMX::Jmx4Perl::Product::Tomcat" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Tomcat.pm"
+ },
+ "JMX::Jmx4Perl::Product::Unknown" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Unknown.pm"
+ },
+ "JMX::Jmx4Perl::Product::Weblogic" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Weblogic.pm"
+ },
+ "JMX::Jmx4Perl::Product::Websphere" : {
+ "file" : "lib/JMX/Jmx4Perl/Product/Websphere.pm"
+ },
+ "JMX::Jmx4Perl::Request" : {
+ "file" : "lib/JMX/Jmx4Perl/Request.pm"
+ },
+ "JMX::Jmx4Perl::Response" : {
+ "file" : "lib/JMX/Jmx4Perl/Response.pm"
+ },
+ "JMX::Jmx4Perl::Util" : {
+ "file" : "lib/JMX/Jmx4Perl/Util.pm"
+ }
+ },
+ "release_status" : "stable",
+ "resources" : {
+ "license" : [
+ "http://opensource.org/licenses/gpl-license.php"
+ ]
+ },
+ "version" : "1.12"
+}
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..ecc258c
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,150 @@
+---
+abstract: 'Easy JMX access to Java EE applications'
+author:
+ - 'Roland Huss ([email protected])'
+build_requires:
+ Module::Build: '0.34'
+ Test::More: '0'
+configure_requires:
+ Module::Build: '0.34'
+dynamic_config: 1
+generated_by: 'Module::Build version 0.4211, CPAN::Meta::Converter version 2.150001'
+license: gpl
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: '1.4'
+name: jmx4perl
+provides:
+ JMX::Jmx4Perl:
+ file: lib/JMX/Jmx4Perl.pm
+ version: '1.12'
+ JMX::Jmx4Perl::Agent:
+ file: lib/JMX/Jmx4Perl/Agent.pm
+ JMX::Jmx4Perl::Agent::Jolokia::ArtifactHandler:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/ArtifactHandler.pm
+ JMX::Jmx4Perl::Agent::Jolokia::DownloadAgent:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/DownloadAgent.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Logger:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Logger.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Logger::None:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Logger.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Meta:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Meta.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier::ChecksumVerifier:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/ChecksumVerifier.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier::GnuPGVerifier:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/GnuPGVerifier.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier::MD5Verifier:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/MD5Verifier.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier::OpenPGPVerifier:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/OpenPGPVerifier.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier::PGPKey:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/PGPKey.pm
+ JMX::Jmx4Perl::Agent::Jolokia::Verifier::SHA1Verifier:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/Verifier/SHA1Verifier.pm
+ JMX::Jmx4Perl::Agent::Jolokia::WebXmlHandler:
+ file: lib/JMX/Jmx4Perl/Agent/Jolokia/WebXmlHandler.pm
+ JMX::Jmx4Perl::Agent::UserAgent:
+ file: lib/JMX/Jmx4Perl/Agent/UserAgent.pm
+ JMX::Jmx4Perl::Alias:
+ file: lib/JMX/Jmx4Perl/Alias.pm
+ JMX::Jmx4Perl::Alias::Object:
+ file: lib/JMX/Jmx4Perl/Alias/Object.pm
+ JMX::Jmx4Perl::Config:
+ file: lib/JMX/Jmx4Perl/Config.pm
+ JMX::Jmx4Perl::J4psh:
+ file: lib/JMX/Jmx4Perl/J4psh.pm
+ JMX::Jmx4Perl::J4psh::Command:
+ file: lib/JMX/Jmx4Perl/J4psh/Command.pm
+ JMX::Jmx4Perl::J4psh::Command::Global:
+ file: lib/JMX/Jmx4Perl/J4psh/Command/Global.pm
+ JMX::Jmx4Perl::J4psh::Command::MBean:
+ file: lib/JMX/Jmx4Perl/J4psh/Command/MBean.pm
+ JMX::Jmx4Perl::J4psh::Command::Server:
+ file: lib/JMX/Jmx4Perl/J4psh/Command/Server.pm
+ JMX::Jmx4Perl::J4psh::CommandHandler:
+ file: lib/JMX/Jmx4Perl/J4psh/CommandHandler.pm
+ JMX::Jmx4Perl::J4psh::CompletionHandler:
+ file: lib/JMX/Jmx4Perl/J4psh/CompletionHandler.pm
+ JMX::Jmx4Perl::J4psh::ServerHandler:
+ file: lib/JMX/Jmx4Perl/J4psh/ServerHandler.pm
+ JMX::Jmx4Perl::J4psh::Shell:
+ file: lib/JMX/Jmx4Perl/J4psh/Shell.pm
+ JMX::Jmx4Perl::Nagios::CactiJmx4Perl:
+ file: lib/JMX/Jmx4Perl/Nagios/CactiJmx4Perl.pm
+ JMX::Jmx4Perl::Nagios::CheckJmx4Perl:
+ file: lib/JMX/Jmx4Perl/Nagios/CheckJmx4Perl.pm
+ JMX::Jmx4Perl::Nagios::MessageHandler:
+ file: lib/JMX/Jmx4Perl/Nagios/MessageHandler.pm
+ JMX::Jmx4Perl::Nagios::SingleCheck:
+ file: lib/JMX/Jmx4Perl/Nagios/SingleCheck.pm
+ JMX::Jmx4Perl::Product::ActiveMQ:
+ file: lib/JMX/Jmx4Perl/Product/ActiveMQ.pm
+ JMX::Jmx4Perl::Product::BaseHandler:
+ file: lib/JMX/Jmx4Perl/Product/BaseHandler.pm
+ JMX::Jmx4Perl::Product::Geronimo:
+ file: lib/JMX/Jmx4Perl/Product/Geronimo.pm
+ JMX::Jmx4Perl::Product::Glassfish:
+ file: lib/JMX/Jmx4Perl/Product/Glassfish.pm
+ JMX::Jmx4Perl::Product::Hadoop:
+ file: lib/JMX/Jmx4Perl/Product/Hadoop.pm
+ JMX::Jmx4Perl::Product::JBoss:
+ file: lib/JMX/Jmx4Perl/Product/JBoss.pm
+ JMX::Jmx4Perl::Product::Jetty:
+ file: lib/JMX/Jmx4Perl/Product/Jetty.pm
+ JMX::Jmx4Perl::Product::Jonas:
+ file: lib/JMX/Jmx4Perl/Product/Jonas.pm
+ JMX::Jmx4Perl::Product::Resin:
+ file: lib/JMX/Jmx4Perl/Product/Resin.pm
+ JMX::Jmx4Perl::Product::SpringDM:
+ file: lib/JMX/Jmx4Perl/Product/SpringDM.pm
+ JMX::Jmx4Perl::Product::Terracotta:
+ file: lib/JMX/Jmx4Perl/Product/Terracotta.pm
+ JMX::Jmx4Perl::Product::Tomcat:
+ file: lib/JMX/Jmx4Perl/Product/Tomcat.pm
+ JMX::Jmx4Perl::Product::Unknown:
+ file: lib/JMX/Jmx4Perl/Product/Unknown.pm
+ JMX::Jmx4Perl::Product::Weblogic:
+ file: lib/JMX/Jmx4Perl/Product/Weblogic.pm
+ JMX::Jmx4Perl::Product::Websphere:
+ file: lib/JMX/Jmx4Perl/Product/Websphere.pm
+ JMX::Jmx4Perl::Request:
+ file: lib/JMX/Jmx4Perl/Request.pm
+ JMX::Jmx4Perl::Response:
+ file: lib/JMX/Jmx4Perl/Response.pm
+ JMX::Jmx4Perl::Util:
+ file: lib/JMX/Jmx4Perl/Util.pm
+requires:
+ Archive::Zip: '0'
+ Carp: '0'
+ Config::General: '2.34'
+ Crypt::Blowfish_PP: '0'
+ Data::Dumper: '0'
+ Digest::MD5: '0'
+ Digest::SHA1: '0'
+ File::SearchPath: '0'
+ File::Temp: '0'
+ Getopt::Long: '0'
+ IO::Socket::Multicast: '0'
+ JSON: '2.12'
+ LWP::UserAgent: '0'
+ Module::Find: '0'
+ Monitoring::Plugin: '0.37'
+ Pod::Usage: '0'
+ Scalar::Util: '0'
+ Sys::SigAction: '0'
+ Term::Clui: '0'
+ Term::ProgressBar: '0'
+ Term::ShellUI: '0'
+ Term::Size: '0.207'
+ Text::ParseWords: '0'
+ Time::HiRes: '0'
+ URI: '1.35'
+ XML::LibXML: '0'
+ XML::Twig: '0'
+ base: '0'
+resources:
+ license: http://opensource.org/licenses/gpl-license.php
+version: '1.12'
diff --git a/README b/README
new file mode 100644
index 0000000..5a573e6
--- /dev/null
+++ b/README
@@ -0,0 +1,148 @@
+ Jmx4Perl
+ ========
+
+INTRODUCTION
+
+ Jmx4Perl provides an alternate way for accessing Java JEE Server
+ management interfaces which are based on JMX (Java Management
+ Extensions). It is an agent based approach, where a small Java
+ Webapplication deployed on the application server provides an
+ HTTP/JSON based access to JMX MBeans registered within the
+ application server.
+
+HOW IT WORKS
+
+ For the agent mode a small Java Agent WAR (web archive) needs to be
+ deployed on the Java application server. This agent is provided by
+ the Jolokia project (www.jolokia.org). There is no need to add any
+ startup parameters to the application server and to open any
+ additional ports. All communication takes places via HTTP where JSON
+ objects are exchanged. Additionally, the agent benefits from the
+ security infrastructure in place which every application server
+ provides for web application. More information about the agent can
+ be found at http://www.jolokia.org
+
+ The Perl module JMX::Jmx4Perl accesses the deployed agent servlet
+ and transform the request's results from JSON into a simple Perl
+ object.
+
+TOOLS
+
+ This distribution comes with several tools, which uses the
+ JMX::Jmx4Perl for accessing the server:
+
+ jmx4perl - Command line tool for gathering JMX information
+ check_jmx4perl - Full featured Nagios Plugin
+ j4psh - Interactive, readline based JMX shell with context
+ sensitive command completion
+ jolokia - Utility for downloading and managing Jolokia
+ agents.
+
+INSTALLATION
+
+ The Perl part installs as any other module via Module::Build, which
+ you need to have installed. Using
+
+ perl Build.PL
+ ./Build installdeps # If there are dependencies missing and you
+ # have Module::Build >= 0.36 installed.
+ ./Build
+ ./Build test
+ ./Build install
+
+ will install the modules. It is highly recommended to install the
+ recommended dependent modules, too to get the full jmx4perl
+ power. The set of 'required' modules is kept small and guarantees
+ only that 'jmx4perl' and the modules around JMX::Jmx4Perl are
+ working properly. The other tools (check_jmx4perl, j4psh and
+ jolokia) require the recommended modules for proper working. Look
+ into Build.PL for which tool requires which module.
+
+ In order to download the Jolokia WAR agent into the local directory
+ as jolokia.war, use the following command
+
+ jolokia
+
+ This agent "jolokia.war" needs to be deployed on the JEE Server to
+ monitor. Please consult http://www.jolokia.org/agent.html for more
+ information how to install the agent. E.g. for Tomcat this war file
+ needs to be copied into the webapps directory.
+
+ To test it, you can use 'jmx4perl' with the URL of the deployed
+ agent:
+
+ jmx4perl http://<jeeserver>:<port>/jolokia
+
+ Consult 'man jmx4perl' for more information about this command
+ utility.
+
+RESOURCES
+
+ * Jmx4perl's source is hosted on github.com. You can clone the
+ repository with git://github.com/rhuss/jmx4perl.git as URL
+
+ * Interesting articles around Jmx4Perl, JMX and Nagios can be found
+ at http://labs.consol.de Checkout the various post categories for
+ selecting a specific topic.
+
+ * www.jmx4perl.org is the canonical entry point for jmx4perl related
+ information.
+
+NOTE
+
+ For you convenience, the latest Module::Build is included in this
+ distribution, so there is no need of a locally install Module::Build
+ for installing this suite. More information about Module::Build can
+ be found http://search.cpan.org/~dagolden/Module-Build/
+
+LICENSE
+
+ Copyright (C) 2009-2011 Roland Huss ([email protected])
+
+ Jmx4perl is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ jmx4perl is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with jmx4perl. If not, see <http://www.gnu.org/licenses/>.
+
+ A commercial license is available as well. You can either apply the
+ GPL or obtain a commercial license for closed source
+ development. Please contact [email protected] for further information.
+
+PROFESSIONAL SERVICES
+
+ Just in case you need professional support for this module (or
+ Nagios, JMX or JEE in general), you might want to have a look at
+ http://www.consol.com/nagios-monitoring . Contact
+ [email protected] for further information (or use the contact
+ form at http://www.consol.com/contact/ )
+
+ACKNOWLEDGMENTS
+
+ Big thanks go to ...
+
+ * Gerhard Lausser, who initially pushed me to think harder
+ about a better way for monitoring JEE Servers with Nagios.
+
+ * Danijel Tasov for patching, patching, patching and keeping
+ an eye on contemporary perl styling.
+
+ * All bug reporters and blog commenters for helping me to
+ increase the overall quality (and for letting me know that
+ this is not software for the ivory tower)
+
+BUGS
+
+ Please report any bugs and/or feature requests at
+ http://rt.cpan.org/Public/Bug/Report.html?Queue=jmx4perl
+
+AUTHOR
+
diff --git a/config/common.cfg b/config/common.cfg
new file mode 100644
index 0000000..78d5bc8
--- /dev/null
+++ b/config/common.cfg
@@ -0,0 +1,34 @@
+
+# Common check definitions which can be used
+# as a base for more specific configurations
+
+# This are mostly convenience, abstract checks
+# which are meant to be mixed into more concrete
+# checks
+
+# =================================================
+
+# A nice label to be used for relative values
+<Check relative_base>
+ Label = %.2r% used (%.2v %u / %.2b %w)
+ # Default values for critical (90%) and warning (80%) thresholds
+ Critical = ${0:90}
+ Warning = ${1:80}
+</Check>
+
+# A incremental check for values per minute
+# $0: used in label to specify what is counted
+# per minute
+<Check count_per_minute>
+ Label = %2.2f $0/minute
+ Delta = 60
+</Check>
+
+# A incremental check for values per hour
+# $0: used in label to specify what is counted
+# per hour
+<Check count_per_hour>
+ Label = %2.2f $0/hour
+ Delta = 3600
+</Check>
+
diff --git a/config/glassfish.cfg b/config/glassfish.cfg
new file mode 100644
index 0000000..4e81305
--- /dev/null
+++ b/config/glassfish.cfg
@@ -0,0 +1,69 @@
+# Glassfish specific checks
+# ===========================
+
+
+# =================
+# JMS with Open MQ
+# For even more metrics, please refer to http://docs.oracle.com/cd/E19316-01/820-6766/gcakw/index.html
+
+# Number of messages within a queue
+# $0: Name of queue
+# $1: Critical (default: 1000)
+# $2: Warning (default: 800)
+<Check gf_omq_queue_count>
+ MBean = com.sun.messaging.jms.server:name="$0",subtype=Monitor,type=Destination,desttype=q
+ Attribute = NumMsgs
+ Name = JMS Queue $0 Count
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Number of messages held for a topic
+# $0: Name of queue
+# $1: Critical (default: 1000)
+# $2: Warning (default: 800)
+<Check gf_omq_topic_count>
+ MBean = com.sun.messaging.jms.server:name="$0",subtype=Monitor,type=Destination,desttype=t
+ Attribute = NumMsgs
+ Name = JMS Topic $0 Count
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Average number of consumers of a topic or queue
+# $0: Name of queue or topic
+# $1: Critical (default: 300)
+# $2: Warning (default: 200)
+<Check gf_omq_consumers_avg>
+ MBean = com.sun.messaging.jms.server:name="$0",subtype=Monitor,type=Destination,*
+ Attribute = AvgNumConsumers
+ Name = Average Number of consumers for $0
+ Critical = ${1:300}
+ Warning = ${2:200}
+</Check>
+
+# Active number of consumers of a topic or queue
+# $0: Name of queue or topic
+# $1: Critical (default: 300)
+# $2: Warning (default: 200)
+<Check gf_omq_consumers_active>
+ MBean = com.sun.messaging.jms.server:name="$0",subtype=Monitor,type=Destination,*
+ Attribute = NumActiveConsumers
+ Name = Number of consumers of $0
+ Critical = ${1:300}
+ Warning = ${2:200}
+</Check>
+
+# Size of all messages within a queue or topic
+# $0: Name of queue or topic
+# $1: Critical (default: 30 MB)
+# $2: Warning (default: 20 MB)
+<Check gf_omq_message_byte>
+ MBean = com.sun.messaging.jms.server:name="$0",subtype=Monitor,type=Destination,*
+ Attribute = TotalMsgBytes
+ Name = Size of messages in $0
+ Critical = ${1:30000000}
+ Warning = ${2:20000000}
+</Check>
+
+
diff --git a/config/jboss.cfg b/config/jboss.cfg
new file mode 100644
index 0000000..c043543
--- /dev/null
+++ b/config/jboss.cfg
@@ -0,0 +1,111 @@
+# JBoss specific checks
+# ========================================================
+
+# JBoss uses tomcat internally
+include tomcat.cfg
+
+# =======================================================
+# Connection-Pools:
+
+# Available connections in a connection pool for a data source
+# Should be not 0
+# $0: Datasource name
+<Check jboss_cpool_available>
+ MBean = *:service=ManagedConnectionPool,name=$0
+ Attribute = AvailableConnectionCount
+ Name = $0 : Available connections
+ Critical = $1
+ Warning = $2
+</Check>
+
+# The reverse: Max. number of connections ever in use
+# $0: Datasource name
+<Check jboss_cpool_used_max>
+ MBean = *:service=ManagedConnectionPool,name=$0
+ Attribute = MaxConnectionsInUseCount
+ Name = $0 : Max. connections in use
+ Critical = $1
+ Warning = $2
+</Check>
+
+# Connections currently in use
+# $0: Datasource name
+<Check jboss_cpool_used>
+ MBean = *:service=ManagedConnectionPool,name=$0
+ Attribute = InUseConnectionCount
+ Name = $0 : Connections in use
+ Critical = $1
+ Warning = $2
+</Check>
+
+# Rate how often connections are created per minute
+# $0: Datasource name
+<Check jboss_cpool_creation_rate>
+ Use = count_per_minute("connections")
+ MBean = *:service=ManagedConnectionPool,name=$0
+ Attribute = ConnectionCreatedCount
+ Name = $0 : Connection creation rate
+ Critical = $1
+ Warning = $2
+</Check>
+
+# =============================================================
+# Workmanager
+
+# Ratio of threads used in the JBoss WorkManager
+<Check jboss_threads>
+ Use = relative_base
+ Value = jboss.jca:service=WorkManagerThreadPool/Instance/poolSize
+ Base = jboss.jca:service=WorkManagerThreadPool/Instance/maximumPoolSize
+ Label = WorkManager Threads: $BASE
+ Name = WorkManager Threads
+</Check>
+
+<Check jboss_threads_2>
+ Use = relative_base
+ Value = jboss.threads:name=WorkManagerThreadPool/CurrentThreadCount
+ Base = jboss.threads:name=WorkManagerThreadPool/MaxThreads
+
+ Label = WorkManager Threads: $BASE
+ Name = WorkManager Threads
+</Check>
+
+# =============================================================
+# JMS
+
+# Rate how fast the number of messages in a JMS queue increases
+# $0: Queue name
+# $1: Critical (default: 1000)
+# $2: Warning (default: 800)
+<Check jboss_jms_queue_rate>
+ Use = count_per_minute("messages")
+ MBean = *:name=$0,service=Queue
+ Attribute = MessageCount
+ Name = JMS Queue $0 : Message count rate
+</Check>
+
+# Number of messages in a JMS queue
+# $0: Queue name
+# $1: Critical (default: 1000)
+# $2: Warning (default: 800)
+<Check jboss_jms_queue_count>
+ MBean = *:name=$0,service=Queue
+ Attribute = MessageCount
+ Name = JMS Queue $0 Count
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Number of messages in a JMS Topic
+# $0: Topic name
+# $1: Critical (default: 1000)
+# $2: Warning (default: 800)
+<Check jboss_jms_topic_count>
+ MBean = *:name=$0,service=Topic
+ Attribute = AllMessageCount
+ Name = JMS Topic $0 Count
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+
diff --git a/config/jboss7.cfg b/config/jboss7.cfg
new file mode 100644
index 0000000..546a36f
--- /dev/null
+++ b/config/jboss7.cfg
@@ -0,0 +1,203 @@
+# JBoss 7 specific checks
+# ========================================================
+
+include "common.cfg"
+
+# Please note that JBoss 7 changed (/wrt JBoss 6) completely with relation to the
+# internal MBean structure
+
+
+# Number of bytes received per minute for a connector
+# $0: Name of connector (e.g. 'http-8080')
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_connector_received_rate>
+ Use = count_per_minute("bytes received")
+ Label = Connector $0 : $BASE
+ Name = ${3:bytes_received}
+ Value = jboss.as.expr:connector=$0,*/bytesReceived
+ Critical = ${1:104857600}
+ Warning = ${2:83886080}
+</Check>
+
+# Number of bytes sent per minute for a connector
+# $0: Name of connector (e.g. 'http-8080')
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_connector_sent_rate>
+ Use = count_per_minute("bytes sent")
+ Label = Connector $0 : $BASE
+ Name = ${3:bytes_sent}
+ Value = jboss.as.expr:connector=$0,*/bytesSent
+ Critical = ${1:104857600}
+ Warning = ${2:83886080}
+</Check>
+
+# Increase of overall processing time per minute for a connector
+# This checks calculates the processing time for a certain
+# interval and scale it to a minute
+# $0: Connector name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_connector_processing_time>
+ Delta = 60
+ Label = Connector $0 : %2.0f ms request processing time / minute
+ Name = ${3:proc_time}
+ Value = jboss.as.expr:connector=$0,*/processingTime
+ Critical = ${1:50000}
+ Warning = ${2:40000}
+</Check>
+
+# Requests per minute for a connector
+# $0: Connector name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_connector_requests>
+ Use = count_per_minute("requests")
+ Label = Connector $0 : $BASE
+ Name = ${3:nr_requests}
+ Value = jboss.as.expr:connector=$0,*/requestCount
+ Critical = ${1:1000}
+ Warning = ${2:900}
+</Check>
+
+# Number of errors for a connector per minute.
+# $0: Connector name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_connector_error_count>
+ Value = jboss.as.expr:connector=$0,*/errorCount
+ Label = Connector $0: %d errors
+ Name = ${3:errors}
+ Critical = ${1:100}
+ Warning = ${2:90}
+ Delta = 60
+</Check>
+
+#################################################################
+
+# Requests per minute for a servlet
+# $0: Web-Module name
+# $1: Servlet name
+# $2: Critical (optional)
+# $3: Warning (optional)
+# $4: Name (optional)
+<Check jboss7_servlet_requests>
+ MBean = jboss.as.expr:deployment=$0,servlet=$1,subdeployment=*,subsystem=web
+ Use = count_per_minute("requests")
+ Attribute = requestCount
+ Name = ${4:request}
+ Critical = ${2:6000}
+ Warning = ${3:5000}
+</Check>
+
+# Increase of overall processing time per minute for a servlet module
+# This is calculate the processing time for a certain
+# interval and extrapolate to a minute
+# $0: Webmodule name
+# $1: Servlet name
+# $2: Critical (optional)
+# $3: Warning (optional)
+# $4: Name (optional)
+<Check jboss7_servlet_processing>
+ MBean = jboss.as.expr:deployment=$0,servlet=$1,subdeployment=*,subsystem=web
+ Attribute = processingTime
+ Delta = 60
+ Label = %2.0f ms request processing time / minute
+ Name = ${3:proc_time}
+ Critical = ${2:50000}
+ Warning = ${3:40000}
+</Check>
+
+# ========================================================
+# Session related checks
+
+# Number of active sessions at this moment
+# $0: Name of web-module
+# $1: Critical (optional)
+# $2: Warning (optional)
+<Check jboss7_session_active>
+ MBean = *:deployment=$0,subsystem=web
+ Attribute = activeSessions
+ Name = ${3:sessions_active}
+ Label = $0: Active Sessions = %v
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Maximum number of active sessions so far
+# $0: Name of web-module
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_session_active_max>
+ MBean = *:deployment=$0,subsystem=web
+ Attribute = maxActive
+ Name = ${3:sessions_max}
+ Label = $0: Max-Active Sessions = %v
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Number of sessions we rejected due to maxActive beeing reached
+# $0: Name of web-module
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_session_rejected>
+ MBean = *:deployment=$0,subsystem=web
+ Attribute = rejectedSessions
+ Name = ${3:sessions_rejected}
+ Label = $0: Rejected Sessions = %v
+ Critical = ${1:500}
+ Warning = ${2:200}
+</Check>
+
+# Average time an expired session had been alive
+# in seconds
+# $0: Name of web-module
+# $1: Critical (7200)
+# $2: Warning (7200)
+# $3: Name (optional)
+<Check jboss7_session_average_lifetime>
+ MBean = *:deployment=$0,subsystem=web
+ Attribute = sessionAverageAliveTime
+ Name = ${3:sessions_avg_life}
+ Label = $0: Average session lifetime = %v
+ Critical = ${1:7200}
+ Warning = ${2:6400}
+</Check>
+
+# Longest time an expired session had been alive
+# in seconds
+# $0: Name of web-module
+# $1: Critical (7200)
+# $2: Warning (6400)
+# $3: Name (optional)
+<Check jboss7_session_max_lifetime>
+ MBean = *:deployment=$0,subsystem=web
+ Attribute = sessionMaxAliveTime
+ Name = ${3:sessions_max_life}
+ Label = $0: Maximum session lifetime = %v
+ Critical = ${1:7200}
+ Warning = ${2:6400}
+</Check>
+
+# Increase rate of sessions per minute
+# $0: Name of web-module
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check jboss7_session_inc>
+ Use = count_per_minute("sessions")
+ MBean = *:deployment=$0,subsystem=web
+ Attribute = sessionCounter
+ Name = ${3:sessions_inc}
+ Critical = ${1:1000}
+ Warning = ${2:900}
+</Check>
diff --git a/config/jetty.cfg b/config/jetty.cfg
new file mode 100644
index 0000000..4583ee1
--- /dev/null
+++ b/config/jetty.cfg
@@ -0,0 +1,191 @@
+# Jetty specific checks
+# ========================================================
+
+include common.cfg
+
+# Servlet running
+# $0: Name of servlet
+<Check jetty_servlet_running>
+ MBean = org.mortbay.jetty.servlet:name=$0,*
+ Attribute = running
+ String = 1
+ Label = $0 running
+ Name = $0 running
+ Critical = false
+</Check>
+
+# Servlet failed status
+# $0: Name of servlet
+<Check jetty_servlet_failed>
+ MBean = org.mortbay.jetty.servlet:name=$0,*
+ Attribute = failed
+ String = 1
+ Label = $0 failing
+ Name = $0 failed
+ Critical = true
+</Check>
+
+# Jetty is low on threads ?
+<Check jetty_threads_low>
+ MBean = org.mortbay.thread:type=queuedthreadpool,*
+ Attribute = lowOnThreads
+ String = 1
+ Label = Low on threads
+ Name = LowOnThreads Flag
+ Critical = true
+</Check>
+
+# Ratio between created threads to maximum threads
+# $0: Critical value (default: 90%)
+# $1: Warning value (default: 80%)
+<Check jetty_threads>
+ Use = relative_base($0,$1)
+ Value = org.mortbay.thread:type=queuedthreadpool,*/threads
+ Base = org.mortbay.thread:type=queuedthreadpool,*/maxThreads
+ Name = Jetty-Threads
+</Check>
+
+# Server is running
+<Check jetty_server_running>
+ MBean = org.mortbay.jetty:type=server,*
+ Attribute = running
+ String = 1
+ Label = Server running
+ Name = Server running
+ Critical = false
+</Check>
+
+# Server failed
+<Check jetty_server_failed>
+ MBean = org.mortbay.jetty:type=server,*
+ Attribute = failed
+ String = 1
+ Label = Server failing
+ Name = ServerFailedFlag
+ Critical = true
+</Check>
+
+# =====================================================================
+# Sessions
+
+# The maximum number of sessions ever created (overall, all webapps)
+# $0: Critical
+# $1: Warning
+<Check jetty_sessions_max>
+ MBean = org.mortbay.jetty.servlet:type=hashsessionmanager,*
+ Attribute = maxSessions
+ Label = Max Sessions = %v
+ Name = MaxSessions
+ Critical = $0
+ Warning = $1
+</Check>
+
+# The current number of sessions (overall, all webapps)
+# $0: Critical (default: 1000)
+# $1: Warning (default: 800)
+<Check jetty_sessions>
+ MBean = org.mortbay.jetty.servlet:type=hashsessionmanager,*
+ Attribute = sessions
+ Label = Sessions = %v
+ Name = Sessions
+ Critical = ${0:1000}
+ Warning = ${1:800}
+</Check>
+
+
+# =====================================================================
+# Requests
+
+# The overall requests / minute
+# 'statsOn' has to be set to true in jetty.xml for letting jetty collects
+# statistics information for the overall connector
+# $0: Critical (default: 6000)
+# $1: Warning (default: 5000)
+<Check jetty_request_nio>
+ Use = count_per_minute("requests")
+ MBean = org.mortbay.jetty.nio:type=selectchannelconnector,*
+ Attribute = requests
+ Name = Requests
+ Critical = ${0:6000}
+ Warning = ${1:5000}
+</Check>
+
+# Number of accepted connections ('statsOn' must be set)
+# $0: Critical (default: 6000)
+# $1: Warning (default: 5000)
+<Check jetty_connections>
+ Use = count_per_minute("connections")
+ MBean = org.mortbay.jetty.nio:type=selectchannelconnector,*
+ Attribute = connections
+ Name = Connections
+ Critical = ${0:6000}
+ Warning = ${1:5000}
+</Check>
+
+# Number of open connections ('statsOn' must be set)
+# $0: Critical (default: 1000)
+# $1: Warning (default: 900)
+<Check jetty_connections_open>
+ MBean = org.mortbay.jetty.nio:type=selectchannelconnector,*
+ Attribute = connectionsOpen
+ Name = ConnectionsOpen
+ Label = Open connections = %v
+ Critical = ${0:1000}
+ Warning = ${1:900}
+</Check>
+
+# ========================================================================
+
+# Add $JETTY_HOME/etc/jetty-stats.xml to the configuration for collecting per
+# request duration statistics.
+#
+# See also http://communitymapbuilder.osgeo.org/display/JETTY/Statistics
+# for details
+
+# Average duration of a request in ms
+# $0: Critical (default: 400ms)
+# $1: Warning (default: 300ms)
+<Check jetty_request_duration_average>
+ MBean = org.mortbay.jetty.handler:type=statisticshandler,*
+ Attribute = requestsDurationAve
+ Name = RequestDurationAverage
+ Label = Average Request Duration = %v ms
+ Critical = ${0:400}
+ Warning = ${1:300}
+</Check>
+
+# Maximum duration of any request in ms
+# $0: Critical (default: 400ms)
+# $1: Warning (default: 300ms)
+<Check jetty_request_duration_max>
+ MBean = org.mortbay.jetty.handler:type=statisticshandler,*
+ Attribute = requestsDurationMax
+ Name = RequestDurationMaximum
+ Label = Maximum Request Duration = %v ms
+ Critical = ${0:1000}
+ Warning = ${1:900}
+</Check>
+
+# Number of Requests per minute
+# $0: Critical (default: 6000)
+# $1: Warning (default: 5000)
+<Check jetty_request_rate>
+ Use = count_per_minute("requests")
+ MBean = org.mortbay.jetty.handler:type=statisticshandler,*
+ Attribute = requests
+ Name = Requests
+ Critical = ${0:6000}
+ Warning = ${1:5000}
+</Check>
+
+# Number of currently active requests
+# $0: Critical (default: 1000)
+# $1: Warning (default: 900)
+<Check jetty_request_active>
+ MBean = org.mortbay.jetty.handler:type=statisticshandler,*
+ Attribute = requestsActive
+ Name = ActiveRequests
+ Label = Active Requests = %v
+ Critical = ${0:1000}
+ Warning = ${1:900}
+</Check>
diff --git a/config/memory.cfg b/config/memory.cfg
new file mode 100644
index 0000000..d39b053
--- /dev/null
+++ b/config/memory.cfg
@@ -0,0 +1,200 @@
+# Memory checks
+# ============================================
+
+include common.cfg
+
+# Base definition for memory relative checks
+# (i.e. checks with a base value). Should
+# not be used directly
+<Check memory_relative_base>
+ Use = relative_base($0,$1)
+ Unit = B
+ BaseUnit = B
+</Check>
+
+# Relative Heap Memory used by the application. This
+# is the ratio between used heap memory and the maximal
+# available heap memory
+# $0: Critical value (optional)
+# $1: Warning value (optional)
+<Check memory_heap>
+ Use = memory_relative_base($0,$1)
+ Value = java.lang:type=Memory/HeapMemoryUsage/used
+ Base = java.lang:type=Memory/HeapMemoryUsage/max
+ Label = Heap-Memory: $BASE
+ Name = Heap
+ MultiCheckPrefix
+</Check>
+
+# Relative non-heap memory. The JVM has memory other than the heap,
+# referred to as non-heap memory. It stores per-class structures such
+# as runtime constant pool, field and method data, and the code for
+# methods and constructors, as well as interned Strings. More detailed
+# information can be obtained from the pool checks defined below
+# $0: Critical value (optional)
+# $1: Warning value (optional)
+<Check memory_non_heap>
+ Use = memory_relative_base
+ Value = java.lang:type=Memory/NonHeapMemoryUsage/used
+ Base = java.lang:type=Memory/NonHeapMemoryUsage/max
+ Label = Non-Heap-Memory: $BASE
+ Name = Non-Heap
+ MultiCheckPrefix
+</Check>
+
+# Java 8 Memory check for MetaSpace. Metaspace is typically unbounded
+# and grows into native (OS) memory. Hence, an absolute thresshold is used
+# here which by default is (C: 80M, W: 60M).
+<Check memory_metaspace>
+ Unit = B
+ Label = %.2v %u meta space used
+ Value = java.lang:name=Metaspace,type=MemoryPool/Usage/used
+ Name = MetaSpace
+ Critical = ${0:83886080}
+ Warning = ${1:62914560}
+ MultiCheckPrefix
+</Check>
+
+# Java 8 Memory check for MetaSpace with an upper configued (-XX:MetaSpaceSize)
+<Check memory_metaspace_relative>
+ Use = memory_relative_base
+ Value = java.lang:name=Metaspace,type=MemoryPool/Usage/used
+ Base = java.lang:name=Metaspace,type=MemoryPool/Usage/max
+ Label = MetaSpace: $BASE
+ Name = MetaSpace
+ MultiCheckPrefix
+</Check>
+
+# =============================================================
+# Memory pool checks. These are specific to a Sun/Oracle JVM.
+
+# Base definition for pool based checks
+# $0: Label prefix and name to used
+# $1: Critical value (optional)
+# $2: Warning value (optional)
+<Check memory_pool_base>
+ Use = memory_relative_base($1,$2)
+ Value = java.lang:type=MemoryPool,name=$0/Usage/used
+ Base = java.lang:type=MemoryPool,name=$0/Usage/max
+ Label = $0 : $BASE
+ Name = $0
+</Check>
+
+# Base definition for garbage collection count
+# This checks count the number of garbage collections per
+# minute
+# $0: Name of garbage collector (used as Label as well)
+# $1: Critical value (default: 30)
+# $2: Warning value (default: 20)
+<Check memory_gc_count_base>
+ Use = count_per_minute("GC count")
+ Value = java.lang:type=GarbageCollector,name=$0/CollectionCount
+
+ Label = $0 : $BASE
+ Name = $0 count
+ Critical = ${1:30}
+ Warning = ${2:20}
+</Check>
+
+# Base definition for garbage time measurements
+# This checks measure the ratio the garbage collection takes from a minute
+# (e.g. how many percent of a minute is used for garbage collecting)
+
+# $0: Name of garbage collector (used as Label as well)
+# $1: Critical value in percent (default: 20)
+# $2: Warning value in percent (default: 10)
+
+# WARNING: THIS CHECK HAS CHANGED IN 1.08. Remove the 'Base' and adapt the label
+# to obtain the old behaviour.
+<Check memory_gc_time_base>
+ Value = java.lang:type=GarbageCollector,name=$0/CollectionTime
+
+ Label = %2.2r% GC Overhead
+ Name = $0 time
+
+ Delta 60
+
+ # Next line switches on relative checking to get the percentual overhead
+ # for a garbage collection
+ Base = 60000
+
+ Critical = ${1:20}
+ Warning = ${2:10}
+</Check>
+
+# The paralled garbage collectors and memory
+# pools switched on with -XX:+UseParallelGC.
+# Used by 64bit server VMs by default.
+<MultiCheck memory_pools_parallel>
+ Check = memory_pool_base("PS Eden Space",100,100)
+ Check = memory_pool_base("PS Survivor Space",100,100)
+ Check = memory_pool_base("PS Old Gen")
+ Check = memory_pool_base("PS Perm Gen")
+</MultiCheck>
+
+<MultiCheck memory_gc_count_parallel>
+ Check = memory_gc_count_base("PS Scavenge")
+ Check = memory_gc_count_base("PS MarkSweep")
+</MultiCheck>
+
+# Since 1.08: Relative time instead of absolute values.
+<MultiCheck memory_gc_time_parallel>
+ Check = memory_gc_time_base("PS Scavenge")
+ Check = memory_gc_time_base("PS MarkSweep")
+</MultiCheck>
+
+# Garbage collectors and memory pools used for
+# -XX:+UseConcMarkSweepGC and -XX:+UseParNewGC
+# used by default by OS X, client vm.
+<MultiCheck memory_pools_concurrent>
+ Check = memory_pool_base("Par Eden Space")
+ Check = memory_pool_base("Par Survivor Space")
+ Check = memory_pool_base("CMS Old Gen")
+ Check = memory_pool_base("CMS Perm Gen")
+</MultiCheck>
+
+<MultiCheck memory_gc_count_concurrent>
+ Check = memory_gc_count_base("ParNew")
+ Check = memory_gc_count_base("ConcurrentMarkSweep")
+</MultiCheck>
+
+# Since 1.08: Relative time instead of absolute values.
+<MultiCheck memory_gc_time_concurrent>
+ Check = memory_gc_time_base("ParNew")
+ Check = memory_gc_time_base("ConcurrentMarkSweep")
+</MultiCheck>
+
+
+# Garbage collector and memory pools used
+# when -XX:+UseSerialGC is used. Seems to be the default
+# on linux for -client and -server VMs
+<MultiCheck memory_pools_serial>
+ Check = memory_pool_base("Eden Space")
+ Check = memory_pool_base("Survivor Space")
+ Check = memory_pool_base("Tenured Gen")
+ Check = memory_pool_base("Perm Gen")
+</MultiCheck>
+
+<MultiCheck memory_gc_count_serial>
+ Check = memory_gc_count_base("Copy")
+ Check = memory_gc_count_base("MarkSweepCompact")
+</MultiCheck>
+
+# Since 1.08: Relative time instead of absolute values.
+<MultiCheck memory_gc_time_serial>
+ Check = memory_gc_time_base("Copy")
+ Check = memory_gc_time_base("MarkSweepCompact")
+</MultiCheck>
+
+<Check memory_code_cache>
+ Use = memory_pool_base("Code Cache")
+</Check>
+
+# ================================================
+# Collection of related checks.
+
+# Overall view to the memory statistics
+<MultiCheck memory>
+ Check memory_heap
+ Check memory_non_heap
+</MultiCheck>
diff --git a/config/metrics.cfg b/config/metrics.cfg
new file mode 100644
index 0000000..4ca0622
--- /dev/null
+++ b/config/metrics.cfg
@@ -0,0 +1,43 @@
+# Checks for Metrics (http://metrics.codahale.com/)
+# =================================================
+
+
+<Check metrics_base>
+ MBean = $0:type=$1,name=$2
+ Label = $0.$2 / $1
+</Check>
+
+#
+#
+#
+<Check metrics_timer_base>
+ Use = metrics_base($1,$2,$3)
+ Attribute = $0
+ Label = $0 for $BASE : %v %u
+ Name = $0
+ Critical = $4
+ Warning = $5
+ Unit = ${6:ms}
+</Check>
+
+<MultiCheck metrics_timer_times>
+ Check metrics_timer_base("Mean",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("StdDev",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("Min",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("Max",$0,$1,$2,$3,$4,$5)
+</MultiCheck>
+
+<MultiCheck metrics_timer_percentile>
+ Check metrics_timer_base("50thPercentile",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("75thPercentile",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("95thPercentile",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("99thPercentile",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("999thPercentile",$0,$1,$2,$3,$4,$5)
+</MultiCheck>
+
+<MultiCheck metrics_timer_rate>
+ Check metrics_timer_base("MeanRate",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("OneMinuteRate",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("FiveMinuteRate",$0,$1,$2,$3,$4,$5)
+ Check metrics_timer_base("FifteenMinuteRate",$0,$1,$2,$3,$4,$5)
+</MultiCheck>
diff --git a/config/threads.cfg b/config/threads.cfg
new file mode 100644
index 0000000..60df925
--- /dev/null
+++ b/config/threads.cfg
@@ -0,0 +1,37 @@
+# Predefined checks for fetching thread statistics
+# from MXBeans
+# ==================================================
+
+include common.cfg
+
+# Check for a thread increase per minute
+# $0 : Critical threshold (default: 60)
+# $1 : Warning threshold (default: 30)
+<Check thread_inc>
+ Use = count_per_minute("Threads")
+ Value = java.lang:type=Threading/ThreadCount
+ Name = Thread-Increase
+ Critical = ${0:~:60}
+ Warning = ${1:~:30}
+</Check>
+
+# Check for monitoring the total (absolute) count of threads
+# active within an application
+# $0 : Critical threshold (default: 1000)
+# $1 : Warning threshold (default: 800)
+<Check thread_count>
+ Value = java.lang:type=Threading/ThreadCount
+ Name = Thread-Count
+ Critical = ${0:1000}
+ Warning = ${1:800}
+</Check>
+
+# Find deadlocked Threads
+<Check thread_deadlock>
+ MBean = java.lang:type=Threading
+ Operation = findDeadlockedThreads
+ Null = no deadlock
+ Name = Thread-Deadlock
+ String = 1
+ Critical = !no deadlock
+</Check>
diff --git a/config/tomcat.cfg b/config/tomcat.cfg
new file mode 100644
index 0000000..3f5bea4
--- /dev/null
+++ b/config/tomcat.cfg
@@ -0,0 +1,245 @@
+# Tomcat specific checks
+# ========================================================
+
+include common.cfg
+
+# Requests per minute for a servlet
+# $0: Web-Module name
+# $1: Servlet name
+# $2: Critical (optional)
+# $3: Warning (optional)
+# $4: Name (optional)
+<Check tc_servlet_requests>
+ MBean = *:j2eeType=Servlet,WebModule=$0,name=$1,*
+ Use = count_per_minute("requests")
+ Attribute = requestCount
+ Name = ${4:request}
+ Critical = ${2:6000}
+ Warning = ${3:5000}
+</Check>
+
+# Check whether an webmodule (can contain multiple servlets)
+# is running
+# $0: Webmodule name (sth like "//localhost/j4p")
+# $1: Name (optional)
+<Check tc_webmodule_running>
+ MBean = *:j2eeType=WebModule,name=$0,*
+ Attribute = state
+ String = 1
+ Label = $0 running
+ Name = ${1:running}
+ Critical = !1
+</Check>
+
+# Increase of overall processing time per minute for a web module
+# This is calculate the processing time for a certain
+# interval and extrapolate to a minute
+# $0: Webmodule name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_webmodule_processing>
+ MBean = *:j2eeType=WebModule,name=$0,*
+ Attribute = processingTime
+ Delta = 60
+ Label = %2.0f ms request processing time / minute
+ Name = ${3:proc_time}
+ Critical = ${1:50000}
+ Warning = ${2:40000}
+</Check>
+
+# ========================================================
+# Session related checks
+
+# Number of active sessions at this moment
+# $0: Path name without leading slash
+# $1: Critical (optional)
+# $2: Warning (optional)
+<Check tc_session_active>
+ MBean = *:path=/$0,type=Manager,*
+ Attribute = activeSessions
+ Name = ${3:sessions_active}
+ Label = $0: Active Sessions = %v
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Maximum number of active sessions so far
+# $0: Path name without leading slash
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_session_active_max>
+ MBean = *:path=/$0,type=Manager,*
+ Attribute = maxActive
+ Name = ${3:sessions_max}
+ Label = $0: Max-Active Sessions = %v
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Number of sessions we rejected due to maxActive beeing reached
+# $0: Path name without leading slash
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_session_rejected>
+ MBean = *:path=/$0,type=Manager,*
+ Attribute = rejectedSessions
+ Name = ${3:sessions_rejected}
+ Label = $0: Rejected Sessions = %v
+ Critical = ${1:500}
+ Warning = ${2:200}
+</Check>
+
+# Average time an expired session had been alive
+# in seconds
+# $0: Path name without leading slash
+# $1: Critical (7200)
+# $2: Warning (7200)
+# $3: Name (optional)
+<Check tc_session_average_lifetime>
+ MBean = *:path=/$0,type=Manager,*
+ Attribute = sessionAverageAliveTime
+ Name = ${3:sessions_avg_life}
+ Label = $0: Average session lifetime = %v
+ Critical = ${1:7200}
+ Warning = ${2:6400}
+</Check>
+
+# Longest time an expired session had been alive
+# in seconds
+# $0: Path name without leading slash
+# $1: Critical (7200)
+# $2: Warning (6400)
+# $3: Name (optional)
+<Check tc_session_max_lifetime>
+ MBean = *:path=/$0,type=Manager,*
+ Attribute = sessionMaxAliveTime
+ Name = ${3:sessions_max_life}
+ Label = $0: Maximum session lifetime = %v
+ Critical = ${1:7200}
+ Warning = ${2:6400}
+</Check>
+
+# Increase rate of sessions per minute
+# $0: Path name without leading slash
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_session_inc>
+ Use = count_per_minute("sessions")
+ MBean = *:path=/$0,type=Manager,*
+ Attribute = sessionCounter
+ Name = ${3:sessions_inc}
+ Critical = ${1:1000}
+ Warning = ${2:900}
+</Check>
+
+# =============================================================
+# Connector related checks
+
+# Number of connector threads in relation to maximum
+# allowed connector threads
+# $0: Name of connector (e.g. 'http-8080')
+# $1: Critical (optional)
+# $2: Warning (optional)
+<Check tc_connector_threads>
+ Use = relative_base($1,$2)
+ Label = Connector $0 : $BASE
+ Name = ${3:connector_threads}
+ Value = *:type=ThreadPool,name=$0/currentThreadsBusy
+ Base = *:type=ThreadPool,name=$0/maxThreads
+</Check>
+
+
+# Number of bytes received per minute for a connector
+# $0: Name of connector (e.g. 'http-8080')
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_connector_received_rate>
+ Use = count_per_minute("bytes received")
+ Label = Connector $0 : $BASE
+ Name = ${3:bytes_received}
+ Value = *:type=GlobalRequestProcessor,name=$0/bytesReceived
+ Critical = ${1:104857600}
+ Warning = ${2:83886080}
+</Check>
+
+# Number of bytes sent per minute for a connector
+# $0: Name of connector (e.g. 'http-8080')
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_connector_sent_rate>
+ Use = count_per_minute("bytes sent")
+ Label = Connector $0 : $BASE
+ Name = ${3:bytes_sent}
+ Value = *:type=GlobalRequestProcessor,name=$0/bytesSent
+ Critical = ${1:104857600}
+ Warning = ${2:83886080}
+</Check>
+
+# Increase of overall processing time per minute for a connector
+# This checks calculates the processing time for a certain
+# interval and scale it to a minute
+# $0: Connector name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_connector_processing_time>
+ Delta = 60
+ Label = Connector $0 : %2.0f ms request processing time / minute
+ Name = ${3:proc_time}
+ Value = *:type=GlobalRequestProcessor,name=$0/processingTime
+ Critical = ${1:50000}
+ Warning = ${2:40000}
+</Check>
+
+# Requests per minute for a connector
+# $0: Connector name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_connector_requests>
+ Use = count_per_minute("requests")
+ Label = Connector $0 : $BASE
+ Name = ${3:nr_requests}
+ Value = *:type=GlobalRequestProcessor,name=$0/requestCount
+ Critical = ${1:1000}
+ Warning = ${2:900}
+</Check>
+
+# Number of errors for a connector per minute.
+# $0: Connector name
+# $1: Critical (optional)
+# $2: Warning (optional)
+# $3: Name (optional)
+<Check tc_connector_error_count>
+ Value = *:type=GlobalRequestProcessor,name=$0/errorCount
+ Label = Connector $0: %d errors
+ Name = ${3:errors}
+ Critical = ${1:100}
+ Warning = ${2:90}
+ Delta = 60
+</Check>
+
+# ==================================================================
+# Relative DB Pool check (active connection vs. maximal available connections)
+# Note that you need to register the datasource globally in order
+# to access the Pool statistics (i.e. within the <GlobalResources> sections)
+# See http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html
+# for more information
+# $0: JNDI-Name of datasource (e.g. jdbc/TestDB)
+# $1: Critical value (optional)
+# $2: Warning value (optional)
+# $3: Name (optional)
+<Check tc_datasource_connections>
+ Value = *:name="$0",type=DataSource,*/numActive
+ Base = *:name="$0",type=DataSource,*/maxActive
+ Name = ${3:dbpool_used}
+ Label = %.2r% DB connections used (%v %u active / %b %w max)
+ Critical = ${1:90}
+ Warning = ${2:80}
+</Check> \ No newline at end of file
diff --git a/config/weblogic.cfg b/config/weblogic.cfg
new file mode 100644
index 0000000..02eb6af
--- /dev/null
+++ b/config/weblogic.cfg
@@ -0,0 +1,95 @@
+# Weblogic specific checks
+# ========================================================
+
+include common.cfg
+
+<Check wls_channel_received_rate>
+ Use = count_per_minute("bytes received")
+ Label = Channel $0 : $BASE
+ Name = bytes_received
+ Value = *:Name=$0,Type=ServerChannelRuntime,*/BytesReceivedCount
+ Critical = ${1:104857600}
+ Warning = ${2:83886080}
+</Check>
+
+<Check wls_channel_sent_rate>
+ Use = count_per_minute("bytes sent")
+ Label = Channel $0 : $BASE
+ Name = bytes_sent
+ Value = *:Name=$0,Type=ServerChannelRuntime,*/BytesSentCount
+ Critical = ${1:104857600}
+ Warning = ${2:83886080}
+</Check>
+
+<Check wls_channel_connections>
+ Label = Channel $0 : %4.4v active connections
+ Name = connections
+ Value = *:Name=$0,Type=ServerChannelRuntime,*/ConnectionsCount
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+<Check wls_webapp_running>
+ Value = *:Type=WebAppComponentRuntime,ApplicationRuntime=$0,*/DeploymentState
+ String = 1
+ Label = $0 is running
+ Name = running
+ Critical = !1
+</Check>
+
+<Check wls_servlet_execution_avg>
+ Value = *:Type=ServletRuntime,ApplicationRuntime=$0,Name=$1,*/ExecutionTimeAverage
+ Label = $0 [$1] : Average execution time %d ms
+ Name = servlet_avg_execution_time
+ Critical = ${2:20000}
+ Warning = ${3:10000}
+</Check>
+
+<Check wls_ws_execution_avg>
+ Value = *:Type=WseeOperationRuntime,ApplicationRuntime=$0,Name=$1,*/ExecutionTimeAverage
+ Label = WS $0 [$1] : Average execution time %d ms
+ Name = ws_avg_execution_time
+ Critical = ${2:150000}
+ Warning = ${3:100000}
+</Check>
+
+<Check wls_ws_response_avg>
+ Value = *:Type=WseeOperationRuntime,ApplicationRuntime=$0,Name=$1,*/ResponseTimeAverage
+ Label = WS $0 [$1] : Average response time %d ms
+ Name = ws_avg_execution_time
+ Critical = ${2:150000}
+ Warning = ${3:100000}
+</Check>
+
+<Check wls_ws_response_error>
+ Value = *:Type=WseeOperationRuntime,ApplicationRuntime=$0,Name=$1,*/ResponseErrorCount
+ Label = WS $0 [$1] : Response error count %d
+ Name = ws_response_errors
+ Critical = ${2:10}
+ Warning = ${3:5}
+</Check>
+
+<Check wls_wmr_pending>
+ Value = *:Type=WorkManagerRuntime,ApplicationRuntime=$0,Name=$1,*/PendingRequests
+ Label = WorkManager $0 [$1] : Pending requests %d
+ Name = ws_wm_pending_requests
+ Critical = ${2:10}
+ Warning = ${3:5}
+</Check>
+
+<Check wls_wmr_threads_stuck>
+ Value = *:Type=WorkManagerRuntime,ApplicationRuntime=$0,Name=$1,*/StuckThreadCount
+ Label = WorkManager $0 [$1] : Stuck threads %d
+ Name = ws_wm_stuck_threads
+ Critical = ${2:10}
+ Warning = ${3:5}
+</Check>
+
+<Check wls_webapp_sessions>
+ Value = *:Type=WebAppComponentRuntime,ApplicationRuntime=$0,*/OpenSessionsCurrentCount
+ Label = Webapp $0 : Open sessions %d
+ Name = ws_webapp_sessions
+ Critical = ${1:2000}
+ Warning = ${2:1500}
+</Check>
+
diff --git a/config/websphere.cfg b/config/websphere.cfg
new file mode 100644
index 0000000..cbfa397
--- /dev/null
+++ b/config/websphere.cfg
@@ -0,0 +1,30 @@
+# Websphere Checks
+# ----------------
+
+# These checks are for WebSphere and has been tested for WebSphere >= 8.0
+# (but should workd with older WebSphere servers as well).
+
+# For most of the test it is required that a customzied Jolokia agent is used
+# which provides simplified access to JSR-77 metrics.
+#
+# These agents can be obtained from the 'jolokia-extra' project: https://github.com/rhuss/jolokia-extra
+# or downloaded from Maven central: http://central.maven.org/maven2/org/jolokia/extra/
+# They all have an classifier "-jsr77" and the first three parts of the version specify
+# the Jolokia core version included.
+# E.g. "jolokia-extra-war-1.2.2.2-jsr77.war" contains Jolokia 1.2.2 (and is the second variant with
+# the JSR-77 specifier)
+
+# Most of these tests utilize the PMI subsystem of WebSphere.
+
+# ===============================================================
+# Including various checks. These config files are self contained,
+# and for performance optimizations could be included separately if only
+# some checks are needed.
+
+include websphere/threads.cfg
+include websphere/http.cfg
+include websphere/jdbc.cfg
+include websphere/jms.cfg
+include websphere/jca.cfg
+include websphere/appstate.cfg
+
diff --git a/config/websphere/appstate.cfg b/config/websphere/appstate.cfg
new file mode 100644
index 0000000..28f2632
--- /dev/null
+++ b/config/websphere/appstate.cfg
@@ -0,0 +1,13 @@
+# ---------------------------------------
+# Check of the application state
+#
+# $0: application name
+<Check was_application_state>
+ MBean WebSphere:j2eeType=J2EEApplication,J2EEName=${0},*
+ Attribute state
+
+ Critical = ${1:1}
+ Label = $0 : status = %v
+ Name = $0-state
+</Check>
+
diff --git a/config/websphere/http.cfg b/config/websphere/http.cfg
new file mode 100644
index 0000000..7ddea20
--- /dev/null
+++ b/config/websphere/http.cfg
@@ -0,0 +1,129 @@
+# ============================================
+# HTTP Checks
+
+include threads.cfg
+
+# HTTP Thread Pool Utilization
+# Check of relative pool size, i.e. the ratio between actual created threads
+# to the number of maximal available threads.
+<Check was_http_pool_size>
+ Use was_thread_pool_size('WebContainer',$0,$1)
+</Check>
+
+# Relative check of all active threads out of the threadpool for the web container
+<Check was_http_pool_active>
+ Use was_thread_pool_active('WebContainer',$0,$1)
+</Check>
+
+# Web-Sessions
+
+# Check for the number of session uses. The maximal number of sessions is not available
+# and should be provided as argument to this check (default is 200).
+#
+# A unique part of the name contained in the 'mbeanIdentifier' key of the MBean
+# must be used for the name (e.g. 'jolokia' for the Jolokia agent).
+#
+# $0: Unique part of the name of the web app (see above)
+# $1: Maximum number of session (default: 200)
+# $2: Critical (default: 90%)
+# $3: Warning (default: 80%)
+<Check was_http_session_count>
+ MBean WebSphere:type=SessionManager,mbeanIdentifier=*${0}*,*
+ Attribute stats
+ Path */*/statistics/LiveCount/current
+
+ # Base value as the number of maximal possible sessions
+ # (or if a proper MBean attribute is found, this could be inserted here)
+ Base ${1:200}
+
+ Critical ${2:90}
+ Warning ${3:80}
+
+ Label $0 : %.2r% sessions in use (%v / %b)
+ Name ${0}-http-sessions
+</Check>
+
+# HTTP Request Count
+# Check for the number of requests per minute for a specific servlet.
+#
+# $0: Part of the servlet name (see above)
+# $1: Critical as requests / minute (no default)
+# $2: Warning as requests / minute (no default)
+<Check was_http_request_count>
+ Use was_request_count($0,$1,$2)
+ MBean WebSphere:type=Servlet,mbeanIdentifier=*${0}*,*
+</Check>
+
+# Check for the number of requests per minute for a specific JSP
+#
+# $0: Part of the JSP name (see above)
+# $1: Critical as requests / minute (1000)
+# $2: Warning as requests / minute (800)
+<Check was_jsp_request_count>
+ Use was_request_count($0,$1,$2)
+ MBean WebSphere:type=JSP,mbeanIdentifier=*${0}*,*
+</Check>
+
+# Base Check for requests counts (servlet or JSPs)
+# $0: Part of the servlet name (see above)
+# $1: Critical as requests / minute (1000)
+# $2: Warning as requests / minute (800)
+<Check was_request_count>
+ Attribute stats
+ Path */*/statistics/RequestCount/count
+ Delta 60
+
+ Critical ${1:1000}
+ Warning ${2:800}
+
+ Label $0 : %2.2q requests / minute
+ Name ${0}-request-count
+</Check>
+
+# HTTP Service Time
+#
+# Check of average processing time per request for a servlet.
+#
+# $0: Part of the servlet name (see above)
+# $1: Critical (default: 10000ms)
+# $2: Warning (default: 5000ms)
+<Check was_http_service_time>
+ Use was_service_time($0,$1,$2)
+ MBean WebSphere:type=Servlet,mbeanIdentifier=*${0}*,*
+ BaseMBean WebSphere:type=Servlet,mbeanIdentifier=*${0}*,*
+</Check>
+
+# Check of average processing time per request for a JSP
+#
+# $0: Part of JSP name (see above)
+# $1: Critical (default: 10000ms)
+# $2: Warning (default: 5000ms)
+<Check was_jsp_service_time>
+ Use was_service_time($0,$1,$2)
+ MBean WebSphere:type=JSP,mbeanIdentifier=*${0}*,*
+ BaseMBean WebSphere:type=JSP,mbeanIdentifier=*${0}*,*
+</Check>
+
+# Base check for total service time checks (suggestion for
+# improvements: Currently the overall average is measured. It would be
+# much better to use only the average till the last
+# measurement. Therefore a "Delta" should be used (without
+# normalization), but unfortunately the base value is not used as 'delta'
+# yet.
+<Check was_service_time>
+ Attribute stats
+ Path */*/statistics/ServiceTime/totalTime
+
+ BaseAttribute stats
+ BasePath */*/statistics/ServiceTime/count
+
+ Delta
+
+ # * 100 because the value is a 'relative' check typical used for percentages
+ Critical{1:1000000}
+ Warning ${2:500000}
+
+ Label %2.2q ms ∅ processing time per request (%v ms total for %b requests)
+ Name $0-request-processing-time
+</Check>
+
diff --git a/config/websphere/jca.cfg b/config/websphere/jca.cfg
new file mode 100644
index 0000000..8801aa9
--- /dev/null
+++ b/config/websphere/jca.cfg
@@ -0,0 +1,43 @@
+# ===============================================================
+# JCA
+
+# JCA connector pool usage
+#
+# ${0} : part of the JCA connector name
+# ${1} : Managed Connection Factory Name (JCA)
+# ${2} : Critical (default: 90 percent)
+# ${3} : Warning (default: 80 percent)
+<Check was_jca_percent_used>
+ MBean WebSphere:j2eeType=JCAResource,mbeanIdentifier=*${0}*,*
+ Attribute stats
+ Path */*/connectionPools/${1}/statistics/PercentUsed/current
+
+ Critical ${2:90}
+ Warning ${3:80}
+
+ Label $1 : %2.0f% connections used
+ Name jca-${1}-${0}-pool
+</Check>
+
+# Average waiting time until a JCA connector is available
+#
+# ${0} : part of the JCA resource name as it appears in the mbeanIdentifier
+# ${1} : Managed Connection Factory Name (JCA)
+# ${2} : Critical (default: 10s)
+# ${3} : Warning (default: 5s)
+<Check was_jca_wait_time>
+ MBean WebSphere:j2eeType=JCAResource,mbeanIdentifier=*${0}*,*
+ Attribute stats
+ Path */*/connectionPools/${1}/statistics/WaitTime/totalTime
+
+ BaseMBean WebSphere:j2eeType=JCAResource,mbeanIdentifier=${0},*
+ BaseAttribute stats
+ BasePath */*/connectionPools/${1}/statistics/WaitTime/count
+
+ Critical ${2:10000}
+ Warning ${3:5000}
+
+ Label $1: %2.2q ms ∅ wait time (%v ms total for %b requests)
+ Name jca-${1}-${0}-wait-time
+</Check>
+
diff --git a/config/websphere/jdbc.cfg b/config/websphere/jdbc.cfg
new file mode 100644
index 0000000..3213d68
--- /dev/null
+++ b/config/websphere/jdbc.cfg
@@ -0,0 +1,88 @@
+# ==============================================================================
+# JDBC Datasources
+
+# JDBC Poolsize Check. This check requires two parameters at least:
+# The name of th JDBC Provider and the data source name. It must be ensured that
+# the pattern used in this check must result in a single data source only.
+#
+# In order to specify this even further, a fourth parameter can be used to
+# match on part of the mbeanIdentifier.
+#
+# ${0} : Name of the JDBC Provider
+# ${1} : DataSource Name
+# ${2} : Critical (default: 90%)
+# ${3} : Warning (default: 80%)
+# ${4} : Part of mbeanIdentifier (default: *)
+
+<Check was_jdbc_percent_used>
+ MBean WebSphere:j2eeType=JDBCResource,name=${0},mbeanIdentifier=${4:*},*
+ Attribute stats
+ Path */*/connectionPools/${1}/statistics/PercentUsed/current
+
+ Critical ${2:90}
+ Warning ${3:80}
+
+ Label $1 : %2.0f % DB Connections used
+ Name jdbc-$0-connections
+</Check>
+
+# Average wait time until a connection is obtained
+
+# ${0} : Name of the JDBC Provider
+# ${1} : Datasource name
+# ${2} : Critical (default: 10s)
+# ${3} : Warning (default: 5s)
+# ${4} : Part of mbeanIdentifier (default: *)
+<Check was_jdbc_wait_time>
+ MBean WebSphere:j2eeType=JDBCResource,name=${0},mbeanIdentifier=${4:*},*
+ Attribute stats
+ Path */*/connectionPools/${1}/statistics/WaitTime/totalTime
+
+ BaseMBean WebSphere:j2eeType=JDBCResource,name=${0},mbeanIdentifier=${4:*},*
+ BaseAttribute stats
+ BasePath */*/connectionPools/${1}/statistics/WaitTime/count
+
+ Critical ${2:10000}
+ Warning ${3:5000}
+
+ Label $1: %2.2q ms ∅ waiting time (%v ms total for %b requests)
+ Name jdbc-$0-average-wait-time
+</Check>
+
+# Check for the number of rolled back transactions
+#
+# $0: Part of the MBean identifier
+# $1: Critical as rollback count / minute
+# $2: Warning as rollback count / minute
+<Check was_transaction_rollback_count>
+ Use was_transaction_count($0,"RolledbackCount",$1,$2)
+</Check>
+
+# Check for the number of active transactions
+#
+# $0: Part of the MBean identifier
+# $1: Critical as rollback count / minute
+# $2: Warning as rollback count / minute
+<Check was_transaction_active_count>
+ Use was_transaction_count($0,"ActiveCount",$1,$2)
+</Check>
+
+# Base-Check for the number of transactions
+#
+# $0: Part of the MBean identifier
+# $1: Attribute name
+# $2: Critical as rollback count / minute
+# $3: Warning as rollback count / minute
+<Check was_transaction_count>
+ MBean WebSphere:type=TransactionService,mbeanIdentifier=*${0}*,*
+ Attribute stats
+ Path */*/statistics/${1}/count
+ Delta 60
+
+ Critical ${2:10}
+ Warning ${3:5}
+
+ Label $0 : %2.2q ${1} / minute
+ Name $1-$0-transaction
+</Check>
+
diff --git a/config/websphere/jms.cfg b/config/websphere/jms.cfg
new file mode 100644
index 0000000..62e2d25
--- /dev/null
+++ b/config/websphere/jms.cfg
@@ -0,0 +1,44 @@
+# =======================================================
+# WebSphere JMS checks
+
+# Check the number of message in a queue
+#
+# $0: Queue Name
+# $1: Critical Threshold (default: 10)
+# $2: Warning Threshold (default: 5)
+<Check was_jms_depth>
+ MBean WebSphere:type=SIBQueuePoint,name=${0},*
+ Attribute depth
+
+ # Messages Thresshold
+ Critical ${1:10}
+ Warning ${2:5}
+
+ Label %v messages in queue ${0}
+ Name jms-{0}-queue
+</Check>
+
+# PMI metrics available over UI but not still via JMX ? -->
+
+# Queues.QueueStats.LocalProducerAttachesCount
+# Queues.QueueStats.LocalProducerCount
+# Queues.QueueStats.LocalConsumerAttachesCount
+# Queues.QueueStats.LocalConsumerCount
+# Queues.QueueStats.TotalMessagesProducedCount
+# Queues.QueueStats.BestEffortNonPersistentMessagesProducedCount
+# Queues.QueueStats.ExpressNonPersistentMessagesProducedCount
+# Queues.QueueStats.ReliableNonPersistentMessagesProducedCount
+# Queues.QueueStats.ReliablePersistentMessagesProducedCount
+# Queues.QueueStats.AssuredPersistentMessagesProducedCount
+# Queues.QueueStats.TotalMessagesConsumedCount
+# Queues.QueueStats.BestEffortNonPersistentMessagesConsumedCount
+# Queues.QueueStats.ExpressNonPersistentMessagesConsumedCount
+# Queues.QueueStats.ReliableNonPersistentMessagesConsumedCount
+# Queues.QueueStats.ReliablePersistentMessagesConsumedCount
+# Queues.QueueStats.AssuredPersistentMessagesConsumedCount
+# Queues.QueueStats.ReportEnabledMessagesExpiredCount
+# Queues.QueueStats.AggregateMessageWaitTime
+# Queues.QueueStats.LocalMessageWaitTime
+# Queues.QueueStats.LocalOldestMessageAge
+# Queues.QueueStats.AvailableMessageCount
+# Queues.QueueStats.UnavailableMessageCount \ No newline at end of file
diff --git a/config/websphere/threads.cfg b/config/websphere/threads.cfg
new file mode 100644
index 0000000..ad78a57
--- /dev/null
+++ b/config/websphere/threads.cfg
@@ -0,0 +1,45 @@
+# ============================================
+# Thread Pool Checks
+
+# Generic Thread-Pool Check for the size of a Thread-Pool
+#
+# $0: Name of ThreadPool (z.B. "WebContainer")
+# $1: Critical (default: 90%)
+# $2: Warning (default: 80%)
+<Check was_thread_pool_size>
+ Use was_thread_pool($0,'PoolSize',$1,$2)
+ Label $0: %2.2r% threads used (%v / %b)
+</Check>
+
+# Generic Thread-Pool Check for the number of active threads
+# within the thread pool
+#
+# $0: Name of ThreadPool (z.B. "WebContainer")
+# $1: Critical (default: 90%)
+# $2: Warning (default: 80%)
+<Check was_thread_pool_active>
+ Use was_thread_pool($0,'ActiveCount',$1,$2,)
+ Label $0: %2.2r% active threads (%v / %b)
+</Check>
+
+# Base Check for thread-pools checks
+# $0: Name of ThreadPool (z.B. "WebContainer")
+# $1: Attribute (PoolSize or ActiveCount)
+# $2: Critical (default: 90%)
+# $3: Warning (default: 80%)
+<Check was_thread_pool>
+ MBean WebSphere:name=${0},type=ThreadPool,*
+ Attribute stats
+ Path */*/statistics/${1}/current
+
+ BaseMBean WebSphere:name=${0},type=ThreadPool,*
+ BaseAttribute stats
+ BasePath */*/statistics/${1}/upperBound
+
+ Critical ${2:90}
+ Warning ${3:80}
+
+ Label = ${0}: %.2r% Threads [${1}] (%v / %b)
+ Name = ${0}-${1}-threadpool
+</Check>
+
diff --git a/config/wildfly.cfg b/config/wildfly.cfg
new file mode 100644
index 0000000..28218b2
--- /dev/null
+++ b/config/wildfly.cfg
@@ -0,0 +1,134 @@
+# Wildfly (JBoss AS 8) specific checks
+# ========================================================
+
+include "common.cfg"
+
+# Wildfly use Undertow instead of Tomcat as its servlet container,
+# webapp specific metrics changed completely.
+
+# Requests per minute for a servlet within a deployed war
+# $0: Web-Module name (i.e. the WAR file name)
+# $1: Servlet name
+# $2: Critical (optional)
+# $3: Warning (optional)
+# $4: Descriptive name (optional)
+<Check wildfly_war_servlet_requests>
+ MBean = jboss.as.expr:subsystem=undertow,deployment=$0,servlet=$1,*
+ Use = count_per_minute("requests")
+ Attribute = requestCount
+ Name = ${4:request}
+ Critical = ${2:6000}
+ Warning = ${3:5000}
+</Check>
+
+# Average request processing time for a servlet within a deployed war
+# $0: Web-Module name (i.e. the WAR file name)
+# $1: Servlet name
+# $2: Critical (optional)
+# $3: Warning (optional)
+# $4: Descriptive name (optional)
+<Check wildfly_war_servlet_request_time>
+ Value = jboss.as.expr:subsystem=undertow,deployment=$0,servlet=$1,*/totalRequestTime
+ Base = jboss.as.expr:subsystem=undertow,deployment=$0,servlet=$1,*/requestCount
+ Delta
+ Label = $0 : $1 : %2.2f ms average request time
+ Name = ${4:$0-$1-request-time}
+ Critical = ${2:6000}
+ Warning = ${3:5000}
+</Check>
+
+# Requests per minute for a servlet, deployed as part of an ear
+# $0: EAR Module (name of the EAR file)
+# $1: Web-Module name (i.e. the WAR file name within the EAR)
+# $2: Servlet name
+# $3: Critical (optional)
+# $4: Warning (optional)
+# $5: Descriptive name (optional)
+<Check wildfly_ear_servlet_requests>
+ MBean = jboss.as.expr:subsystem=undertow,deployment=$0,subdeployment=$1,servlet=$2,*
+ Use = count_per_minute("requests")
+ Attribute = requestCount
+ Name = ${5:request}
+ Critical = ${3:6000}
+ Warning = ${2:5000}
+</Check>
+
+# Average request processing time, deployed as part of an ear
+# $0: EAR Module name (i.e. the EAR file name)
+# $1: Web-Module name (i.e. the WAR file name)
+# $2: Servlet name
+# $3: Critical (optional)
+# $4: Warning (optional)
+<Check wildfly_ear_servlet_request_time>
+ Value = jboss.as.expr:subsystem=undertow,deployment=$0,subdeployment=$1,servlet=$2,*/totalRequestTime
+ Base = jboss.as.expr:subsystem=undertow,deployment=$0,subdeployment=$1,servlet=$2,*/requestCount
+ Delta
+ Label = $0 : $1 : $2 : %2.2f ms average request time
+ Name = $0-$1-$2-request
+ Critical = ${3:6000}
+ Warning = ${4:5000}
+</Check>
+
+# Check whether the webapplications deployment is in status OK
+# $0: Web-Module name (i.e. the WAR file name)
+# $1: Name (optional)
+<Check wildfly_deployment_status>
+ Value = jboss.as.expr:deployment=$0/status
+ String = 1
+ Critical = !OK
+ Label = $0 status
+ Name = ${1:status}
+</Check>
+
+# Check whether a webapplication is enabled
+# $0: Web-Module name (i.e. the WAR file name)
+# $1: Name (optional)
+<Check wildfly_deployment_enabled>
+ Value = jboss.as.expr:deployment=$0/enabled
+ String = 1
+ Critical = !true
+ Label = $0 enabled
+ Name = ${1:enabled}
+</Check>
+
+# Check number of active session for a webapp, deployed as a war
+# $0: Web-Module name (i.e. the WAR file name)
+# $1: Critical (optional) (absolute number of active sessions allowed)
+# $2: Warning (optional) (absolute number of active sessions allowed)
+# $3: Descriptive name (optional)
+<Check wildfly_war_webapp_active_sessions>
+ Value = jboss.as.expr:subsystem=undertow,deployment=$0/activeSessions
+ Label = %v active sessions
+ Name = ${3:active sessions}
+ Critical = ${1:1000}
+ Warning = ${2:800}
+</Check>
+
+# Check number of active session for a webapp, deployed as part of an ear
+# $0: EAR Module (name of the EAR file)
+# $1: Web-Module name (i.e. the WAR file name within the ear)
+# $2: Critical (optional) (absolute number of active sessions allowed)
+# $3: Warning (optional) (absolute number of active sessions allowed)
+# $4: Descriptive name (optional)
+<Check wildfly_ear_webapp_active_sessions>
+ Value = jboss.as.expr:subsystem=undertow,deployment=$0,subdeployment=$1/activeSessions
+ Label = %v active sessions
+ Name = ${4:active sessions}
+ Critical = ${2:1000}
+ Warning = ${3:800}
+</Check>
+
+# Check for available database connections
+# $0: Name of datasource (e.g. "ExampleDS")
+# $1: Critical value (optional, default: 1)
+# $2: Warning value (optional, default: 5)
+# $3: Name (optional)
+<Check wildfly_datasource_connections>
+ Value = jboss.as.expr:data-source=${0},statistics=pool,subsystem=datasources/AvailableCount
+ Name = ${3:dbpool_available}
+ Label = %.2v DB connections available
+ Critical = ${1:1:}
+ Warning = ${2:5:}
+</Check>
+
+
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..263b61c
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,42 @@
+# ==================================================
+# Dockerfile for jmx4perl Tools
+# ==================================================
+FROM alpine:3.2
+
+ENV JMX4PERL_VERSION 1.12
+
+RUN apk add --update \
+ build-base \
+ wget \
+ perl \
+ perl-dev \
+ readline \
+ readline-dev \
+ ncurses \
+ ncurses-dev \
+ libxml2-dev \
+ expat-dev \
+ gnupg1 \
+ && cpan App::cpanminus < /dev/null \
+ && cpanm install -n Term::ReadKey \
+ && cpanm install \
+ JSON::XS \
+ Term::ReadLine::Gnu \
+ && cpanm install ROLAND/jmx4perl-${JMX4PERL_VERSION}.tar.gz \
+ && rm -rf /var/cache/apk/* \
+ && apk del \
+ build-base \
+ perl-dev \
+ readline-dev \
+ ncurses-dev \
+ libxml2-dev \
+ expat-dev \
+ && mkdir /jolokia
+
+WORKDIR /jolokia
+VOLUME /jolokia
+
+CMD [ "jmx4perl", "--version" ]
+
+
+
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..88576ad
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,43 @@
+## Jmx4Perl Tools 1.12
+
+This Docker image is intended to provided an easy access to the
+[Jmx4Perl](http://www.jmx4perl.org) Tools, i.e.
+
+* **[jmx4perl](http://search.cpan.org/~roland/jmx4perl/scripts/jmx4perl)** -- Command line
+* **[j4psh](http://search.cpan.org/~roland/jmx4perl/scripts/j4psh)**
+ -- JMX shell
+* **[jolokia](http://search.cpan.org/~roland/jmx4perl/scripts/jolokia)**
+ -- Jolokia agent management tool
+* **[check_jmx4perl](http://search.cpan.org/~roland/jmx4perl/scripts/check_jmx4perl)**
+ -- Send Jolokia Requests from the command line
+
+Please refer to the upstream tool documentation for details.
+
+Examples:
+
+````shell
+# Get some basic information of the server
+docker run --rm -it jolokia/jmx4perl jmx4perl http://localhost:8080/jolokia
+
+# Download the current jolokia.war agent
+docker run --rm -it -v `pwd`:/jolokia jolokia/jmx4perl jolokia
+
+# Start an interactive JMX shell, server "tomcat" is defined in ~/.j4p/jmx4perl.config
+docker run --rm -it -v ~/.j4p:/root/.j4p jolokia/jmx4perl j4psh tomcat
+````
+
+If you put your server definitions into `~/.j4p/jmx4perl.config` you
+can use them by volume mounting them with `-v
+~/.j4p:/root/.j4p`. For the management tool `jolokia` it is
+recommended to mount the local directory with `-v $(pwd):/jolokia` so
+that downloaded artefacts are stored in the current host directory
+
+To simplify the usage, the following shell setup can be used:
+
+````shell
+function j4p_docker {
+ alias jmx4perl="docker run --rm -it -v ~/.j4p:/root/.j4p jolokia/jmx4perl jmx4perl"
+ alias jolokia="docker run --rm -it -v `pwd`:/jolokia jolokia/jmx4perl jolokia"
+ alias j4psh="docker run --rm -it -v ~/.j4p:/root/.j4p jolokia/jmx4perl j4psh"
+}
+````
diff --git a/examples/jsr77.pl b/examples/jsr77.pl
new file mode 100755
index 0000000..e4cdf03
--- /dev/null
+++ b/examples/jsr77.pl
@@ -0,0 +1,158 @@
+#!/usr/bin/perl
+
+use JMX::Jmx4Perl;
+use strict;
+use Data::Dumper;
+use Getopt::Std;
+
+my %opts;
+getopts('s',\%opts);
+
+
+my $url = $ARGV[0] || die "No url given\n";
+
+my $jmx = JMX::Jmx4Perl->new(url => $url,verbose => 0);
+
+my $MODULE_HANDLER = init_handler($jmx);
+my %VISITED = ();
+
+my $product = $jmx->product;
+print "Product: ",$product->name," ",$product->version,"\n";
+print "JSR77 : ",$product->jsr77 ? "Yes" : "No","\n\n";
+
+my $domains = $jmx->search("*:j2eeType=J2EEDomain,*");
+$domains = [ "(none)" ] unless $domains;
+# Special fix for geronimo which seems to have a problem with properly spelling
+# the domain name
+#push @$domains,"Geronimo:j2eeType=J2EEDomain,name=Geronimo" if grep { /^geronimo:/ } @$domains;
+for my $d (@{$domains || []}) {
+ my $dn = $d eq "(none)" ? "*" : _print(1,$d,"Domain");
+ my $servers = $jmx->search("$dn:j2eeType=J2EEServer,*");
+ if (!$servers && $d eq "(none)") {
+ # That's probably not a real jsr77 container
+ # We are looking up all J2EEObject on our own without server and domain
+ my $objects = [ grep { /j2eeType/ } @{$jmx->search("*:*")} ];
+ print_modules(1,$objects);
+ } elsif (!$servers) {
+ print " == No servers defined for domain $dn ==\n";
+ } else {
+ for my $s (@{$servers || []}) {
+ my $sn = _print(2,$s,"Server");
+ for my $o (qw(deployedObjects resources javaVMs)) {
+ my $objects = $jmx->get_attribute($s,$o);
+ print_modules(3,$objects);
+ }
+ }
+
+ }
+ print "\n";
+}
+
+# Special JBoss handling, since it seems than deployed WARs (WebModules)
+# don't appear below a server but stand on their own (despite the rules
+# layed out in JSR77)
+if ($product->id eq "jboss" || $product->id eq "weblogic") {
+ my $web_modules = $jmx->search("*:j2eeType=WebModule,*");
+ if ($web_modules) {
+ print "\n=============================================\nJBoss WebModules:\n";
+ my $new = [ grep { !$VISITED{$_} } @$web_modules ];
+ print_modules(1,$new);
+ }
+}
+
+sub init_handler {
+ my $jmx = shift;
+ return {
+ "J2EEApplication" => "modules",
+ "AppClientModule" => 0,
+ "ResourceAdapterModule" => "resourceAdapters",
+ "WebModule" => "servlets",
+ "Servlet" => 0,
+ "EJBModule" => "ejbs",
+ "MessageDrivenBean" => 0,
+ "EntityBean" => 0,
+ "StatelessSessionBean" => 0,
+ "StatefulSessionBean" => 0,
+ "JCAResource" => "connectionFactories",
+ "JCAConnectionFactory" => "managedConnectionFactory",
+ "JCAManagedConnectionFactory" => 0,
+ "JavaMailResource" => 0,
+ "JDBCResource" => "jdbcDataSources",
+ "JDBCDataSource" => "jdbcDriver",
+ "JDBCDriver" => 0,
+ "JMSResource" => 0,
+ "JNDIResource" => 0,
+ "JTAResource" => 0,
+ "RMI_IIOPResource" => 0,
+ "URLResource" => 0,
+ "JVM" => sub {
+ my ($l,$mod) = @_;
+ print " ",
+ join(", ",map { $jmx->get_attribute($mod,$_) } qw(javaVendor javaVersion node)),"\n";
+ },
+ # JBoss specific:
+ "ServiceModule" => 0,
+ "MBean" => 0
+ };
+}
+
+sub print_modules {
+ my ($l,$objects) = @_;
+ for my $k (sort keys %$MODULE_HANDLER) {
+ my @mods = grep { $_ =~ /j2eeType=$k/ } @$objects;
+ if (@mods) {
+ my $handler = $MODULE_HANDLER->{$k};
+ for my $mod (@mods) {
+ _print($l,$mod);
+ if (ref($handler) eq "CODE") {
+ $handler->($l,$mod);
+ } elsif ($handler && !ref($handler)) {
+ my $modules = $jmx->get_attribute($mod,$handler);
+ if ($modules) {
+ $modules = ref($modules) eq "ARRAY" ? $modules : [ $modules ];
+ # Fix for Jonas 4.1.2 with jetty, which includes the
+ # WebModule itself in the list of contained Servlets
+ $modules = [ grep { $_ !~ /j2eeType=$k/} @$modules ];
+ print_modules($l+1,$modules) if scalar(@$modules);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+sub _print {
+ my ($i,$s,$t) = @_;
+ $VISITED{$s} = $s;
+ my $n = extract_name($s);
+ unless ($t) {
+ $t = $1 if $s =~ /j2eeType=(\w+)/;
+ }
+ my $can_stat = check_for_statistics($s);
+ print " " x $i,$t,": ",$n,($can_stat ? " [S] " : ""),"\n";
+ print " " x $i," " x length($t)," ",$s,"\n";
+ if ($opts{s} && $can_stat) {
+ eval {
+ my $ret = $jmx->get_attribute($s,"stats");
+ print Dumper($ret);
+ };
+ }
+ return $n;
+}
+
+sub check_for_statistics {
+ my $mbean = shift;
+ my $ret;
+ eval {
+ $ret = $jmx->get_attribute($mbean,"statisticsProvider");
+ };
+ return [email protected] ? undef : lc($ret) eq "true";
+}
+
+sub extract_name {
+ my $s = shift;
+ $s =~ /.*:.*name=([^,]+)/;
+ return $1;
+}
diff --git a/examples/memory.pl b/examples/memory.pl
new file mode 100644
index 0000000..29ab958
--- /dev/null
+++ b/examples/memory.pl
@@ -0,0 +1,11 @@
+#!/usr/bin/perl
+use JMX::Jmx4Perl;
+use strict;
+my $jmx = new JMX::Jmx4Perl(url => "http://localhost:8080/jolokia");
+my $memory = $jmx->get_attribute("java.lang:type=Memory","HeapMemoryUsage");
+my ($used,$max) = ($memory->{used},$memory->{max});
+if ($memory->{used} / $memory->{max} > 0.9) {
+ print "Memory exceeds 90% (used: $used / max: $max = ",int($used * 100 / $max),"%)\n";
+ system("/etc/init.d/tomcat restart");
+ sleep(120);
+}
diff --git a/examples/memory.sh b/examples/memory.sh
new file mode 100644
index 0000000..fab9853
--- /dev/null
+++ b/examples/memory.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+base_url="http://localhost:9090/jolokia"
+memory_url="${base_url}/read/java.lang:type=Memory/HeapMemoryUsage"
+used=`wget -q -O - "${memory_url}/used" | sed 's/^.*"value":"\([0-9]*\)".*$/\1/'`
+max=`wget -q -O - "${memory_url}/max" | sed 's/^.*"value":"\([0-9]*\)".*$/\1/'`
+usage=$((${used}*100/${max}))
+if [ $usage -gt 5 ]; then
+ echo "Memory exceeds 80% (used: $used / max: $max = ${usage}\%)";
+ exit 1;
+else
+ exit 0;
+fi
diff --git a/examples/remote.pl b/examples/remote.pl
new file mode 100644
index 0000000..fa51dfc
--- /dev/null
+++ b/examples/remote.pl
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+use JMX::Jmx4Perl;
+use JMX::Jmx4Perl::Request;
+use JMX::Jmx4Perl::Alias;
+use Data::Dumper;
+use Time::HiRes qw(gettimeofday tv_interval);
+my $jmx = new JMX::Jmx4Perl(url => "http://localhost:8888/jolokia-proxy",
+ target => {
+ url => "service:jmx:rmi:///jndi/rmi://bhut:9999/jmxrmi",
+ env => {
+ user => "monitorRole",
+ password => "consol",
+ }
+ }
+ );
+my $req1 = new JMX::Jmx4Perl::Request(READ,{
+ mbean => "java.lang:type=Memory",
+ attribute => "HeapMemoryUsage",
+ }
+ );
+my $req2 = new JMX::Jmx4Perl::Request(LIST);
+my $req3 = new JMX::Jmx4Perl::Request(READ,{
+ mbean => "jboss.system:type=ServerInfo",
+ attribute => "HostAddress"
+ }
+ );
+my $t0 = [gettimeofday];
+my @resp = $jmx->request($req3);
+print "Duration: ",tv_interval($t0,[gettimeofday]),"\n";
+print Dumper(@resp);
diff --git a/examples/threadDump.pl b/examples/threadDump.pl
new file mode 100755
index 0000000..83ec9d0
--- /dev/null
+++ b/examples/threadDump.pl
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+
+use Getopt::Long;
+use JMX::Jmx4Perl;
+use Data::Dumper;
+use strict;
+use warnings;
+
+=head1 NAME
+
+threadDump.pl - Print a thread dump of an JEE Server
+
+=head1 SYNOPSIS
+
+ threadDumpl.pl -f org.jmx4perl http://localhost:8080/j4p
+
+ http-0.0.0.0-8080-1 (RUNNABLE):
+ ....
+ sun.management.ThreadImpl.dumpThreads0(ThreadImpl.java:unknown)
+ org.jmx4perl.handler.ExecHandler.doHandleRequest(ExecHandler.java:77)
+ org.jmx4perl.handler.RequestHandler.handleRequest(RequestHandler.java:89)
+ org.jmx4perl.MBeanServerHandler.dispatchRequest(MBeanServerHandler.java:73)
+ org.jmx4perl.AgentServlet.callRequestHandler(AgentServlet.java:205)
+ org.jmx4perl.AgentServlet.handle(AgentServlet.java:152)
+ org.jmx4perl.AgentServlet.doGet(AgentServlet.java:129)
+ ....
+
+=head1 DESCRIPTION
+
+For JEE Server running with Java 6, this simple script prints out a thread
+dump, possibly filtered by package name. This is done by executing the MBean
+C<java.lang:type=Threading>'s operation C<dumpAllThreads>.
+
+=cut
+
+my %opts = ();
+my $result = GetOptions(\%opts,
+ "user|u=s","password|p=s",
+ "proxy=s",
+ "proxy-user=s","proxy-password=s",
+ "filter|f=s",
+ "verbose|v!",
+ "help|h!" => sub { Getopt::Long::HelpMessage() }
+ );
+
+my $url = $ARGV[0] || die "No URL to j4p agent given\n";
+my $jmx = new JMX::Jmx4Perl(url => $url,user => $opts{user},password => $opts{password},
+ proxy => $opts{proxy}, proxy_user => $opts{"proxy-user"});
+
+my $dump;
+eval {
+ $dump = $jmx->execute("java.lang:type=Threading","dumpAllThreads","false","false");
+};
+die "Cannot execute thread dump. Remember, $0 works only with Java >= 1.6\[email protected]\n" if [email protected];
+
+my @filters = split ",",$opts{filter} if $opts{filter};
+for my $thread (@$dump) {
+ print "-" x 75,"\n" if print_thread($thread);;
+}
+
+sub print_thread {
+ my $thread = shift;
+ my $st = get_stacktrace($thread->{stackTrace});
+ if ($st) {
+ print $thread->{threadName}," (",$thread->{threadState},"):\n";
+ print $st;
+ return 1;
+ } else {
+ return undef;
+ }
+}
+
+sub get_stacktrace {
+ my $trace = shift;
+ my $ret = "";
+ my $found = 0;
+ my $flag = 1;
+ my $last_line;
+ for my $l (@$trace) {
+ my $class = $l->{className};
+ if ([email protected] || grep { $class =~ /^\Q$_\E/ } @filters) {
+ $ret .= $last_line if ($last_line && !$found);
+ $ret .= format_stack_line($l);
+ $found = 1;
+ $flag = 1;
+ } elsif ($flag) {
+ $flag = 0;
+ $ret .= " ....\n";
+ $last_line = format_stack_line($l);
+ }
+ }
+ return $found ? $ret : undef;
+}
+
+sub format_stack_line {
+ my $l = shift;
+ my $ret = " ".$l->{className}.".".$l->{methodName}."(".$l->{fileName}.":";
+ $ret .= $l->{lineNumber} > 0 ? $l->{lineNumber} : "unknown";
+ $ret .= ")\n";
+ return $ret;
+
+}
diff --git a/inc/Module-Build/Module/Build.pm b/inc/Module-Build/Module/Build.pm
new file mode 100644
index 0000000..ce0cfae
--- /dev/null
+++ b/inc/Module-Build/Module/Build.pm
@@ -0,0 +1,1098 @@
+package Module::Build;
+
+# This module doesn't do much of anything itself, it inherits from the
+# modules that do the real work. The only real thing it has to do is
+# figure out which OS-specific module to pull in. Many of the
+# OS-specific modules don't do anything either - most of the work is
+# done in Module::Build::Base.
+
+use strict;
+use File::Spec ();
+use File::Path ();
+use File::Basename ();
+
+use Module::Build::Base;
+
+use vars qw($VERSION @ISA);
[email protected] = qw(Module::Build::Base);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+
+# Okay, this is the brute-force method of finding out what kind of
+# platform we're on. I don't know of a systematic way. These values
+# came from the latest (bleadperl) perlport.pod.
+
+my %OSTYPES = qw(
+ aix Unix
+ bsdos Unix
+ dgux Unix
+ dragonfly Unix
+ dynixptx Unix
+ freebsd Unix
+ linux Unix
+ haiku Unix
+ hpux Unix
+ irix Unix
+ darwin Unix
+ machten Unix
+ midnightbsd Unix
+ mirbsd Unix
+ next Unix
+ openbsd Unix
+ netbsd Unix
+ dec_osf Unix
+ nto Unix
+ svr4 Unix
+ svr5 Unix
+ sco_sv Unix
+ unicos Unix
+ unicosmk Unix
+ solaris Unix
+ sunos Unix
+ cygwin Unix
+ os2 Unix
+ interix Unix
+ gnu Unix
+ gnukfreebsd Unix
+ nto Unix
+
+ dos Windows
+ MSWin32 Windows
+
+ os390 EBCDIC
+ os400 EBCDIC
+ posix-bc EBCDIC
+ vmesa EBCDIC
+
+ MacOS MacOS
+ VMS VMS
+ VOS VOS
+ riscos RiscOS
+ amigaos Amiga
+ mpeix MPEiX
+ );
+
+# Inserts the given module into the @ISA hierarchy between
+# Module::Build and its immediate parent
+sub _interpose_module {
+ my ($self, $mod) = @_;
+ eval "use $mod";
+
+ no strict 'refs';
+ my $top_class = $mod;
+ while (@{"${top_class}::ISA"}) {
+ last if ${"${top_class}::ISA"}[0] eq $ISA[0];
+ $top_class = ${"${top_class}::ISA"}[0];
+ }
+
+ @{"${top_class}::ISA"} = @ISA;
+ @ISA = ($mod);
+}
+
+if (grep {-e File::Spec->catfile($_, qw(Module Build Platform), $^O) . '.pm'} @INC) {
+ __PACKAGE__->_interpose_module("Module::Build::Platform::$^O");
+
+} elsif (exists $OSTYPES{$^O}) {
+ __PACKAGE__->_interpose_module("Module::Build::Platform::$OSTYPES{$^O}");
+
+} else {
+ warn "Unknown OS type '$^O' - using default settings\n";
+}
+
+sub os_type { $OSTYPES{$^O} }
+
+sub is_vmsish { return ((os_type() || '') eq 'VMS') }
+sub is_windowsish { return ((os_type() || '') eq 'Windows') }
+sub is_unixish { return ((os_type() || '') eq 'Unix') }
+
+1;
+
+__END__
+
+=for :stopwords
+bindoc binhtml destdir distcheck distclean distdir distmeta distsign disttest
+fakeinstall html installdirs installsitebin installsitescript installvendorbin
+installvendorscript libdoc libhtml pardist ppd ppmdist realclean skipcheck
+testall testcover testdb testpod testpodcoverage versioninstall
+
+=head1 NAME
+
+Module::Build - Build and install Perl modules
+
+
+=head1 SYNOPSIS
+
+Standard process for building & installing modules:
+
+ perl Build.PL
+ ./Build
+ ./Build test
+ ./Build install
+
+Or, if you're on a platform (like DOS or Windows) that doesn't require
+the "./" notation, you can do this:
+
+ perl Build.PL
+ Build
+ Build test
+ Build install
+
+
+=head1 DESCRIPTION
+
+C<Module::Build> is a system for building, testing, and installing
+Perl modules. It is meant to be an alternative to
+C<ExtUtils::MakeMaker>. Developers may alter the behavior of the
+module through subclassing in a much more straightforward way than
+with C<MakeMaker>. It also does not require a C<make> on your system
+- most of the C<Module::Build> code is pure-perl and written in a very
+cross-platform way. In fact, you don't even need a shell, so even
+platforms like MacOS (traditional) can use it fairly easily. Its only
+prerequisites are modules that are included with perl 5.6.0, and it
+works fine on perl 5.005 if you can install a few additional modules.
+
+See L<"MOTIVATIONS"> for more comparisons between C<ExtUtils::MakeMaker>
+and C<Module::Build>.
+
+To install C<Module::Build>, and any other module that uses
+C<Module::Build> for its installation process, do the following:
+
+ perl Build.PL # 'Build.PL' script creates the 'Build' script
+ ./Build # Need ./ to ensure we're using this "Build" script
+ ./Build test # and not another one that happens to be in the PATH
+ ./Build install
+
+This illustrates initial configuration and the running of three
+'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
+
+
+You can run the 'help' action for a complete list of actions.
+
+
+=head1 GUIDE TO DOCUMENTATION
+
+The documentation for C<Module::Build> is broken up into three sections:
+
+=over
+
+=item General Usage (L<Module::Build>)
+
+This is the document you are currently reading. It describes basic
+usage and background information. Its main purpose is to assist the
+user who wants to learn how to invoke and control C<Module::Build>
+scripts at the command line.
+
+=item Authoring Reference (L<Module::Build::Authoring>)
+
+This document describes the structure and organization of
+C<Module::Build>, and the relevant concepts needed by authors who are
+writing F<Build.PL> scripts for a distribution or controlling
+C<Module::Build> processes programmatically.
+
+=item API Reference (L<Module::Build::API>)
+
+This is a reference to the C<Module::Build> API.
+
+=item Cookbook (L<Module::Build::Cookbook>)
+
+This document demonstrates how to accomplish many common tasks. It
+covers general command line usage and authoring of F<Build.PL>
+scripts. Includes working examples.
+
+=back
+
+
+=head1 ACTIONS
+
+There are some general principles at work here. First, each task when
+building a module is called an "action". These actions are listed
+above; they correspond to the building, testing, installing,
+packaging, etc., tasks.
+
+Second, arguments are processed in a very systematic way. Arguments
+are always key=value pairs. They may be specified at C<perl Build.PL>
+time (i.e. C<perl Build.PL destdir=/my/secret/place>), in which case
+their values last for the lifetime of the C<Build> script. They may
+also be specified when executing a particular action (i.e.
+C<Build test verbose=1>), in which case their values last only for the
+lifetime of that command. Per-action command line parameters take
+precedence over parameters specified at C<perl Build.PL> time.
+
+The build process also relies heavily on the C<Config.pm> module.
+If the user wishes to override any of the
+values in C<Config.pm>, she may specify them like so:
+
+ perl Build.PL --config cc=gcc --config ld=gcc
+
+The following build actions are provided by default.
+
+=over 4
+
+=item build
+
+[version 0.01]
+
+If you run the C<Build> script without any arguments, it runs the
+C<build> action, which in turn runs the C<code> and C<docs> actions.
+
+This is analogous to the C<MakeMaker> I<make all> target.
+
+=item clean
+
+[version 0.01]
+
+This action will clean up any files that the build process may have
+created, including the C<blib/> directory (but not including the
+C<_build/> directory and the C<Build> script itself).
+
+=item code
+
+[version 0.20]
+
+This action builds your code base.
+
+By default it just creates a C<blib/> directory and copies any C<.pm>
+and C<.pod> files from your C<lib/> directory into the C<blib/>
+directory. It also compiles any C<.xs> files from C<lib/> and places
+them in C<blib/>. Of course, you need a working C compiler (probably
+the same one that built perl itself) for the compilation to work
+properly.
+
+The C<code> action also runs any C<.PL> files in your F<lib/>
+directory. Typically these create other files, named the same but
+without the C<.PL> ending. For example, a file F<lib/Foo/Bar.pm.PL>
+could create the file F<lib/Foo/Bar.pm>. The C<.PL> files are
+processed first, so any C<.pm> files (or other kinds that we deal
+with) will get copied correctly.
+
+=item config_data
+
+[version 0.26]
+
+...
+
+=item diff
+
+[version 0.14]
+
+This action will compare the files about to be installed with their
+installed counterparts. For .pm and .pod files, a diff will be shown
+(this currently requires a 'diff' program to be in your PATH). For
+other files like compiled binary files, we simply report whether they
+differ.
+
+A C<flags> parameter may be passed to the action, which will be passed
+to the 'diff' program. Consult your 'diff' documentation for the
+parameters it will accept - a good one is C<-u>:
+
+ ./Build diff flags=-u
+
+=item dist
+
+[version 0.02]
+
+This action is helpful for module authors who want to package up their
+module for source distribution through a medium like CPAN. It will create a
+tarball of the files listed in F<MANIFEST> and compress the tarball using
+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
+C<tar> (and optional C<gzip>) parameter:
+
+ ./Build dist --tar C:\path\to\tar.exe --gzip C:\path\to\zip.exe
+
+=item distcheck
+
+[version 0.05]
+
+Reports which files are in the build directory but not in the
+F<MANIFEST> file, and vice versa. (See L<manifest> for details.)
+
+=item distclean
+
+[version 0.05]
+
+Performs the 'realclean' action and then the 'distcheck' action.
+
+=item distdir
+
+[version 0.05]
+
+Creates a "distribution directory" named C<$dist_name-$dist_version>
+(if that directory already exists, it will be removed first), then
+copies all the files listed in the F<MANIFEST> file to that directory.
+This directory is what the distribution tarball is created from.
+
+=item distmeta
+
+[version 0.21]
+
+Creates the F<META.yml> file that describes the distribution.
+
+F<META.yml> is a file containing various bits of I<metadata> about the
+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
+with Module::Build will be used to write the META.yml file, and this
+will most likely be fine.
+
+F<META.yml> file must also be listed in F<MANIFEST> - if it's not, a
+warning will be issued.
+
+The current version of the F<META.yml> specification can be found at
+L<http://module-build.sourceforge.net/META-spec-current.html>
+
+=item distsign
+
+[version 0.16]
+
+Uses C<Module::Signature> to create a SIGNATURE file for your
+distribution, and adds the SIGNATURE file to the distribution's
+MANIFEST.
+
+=item disttest
+
+[version 0.05]
+
+Performs the 'distdir' action, then switches into that directory and
+runs a C<perl Build.PL>, followed by the 'build' and 'test' actions in
+that directory.
+
+=item docs
+
+[version 0.20]
+
+This will generate documentation (e.g. Unix man pages and HTML
+documents) for any installable items under B<blib/> that
+contain POD. If there are no C<bindoc> or C<libdoc> installation
+targets defined (as will be the case on systems that don't support
+Unix manpages) no action is taken for manpages. If there are no
+C<binhtml> or C<libhtml> installation targets defined no action is
+taken for HTML documents.
+
+=item fakeinstall
+
+[version 0.02]
+
+This is just like the C<install> action, but it won't actually do
+anything, it will just report what it I<would> have done if you had
+actually run the C<install> action.
+
+=item help
+
+[version 0.03]
+
+This action will simply print out a message that is meant to help you
+use the build process. It will show you a list of available build
+actions too.
+
+With an optional argument specifying an action name (e.g. C<Build help
+test>), the 'help' action will show you any POD documentation it can
+find for that action.
+
+=item html
+
+[version 0.26]
+
+This will generate HTML documentation for any binary or library files
+under B<blib/> that contain POD. The HTML documentation will only be
+installed if the install paths can be determined from values in
+C<Config.pm>. You can also supply or override install paths on the
+command line by specifying C<install_path> values for the C<binhtml>
+and/or C<libhtml> installation targets.
+
+=item install
+
+[version 0.01]
+
+This action will use C<ExtUtils::Install> to install the files from
+C<blib/> into the system. See L<"INSTALL PATHS">
+for details about how Module::Build determines where to install
+things, and how to influence this process.
+
+If you want the installation process to look around in C<@INC> for
+other versions of the stuff you're installing and try to delete it,
+you can use the C<uninst> parameter, which tells C<ExtUtils::Install> to
+do so:
+
+ ./Build install uninst=1
+
+This can be a good idea, as it helps prevent multiple versions of a
+module from being present on your system, which can be a confusing
+situation indeed.
+
+=item manifest
+
+[version 0.05]
+
+This is an action intended for use by module authors, not people
+installing modules. It will bring the F<MANIFEST> up to date with the
+files currently present in the distribution. You may use a
+F<MANIFEST.SKIP> file to exclude certain files or directories from
+inclusion in the F<MANIFEST>. F<MANIFEST.SKIP> should contain a bunch
+of regular expressions, one per line. If a file in the distribution
+directory matches any of the regular expressions, it won't be included
+in the F<MANIFEST>.
+
+The following is a reasonable F<MANIFEST.SKIP> starting point, you can
+add your own stuff to it:
+
+ ^_build
+ ^Build$
+ ^blib
+ ~$
+ \.bak$
+ ^MANIFEST\.SKIP$
+ CVS
+
+See the L<distcheck> and L<skipcheck> actions if you want to find out
+what the C<manifest> action would do, without actually doing anything.
+
+=item manpages
+
+[version 0.28]
+
+This will generate man pages for any binary or library files under
+B<blib/> that contain POD. The man pages will only be installed if the
+install paths can be determined from values in C<Config.pm>. You can
+also supply or override install paths by specifying there values on
+the command line with the C<bindoc> and C<libdoc> installation
+targets.
+
+=item pardist
+
+[version 0.2806]
+
+Generates a PAR binary distribution for use with L<PAR> or L<PAR::Dist>.
+
+It requires that the PAR::Dist module (version 0.17 and up) is
+installed on your system.
+
+=item ppd
+
+[version 0.20]
+
+Build a PPD file for your distribution.
+
+This action takes an optional argument C<codebase> which is used in
+the generated PPD file to specify the (usually relative) URL of the
+distribution. By default, this value is the distribution name without
+any path information.
+
+Example:
+
+ ./Build ppd --codebase "MSWin32-x86-multi-thread/Module-Build-0.21.tar.gz"
+
+=item ppmdist
+
+[version 0.23]
+
+Generates a PPM binary distribution and a PPD description file. This
+action also invokes the C<ppd> action, so it can accept the same
+C<codebase> argument described under that action.
+
+This uses the same mechanism as the C<dist> action to tar & zip its
+output, so you can supply C<tar> and/or C<gzip> parameters to affect
+the result.
+
+=item prereq_data
+
+[version 0.32]
+
+This action prints out a Perl data structure of all prerequisites and the versions
+required. The output can be loaded again using C<eval()>. This can be useful for
+external tools that wish to query a Build script for prerequisites.
+
+=item prereq_report
+
+[version 0.28]
+
+This action prints out a list of all prerequisites, the versions required, and
+the versions actually installed. This can be useful for reviewing the
+configuration of your system prior to a build, or when compiling data to send
+for a bug report.
+
+=item pure_install
+
+[version 0.28]
+
+This action is identical to the C<install> action. In the future,
+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.
+
+=item realclean
+
+[version 0.01]
+
+This action is just like the C<clean> action, but also removes the
+C<_build> directory and the C<Build> script. If you run the
+C<realclean> action, you are essentially starting over, so you will
+have to re-create the C<Build> script again.
+
+=item retest
+
+[version 0.2806]
+
+This is just like the C<test> action, but doesn't actually build the
+distribution first, and doesn't add F<blib/> to the load path, and
+therefore will test against a I<previously> installed version of the
+distribution. This can be used to verify that a certain installed
+distribution still works, or to see whether newer versions of a
+distribution still pass the old regression tests, and so on.
+
+=item skipcheck
+
+[version 0.05]
+
+Reports which files are skipped due to the entries in the
+F<MANIFEST.SKIP> file (See L<manifest> for details)
+
+=item test
+
+[version 0.01]
+
+This will use C<Test::Harness> or C<TAP::Harness> to run any regression
+tests and report their results. Tests can be defined in the standard
+places: a file called C<test.pl> in the top-level directory, or several
+files ending with C<.t> in a C<t/> directory.
+
+If you want tests to be 'verbose', i.e. show details of test execution
+rather than just summary information, pass the argument C<verbose=1>.
+
+If you want to run tests under the perl debugger, pass the argument
+C<debugger=1>.
+
+If you want to have Module::Build find test files with different file
+name extensions, pass the C<test_file_exts> argument with an array
+of extensions, such as C<[qw( .t .s .z )]>.
+
+If you want test to be run by C<TAP::Harness>, rather than C<Test::Harness>,
+pass the argument C<tap_harness_args> as an array reference of arguments to
+pass to the TAP::Harness constructor.
+
+In addition, if a file called C<visual.pl> exists in the top-level
+directory, this file will be executed as a Perl script and its output
+will be shown to the user. This is a good place to put speed tests or
+other tests that don't use the C<Test::Harness> format for output.
+
+To override the choice of tests to run, you may pass a C<test_files>
+argument whose value is a whitespace-separated list of test scripts to
+run. This is especially useful in development, when you only want to
+run a single test to see whether you've squashed a certain bug yet:
+
+ ./Build test --test_files t/something_failing.t
+
+You may also pass several C<test_files> arguments separately:
+
+ ./Build test --test_files t/one.t --test_files t/two.t
+
+or use a C<glob()>-style pattern:
+
+ ./Build test --test_files 't/01-*.t'
+
+=item testall
+
+[version 0.2807]
+
+[Note: the 'testall' action and the code snippets below are currently
+in alpha stage, see
+L<"http://www.nntp.perl.org/group/perl.module.build/2007/03/msg584.html"> ]
+
+Runs the C<test> action plus each of the C<test$type> actions defined by
+the keys of the C<test_types> parameter.
+
+Currently, you need to define the ACTION_test$type method yourself and
+enumerate them in the test_types parameter.
+
+ my $mb = Module::Build->subclass(
+ code => q(
+ sub ACTION_testspecial { shift->generic_test(type => 'special'); }
+ sub ACTION_testauthor { shift->generic_test(type => 'author'); }
+ )
+ )->new(
+ ...
+ test_types => {
+ special => '.st',
+ author => ['.at', '.pt' ],
+ },
+ ...
+
+=item testcover
+
+[version 0.26]
+
+Runs the C<test> action using C<Devel::Cover>, generating a
+code-coverage report showing which parts of the code were actually
+exercised during the tests.
+
+To pass options to C<Devel::Cover>, set the C<$DEVEL_COVER_OPTIONS>
+environment variable:
+
+ DEVEL_COVER_OPTIONS=-ignore,Build ./Build testcover
+
+=item testdb
+
+[version 0.05]
+
+This is a synonym for the 'test' action with the C<debugger=1>
+argument.
+
+=item testpod
+
+[version 0.25]
+
+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.
+
+=item testpodcoverage
+
+[version 0.28]
+
+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.
+
+=item versioninstall
+
+[version 0.16]
+
+** Note: since C<only.pm> is so new, and since we just recently added
+support for it here too, this feature is to be considered
+experimental. **
+
+If you have the C<only.pm> module installed on your system, you can
+use this action to install a module into the version-specific library
+trees. This means that you can have several versions of the same
+module installed and C<use> a specific one like this:
+
+ use only MyModule => 0.55;
+
+To override the default installation libraries in C<only::config>,
+specify the C<versionlib> parameter when you run the C<Build.PL> script:
+
+ perl Build.PL --versionlib /my/version/place/
+
+To override which version the module is installed as, specify the
+C<versionlib> parameter when you run the C<Build.PL> script:
+
+ perl Build.PL --version 0.50
+
+See the C<only.pm> documentation for more information on
+version-specific installs.
+
+=back
+
+
+=head1 OPTIONS
+
+=head2 Command Line Options
+
+The following options can be used during any invocation of C<Build.PL>
+or the Build script, during any action. For information on other
+options specific to an action, see the documentation for the
+respective action.
+
+NOTE: There is some preliminary support for options to use the more
+familiar long option style. Most options can be preceded with the
+C<--> long option prefix, and the underscores changed to dashes
+(e.g. C<--use-rcfile>). Additionally, the argument to boolean options is
+optional, and boolean options can be negated by prefixing them with
+C<no> or C<no-> (e.g. C<--noverbose> or C<--no-verbose>).
+
+=over 4
+
+=item quiet
+
+Suppress informative messages on output.
+
+=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
+we're now running under is the same version that was initially invoked
+when building the distribution (i.e. when the C<Build.PL> script was
+first run). Use with caution.
+
+=back
+
+
+=head2 Default Options File (F<.modulebuildrc>)
+
+[version 0.28]
+
+When Module::Build starts up, it will look first for a file,
+F<$ENV{HOME}/.modulebuildrc>. If it's not found there, it will look
+in the the F<.modulebuildrc> file in the directories referred to by
+the environment variables C<HOMEDRIVE> + C<HOMEDIR>, C<USERPROFILE>,
+C<APPDATA>, C<WINDIR>, C<SYS$LOGIN>. If the file exists, the options
+specified there will be used as defaults, as if they were typed on the
+command line. The defaults can be overridden by specifying new values
+on the command line.
+
+The action name must come at the beginning of the line, followed by any
+amount of whitespace and then the options. Options are given the same
+as they would be on the command line. They can be separated by any
+amount of whitespace, including newlines, as long there is whitespace at
+the beginning of each continued line. Anything following a hash mark (C<#>)
+is considered a comment, and is stripped before parsing. If more than
+one line begins with the same action name, those lines are merged into
+one set of options.
+
+Besides the regular actions, there are two special pseudo-actions: the
+key C<*> (asterisk) denotes any global options that should be applied
+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
+
+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.
+
+
+=head1 INSTALL PATHS
+
+[version 0.19]
+
+When you invoke Module::Build's C<build> action, it needs to figure
+out where to install things. The nutshell version of how this works
+is that default installation locations are determined from
+F<Config.pm>, and they may be overridden by using the C<install_path>
+parameter. An C<install_base> parameter lets you specify an
+alternative installation root like F</home/foo>, and a C<destdir> lets
+you specify a temporary installation directory like F</tmp/install> in
+case you want to create bundled-up installable packages.
+
+Natively, Module::Build provides default installation locations for
+the following types of installable items:
+
+=over 4
+
+=item lib
+
+Usually pure-Perl module files ending in F<.pm>.
+
+=item arch
+
+"Architecture-dependent" module files, usually produced by compiling
+XS, L<Inline>, or similar code.
+
+=item script
+
+Programs written in pure Perl. In order to improve reuse, try to make
+these as small as possible - put the code into modules whenever
+possible.
+
+=item bin
+
+"Architecture-dependent" executable programs, i.e. compiled C code or
+something. Pretty rare to see this in a perl distribution, but it
+happens.
+
+=item bindoc
+
+Documentation for the stuff in C<script> and C<bin>. Usually
+generated from the POD in those files. Under Unix, these are manual
+pages belonging to the 'man1' category.
+
+=item libdoc
+
+Documentation for the stuff in C<lib> and C<arch>. This is usually
+generated from the POD in F<.pm> files. Under Unix, these are manual
+pages belonging to the 'man3' category.
+
+=item binhtml
+
+This is the same as C<bindoc> above, but applies to HTML documents.
+
+=item libhtml
+
+This is the same as C<bindoc> above, but applies to HTML documents.
+
+=back
+
+Four other parameters let you control various aspects of how
+installation paths are determined:
+
+=over 4
+
+=item installdirs
+
+The default destinations for these installable things come from
+entries in your system's C<Config.pm>. You can select from three
+different sets of default locations by setting the C<installdirs>
+parameter as follows:
+
+ 'installdirs' set to:
+ core site vendor
+
+ uses the following defaults from Config.pm:
+
+ lib => installprivlib installsitelib installvendorlib
+ arch => installarchlib installsitearch installvendorarch
+ script => installscript installsitebin installvendorbin
+ bin => installbin installsitebin installvendorbin
+ bindoc => installman1dir installsiteman1dir installvendorman1dir
+ libdoc => installman3dir installsiteman3dir installvendorman3dir
+ binhtml => installhtml1dir installsitehtml1dir installvendorhtml1dir [*]
+ libhtml => installhtml3dir installsitehtml3dir installvendorhtml3dir [*]
+
+ * Under some OS (eg. MSWin32) the destination for HTML documents is
+ determined by the C<Config.pm> entry C<installhtmldir>.
+
+The default value of C<installdirs> is "site". If you're creating
+vendor distributions of module packages, you may want to do something
+like this:
+
+ perl Build.PL --installdirs vendor
+
+or
+
+ ./Build install --installdirs vendor
+
+If you're installing an updated version of a module that was included
+with perl itself (i.e. a "core module"), then you may set
+C<installdirs> to "core" to overwrite the module in its present
+location.
+
+(Note that the 'script' line is different from C<MakeMaker> -
+unfortunately there's no such thing as "installsitescript" or
+"installvendorscript" entry in C<Config.pm>, so we use the
+"installsitebin" and "installvendorbin" entries to at least get the
+general location right. In the future, if C<Config.pm> adds some more
+appropriate entries, we'll start using those.)
+
+=item install_path
+
+Once the defaults have been set, you can override them.
+
+On the command line, that would look like this:
+
+ perl Build.PL --install_path lib=/foo/lib --install_path arch=/foo/lib/arch
+
+or this:
+
+ ./Build install --install_path lib=/foo/lib --install_path arch=/foo/lib/arch
+
+=item install_base
+
+You can also set the whole bunch of installation paths by supplying the
+C<install_base> parameter to point to a directory on your system. For
+instance, if you set C<install_base> to "/home/ken" on a Linux
+system, you'll install as follows:
+
+ lib => /home/ken/lib/perl5
+ arch => /home/ken/lib/perl5/i386-linux
+ script => /home/ken/bin
+ bin => /home/ken/bin
+ bindoc => /home/ken/man/man1
+ libdoc => /home/ken/man/man3
+ binhtml => /home/ken/html
+ libhtml => /home/ken/html
+
+Note that this is I<different> from how C<MakeMaker>'s C<PREFIX>
+parameter works. C<install_base> just gives you a default layout under the
+directory you specify, which may have little to do with the
+C<installdirs=site> layout.
+
+The exact layout under the directory you specify may vary by system -
+we try to do the "sensible" thing on each platform.
+
+=item destdir
+
+If you want to install everything into a temporary directory first
+(for instance, if you want to create a directory tree that a package
+manager like C<rpm> or C<dpkg> could create a package from), you can
+use the C<destdir> parameter:
+
+ perl Build.PL --destdir /tmp/foo
+
+or
+
+ ./Build install --destdir /tmp/foo
+
+This will effectively install to "/tmp/foo/$sitelib",
+"/tmp/foo/$sitearch", and the like, except that it will use
+C<File::Spec> to make the pathnames work correctly on whatever
+platform you're installing on.
+
+=item prefix
+
+Provided for compatibility with C<ExtUtils::MakeMaker>'s PREFIX argument.
+C<prefix> should be used when you wish Module::Build to install your
+modules, documentation and scripts in the same place
+C<ExtUtils::MakeMaker> does.
+
+The following are equivalent.
+
+ perl Build.PL --prefix /tmp/foo
+ perl Makefile.PL PREFIX=/tmp/foo
+
+Because of the very complex nature of the prefixification logic, the
+behavior of PREFIX in C<MakeMaker> has changed subtly over time.
+Module::Build's --prefix logic is equivalent to the PREFIX logic found
+in C<ExtUtils::MakeMaker> 6.30.
+
+If you do not need to retain compatibility with C<ExtUtils::MakeMaker> or
+are starting a fresh Perl installation we recommend you use
+C<install_base> instead (and C<INSTALL_BASE> in C<ExtUtils::MakeMaker>).
+See L<Module::Build::Cookbook/Instaling in the same location as
+ExtUtils::MakeMaker> for further information.
+
+
+=back
+
+
+=head1 MOTIVATIONS
+
+There are several reasons I wanted to start over, and not just fix
+what I didn't like about C<MakeMaker>:
+
+=over 4
+
+=item *
+
+I don't like the core idea of C<MakeMaker>, namely that C<make> should be
+involved in the build process. Here are my reasons:
+
+=over 4
+
+=item +
+
+When a person is installing a Perl module, what can you assume about
+their environment? Can you assume they have C<make>? No, but you can
+assume they have some version of Perl.
+
+=item +
+
+When a person is writing a Perl module for intended distribution, can
+you assume that they know how to build a Makefile, so they can
+customize their build process? No, but you can assume they know Perl,
+and could customize that way.
+
+=back
+
+For years, these things have been a barrier to people getting the
+build/install process to do what they want.
+
+=item *
+
+There are several architectural decisions in C<MakeMaker> that make it
+very difficult to customize its behavior. For instance, when using
+C<MakeMaker> you do C<use ExtUtils::MakeMaker>, but the object created in
+C<WriteMakefile()> is actually blessed into a package name that's
+created on the fly, so you can't simply subclass
+C<ExtUtils::MakeMaker>. There is a workaround C<MY> package that lets
+you override certain C<MakeMaker> methods, but only certain explicitly
+preselected (by C<MakeMaker>) methods can be overridden. Also, the method
+of customization is very crude: you have to modify a string containing
+the Makefile text for the particular target. Since these strings
+aren't documented, and I<can't> be documented (they take on different
+values depending on the platform, version of perl, version of
+C<MakeMaker>, etc.), you have no guarantee that your modifications will
+work on someone else's machine or after an upgrade of C<MakeMaker> or
+perl.
+
+=item *
+
+It is risky to make major changes to C<MakeMaker>, since it does so many
+things, is so important, and generally works. C<Module::Build> is an
+entirely separate package so that I can work on it all I want, without
+worrying about backward compatibility.
+
+=item *
+
+Finally, Perl is said to be a language for system administration.
+Could it really be the case that Perl isn't up to the task of building
+and installing software? Even if that software is a bunch of stupid
+little C<.pm> files that just need to be copied from one place to
+another? My sense was that we could design a system to accomplish
+this in a flexible, extensible, and friendly manner. Or die trying.
+
+=back
+
+
+=head1 TO DO
+
+The current method of relying on time stamps to determine whether a
+derived file is out of date isn't likely to scale well, since it
+requires tracing all dependencies backward, it runs into problems on
+NFS, and it's just generally flimsy. It would be better to use an MD5
+signature or the like, if available. See C<cons> for an example.
+
+ - append to perllocal.pod
+ - add a 'plugin' functionality
+
+
+=head1 AUTHOR
+
+Ken Williams <kwil[email protected]>
+
+Development questions, bug reports, and patches should be sent to the
+Module-Build mailing list at <[email protected]>.
+
+Bug reports are also welcome at
+<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Module-Build>.
+
+The latest development version is available from the Subversion
+repository at <https://svn.perl.org/modules/Module-Build/trunk/>
+
+
+=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::Cookbook>, L<Module::Build::Authoring>,
+L<Module::Build::API>, L<ExtUtils::MakeMaker>, L<YAML>
+
+F<META.yml> Specification:
+L<http://module-build.sourceforge.net/META-spec-current.html>
+
+L<http://www.dsmit.com/cons/>
+
+L<http://search.cpan.org/dist/PerlBuildSystem/>
+
+=cut
diff --git a/inc/Module-Build/Module/Build/API.pod b/inc/Module-Build/Module/Build/API.pod
new file mode 100644
index 0000000..18ffe5f
--- /dev/null
+++ b/inc/Module-Build/Module/Build/API.pod
@@ -0,0 +1,1927 @@
+=head1 NAME
+
+Module::Build::API - API Reference for Module Authors
+
+=for :stopwords apache bsd distdir distsign gpl installdirs lgpl mit mozilla packlists
+
+=head1 DESCRIPTION
+
+I list here some of the most important methods in C<Module::Build>.
+Normally you won't need to deal with these methods unless you want to
+subclass C<Module::Build>. But since one of the reasons I created
+this module in the first place was so that subclassing is possible
+(and easy), I will certainly write more docs as the interface
+stabilizes.
+
+
+=head2 CONSTRUCTORS
+
+=over 4
+
+=item current()
+
+[version 0.20]
+
+This method returns a reasonable facsimile of the currently-executing
+C<Module::Build> object representing the current build. You can use
+this object to query its L</notes()> method, inquire about installed
+modules, and so on. This is a great way to share information between
+different parts of your build process. For instance, you can ask
+the user a question during C<perl Build.PL>, then use their answer
+during a regression test:
+
+ # In Build.PL:
+ my $color = $build->prompt("What is your favorite color?");
+ $build->notes(color => $color);
+
+ # In t/colortest.t:
+ use Module::Build;
+ my $build = Module::Build->current;
+ my $color = $build->notes('color');
+ ...
+
+The way the C<current()> method is currently implemented, there may be
+slight differences between the C<$build> object in Build.PL and the
+one in C<t/colortest.t>. It is our goal to minimize these differences
+in future releases of Module::Build, so please report any anomalies
+you find.
+
+One important caveat: in its current implementation, C<current()> will
+B<NOT> work correctly if you have changed out of the directory that
+C<Module::Build> was invoked from.
+
+=item new()
+
+[version 0.03]
+
+Creates a new Module::Build object. Arguments to the new() method are
+listed below. Most arguments are optional, but you must provide
+either the L</module_name> argument, or L</dist_name> and one of
+L</dist_version> or L</dist_version_from>. In other words, you must
+provide enough information to determine both a distribution name and
+version.
+
+
+=over 4
+
+=item add_to_cleanup
+
+[version 0.19]
+
+An array reference of files to be cleaned up when the C<clean> action
+is performed. See also the L<add_to_cleanup()|/"add_to_cleanup(@files)">
+method.
+
+=item auto_configure_requires
+
+[version 0.34]
+
+This parameter determines whether Module::Build will add itself
+automatically to configure_requires (and build_requires) if Module::Build
+is not already there. The default value is true.
+
+=item auto_features
+
+[version 0.26]
+
+This parameter supports the setting of features (see
+L</feature($name)>) automatically based on a set of prerequisites. For
+instance, for a module that could optionally use either MySQL or
+PostgreSQL databases, you might use C<auto_features> like this:
+
+ my $build = Module::Build->new
+ (
+ ...other stuff here...
+ auto_features => {
+ pg_support => {
+ description => "Interface with Postgres databases",
+ requires => { 'DBD::Pg' => 23.3,
+ 'DateTime::Format::Pg' => 0 },
+ },
+ mysql_support => {
+ description => "Interface with MySQL databases",
+ requires => { 'DBD::mysql' => 17.9,
+ 'DateTime::Format::MySQL' => 0 },
+ },
+ }
+ );
+
+For each feature named, the required prerequisites will be checked, and
+if there are no failures, the feature will be enabled (set to C<1>).
+Otherwise the failures will be displayed to the user and the feature
+will be disabled (set to C<0>).
+
+See the documentation for L</requires> for the details of how
+requirements can be specified.
+
+=item autosplit
+
+[version 0.04]
+
+An optional C<autosplit> argument specifies a file which should be run
+through the L<AutoSplit::autosplit()|AutoSplit/autosplit> function.
+If multiple files should be split, the argument may be given as an
+array of the files to split.
+
+In general I don't consider autosplitting a great idea, because it's
+not always clear that autosplitting achieves its intended performance
+benefits. It may even harm performance in environments like mod_perl,
+where as much as possible of a module's code should be loaded during
+startup.
+
+=item build_class
+
+[version 0.28]
+
+The Module::Build class or subclass to use in the build script.
+Defaults to "Module::Build" or the class name passed to or created by
+a call to L</subclass()>. This property is useful if you're
+writing a custom Module::Build subclass and have a bootstrapping
+problem--that is, your subclass requires modules that may not be
+installed when C<perl Build.PL> is executed, but you've listed in
+L</build_requires> so that they should be available when C<./Build> is
+executed.
+
+=item build_requires
+
+[version 0.07]
+
+Modules listed in this section are necessary to build and install the
+given module, but are not necessary for regular usage of it. This is
+actually an important distinction - it allows for tighter control over
+the body of installed modules, and facilitates correct dependency
+checking on binary/packaged distributions of the module.
+
+See the documentation for L<Module::Build::Authoring/"PREREQUISITES">
+for the details of how requirements can be specified.
+
+=item create_packlist
+
+[version 0.28]
+
+If true, this parameter tells Module::Build to create a F<.packlist>
+file during the C<install> action, just like C<ExtUtils::MakeMaker> does.
+The file is created in a subdirectory of the C<arch> installation
+location. It is used by some other tools (CPAN, CPANPLUS, etc.) for
+determining what files are part of an install.
+
+The default value is true. This parameter was introduced in
+Module::Build version 0.2609; previously no packlists were ever
+created by Module::Build.
+
+=item c_source
+
+[version 0.04]
+
+An optional C<c_source> argument specifies a directory which contains
+C source files that the rest of the build may depend on. Any C<.c>
+files in the directory will be compiled to object files. The
+directory will be added to the search path during the compilation and
+linking phases of any C or XS files.
+
+=item conflicts
+
+[version 0.07]
+
+Modules listed in this section conflict in some serious way with the
+given module. C<Module::Build> (or some higher-level tool) will
+refuse to install the given module if the given module/version is also
+installed.
+
+See the documentation for L<Module::Build::Authoring/"PREREQUISITES">
+for the details of how requirements can be specified.
+
+=item create_license
+
+[version 0.31]
+
+This parameter tells Module::Build to automatically create a
+F<LICENSE> file at the top level of your distribution, containing the
+full text of the author's chosen license. This requires
+C<Software::License> on the author's machine, and further requires
+that the C<license> parameter specifies a license that it knows about.
+
+=item create_makefile_pl
+
+[version 0.19]
+
+This parameter lets you use C<Module::Build::Compat> during the
+C<distdir> (or C<dist>) action to automatically create a Makefile.PL
+for compatibility with C<ExtUtils::MakeMaker>. The parameter's value
+should be one of the styles named in the L<Module::Build::Compat>
+documentation.
+
+=item create_readme
+
+[version 0.22]
+
+This parameter tells Module::Build to automatically create a F<README>
+file at the top level of your distribution. Currently it will simply
+use C<Pod::Text> (or C<Pod::Readme> if it's installed) on the file
+indicated by C<dist_version_from> and put the result in the F<README>
+file. This is by no means the only recommended style for writing a
+F<README>, but it seems to be one common one used on the CPAN.
+
+If you generate a F<README> in this way, it's probably a good idea to
+create a separate F<INSTALL> file if that information isn't in the
+generated F<README>.
+
+=item dist_abstract
+
+[version 0.20]
+
+This should be a short description of the distribution. This is used when
+generating metadata for F<META.yml> and PPD files. If it is not given
+then C<Module::Build> looks in the POD of the module from which it gets
+the distribution's version. If it finds a POD section marked "=head1
+NAME", then it looks for the first line matching C<\s+-\s+(.+)>,
+and uses the captured text as the abstract.
+
+=item dist_author
+
+[version 0.20]
+
+This should be something like "John Doe <[email protected]>", or if
+there are multiple authors, an anonymous array of strings may be
+specified. This is used when generating metadata for F<META.yml> and
+PPD files. If this is not specified, then C<Module::Build> looks at
+the module from which it gets the distribution's version. If it finds
+a POD section marked "=head1 AUTHOR", then it uses the contents of
+this section.
+
+=item dist_name
+
+[version 0.11]
+
+Specifies the name for this distribution. Most authors won't need to
+set this directly, they can use C<module_name> to set C<dist_name> to
+a reasonable default. However, some agglomerative distributions like
+C<libwww-perl> or C<bioperl> have names that don't correspond directly
+to a module name, so C<dist_name> can be set independently.
+
+=item dist_version
+
+[version 0.11]
+
+Specifies a version number for the distribution. See L</module_name>
+or L</dist_version_from> for ways to have this set automatically from a
+C<$VERSION> variable in a module. One way or another, a version
+number needs to be set.
+
+=item dist_version_from
+
+[version 0.11]
+
+Specifies a file to look for the distribution version in. Most
+authors won't need to set this directly, they can use L</module_name>
+to set it to a reasonable default.
+
+The version is extracted from the specified file according to the same
+rules as L<ExtUtils::MakeMaker> and C<CPAN.pm>. It involves finding
+the first line that matches the regular expression
+
+ /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/
+
+eval()-ing that line, then checking the value of the C<$VERSION>
+variable. Quite ugly, really, but all the modules on CPAN depend on
+this process, so there's no real opportunity to change to something
+better.
+
+If the target file of L</dist_version_from> contains more than one package
+declaration, the version returned will be the one matching the configured
+L</module_name>.
+
+=item dynamic_config
+
+[version 0.07]
+
+A boolean flag indicating whether the F<Build.PL> file must be
+executed, or whether this module can be built, tested and installed
+solely from consulting its metadata file. The main reason to set this
+to a true value is that your module performs some dynamic
+configuration as part of its build/install process. If the flag is
+omitted, the F<META.yml> spec says that installation tools should
+treat it as 1 (true), because this is a safer way to behave.
+
+Currently C<Module::Build> doesn't actually do anything with this flag
+- it's up to higher-level tools like C<CPAN.pm> to do something useful
+with it. It can potentially bring lots of security, packaging, and
+convenience improvements.
+
+=item extra_compiler_flags
+
+=item extra_linker_flags
+
+[version 0.19]
+
+These parameters can contain array references (or strings, in which
+case they will be split into arrays) to pass through to the compiler
+and linker phases when compiling/linking C code. For example, to tell
+the compiler that your code is C++, you might do:
+
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ extra_compiler_flags => ['-x', 'c++'],
+ );
+
+To link your XS code against glib you might write something like:
+
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ dynamic_config => 1,
+ extra_compiler_flags => scalar `glib-config --cflags`,
+ extra_linker_flags => scalar `glib-config --libs`,
+ );
+
+=item get_options
+
+[version 0.26]
+
+You can pass arbitrary command line options to F<Build.PL> or
+F<Build>, and they will be stored in the Module::Build object and can
+be accessed via the L</args()> method. However, sometimes you want
+more flexibility out of your argument processing than this allows. In
+such cases, use the C<get_options> parameter to pass in a hash
+reference of argument specifications, and the list of arguments to
+F<Build.PL> or F<Build> will be processed according to those
+specifications before they're passed on to C<Module::Build>'s own
+argument processing.
+
+The supported option specification hash keys are:
+
+
+=over 4
+
+=item type
+
+The type of option. The types are those supported by Getopt::Long; consult
+its documentation for a complete list. Typical types are C<=s> for strings,
+C<+> for additive options, and C<!> for negatable options. If the
+type is not specified, it will be considered a boolean, i.e. no
+argument is taken and a value of 1 will be assigned when the option is
+encountered.
+
+=item store
+
+A reference to a scalar in which to store the value passed to the option.
+If not specified, the value will be stored under the option name in the
+hash returned by the C<args()> method.
+
+=item default
+
+A default value for the option. If no default value is specified and no option
+is passed, then the option key will not exist in the hash returned by
+C<args()>.
+
+=back
+
+
+You can combine references to your own variables or subroutines with
+unreferenced specifications, for which the result will also be stored in the
+hash returned by C<args()>. For example:
+
+ my $loud = 0;
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ get_options => {
+ loud => { store => \$loud },
+ dbd => { type => '=s' },
+ quantity => { type => '+' },
+ }
+ );
+
+ print STDERR "HEY, ARE YOU LISTENING??\n" if $loud;
+ print "We'll use the ", $build->args('dbd'), " DBI driver\n";
+ print "Are you sure you want that many?\n"
+ if $build->args('quantity') > 2;
+
+The arguments for such a specification can be called like so:
+
+ perl Build.PL --loud --dbd=DBD::pg --quantity --quantity --quantity
+
+B<WARNING:> Any option specifications that conflict with Module::Build's own
+options (defined by its properties) will throw an exception.
+
+Consult the Getopt::Long documentation for details on its usage.
+
+=item include_dirs
+
+[version 0.24]
+
+Specifies any additional directories in which to search for C header
+files. May be given as a string indicating a single directory, or as
+a list reference indicating multiple directories.
+
+=item install_path
+
+[version 0.19]
+
+You can set paths for individual installable elements by using the
+C<install_path> parameter:
+
+ my $build = Module::Build->new
+ (
+ ...other stuff here...
+ install_path => {
+ lib => '/foo/lib',
+ arch => '/foo/lib/arch',
+ }
+ );
+
+=item installdirs
+
+[version 0.19]
+
+Determines where files are installed within the normal perl hierarchy
+as determined by F<Config.pm>. Valid values are: C<core>, C<site>,
+C<vendor>. The default is C<site>. See
+L<Module::Build/"INSTALL PATHS">
+
+=item license
+
+[version 0.07]
+
+Specifies the licensing terms of your distribution. Valid options include:
+
+
+=over 4
+
+=item apache
+
+The distribution is licensed under the Apache Software License
+(L<http://opensource.org/licenses/apachepl.php>).
+
+=item artistic
+
+The distribution is licensed under the Artistic License, as specified
+by the F<Artistic> file in the standard Perl distribution.
+
+=item artistic_2
+
+The distribution is licensed under the Artistic 2.0 License
+(L<http://opensource.org/licenses/artistic-license-2.0.php>.)
+
+=item bsd
+
+The distribution is licensed under the BSD License
+(L<http://www.opensource.org/licenses/bsd-license.php>).
+
+=item gpl
+
+The distribution is licensed under the terms of the GNU General
+Public License (L<http://www.opensource.org/licenses/gpl-license.php>).
+
+=item lgpl
+
+The distribution is licensed under the terms of the GNU Lesser
+General Public License
+(L<http://www.opensource.org/licenses/lgpl-license.php>).
+
+=item mit
+
+The distribution is licensed under the MIT License
+(L<http://opensource.org/licenses/mit-license.php>).
+
+=item mozilla
+
+The distribution is licensed under the Mozilla Public
+License. (L<http://opensource.org/licenses/mozilla1.0.php> or
+L<http://opensource.org/licenses/mozilla1.1.php>)
+
+=item open_source
+
+The distribution is licensed under some other Open Source
+Initiative-approved license listed at
+L<http://www.opensource.org/licenses/>.
+
+=item perl
+
+The distribution may be copied and redistributed under the same terms
+as Perl itself (this is by far the most common licensing option for
+modules on CPAN). This is a dual license, in which the user may
+choose between either the GPL or the Artistic license.
+
+=item restrictive
+
+The distribution may not be redistributed without special permission
+from the author and/or copyright holder.
+
+=item unrestricted
+
+The distribution is licensed under a license that is B<not> approved
+by www.opensource.org but that allows distribution without
+restrictions.
+
+=back
+
+
+Note that you must still include the terms of your license in your
+documentation - this field only lets automated tools figure out your
+licensing restrictions. Humans still need something to read. If you
+choose to provide this field, you should make sure that you keep it in
+sync with your written documentation if you ever change your licensing
+terms.
+
+You may also use a license type of C<unknown> if you don't wish to
+specify your terms in the metadata.
+
+It is a fatal error to use a license other than the ones mentioned
+above. This is not because I wish to impose licensing terms on you -
+please let me know if you would like another license option to be
+added to the list. I just started out with a small set of licenses to
+keep things simple, figuring I'd let people with actual working
+knowledge in this area tell me what to do. So if that's you, drop me
+a line.
+
+=item meta_add
+
+[version 0.28]
+
+A hash of key/value pairs that should be added to the F<META.yml> file
+during the C<distmeta> action. Any existing entries with the same
+names will be overridden.
+
+See the L</"MODULE METADATA"> section for details.
+
+=item meta_merge
+
+[version 0.28]
+
+A hash of key/value pairs that should be merged into the F<META.yml>
+file during the C<distmeta> action. Any existing entries with the
+same names will be overridden.
+
+The only difference between C<meta_add> and C<meta_merge> is their
+behavior on hash-valued and array-valued entries: C<meta_add> will
+completely blow away the existing hash or array value, but
+C<meta_merge> will merge the supplied data into the existing hash or
+array value.
+
+See the L</"MODULE METADATA"> section for details.
+
+=item module_name
+
+[version 0.03]
+
+The C<module_name> is a shortcut for setting default values of
+C<dist_name> and C<dist_version_from>, reflecting the fact that the
+majority of CPAN distributions are centered around one "main" module.
+For instance, if you set C<module_name> to C<Foo::Bar>, then
+C<dist_name> will default to C<Foo-Bar> and C<dist_version_from> will
+default to C<lib/Foo/Bar.pm>. C<dist_version_from> will in turn be
+used to set C<dist_version>.
+
+Setting C<module_name> won't override a C<dist_*> parameter you
+specify explicitly.
+
+=item PL_files
+
+[version 0.06]
+
+An optional parameter specifying a set of C<.PL> files in your
+distribution. These will be run as Perl scripts prior to processing
+the rest of the files in your distribution with the name of the file
+they're generating as an argument. They are usually used as templates
+for creating other files dynamically, so that a file like
+C<lib/Foo/Bar.pm.PL> might create the file C<lib/Foo/Bar.pm>.
+
+The files are specified with the C<.PL> files as hash keys, and the
+file(s) they generate as hash values, like so:
+
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ ...
+ PL_files => { 'lib/Foo/Bar.pm.PL' => 'lib/Foo/Bar.pm' },
+ );
+
+Note that the path specifications are I<always> given in Unix-like
+format, not in the style of the local system.
+
+If your C<.PL> scripts don't create any files, or if they create files
+with unexpected names, or even if they create multiple files, you can
+indicate that so that Module::Build can properly handle these created
+files:
+
+ PL_files => {
+ 'lib/Foo/Bar.pm.PL' => 'lib/Foo/Bar.pm',
+ 'lib/something.PL' => ['/lib/something', '/lib/else'],
+ 'lib/funny.PL' => [],
+ }
+
+Here's an example of a simple PL file.
+
+ my $output_file = shift;
+ open my $fh, ">", $output_file or die "Can't open $output_file: $!";
+
+ print $fh <<'END';
+ #!/usr/bin/perl
+
+ print "Hello, world!\n";
+ END
+
+PL files are not installed by default, so its safe to put them in
+F<lib/> and F<bin/>.
+
+
+=item pm_files
+
+[version 0.19]
+
+An optional parameter specifying the set of C<.pm> files in this
+distribution, specified as a hash reference whose keys are the files'
+locations in the distributions, and whose values are their logical
+locations based on their package name, i.e. where they would be found
+in a "normal" Module::Build-style distribution. This parameter is
+mainly intended to support alternative layouts of files.
+
+For instance, if you have an old-style C<MakeMaker> distribution for a
+module called C<Foo::Bar> and a F<Bar.pm> file at the top level of the
+distribution, you could specify your layout in your C<Build.PL> like
+this:
+
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ ...
+ pm_files => { 'Bar.pm' => 'lib/Foo/Bar.pm' },
+ );
+
+Note that the values should include C<lib/>, because this is where
+they would be found in a "normal" Module::Build-style distribution.
+
+Note also that the path specifications are I<always> given in
+Unix-like format, not in the style of the local system.
+
+=item pod_files
+
+[version 0.19]
+
+Just like C<pm_files>, but used for specifying the set of C<.pod>
+files in your distribution.
+
+=item recommends
+
+[version 0.08]
+
+This is just like the L</requires> argument, except that modules listed
+in this section aren't essential, just a good idea. We'll just print
+a friendly warning if one of these modules aren't found, but we'll
+continue running.
+
+If a module is recommended but not required, all tests should still
+pass if the module isn't installed. This may mean that some tests
+may be skipped if recommended dependencies aren't present.
+
+Automated tools like CPAN.pm should inform the user when recommended
+modules aren't installed, and it should offer to install them if it
+wants to be helpful.
+
+See the documentation for L<Module::Build::Authoring/"PREREQUISITES">
+for the details of how requirements can be specified.
+
+=item recursive_test_files
+
+[version 0.28]
+
+Normally, C<Module::Build> does not search subdirectories when looking
+for tests to run. When this options is set it will search recursively
+in all subdirectories of the standard 't' test directory.
+
+=item requires
+
+[version 0.07]
+
+An optional C<requires> argument specifies any module prerequisites
+that the current module depends on.
+
+One note: currently C<Module::Build> doesn't actually I<require> the
+user to have dependencies installed, it just strongly urges. In the
+future we may require it. There's also a L</recommends> section for
+things that aren't absolutely required.
+
+Automated tools like CPAN.pm should refuse to install a module if one
+of its dependencies isn't satisfied, unless a "force" command is given
+by the user. If the tools are helpful, they should also offer to
+install the dependencies.
+
+A synonym for C<requires> is C<prereq>, to help succour people
+transitioning from C<ExtUtils::MakeMaker>. The C<requires> term is
+preferred, but the C<prereq> term will remain valid in future
+distributions.
+
+See the documentation for L<Module::Build::Authoring/"PREREQUISITES">
+for the details of how requirements can be specified.
+
+=item script_files
+
+[version 0.18]
+
+An optional parameter specifying a set of files that should be
+installed as executable Perl scripts when the module is installed.
+May be given as an array reference of the files, as a hash reference
+whose keys are the files (and whose values will currently be ignored),
+as a string giving the name of a directory in which to find scripts,
+or as a string giving the name of a single script file.
+
+The default is to install any scripts found in a F<bin> directory at
+the top level of the distribution, minus any keys of L<PL_files>.
+
+For backward compatibility, you may use the parameter C<scripts>
+instead of C<script_files>. Please consider this usage deprecated,
+though it will continue to exist for several version releases.
+
+=item sign
+
+[version 0.16]
+
+If a true value is specified for this parameter, L<Module::Signature>
+will be used (via the 'distsign' action) to create a SIGNATURE file
+for your distribution during the 'distdir' action, and to add the
+SIGNATURE file to the MANIFEST (therefore, don't add it yourself).
+
+The default value is false. In the future, the default may change to
+true if you have C<Module::Signature> installed on your system.
+
+=item test_files
+
+[version 0.23]
+
+An optional parameter specifying a set of files that should be used as
+C<Test::Harness>-style regression tests to be run during the C<test>
+action. May be given as an array reference of the files, or as a hash
+reference whose keys are the files (and whose values will currently be
+ignored). If the argument is given as a single string (not in an
+array reference), that string will be treated as a C<glob()> pattern
+specifying the files to use.
+
+The default is to look for a F<test.pl> script in the top-level
+directory of the distribution, and any files matching the glob pattern
+C<*.t> in the F<t/> subdirectory. If the C<recursive_test_files>
+property is true, then the C<t/> directory will be scanned recursively
+for C<*.t> files.
+
+=item use_tap_harness
+
+[version 0.2808_03]
+
+An optional parameter indicating whether or not to use TAP::Harness for
+testing rather than Test::Harness. Defaults to false. If set to true, you must
+therefore be sure to add TAP::Harness as a requirement for your module in
+L</build_requires>. Implicitly set to a true value if C<tap_harness_args> is
+specified.
+
+=item tap_harness_args
+
+[version 0.2808_03]
+
+An optional parameter specifying parameters to be passed to TAP::Harness when
+running tests. Must be given as a hash reference of parameters; see the
+L<TAP::Harness|TAP::Harness> documentation for details. Note that specifying
+this parameter will implicitly set C<use_tap_harness> to a true value. You
+must therefore be sure to add TAP::Harness as a requirement for your module in
+L</build_requires>.
+
+=item xs_files
+
+[version 0.19]
+
+Just like C<pm_files>, but used for specifying the set of C<.xs>
+files in your distribution.
+
+=back
+
+
+=item new_from_context(%args)
+
+[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.
+
+=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()>.
+
+=item subclass()
+
+[version 0.06]
+
+This creates a new C<Module::Build> subclass on the fly, as described
+in the L<Module::Build::Authoring/"SUBCLASSING"> section. The caller
+must provide either a C<class> or C<code> parameter, or both. The
+C<class> parameter indicates the name to use for the new subclass, and
+defaults to C<MyModuleBuilder>. The C<code> parameter specifies Perl
+code to use as the body of the subclass.
+
+=item add_property
+
+[version 0.31]
+
+ package 'My::Build';
+ use base 'Module::Build';
+ __PACKAGE__->add_property( 'pedantic' );
+ __PACKAGE__->add_property( answer => 42 );
+ __PACKAGE__->add_property(
+ 'epoch',
+ default => sub { time },
+ check => sub {
+ return 1 if /^\d+$/;
+ shift->property_error( "'$_' is not an epoch time" );
+ return 0;
+ },
+ );
+
+Adds a property to a Module::Build class. Properties are those attributes of a
+Module::Build object which can be passed to the constructor and which have
+accessors to get and set them. All of the core properties, such as
+C<module_name> and C<license>, are defined using this class method.
+
+The first argument to C<add_property()> is always the name of the property.
+The second argument can be either a default value for the property, or a list
+of key/value pairs. The supported keys are:
+
+=over
+
+=item C<default>
+
+The default value. May optionally be specified as a code reference, in which
+case the return value from the execution of the code reference will be used.
+If you need the default to be a code reference, just use a code reference to
+return it, e.g.:
+
+ default => sub { sub { ... } },
+
+=item C<check>
+
+A code reference that checks that a value specified for the property is valid.
+During the execution of the code reference, the new value will be included in
+the C<$_> variable. If the value is correct, the C<check> code reference
+should return true. If the value is not correct, it sends an error message to
+C<property_error()> and returns false.
+
+=back
+
+When this method is called, a new property will be installed in the
+Module::Build class, and an accessor will be built to allow the property to be
+get or set on the build object.
+
+ print $build->pedantic, $/;
+ $build->pedantic(0);
+
+If the default value is a hash reference, this generates a special-case
+accessor method, wherein individual key/value pairs may be set or fetched:
+
+ print "stuff{foo} is: ", $build->stuff( 'foo' ), $/;
+ $build->stuff( foo => 'bar' );
+ print $build->stuff( 'foo' ), $/; # Outputs "bar"
+
+Of course, you can still set the entire hash reference at once, as well:
+
+ $build->stuff( { foo => 'bar', baz => 'yo' } );
+
+In either case, if a C<check> has been specified for the property, it will be
+applied to the entire hash. So the check code reference should look something
+like:
+
+ check => sub {
+ return 1 if defined $_ && exists $_->{foo};
+ shift->property_error(qq{Property "stuff" needs "foo"});
+ return 0;
+ },
+
+=item property_error
+
+[version 0.31]
+
+=back
+
+
+=head2 METHODS
+
+=over 4
+
+=item add_build_element($type)
+
+[version 0.26]
+
+Adds a new type of entry to the build process. Accepts a single
+string specifying its type-name. There must also be a method defined
+to process things of that type, e.g. if you add a build element called
+C<'foo'>, then you must also define a method called
+C<process_foo_files()>.
+
+See also
+L<Module::Build::Cookbook/"Adding new file types to the build process">.
+
+=item add_to_cleanup(@files)
+
+[version 0.03]
+
+You may call C<< $self->add_to_cleanup(@patterns) >> to tell
+C<Module::Build> that certain files should be removed when the user
+performs the C<Build clean> action. The arguments to the method are
+patterns suitable for passing to Perl's C<glob()> function, specified
+in either Unix format or the current machine's native format. It's
+usually convenient to use Unix format when you hard-code the filenames
+(e.g. in F<Build.PL>) and the native format when the names are
+programmatically generated (e.g. in a testing script).
+
+I decided to provide a dynamic method of the C<$build> object, rather
+than just use a static list of files named in the F<Build.PL>, because
+these static lists can get difficult to manage. I usually prefer to
+keep the responsibility for registering temporary files close to the
+code that creates them.
+
+=item args()
+
+[version 0.26]
+
+ my $args_href = $build->args;
+ my %args = $build->args;
+ my $arg_value = $build->args($key);
+ $build->args($key, $value);
+
+This method is the preferred interface for retrieving the arguments passed via
+command line options to F<Build.PL> or F<Build>, minus the Module-Build
+specific options.
+
+When called in in a scalar context with no arguments, this method returns a
+reference to the hash storing all of the arguments; in an array context, it
+returns the hash itself. When passed a single argument, it returns the value
+stored in the args hash for that option key. When called with two arguments,
+the second argument is assigned to the args hash under the key passed as the
+first argument.
+
+=item autosplit_file($from, $to)
+
+[version 0.28]
+
+Invokes the L<AutoSplit> module on the C<$from> file, sending the
+output to the C<lib/auto> directory inside C<$to>. C<$to> is
+typically the C<blib/> directory.
+
+=item base_dir()
+
+[version 0.14]
+
+Returns a string containing the root-level directory of this build,
+i.e. where the C<Build.PL> script and the C<lib> directory can be
+found. This is usually the same as the current working directory,
+because the C<Build> script will C<chdir()> into this directory as
+soon as it begins execution.
+
+=item build_requires()
+
+[version 0.21]
+
+Returns a hash reference indicating the C<build_requires>
+prerequisites that were passed to the C<new()> method.
+
+=item can_action( $action )
+
+Returns a reference to the method that defines C<$action>, or false
+otherwise. This is handy for actions defined (or maybe not!) in subclasses.
+
+[version 0.32_xx]
+
+=item cbuilder()
+
+[version 0.2809]
+
+Returns the internal ExtUtils::CBuilder object that can be used for
+compiling & linking C code. If no such object is available (e.g. if
+the system has no compiler installed) an exception will be thrown.
+
+=item check_installed_status($module, $version)
+
+[version 0.11]
+
+This method returns a hash reference indicating whether a version
+dependency on a certain module is satisfied. The C<$module> argument
+is given as a string like C<"Data::Dumper"> or C<"perl">, and the
+C<$version> argument can take any of the forms described in L</requires>
+above. This allows very fine-grained version checking.
+
+The returned hash reference has the following structure:
+
+ {
+ ok => $whether_the_dependency_is_satisfied,
+ have => $version_already_installed,
+ need => $version_requested, # Same as incoming $version argument
+ message => $informative_error_message,
+ }
+
+If no version of C<$module> is currently installed, the C<have> value
+will be the string C<< "<none>" >>. Otherwise the C<have> value will
+simply be the version of the installed module. Note that this means
+that if C<$module> is installed but doesn't define a version number,
+the C<have> value will be C<undef> - this is why we don't use C<undef>
+for the case when C<$module> isn't installed at all.
+
+This method may be called either as an object method
+(C<< $build->check_installed_status($module, $version) >>)
+or as a class method
+(C<< Module::Build->check_installed_status($module, $version) >>).
+
+=item check_installed_version($module, $version)
+
+[version 0.05]
+
+Like L<check_installed_status()|/"check_installed_status($module, $version)">,
+but simply returns true or false depending on whether module
+C<$module> satisfies the dependency C<$version>.
+
+If the check succeeds, the return value is the actual version of
+C<$module> installed on the system. This allows you to do the
+following:
+
+ my $installed = $build->check_installed_version('DBI', '1.15');
+ if ($installed) {
+ print "Congratulations, version $installed of DBI is installed.\n";
+ } else {
+ die "Sorry, you must install DBI.\n";
+ }
+
+If the check fails, we return false and set C<[email protected]> to an informative
+error message.
+
+If C<$version> is any non-true value (notably zero) and any version of
+C<$module> is installed, we return true. In this case, if C<$module>
+doesn't define a version, or if its version is zero, we return the
+special value "0 but true", which is numerically zero, but logically
+true.
+
+In general you might prefer to use C<check_installed_status> if you
+need detailed information, or this method if you just need a yes/no
+answer.
+
+=item compare_versions($v1, $op, $v2)
+
+[version 0.28]
+
+Compares two module versions C<$v1> and C<$v2> using the operator
+C<$op>, which should be one of Perl's numeric operators like C<!=> or
+C<< >= >> or the like. We do at least a halfway-decent job of
+handling versions that aren't strictly numeric, like C<0.27_02>, but
+exotic stuff will likely cause problems.
+
+In the future, the guts of this method might be replaced with a call
+out to C<version.pm>.
+
+=item config($key)
+
+=item config($key, $value)
+
+=item config() [deprecated]
+
+[version 0.22]
+
+With a single argument C<$key>, returns the value associated with that
+key in the C<Config.pm> hash, including any changes the author or user
+has specified.
+
+With C<$key> and C<$value> arguments, sets the value for future
+callers of C<config($key)>.
+
+With no arguments, returns a hash reference containing all such
+key-value pairs. This usage is deprecated, though, because it's a
+resource hog and violates encapsulation.
+
+=item config_data($name)
+
+=item config_data($name => $value)
+
+[version 0.26]
+
+With a single argument, returns the value of the configuration
+variable C<$name>. With two arguments, sets the given configuration
+variable to the given value. The value may be any Perl scalar that's
+serializable with C<Data::Dumper>. For instance, if you write a
+module that can use a MySQL or PostgreSQL back-end, you might create
+configuration variables called C<mysql_connect> and
+C<postgres_connect>, and set each to an array of connection parameters
+for C<< DBI->connect() >>.
+
+Configuration values set in this way using the Module::Build object
+will be available for querying during the build/test process and after
+installation via the generated C<...::ConfigData> module, as
+C<< ...::ConfigData->config($name) >>.
+
+The L<feature()|/"feature($name)"> and C<config_data()> methods represent
+Module::Build's main support for configuration of installed modules.
+See also L<Module::Build::Authoring/"SAVING CONFIGURATION INFORMATION">.
+
+=item conflicts()
+
+[version 0.21]
+
+Returns a hash reference indicating the C<conflicts> prerequisites
+that were passed to the C<new()> method.
+
+=item contains_pod($file)
+
+[version 0.20]
+
+[Deprecated] Please see L<Module::Build::ModuleInfo> instead.
+
+Returns true if the given file appears to contain POD documentation.
+Currently this checks whether the file has a line beginning with
+'=pod', '=head', or '=item', but the exact semantics may change in the
+future.
+
+=item copy_if_modified(%parameters)
+
+[version 0.19]
+
+Takes the file in the C<from> parameter and copies it to the file in
+the C<to> parameter, or the directory in the C<to_dir> parameter, if
+the file has changed since it was last copied (or if it doesn't exist
+in the new location). By default the entire directory structure of
+C<from> will be copied into C<to_dir>; an optional C<flatten>
+parameter will copy into C<to_dir> without doing so.
+
+Returns the path to the destination file, or C<undef> if nothing
+needed to be copied.
+
+Any directories that need to be created in order to perform the
+copying will be automatically created.
+
+The destination file is set to read-only. If the source file has the
+executable bit set, then the destination file will be made executable.
+
+=item create_build_script()
+
+[version 0.05]
+
+Creates an executable script called C<Build> in the current directory
+that will be used to execute further user actions. This script is
+roughly analogous (in function, not in form) to the Makefile created
+by C<ExtUtils::MakeMaker>. This method also creates some temporary
+data in a directory called C<_build/>. Both of these will be removed
+when the C<realclean> action is performed.
+
+Among the files created in C<_build/> is a F<_build/prereqs> file
+containing the set of prerequisites for this distribution, as a hash
+of hashes. This file may be C<eval()>-ed to obtain the authoritative
+set of prerequisites, which might be different from the contents of
+F<META.yml> (because F<Build.PL> might have set them dynamically).
+But fancy developers take heed: do not put any fancy custom runtime
+code in the F<_build/prereqs> file, leave it as a static declaration
+containing only strings and numbers. Similarly, do not alter the
+structure of the internal C<< $self->{properties}{requires} >> (etc.)
+data members, because that's where this data comes from.
+
+=item current_action()
+
+[version 0.28]
+
+Returns the name of the currently-running action, such as "build" or
+"test". This action is not necessarily the action that was originally
+invoked by the user. For example, if the user invoked the "test"
+action, current_action() would initially return "test". However,
+action "test" depends on action "code", so current_action() will
+return "code" while that dependency is being executed. Once that
+action has completed, current_action() will again return "test".
+
+If you need to know the name of the original action invoked by the
+user, see L</invoked_action()> below.
+
+=item depends_on(@actions)
+
+[version 0.28]
+
+Invokes the named action or list of actions in sequence. Using this
+method is preferred to calling the action explicitly because it
+performs some internal record-keeping, and it ensures that the same
+action is not invoked multiple times (note: in future versions of
+Module::Build it's conceivable that this run-only-once mechanism will
+be changed to something more intelligent).
+
+Note that the name of this method is something of a misnomer; it
+should really be called something like
+C<invoke_actions_unless_already_invoked()> or something, but for
+better or worse (perhaps better!) we were still thinking in
+C<make>-like dependency terms when we created this method.
+
+See also L<dispatch()|/"dispatch($action, %args)">. The main
+distinction between the two is that C<depends_on()> is meant to call
+an action from inside another action, whereas C<dispatch()> is meant
+to set the very top action in motion.
+
+=item dir_contains($first_dir, $second_dir)
+
+[version 0.28]
+
+Returns true if the first directory logically contains the second
+directory. This is just a convenience function because C<File::Spec>
+doesn't really provide an easy way to figure this out (but
+C<Path::Class> does...).
+
+=item dispatch($action, %args)
+
+[version 0.03]
+
+Invokes the build action C<$action>. Optionally, a list of options
+and their values can be passed in. This is equivalent to invoking an
+action at the command line, passing in a list of options.
+
+Custom options that have not been registered must be passed in as a
+hash reference in a key named "args":
+
+ $build->dispatch('foo', verbose => 1, args => { my_option => 'value' });
+
+This method is intended to be used to programmatically invoke build
+actions, e.g. by applications controlling Module::Build-based builds
+rather than by subclasses.
+
+See also L<depends_on()|/"depends_on(@actions)">. The main
+distinction between the two is that C<depends_on()> is meant to call
+an action from inside another action, whereas C<dispatch()> is meant
+to set the very top action in motion.
+
+=item dist_dir()
+
+[version 0.28]
+
+Returns the name of the directory that will be created during the
+C<dist> action. The name is derived from the C<dist_name> and
+C<dist_version> properties.
+
+=item dist_name()
+
+[version 0.21]
+
+Returns the name of the current distribution, as passed to the
+C<new()> method in a C<dist_name> or modified C<module_name>
+parameter.
+
+=item dist_version()
+
+[version 0.21]
+
+Returns the version of the current distribution, as determined by the
+C<new()> method from a C<dist_version>, C<dist_version_from>, or
+C<module_name> parameter.
+
+=item do_system($cmd, @args)
+
+[version 0.21]
+
+This is a fairly simple wrapper around Perl's C<system()> built-in
+command. Given a command and an array of optional arguments, this
+method will print the command to C<STDOUT>, and then execute it using
+Perl's C<system()>. It returns true or false to indicate success or
+failure (the opposite of how C<system()> works, but more intuitive).
+
+Note that if you supply a single argument to C<do_system()>, it
+will/may be processed by the system's shell, and any special
+characters will do their special things. If you supply multiple
+arguments, no shell will get involved and the command will be executed
+directly.
+
+=item feature($name)
+
+=item feature($name => $value)
+
+[version 0.26]
+
+With a single argument, returns true if the given feature is set.
+With two arguments, sets the given feature to the given boolean value.
+In this context, a "feature" is any optional functionality of an
+installed module. For instance, if you write a module that could
+optionally support a MySQL or PostgreSQL backend, you might create
+features called C<mysql_support> and C<postgres_support>, and set them
+to true/false depending on whether the user has the proper databases
+installed and configured.
+
+Features set in this way using the Module::Build object will be
+available for querying during the build/test process and after
+installation via the generated C<...::ConfigData> module, as
+C<< ...::ConfigData->feature($name) >>.
+
+The C<feature()> and C<config_data()> methods represent
+Module::Build's main support for configuration of installed modules.
+See also L<Module::Build::Authoring/"SAVING CONFIGURATION INFORMATION">.
+
+=item fix_shebang_line(@files)
+
+[version 0.??]
+
+Modify any "shebang" line in the specified files to use the path to the
+perl executable being used for the current build. Files are modified
+in-place. The existing shebang line must have a command that contains
+"C<perl>"; arguments to the command do not count. In particular, this
+means that the use of C<#!/usr/bin/env perl> will not be changed.
+
+For an explanation of shebang lines, see
+L<http://en.wikipedia.org/wiki/Shebang_%28Unix%29>.
+
+=item have_c_compiler()
+
+[version 0.21]
+
+Returns true if the current system seems to have a working C compiler.
+We currently determine this by attempting to compile a simple C source
+file and reporting whether the attempt was successful.
+
+=item install_base_relpaths()
+
+=item install_base_relpaths($type)
+
+=item install_base_relpaths($type => $path)
+
+[version 0.28]
+
+Set or retrieve the relative paths that are appended to
+C<install_base> for any installable element. This is useful if you
+want to set the relative install path for custom build elements.
+
+With no argument, it returns a reference to a hash containing all
+elements and their respective values. This hash should not be modified
+directly; use the multiple argument below form to change values.
+
+The single argument form returns the value associated with the
+element C<$type>.
+
+The multiple argument form allows you to set the paths for element types.
+C<$value> must be a relative path using Unix-like paths. (A series of
+directories separated by slashes, e.g. C<foo/bar>.) The return value is a
+localized path based on C<$value>.
+
+Assigning the value C<undef> to an element causes it to be removed.
+
+=item install_destination($type)
+
+[version 0.28]
+
+Returns the directory in which items of type C<$type> (e.g. C<lib>,
+C<arch>, C<bin>, or anything else returned by the L</install_types()>
+method) will be installed during the C<install> action. Any settings
+for C<install_path>, C<install_base>, and C<prefix> are taken into
+account when determining the return value.
+
+=item install_path()
+
+=item install_path($type)
+
+=item install_path($type => $path)
+
+[version 0.28]
+
+Set or retrieve paths for specific installable elements. This is
+useful when you want to examine any explicit install paths specified
+by the user on the command line, or if you want to set the install
+path for a specific installable element based on another attribute
+like C<install_base()>.
+
+With no argument, it returns a reference to a hash containing all
+elements and their respective values. This hash should not be modified
+directly; use the multiple argument below form to change values.
+
+The single argument form returns the value associated with the
+element C<$type>.
+
+The multiple argument form allows you to set the paths for element types.
+The supplied C<$path> should be an absolute path to install elements
+of C<$type>. The return value is C<$path>.
+
+Assigning the value C<undef> to an element causes it to be removed.
+
+=item install_types()
+
+[version 0.28]
+
+Returns a list of installable types that this build knows about.
+These types each correspond to the name of a directory in F<blib/>,
+and the list usually includes items such as C<lib>, C<arch>, C<bin>,
+C<script>, C<libdoc>, C<bindoc>, and if HTML documentation is to be
+built, C<libhtml> and C<binhtml>. Other user-defined types may also
+exist.
+
+=item invoked_action()
+
+[version 0.28]
+
+This is the name of the original action invoked by the user. This
+value is set when the user invokes F<Build.PL>, the F<Build> script,
+or programmatically through the L<dispatch()|/"dispatch($action, %args)">
+method. It does not change as sub-actions are executed as
+dependencies are evaluated.
+
+To get the name of the currently executing dependency, see
+L</current_action()> above.
+
+=item notes()
+
+=item notes($key)
+
+=item notes($key => $value)
+
+[version 0.20]
+
+The C<notes()> value allows you to store your own persistent
+information about the build, and to share that information among
+different entities involved in the build. See the example in the
+C<current()> method.
+
+The C<notes()> method is essentially a glorified hash access. With no
+arguments, C<notes()> returns the entire hash of notes. With one argument,
+C<notes($key)> returns the value associated with the given key. With two
+arguments, C<notes($key, $value)> sets the value associated with the given key
+to C<$value> and returns the new value.
+
+The lifetime of the C<notes> data is for "a build" - that is, the
+C<notes> hash is created when C<perl Build.PL> is run (or when the
+C<new()> method is run, if the Module::Build Perl API is being used
+instead of called from a shell), and lasts until C<perl Build.PL> is
+run again or the C<clean> action is run.
+
+=item orig_dir()
+
+[version 0.28]
+
+Returns a string containing the working directory that was in effect
+before the F<Build> script chdir()-ed into the C<base_dir>. This
+might be useful for writing wrapper tools that might need to chdir()
+back out.
+
+=item os_type()
+
+[version 0.04]
+
+If you're subclassing Module::Build and some code needs to alter its
+behavior based on the current platform, you may only need to know
+whether you're running on Windows, Unix, MacOS, VMS, etc., and not the
+fine-grained value of Perl's C<$^O> variable. The C<os_type()> method
+will return a string like C<Windows>, C<Unix>, C<MacOS>, C<VMS>, or
+whatever is appropriate. If you're running on an unknown platform, it
+will return C<undef> - there shouldn't be many unknown platforms
+though.
+
+=item is_vmsish()
+
+=item is_windowsish()
+
+=item is_unixish()
+
+Convenience functions that return a boolean value indicating whether
+this platform behaves respectively like VMS, Windows, or Unix. For
+arbitrary reasons other platforms don't get their own such functions,
+at least not yet.
+
+
+=item prefix_relpaths()
+
+=item prefix_relpaths($installdirs)
+
+=item prefix_relpaths($installdirs, $type)
+
+=item prefix_relpaths($installdirs, $type => $path)
+
+[version 0.28]
+
+Set or retrieve the relative paths that are appended to C<prefix> for
+any installable element. This is useful if you want to set the
+relative install path for custom build elements.
+
+With no argument, it returns a reference to a hash containing all
+elements and their respective values as defined by the current
+C<installdirs> setting.
+
+With a single argument, it returns a reference to a hash containing
+all elements and their respective values as defined by
+C<$installdirs>.
+
+The hash returned by the above calls should not be modified directly;
+use the three-argument below form to change values.
+
+The two argument form returns the value associated with the
+element C<$type>.
+
+The multiple argument form allows you to set the paths for element types.
+C<$value> must be a relative path using Unix-like paths. (A series of
+directories separated by slashes, e.g. C<foo/bar>.) The return value is a
+localized path based on C<$value>.
+
+Assigning the value C<undef> to an element causes it to be removed.
+
+=item prepare_metadata()
+
+[version 0.28]
+
+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.
+
+ 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;
+ }
+
+=item prereq_failures()
+
+[version 0.11]
+
+Returns a data structure containing information about any failed
+prerequisites (of any of the types described above), or C<undef> if
+all prerequisites are met.
+
+The data structure returned is a hash reference. The top level keys
+are the type of prerequisite failed, one of "requires",
+"build_requires", "conflicts", or "recommends". The associated values
+are hash references whose keys are the names of required (or
+conflicting) modules. The associated values of those are hash
+references indicating some information about the failure. For example:
+
+ {
+ have => '0.42',
+ need => '0.59',
+ message => 'Version 0.42 is installed, but we need version 0.59',
+ }
+
+or
+
+ {
+ have => '<none>',
+ need => '0.59',
+ message => 'Prerequisite Foo isn't installed',
+ }
+
+This hash has the same structure as the hash returned by the
+C<check_installed_status()> method, except that in the case of
+"conflicts" dependencies we change the "need" key to "conflicts" and
+construct a proper message.
+
+Examples:
+
+ # Check a required dependency on Foo::Bar
+ if ( $build->prereq_failures->{requires}{Foo::Bar} ) { ...
+
+ # Check whether there were any failures
+ if ( $build->prereq_failures ) { ...
+
+ # Show messages for all failures
+ my $failures = $build->prereq_failures;
+ while (my ($type, $list) = each %$failures) {
+ while (my ($name, $hash) = each %$list) {
+ print "Failure for $name: $hash->{message}\n";
+ }
+ }
+
+=item prereq_data()
+
+[version 0.32]
+
+Returns a reference to a hash describing all prerequisites. The keys of the
+hash will the various prerequisite types ('requires', 'build_requires',
+'configure_requires', 'recommends', or 'conflicts') and the values will
+references to hashes of module names and version numbers. Only prerequisites
+types that are defined will be included. The C<prereq_data> action is just a
+thin wrapper around the C<prereq_data()> method and dumps the hash as a string
+that can be loaded using C<eval()>.
+
+=item prereq_report()
+
+[version 0.28]
+
+Returns a human-readable (table-form) string showing all
+prerequisites, the versions required, and the versions actually
+installed. This can be useful for reviewing the configuration of your
+system prior to a build, or when compiling data to send for a bug
+report. The C<prereq_report> action is just a thin wrapper around the
+C<prereq_report()> method.
+
+=item prompt($message, $default)
+
+[version 0.12]
+
+Asks the user a question and returns their response as a string. The
+first argument specifies the message to display to the user (for
+example, C<"Where do you keep your money?">). The second argument,
+which is optional, specifies a default answer (for example,
+C<"wallet">). The user will be asked the question once.
+
+If C<prompt()> detects that it is not running interactively and there
+is nothing on STDIN or if the PERL_MM_USE_DEFAULT environment variable
+is set to true, the $default will be used without prompting.
+
+To prevent automated processes from blocking, the user must either set
+PERL_MM_USE_DEFAULT or attach something to STDIN (this can be a
+pipe/file containing a scripted set of answers or /dev/null.)
+
+If no $default is provided an empty string will be used instead. In
+non-interactive mode, the absence of $default is an error (though
+explicitly passing C<undef()> as the default is valid as of 0.27.)
+
+This method may be called as a class or object method.
+
+=item recommends()
+
+[version 0.21]
+
+Returns a hash reference indicating the C<recommends> prerequisites
+that were passed to the C<new()> method.
+
+=item requires()
+
+[version 0.21]
+
+Returns a hash reference indicating the C<requires> prerequisites that
+were passed to the C<new()> method.
+
+=item rscan_dir($dir, $pattern)
+
+[version 0.28]
+
+Uses C<File::Find> to traverse the directory C<$dir>, returning a
+reference to an array of entries matching C<$pattern>. C<$pattern>
+may either be a regular expression (using C<qr//> or just a plain
+string), or a reference to a subroutine that will return true for
+wanted entries. If C<$pattern> is not given, all entries will be
+returned.
+
+Examples:
+
+ # All the *.pm files in lib/
+ $m->rscan_dir('lib', qr/\.pm$/)
+
+ # All the files in blib/ that aren't *.html files
+ $m->rscan_dir('blib', sub {-f $_ and not /\.html$/});
+
+ # All the files in t/
+ $m->rscan_dir('t');
+
+=item runtime_params()
+
+=item runtime_params($key)
+
+[version 0.28]
+
+The C<runtime_params()> method stores the values passed on the command line
+for valid properties (that is, any command line options for which
+C<valid_property()> returns a true value). The value on the command line may
+override the default value for a property, as well as any value specified in a
+call to C<new()>. This allows you to programmatically tell if C<perl Build.PL>
+or any execution of C<./Build> had command line options specified that
+override valid properties.
+
+The C<runtime_params()> method is essentially a glorified read-only hash. With
+no arguments, C<runtime_params()> returns the entire hash of properties
+specified on the command line. With one argument, C<runtime_params($key)>
+returns the value associated with the given key.
+
+The lifetime of the C<runtime_params> data is for "a build" - that is, the
+C<runtime_params> hash is created when C<perl Build.PL> is run (or when the
+C<new()> method is called, if the Module::Build Perl API is being used instead
+of called from a shell), and lasts until C<perl Build.PL> is run again or the
+C<clean> action is run.
+
+=item script_files()
+
+[version 0.18]
+
+Returns a hash reference whose keys are the perl script files to be
+installed, if any. This corresponds to the C<script_files> parameter to the
+C<new()> method. With an optional argument, this parameter may be set
+dynamically.
+
+For backward compatibility, the C<scripts()> method does exactly the
+same thing as C<script_files()>. C<scripts()> is deprecated, but it
+will stay around for several versions to give people time to
+transition.
+
+=item up_to_date($source_file, $derived_file)
+
+=item up_to_date(\@source_files, \@derived_files)
+
+[version 0.20]
+
+This method can be used to compare a set of source files to a set of
+derived files. If any of the source files are newer than any of the
+derived files, it returns false. Additionally, if any of the derived
+files do not exist, it returns false. Otherwise it returns true.
+
+The arguments may be either a scalar or an array reference of file
+names.
+
+=item y_n($message, $default)
+
+[version 0.12]
+
+Asks the user a yes/no question using C<prompt()> and returns true or
+false accordingly. The user will be asked the question repeatedly
+until they give an answer that looks like "yes" or "no".
+
+The first argument specifies the message to display to the user (for
+example, C<"Shall I invest your money for you?">), and the second
+argument specifies the default answer (for example, C<"y">).
+
+Note that the default is specified as a string like C<"y"> or C<"n">,
+and the return value is a Perl boolean value like 1 or 0. I thought
+about this for a while and this seemed like the most useful way to do
+it.
+
+This method may be called as a class or object method.
+
+=back
+
+
+=head2 Autogenerated Accessors
+
+In addition to the aforementioned methods, there are also some get/set
+accessor methods for the following properties:
+
+=over 4
+
+=item PL_files()
+
+=item allow_mb_mismatch()
+
+=item auto_configure_requires()
+
+=item autosplit()
+
+=item base_dir()
+
+=item bindoc_dirs()
+
+=item blib()
+
+=item build_bat()
+
+=item build_class()
+
+=item build_elements()
+
+=item build_requires()
+
+=item build_script()
+
+=item c_source()
+
+=item config_dir()
+
+=item configure_requires()
+
+=item conflicts()
+
+=item create_license()
+
+=item create_makefile_pl()
+
+=item create_packlist()
+
+=item create_readme()
+
+=item debugger()
+
+=item destdir()
+
+=item get_options()
+
+=item html_css()
+
+=item include_dirs()
+
+=item install_base()
+
+=item install_sets()
+
+=item installdirs()
+
+=item libdoc_dirs()
+
+=item license()
+
+=item magic_number()
+
+=item mb_version()
+
+=item meta_add()
+
+=item meta_merge()
+
+=item metafile()
+
+=item module_name()
+
+=item orig_dir()
+
+=item original_prefix()
+
+=item perl()
+
+=item pm_files()
+
+=item pod_files()
+
+=item pollute()
+
+=item prefix()
+
+=item prereq_action_types()
+
+=item program_name()
+
+=item quiet()
+
+=item recommends()
+
+=item recurse_into()
+
+=item recursive_test_files()
+
+=item requires()
+
+=item scripts()
+
+=item sign()
+
+=item tap_harness_args()
+
+=item test_file_exts()
+
+=item use_rcfile()
+
+=item use_tap_harness()
+
+=item verbose()
+
+=item xs_files()
+
+=back
+
+
+=head1 MODULE METADATA
+
+If you would like to add other useful metadata, C<Module::Build>
+supports this with the C<meta_add> and C<meta_merge> arguments to
+L</new>. The authoritative list of supported metadata can be found at
+L<http://module-build.sourceforge.net/META-spec-current.html>, but for
+convenience - here are a few of the more useful ones:
+
+=over 4
+
+=item keywords
+
+For describing the distribution using keyword (or "tags") in order to
+make CPAN.org indexing and search more efficient and useful.
+
+See L<http://module-build.sourceforge.net/META-spec-current.html#keywords>.
+
+=item resources
+
+A list of additional resources available for users of the
+distribution. This can include links to a homepage on the web, a
+bug tracker, the repository location, a even subscription page for the
+distribution mailing list.
+
+See L<http://module-build.sourceforge.net/META-spec-current.html#resources>.
+
+=back
+
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+
+=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), L<Module::Build::Authoring>(3),
+L<Module::Build::Cookbook>(3), L<ExtUtils::MakeMaker>(3), L<YAML>(3)
+
+F<META.yml> Specification:
+L<http://module-build.sourceforge.net/META-spec-current.html>
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Authoring.pod b/inc/Module-Build/Module/Build/Authoring.pod
new file mode 100644
index 0000000..38fb3f0
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Authoring.pod
@@ -0,0 +1,323 @@
+=head1 NAME
+
+Module::Build::Authoring - Authoring Module::Build modules
+
+
+=head1 DESCRIPTION
+
+When creating a C<Build.PL> script for a module, something like the
+following code will typically be used:
+
+ use Module::Build;
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ requires => {
+ 'perl' => '5.6.1',
+ 'Some::Module' => '1.23',
+ 'Other::Module' => '>= 1.2, != 1.5, < 2.0',
+ },
+ );
+ $build->create_build_script;
+
+A simple module could get away with something as short as this for its
+C<Build.PL> script:
+
+ use Module::Build;
+ Module::Build->new(
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ )->create_build_script;
+
+The model used by C<Module::Build> is a lot like the C<MakeMaker>
+metaphor, with the following correspondences:
+
+ In Module::Build In ExtUtils::MakeMaker
+ --------------------------- ------------------------
+ Build.PL (initial script) Makefile.PL (initial script)
+ Build (a short perl script) Makefile (a long Makefile)
+ _build/ (saved state info) various config text in the Makefile
+
+Any customization can be done simply by subclassing C<Module::Build>
+and adding a method called (for example) C<ACTION_test>, overriding
+the default 'test' action. You could also add a method called
+C<ACTION_whatever>, and then you could perform the action C<Build
+whatever>.
+
+For information on providing compatibility with
+C<ExtUtils::MakeMaker>, see L<Module::Build::Compat> and
+L<http://www.makemaker.org/wiki/index.cgi?ModuleBuildConversionGuide>.
+
+
+=head1 STRUCTURE
+
+Module::Build creates a class hierarchy conducive to customization.
+Here is the parent-child class hierarchy in classy ASCII art:
+
+ /--------------------\
+ | Your::Parent | (If you subclass Module::Build)
+ \--------------------/
+ |
+ |
+ /--------------------\ (Doesn't define any functionality
+ | Module::Build | of its own - just figures out what
+ \--------------------/ other modules to load.)
+ |
+ |
+ /-----------------------------------\ (Some values of $^O may
+ | Module::Build::Platform::$^O | define specialized functionality.
+ \-----------------------------------/ Otherwise it's ...::Default, a
+ | pass-through class.)
+ |
+ /--------------------------\
+ | Module::Build::Base | (Most of the functionality of
+ \--------------------------/ Module::Build is defined here.)
+
+
+=head1 SUBCLASSING
+
+Right now, there are two ways to subclass Module::Build. The first
+way is to create a regular module (in a C<.pm> file) that inherits
+from Module::Build, and use that module's class instead of using
+Module::Build directly:
+
+ ------ in Build.PL: ----------
+ #!/usr/bin/perl
+
+ use lib q(/nonstandard/library/path);
+ use My::Builder; # Or whatever you want to call it
+
+ my $build = My::Builder->new
+ (
+ module_name => 'Foo::Bar', # All the regular args...
+ license => 'perl',
+ dist_author => 'A N Other <[email protected]>',
+ requires => { Carp => 0 }
+ );
+ $build->create_build_script;
+
+This is relatively straightforward, and is the best way to do things
+if your My::Builder class contains lots of code. The
+C<create_build_script()> method will ensure that the current value of
+C<@INC> (including the C</nonstandard/library/path>) is propagated to
+the Build script, so that My::Builder can be found when running build
+actions. If you find that you need to C<chdir> into a different directories
+in your subclass methods or actions, be sure to always return to the original
+directory (available via the C<base_dir()> method before returning control
+to the parent class. This is important to avoid data serialization problems.
+
+For very small additions, Module::Build provides a C<subclass()>
+method that lets you subclass Module::Build more conveniently, without
+creating a separate file for your module:
+
+ ------ in Build.PL: ----------
+ #!/usr/bin/perl
+
+ use Module::Build;
+ my $class = Module::Build->subclass
+ (
+ class => 'My::Builder',
+ code => q{
+ sub ACTION_foo {
+ print "I'm fooing to death!\n";
+ }
+ },
+ );
+
+ my $build = $class->new
+ (
+ module_name => 'Foo::Bar', # All the regular args...
+ license => 'perl',
+ dist_author => 'A N Other <[email protected]>',
+ requires => { Carp => 0 }
+ );
+ $build->create_build_script;
+
+Behind the scenes, this actually does create a C<.pm> file, since the
+code you provide must persist after Build.PL is run if it is to be
+very useful.
+
+See also the documentation for the L<Module::Build::API/"subclass()">
+method.
+
+
+=head1 PREREQUISITES
+
+=head2 Types of prerequisites
+
+To specify what versions of other modules are used by this
+distribution, several types of prerequisites can be defined with the
+following parameters:
+
+=over 3
+
+=item configure_requires
+
+Items that must be installed I<before> configuring this distribution
+(i.e. before running the F<Build.PL> script). This might be a
+specific minimum version of C<Module::Build> or any other module the
+F<Build.PL> needs in order to do its stuff. Clients like C<CPAN.pm>
+or C<CPANPLUS> will be expected to pick C<configure_requires> out of the
+F<META.yml> file and install these items before running the
+C<Build.PL>.
+
+If no configure_requires is specified, the current version of Module::Build
+is automatically added to configure_requires.
+
+=item build_requires
+
+Items that are necessary for building and testing this distribution,
+but aren't necessary after installation. This can help users who only
+want to install these items temporarily. It also helps reduce the
+size of the CPAN dependency graph if everything isn't smooshed into
+C<requires>.
+
+=item requires
+
+Items that are necessary for basic functioning.
+
+=item recommends
+
+Items that are recommended for enhanced functionality, but there are
+ways to use this distribution without having them installed. You
+might also think of this as "can use" or "is aware of" or "changes
+behavior in the presence of".
+
+=item conflicts
+
+Items that can cause problems with this distribution when installed.
+This is pretty rare.
+
+=back
+
+=head2 Format of prerequisites
+
+The prerequisites are given in a hash reference, where the keys are
+the module names and the values are version specifiers:
+
+ requires => {
+ Foo::Module => '2.4',
+ Bar::Module => 0,
+ Ken::Module => '>= 1.2, != 1.5, < 2.0',
+ perl => '5.6.0'
+ },
+
+The above four version specifiers have different effects. The value
+C<'2.4'> means that B<at least> version 2.4 of C<Foo::Module> must be
+installed. The value C<0> means that B<any> version of C<Bar::Module>
+is acceptable, even if C<Bar::Module> doesn't define a version. The
+more verbose value C<'E<gt>= 1.2, != 1.5, E<lt> 2.0'> means that
+C<Ken::Module>'s version must be B<at least> 1.2, B<less than> 2.0,
+and B<not equal to> 1.5. The list of criteria is separated by commas,
+and all criteria must be satisfied.
+
+A special C<perl> entry lets you specify the versions of the Perl
+interpreter that are supported by your module. The same version
+dependency-checking semantics are available, except that we also
+understand perl's new double-dotted version numbers.
+
+=head2 XS Extensions
+
+Modules which need to compile XS code should list C<ExtUtils::CBuilder>
+as a C<build_requires> element.
+
+
+=head1 SAVING CONFIGURATION INFORMATION
+
+Module::Build provides a very convenient way to save configuration
+information that your installed modules (or your regression tests) can
+access. If your Build process calls the C<feature()> or
+C<config_data()> methods, then a C<Foo::Bar::ConfigData> module will
+automatically be created for you, where C<Foo::Bar> is the
+C<module_name> parameter as passed to C<new()>. This module provides
+access to the data saved by these methods, and a way to update the
+values. There is also a utility script called C<config_data>
+distributed with Module::Build that provides a command line interface
+to this same functionality. See also the generated
+C<Foo::Bar::ConfigData> documentation, and the C<config_data>
+script's documentation, for more information.
+
+
+=head1 STARTING MODULE DEVELOPMENT
+
+When starting development on a new module, it's rarely worth your time
+to create a tree of all the files by hand. Some automatic
+module-creators are available: the oldest is C<h2xs>, which has
+shipped with perl itself for a long time. Its name reflects the fact
+that modules were originally conceived of as a way to wrap up a C
+library (thus the C<h> part) into perl extensions (thus the C<xs>
+part).
+
+These days, C<h2xs> has largely been superseded by modules like
+C<ExtUtils::ModuleMaker>, and C<Module::Starter>. They have varying
+degrees of support for C<Module::Build>.
+
+
+=head1 AUTOMATION
+
+One advantage of Module::Build is that since it's implemented as Perl
+methods, you can invoke these methods directly if you want to install
+a module non-interactively. For instance, the following Perl script
+will invoke the entire build/install procedure:
+
+ my $build = Module::Build->new(module_name => 'MyModule');
+ $build->dispatch('build');
+ $build->dispatch('test');
+ $build->dispatch('install');
+
+If any of these steps encounters an error, it will throw a fatal
+exception.
+
+You can also pass arguments as part of the build process:
+
+ my $build = Module::Build->new(module_name => 'MyModule');
+ $build->dispatch('build');
+ $build->dispatch('test', verbose => 1);
+ $build->dispatch('install', sitelib => '/my/secret/place/');
+
+Building and installing modules in this way skips creating the
+C<Build> script.
+
+
+=head1 MIGRATION
+
+Note that if you want to provide both a F<Makefile.PL> and a
+F<Build.PL> for your distribution, you probably want to add the
+following to C<WriteMakefile> in your F<Makefile.PL> so that C<MakeMaker>
+doesn't try to run your F<Build.PL> as a normal F<.PL> file:
+
+ PL_FILES => {},
+
+You may also be interested in looking at the C<Module::Build::Compat>
+module, which can automatically create various kinds of F<Makefile.PL>
+compatibility layers.
+
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+Development questions, bug reports, and patches should be sent to the
+Module-Build mailing list at <[email protected]>.
+
+Bug reports are also welcome at
+<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Module-Build>.
+
+The latest development version is available from the Subversion
+repository at <https://svn.perl.org/modules/Module-Build/trunk/>
+
+
+=head1 SEE ALSO
+
+perl(1), L<Module::Build>(3), L<Module::Build::API>(3),
+L<Module::Build::Cookbook>(3), L<ExtUtils::MakeMaker>(3), L<YAML>(3)
+
+F<META.yml> Specification:
+L<http://module-build.sourceforge.net/META-spec-current.html>
+
+L<http://www.dsmit.com/cons/>
+
+L<http://search.cpan.org/dist/PerlBuildSystem/>
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Base.pm b/inc/Module-Build/Module/Build/Base.pm
new file mode 100644
index 0000000..a93f058
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Base.pm
@@ -0,0 +1,4653 @@
+# -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
+# vim:ts=8:sw=2:et:sta:sts=2
+package Module::Build::Base;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+BEGIN { require 5.00503 }
+
+use Carp;
+use Cwd ();
+use File::Copy ();
+use File::Find ();
+use File::Path ();
+use File::Basename ();
+use File::Spec 0.82 ();
+use File::Compare ();
+use Module::Build::Dumper ();
+use IO::File ();
+use Text::ParseWords ();
+
+use Module::Build::ModuleInfo;
+use Module::Build::Notes;
+use Module::Build::Config;
+
+
+#################### Constructors ###########################
+sub new {
+ my $self = shift()->_construct(@_);
+
+ $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->dist_name;
+ $self->dist_version;
+
+ $self->_set_install_paths;
+ $self->_find_nested_builds;
+
+ return $self;
+}
+
+sub resume {
+ my $package = shift;
+ my $self = $package->_construct(@_);
+ $self->read_config;
+
+ # 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
+ # subclass here and re-delegate the resume() method to it.
+ unless ( UNIVERSAL::isa($package, $self->build_class) ) {
+ my $build_class = $self->build_class;
+ my $config_dir = $self->config_dir || '_build';
+ my $build_lib = File::Spec->catdir( $config_dir, 'lib' );
+ unshift( @INC, $build_lib );
+ unless ( $build_class->can('new') ) {
+ eval "require $build_class; 1" or die "Failed to re-load '$build_class': [email protected]";
+ }
+ return $build_class->resume(@_);
+ }
+
+ unless ($self->_perl_is_same($self->{properties}{perl})) {
+ my $perl = $self->find_perl_interpreter;
+ $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) {
+ my $mb_version = $Module::Build::VERSION;
+ die(" * ERROR: Configuration was initially created with Module::Build version '$self->{properties}{mb_version}',\n".
+ " but we are now using version '$mb_version'. Please re-run the Build.PL or Makefile.PL script,\n".
+ " 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';
+
+ $self->_set_install_paths;
+
+ 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';
+ }
+ return $package->resume;
+}
+
+sub current {
+ # hmm, wonder what the right thing to do here is
+ local @ARGV;
+ return shift()->resume;
+}
+
+sub _construct {
+ my ($package, %input) = @_;
+
+ my $args = delete $input{args} || {};
+ my $config = delete $input{config} || {};
+
+ my $self = bless {
+ args => {%$args},
+ config => Module::Build::Config->new(values => $config),
+ properties => {
+ base_dir => $package->cwd,
+ mb_version => $Module::Build::VERSION,
+ %input,
+ },
+ phash => {},
+ stash => {}, # temporary caching, not stored in _build
+ }, $package;
+
+ $self->_set_defaults;
+ my ($p, $ph) = ($self->{properties}, $self->{phash});
+
+ foreach (qw(notes config_data features runtime_params cleanup auto_features)) {
+ my $file = File::Spec->catfile($self->config_dir, $_);
+ $ph->{$_} = Module::Build::Notes->new(file => $file);
+ $ph->{$_}->restore if -e $file;
+ if (exists $p->{$_}) {
+ my $vals = delete $p->{$_};
+ while (my ($k, $v) = each %$vals) {
+ $self->$_($k, $v);
+ }
+ }
+ }
+
+ # The following warning could be unnecessary if the user is running
+ # an embedded perl, but there aren't too many of those around, and
+ # embedded perls aren't usually used to install modules, and the
+ # installation process sometimes needs to run external scripts
+ # (e.g. to run tests).
+ $p->{perl} = $self->find_perl_interpreter
+ or $self->log_warn("Warning: Can't locate your perl binary");
+
+ my $blibdir = sub { File::Spec->catdir($p->{blib}, @_) };
+ $p->{bindoc_dirs} ||= [ $blibdir->("script") ];
+ $p->{libdoc_dirs} ||= [ $blibdir->("lib"), $blibdir->("arch") ];
+
+ $p->{dist_author} = [ $p->{dist_author} ] if defined $p->{dist_author} and not ref $p->{dist_author};
+
+ # Synonyms
+ $p->{requires} = delete $p->{prereq} if defined $p->{prereq};
+ $p->{script_files} = delete $p->{scripts} if defined $p->{scripts};
+
+ # Convert to from shell strings to arrays
+ for ('extra_compiler_flags', 'extra_linker_flags') {
+ $p->{$_} = [ $self->split_like_shell($p->{$_}) ] if exists $p->{$_};
+ }
+
+ # Convert to arrays
+ for ('include_dirs') {
+ $p->{$_} = [ $p->{$_} ] if exists $p->{$_} && !ref $p->{$_}
+ }
+
+ $self->add_to_cleanup( @{delete $p->{add_to_cleanup}} )
+ if $p->{add_to_cleanup};
+
+ return $self;
+}
+
+################## End constructors #########################
+
+sub log_info {
+ my $self = shift;
+ print @_ unless(ref($self) and $self->quiet);
+}
+sub log_verbose {
+ my $self = shift;
+ $self->log_info(@_) if(ref($self) and $self->verbose);
+}
+sub log_warn {
+ # Try to make our call stack invisible
+ shift;
+ if (@_ and $_[-1] !~ /\n$/) {
+ my (undef, $file, $line) = caller();
+ warn @_, " at $file line $line.\n";
+ } else {
+ warn @_;
+ }
+}
+
+
+sub _set_install_paths {
+ my $self = shift;
+ my $c = $self->{config};
+ my $p = $self->{properties};
+
+ my @libstyle = $c->get('installstyle') ?
+ File::Spec->splitdir($c->get('installstyle')) : qw(lib perl5);
+ my $arch = $c->get('archname');
+ my $version = $c->get('version');
+
+ my $bindoc = $c->get('installman1dir') || undef;
+ my $libdoc = $c->get('installman3dir') || undef;
+
+ my $binhtml = $c->get('installhtml1dir') || $c->get('installhtmldir') || undef;
+ my $libhtml = $c->get('installhtml3dir') || $c->get('installhtmldir') || undef;
+
+ $p->{install_sets} =
+ {
+ core => {
+ lib => $c->get('installprivlib'),
+ arch => $c->get('installarchlib'),
+ bin => $c->get('installbin'),
+ script => $c->get('installscript'),
+ bindoc => $bindoc,
+ libdoc => $libdoc,
+ binhtml => $binhtml,
+ libhtml => $libhtml,
+ },
+ site => {
+ lib => $c->get('installsitelib'),
+ arch => $c->get('installsitearch'),
+ bin => $c->get('installsitebin') || $c->get('installbin'),
+ script => $c->get('installsitescript') ||
+ $c->get('installsitebin') || $c->get('installscript'),
+ bindoc => $c->get('installsiteman1dir') || $bindoc,
+ libdoc => $c->get('installsiteman3dir') || $libdoc,
+ binhtml => $c->get('installsitehtml1dir') || $binhtml,
+ libhtml => $c->get('installsitehtml3dir') || $libhtml,
+ },
+ vendor => {
+ lib => $c->get('installvendorlib'),
+ arch => $c->get('installvendorarch'),
+ bin => $c->get('installvendorbin') || $c->get('installbin'),
+ script => $c->get('installvendorscript') ||
+ $c->get('installvendorbin') || $c->get('installscript'),
+ bindoc => $c->get('installvendorman1dir') || $bindoc,
+ libdoc => $c->get('installvendorman3dir') || $libdoc,
+ binhtml => $c->get('installvendorhtml1dir') || $binhtml,
+ libhtml => $c->get('installvendorhtml3dir') || $libhtml,
+ },
+ };
+
+ $p->{original_prefix} =
+ {
+ core => $c->get('installprefixexp') || $c->get('installprefix') ||
+ $c->get('prefixexp') || $c->get('prefix') || '',
+ site => $c->get('siteprefixexp'),
+ vendor => $c->get('usevendorprefix') ? $c->get('vendorprefixexp') : '',
+ };
+ $p->{original_prefix}{site} ||= $p->{original_prefix}{core};
+
+ # Note: you might be tempted to use $Config{installstyle} here
+ # instead of hard-coding lib/perl5, but that's been considered and
+ # (at least for now) rejected. `perldoc Config` has some wisdom
+ # about it.
+ $p->{install_base_relpaths} =
+ {
+ lib => ['lib', 'perl5'],
+ arch => ['lib', 'perl5', $arch],
+ bin => ['bin'],
+ script => ['bin'],
+ bindoc => ['man', 'man1'],
+ libdoc => ['man', 'man3'],
+ binhtml => ['html'],
+ libhtml => ['html'],
+ };
+
+ $p->{prefix_relpaths} =
+ {
+ core => {
+ lib => [@libstyle],
+ arch => [@libstyle, $version, $arch],
+ bin => ['bin'],
+ script => ['bin'],
+ bindoc => ['man', 'man1'],
+ libdoc => ['man', 'man3'],
+ binhtml => ['html'],
+ libhtml => ['html'],
+ },
+ vendor => {
+ lib => [@libstyle],
+ arch => [@libstyle, $version, $arch],
+ bin => ['bin'],
+ script => ['bin'],
+ bindoc => ['man', 'man1'],
+ libdoc => ['man', 'man3'],
+ binhtml => ['html'],
+ libhtml => ['html'],
+ },
+ site => {
+ lib => [@libstyle, 'site_perl'],
+ arch => [@libstyle, 'site_perl', $version, $arch],
+ bin => ['bin'],
+ script => ['bin'],
+ bindoc => ['man', 'man1'],
+ libdoc => ['man', 'man3'],
+ binhtml => ['html'],
+ libhtml => ['html'],
+ },
+ };
+
+}
+
+sub _find_nested_builds {
+ my $self = shift;
+ my $r = $self->recurse_into or return;
+
+ my ($file, @r);
+ if (!ref($r) && $r eq 'auto') {
+ local *DH;
+ opendir DH, $self->base_dir
+ or die "Can't scan directory " . $self->base_dir . " for nested builds: $!";
+ while (defined($file = readdir DH)) {
+ my $subdir = File::Spec->catdir( $self->base_dir, $file );
+ next unless -d $subdir;
+ push @r, $subdir if -e File::Spec->catfile( $subdir, 'Build.PL' );
+ }
+ }
+
+ $self->recurse_into(\@r);
+}
+
+sub cwd {
+ return Cwd::cwd();
+}
+
+sub _quote_args {
+ # Returns a string that can become [part of] a command line with
+ # proper quoting so that the subprocess sees this same list of args.
+ my ($self, @args) = @_;
+
+ my @quoted;
+
+ for (@args) {
+ if ( /^[^\s*?!\$<>;\\|'"\[\]\{\}]+$/ ) {
+ # Looks pretty safe
+ push @quoted, $_;
+ } else {
+ # XXX this will obviously have to improve - is there already a
+ # core module lying around that does proper quoting?
+ s/('+)/'"$1"'/g;
+ push @quoted, qq('$_');
+ }
+ }
+
+ return join " ", @quoted;
+}
+
+sub _backticks {
+ my ($self, @cmd) = @_;
+ if ($self->have_forkpipe) {
+ local *FH;
+ my $pid = open *FH, "-|";
+ if ($pid) {
+ return wantarray ? <FH> : join '', <FH>;
+ } else {
+ die "Can't execute @cmd: $!\n" unless defined $pid;
+ exec { $cmd[0] } @cmd;
+ }
+ } else {
+ my $cmd = $self->_quote_args(@cmd);
+ return `$cmd`;
+ }
+}
+
+# Tells us whether the construct open($fh, '-|', @command) is
+# supported. It would probably be better to dynamically sense this.
+sub have_forkpipe { 1 }
+
+# Determine whether a given binary is the same as the perl
+# (configuration) that started this process.
+sub _perl_is_same {
+ my ($self, $perl) = @_;
+
+ my @cmd = ($perl);
+
+ # When run from the perl core, @INC will include the directories
+ # where perl is yet to be installed. We need to reference the
+ # absolute path within the source distribution where it can find
+ # it's Config.pm This also prevents us from picking up a Config.pm
+ # from a different configuration that happens to be already
+ # installed in @INC.
+ if ($ENV{PERL_CORE}) {
+ push @cmd, '-I' . File::Spec->catdir(File::Basename::dirname($perl), 'lib');
+ }
+
+ push @cmd, qw(-MConfig=myconfig -e print -e myconfig);
+ return $self->_backticks(@cmd) eq Config->myconfig;
+}
+
+# cache _discover_perl_interpreter() results
+{
+ my $known_perl;
+ sub find_perl_interpreter {
+ my $self = shift;
+
+ return $known_perl if defined($known_perl);
+ return $known_perl = $self->_discover_perl_interpreter;
+ }
+}
+
+# Returns the absolute path of the perl interpreter used to invoke
+# this process. The path is derived from $^X or $Config{perlpath}. On
+# some platforms $^X contains the complete absolute path of the
+# interpreter, on other it may contain a relative path, or simply
+# 'perl'. This can also vary depending on whether a path was supplied
+# when perl was invoked. Additionally, the value in $^X may omit the
+# executable extension on platforms that use one. It's a fatal error
+# if the interpreter can't be found because it can result in undefined
+# behavior by routines that depend on it (generating errors or
+# invoking the wrong perl.)
+sub _discover_perl_interpreter {
+ my $proto = shift;
+ my $c = ref($proto) ? $proto->{config} : 'Module::Build::Config';
+
+ my $perl = $^X;
+ my $perl_basename = File::Basename::basename($perl);
+
+ my @potential_perls;
+
+ # Try 1, Check $^X for absolute path
+ push( @potential_perls, $perl )
+ if File::Spec->file_name_is_absolute($perl);
+
+ # Try 2, Check $^X for a valid relative path
+ my $abs_perl = File::Spec->rel2abs($perl);
+ push( @potential_perls, $abs_perl );
+
+ # Try 3, Last ditch effort: These two option use hackery to try to locate
+ # a suitable perl. The hack varies depending on whether we are running
+ # from an installed perl or an uninstalled perl in the perl source dist.
+ if ($ENV{PERL_CORE}) {
+
+ # Try 3.A, If we are in a perl source tree, running an uninstalled
+ # perl, we can keep moving up the directory tree until we find our
+ # binary. We wouldn't do this under any other circumstances.
+
+ # CBuilder is also in the core, so it should be available here
+ require ExtUtils::CBuilder;
+ my $perl_src = Cwd::realpath( ExtUtils::CBuilder->perl_src );
+ if ( defined($perl_src) && length($perl_src) ) {
+ my $uninstperl =
+ File::Spec->rel2abs(File::Spec->catfile( $perl_src, $perl_basename ));
+ push( @potential_perls, $uninstperl );
+ }
+
+ } else {
+
+ # Try 3.B, First look in $Config{perlpath}, then search the user's
+ # PATH. We do not want to do either if we are running from an
+ # uninstalled perl in a perl source tree.
+
+ push( @potential_perls, $c->get('perlpath') );
+
+ push( @potential_perls,
+ map File::Spec->catfile($_, $perl_basename), File::Spec->path() );
+ }
+
+ # Now that we've enumerated the potential perls, it's time to test
+ # them to see if any of them match our configuration, returning the
+ # absolute path of the first successful match.
+ my $exe = $c->get('exe_ext');
+ foreach my $thisperl ( @potential_perls ) {
+
+ if (defined $exe) {
+ $thisperl .= $exe unless $thisperl =~ m/$exe$/i;
+ }
+
+ if ( -f $thisperl && $proto->_perl_is_same($thisperl) ) {
+ return $thisperl;
+ }
+ }
+
+ # We've tried all alternatives, and didn't find a perl that matches
+ # our configuration. Throw an exception, and list alternatives we tried.
+ my @paths = map File::Basename::dirname($_), @potential_perls;
+ die "Can't locate the perl binary used to run this script " .
+ "in (@paths)\n";
+}
+
+sub _is_interactive {
+ return -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ; # Pipe?
+}
+
+# NOTE this is a blocking operation if(-t STDIN)
+sub _is_unattended {
+ my $self = shift;
+ return $ENV{PERL_MM_USE_DEFAULT} ||
+ ( !$self->_is_interactive && eof STDIN );
+}
+
+sub _readline {
+ my $self = shift;
+ return undef if $self->_is_unattended;
+
+ my $answer = <STDIN>;
+ chomp $answer if defined $answer;
+ return $answer;
+}
+
+sub prompt {
+ my $self = shift;
+ my $mess = shift
+ or die "prompt() called without a prompt message";
+
+ # use a list to distinguish a default of undef() from no default
+ my @def;
+ @def = (shift) if @_;
+ # use dispdef for output
+ my @dispdef = scalar(@def) ?
+ ('[', (defined($def[0]) ? $def[0] . ' ' : ''), ']') :
+ (' ', '');
+
+ local $|=1;
+ print "$mess ", @dispdef;
+
+ if ( $self->_is_unattended && [email protected] ) {
+ die <<EOF;
+ERROR: This build seems to be unattended, but there is no default value
+for this question. Aborting.
+EOF
+ }
+
+ my $ans = $self->_readline();
+
+ if ( !defined($ans) # Ctrl-D or unattended
+ or !length($ans) ) { # User hit return
+ print "$dispdef[1]\n";
+ $ans = scalar(@def) ? $def[0] : '';
+ }
+
+ return $ans;
+}
+
+sub y_n {
+ my $self = shift;
+ my ($mess, $def) = @_;
+
+ die "y_n() called without a prompt message" unless $mess;
+ die "Invalid default value: y_n() default must be 'y' or 'n'"
+ if $def && $def !~ /^[yn]/i;
+
+ my $answer;
+ while (1) { # XXX Infinite or a large number followed by an exception ?
+ $answer = $self->prompt(@_);
+ return 1 if $answer =~ /^y/i;
+ return 0 if $answer =~ /^n/i;
+ local $|=1;
+ print "Please answer 'y' or 'n'.\n";
+ }
+}
+
+sub current_action { shift->{action} }
+sub invoked_action { shift->{invoked_action} }
+
+sub notes { shift()->{phash}{notes}->access(@_) }
+sub config_data { shift()->{phash}{config_data}->access(@_) }
+sub runtime_params { shift->{phash}{runtime_params}->read( @_ ? shift : () ) } # Read-only
+sub auto_features { shift()->{phash}{auto_features}->access(@_) }
+
+sub features {
+ my $self = shift;
+ my $ph = $self->{phash};
+
+ if (@_) {
+ my $key = shift;
+ if ($ph->{features}->exists($key)) {
+ return $ph->{features}->access($key, @_);
+ }
+
+ 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;
+ }
+
+ return $ph->{features}->access($key, @_);
+ }
+
+ # No args - get the auto_features & overlay the regular features
+ my %features;
+ my %auto_features = $ph->{auto_features}->access();
+ while (my ($name, $info) = each %auto_features) {
+ my $failures = $self->prereq_failures($info);
+ my $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/,
+ keys %$failures ) ? 1 : 0;
+ $features{$name} = $disabled ? 0 : 1;
+ }
+ %features = (%features, $ph->{features}->access());
+
+ return wantarray ? %features : \%features;
+}
+BEGIN { *feature = \&features } # Alias
+
+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.
+ return $self->feature(@_);
+ } else {
+ require Module::Build::ConfigData;
+ return Module::Build::ConfigData->feature(@_);
+ }
+}
+
+
+sub add_build_element {
+ my ($self, $elem) = @_;
+ my $elems = $self->build_elements;
+ push @$elems, $elem unless grep { $_ eq $elem } @$elems;
+}
+
+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 ???
+ my $notes_pm = File::Spec->catfile($self->blib, 'lib', split /::/, "$notes_name.pm");
+
+ return if $self->up_to_date(['Build.PL',
+ $self->config_file('config_data'),
+ $self->config_file('features')
+ ], $notes_pm);
+
+ $self->log_info("Writing config notes to $notes_pm\n");
+ File::Path::mkpath(File::Basename::dirname($notes_pm));
+
+ Module::Build::Notes->write_config_data
+ (
+ file => $notes_pm,
+ module => $module_name,
+ config_module => $notes_name,
+ config_data => scalar $self->config_data,
+ feature => scalar $self->{phash}{features}->access(),
+ auto_features => scalar $self->auto_features,
+ );
+}
+
+########################################################################
+{ # enclosing these lexicals -- TODO
+ my %valid_properties = ( __PACKAGE__, {} );
+ my %additive_properties;
+
+ sub _mb_classes {
+ my $class = ref($_[0]) || $_[0];
+ return ($class, $class->mb_parents);
+ }
+
+ sub valid_property {
+ my ($class, $prop) = @_;
+ return grep exists( $valid_properties{$_}{$prop} ), $class->_mb_classes;
+ }
+
+ sub valid_properties {
+ return keys %{ shift->valid_properties_defaults() };
+ }
+
+ sub valid_properties_defaults {
+ my %out;
+ for (reverse shift->_mb_classes) {
+ @out{ keys %{ $valid_properties{$_} } } = map {
+ $_->()
+ } values %{ $valid_properties{$_} };
+ }
+ return \%out;
+ }
+
+ sub array_properties {
+ for (shift->_mb_classes) {
+ return @{$additive_properties{$_}->{ARRAY}}
+ if exists $additive_properties{$_}->{ARRAY};
+ }
+ }
+
+ sub hash_properties {
+ for (shift->_mb_classes) {
+ return @{$additive_properties{$_}->{'HASH'}}
+ if exists $additive_properties{$_}->{'HASH'};
+ }
+ }
+
+ sub add_property {
+ my ($class, $property) = (shift, shift);
+ die "Property '$property' already exists"
+ if $class->valid_property($property);
+ my %p = @_ == 1 ? ( default => shift ) : @_;
+
+ my $type = ref $p{default};
+ $valid_properties{$class}{$property} = $type eq 'CODE'
+ ? $p{default}
+ : sub { $p{default} };
+
+ push @{$additive_properties{$class}->{$type}}, $property
+ if $type;
+
+ unless ($class->can($property)) {
+ # TODO probably should put these in a util package
+ my $sub = $type eq 'HASH'
+ ? _make_hash_accessor($property, \%p)
+ : _make_accessor($property, \%p);
+ no strict 'refs';
+ *{"$class\::$property"} = $sub;
+ }
+
+ return $class;
+ }
+
+ sub property_error {
+ my $self = shift;
+ die 'ERROR: ', @_;
+ }
+
+ sub _set_defaults {
+ my $self = shift;
+
+ # Set the build class.
+ $self->{properties}{build_class} ||= ref $self;
+
+ # If there was no orig_dir, set to the same as base_dir
+ $self->{properties}{orig_dir} ||= $self->{properties}{base_dir};
+
+ my $defaults = $self->valid_properties_defaults;
+
+ foreach my $prop (keys %$defaults) {
+ $self->{properties}{$prop} = $defaults->{$prop}
+ unless exists $self->{properties}{$prop};
+ }
+
+ # Copy defaults for arrays any arrays.
+ for my $prop ($self->array_properties) {
+ $self->{properties}{$prop} = [@{$defaults->{$prop}}]
+ unless exists $self->{properties}{$prop};
+ }
+ # Copy defaults for arrays any hashes.
+ for my $prop ($self->hash_properties) {
+ $self->{properties}{$prop} = {%{$defaults->{$prop}}}
+ unless exists $self->{properties}{$prop};
+ }
+ }
+
+} # end closure
+########################################################################
+sub _make_hash_accessor {
+ my ($property, $p) = @_;
+ my $check = $p->{check} || sub { 1 };
+
+ return sub {
+ my $self = shift;
+
+ # This is only here to deprecate the historic accident of calling
+ # properties as class methods - I suspect it only happens in our
+ # test suite.
+ unless(ref($self)) {
+ carp("\n$property not a class method (@_)");
+ return;
+ }
+
+ my $x = $self->{properties};
+ return $x->{$property} unless @_;
+
+ my $prop = $x->{$property};
+ if ( defined $_[0] && !ref $_[0] ) {
+ if ( @_ == 1 ) {
+ return exists $prop->{$_[0]} ? $prop->{$_[0]} : undef;
+ } elsif ( @_ % 2 == 0 ) {
+ my %new = (%{ $prop }, @_);
+ local $_ = \%new;
+ $x->{$property} = \%new if $check->($self);
+ return $x->{$property};
+ } else {
+ die "Unexpected arguments for property '$property'\n";
+ }
+ } else {
+ die "Unexpected arguments for property '$property'\n"
+ if defined $_[0] && ref $_[0] ne 'HASH';
+ local $_ = $_[0];
+ $x->{$property} = shift if $check->($self);
+ }
+ };
+}
+########################################################################
+sub _make_accessor {
+ my ($property, $p) = @_;
+ my $check = $p->{check} || sub { 1 };
+
+ return sub {
+ my $self = shift;
+
+ # This is only here to deprecate the historic accident of calling
+ # properties as class methods - I suspect it only happens in our
+ # test suite.
+ unless(ref($self)) {
+ carp("\n$property not a class method (@_)");
+ return;
+ }
+
+ my $x = $self->{properties};
+ return $x->{$property} unless @_;
+ local $_ = $_[0];
+ $x->{$property} = shift if $check->($self);
+ return $x->{$property};
+ };
+}
+########################################################################
+
+# Add the default properties.
+__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_script => 'Build');
+__PACKAGE__->add_property(build_bat => 0);
+__PACKAGE__->add_property(config_dir => '_build');
+__PACKAGE__->add_property(include_dirs => []);
+__PACKAGE__->add_property(metafile => 'META.yml');
+__PACKAGE__->add_property(recurse_into => []);
+__PACKAGE__->add_property(use_rcfile => 1);
+__PACKAGE__->add_property(create_packlist => 1);
+__PACKAGE__->add_property(allow_mb_mismatch => 0);
+__PACKAGE__->add_property(config => undef);
+__PACKAGE__->add_property(test_file_exts => ['.t']);
+__PACKAGE__->add_property(use_tap_harness => 0);
+__PACKAGE__->add_property(tap_harness_args => {});
+__PACKAGE__->add_property(
+ 'installdirs',
+ default => 'site',
+ check => sub {
+ return 1 if /^(core|site|vendor)$/;
+ return shift->property_error(
+ $_ eq 'perl'
+ ? 'Perhaps you meant installdirs to be "core" rather than "perl"?'
+ : 'installdirs must be one of "core", "site", or "vendor"'
+ );
+ return shift->property_error("Perhaps you meant 'core'?") if $_ eq 'perl';
+ return 0;
+ },
+);
+
+{
+ my $Is_ActivePerl = eval {require ActivePerl::DocTools};
+ __PACKAGE__->add_property(html_css => $Is_ActivePerl ? 'Active.css' : '');
+}
+
+{
+ my @prereq_action_types = qw(requires build_requires conflicts recommends);
+ foreach my $type (@prereq_action_types) {
+ __PACKAGE__->add_property($type => {});
+ }
+ __PACKAGE__->add_property(prereq_action_types => \@prereq_action_types);
+}
+
+__PACKAGE__->add_property($_ => {}) for qw(
+ get_options
+ install_base_relpaths
+ install_path
+ install_sets
+ meta_add
+ meta_merge
+ original_prefix
+ prefix_relpaths
+ configure_requires
+);
+
+__PACKAGE__->add_property($_) for qw(
+ PL_files
+ autosplit
+ base_dir
+ bindoc_dirs
+ c_source
+ create_license
+ create_makefile_pl
+ create_readme
+ debugger
+ destdir
+ dist_abstract
+ dist_author
+ dist_name
+ dist_version
+ dist_version_from
+ extra_compiler_flags
+ extra_linker_flags
+ has_config_data
+ install_base
+ libdoc_dirs
+ license
+ magic_number
+ mb_version
+ module_name
+ orig_dir
+ perl
+ pm_files
+ pod_files
+ pollute
+ prefix
+ program_name
+ quiet
+ recursive_test_files
+ script_files
+ scripts
+ sign
+ test_files
+ verbose
+ xs_files
+);
+
+sub config {
+ my $self = shift;
+ my $c = ref($self) ? $self->{config} : 'Module::Build::Config';
+ return $c->all_config unless @_;
+
+ my $key = shift;
+ return $c->get($key) unless @_;
+
+ my $val = shift;
+ return $c->set($key => $val);
+}
+
+sub mb_parents {
+ # Code borrowed from Class::ISA.
+ my @in_stack = (shift);
+ my %seen = ($in_stack[0] => 1);
+
+ my ($current, @out);
+ while (@in_stack) {
+ next unless defined($current = shift @in_stack)
+ && $current->isa('Module::Build::Base');
+ push @out, $current;
+ next if $current eq 'Module::Build::Base';
+ no strict 'refs';
+ unshift @in_stack,
+ map {
+ my $c = $_; # copy, to avoid being destructive
+ substr($c,0,2) = "main::" if substr($c,0,2) eq '::';
+ # Canonize the :: -> main::, ::foo -> main::foo thing.
+ # Should I ever canonize the Foo'Bar = Foo::Bar thing?
+ $seen{$c}++ ? () : $c;
+ } @{"$current\::ISA"};
+
+ # I.e., if this class has any parents (at least, ones I've never seen
+ # before), push them, in order, onto the stack of classes I need to
+ # explore.
+ }
+ shift @out;
+ return @out;
+}
+
+sub extra_linker_flags { shift->_list_accessor('extra_linker_flags', @_) }
+sub extra_compiler_flags { shift->_list_accessor('extra_compiler_flags', @_) }
+
+sub _list_accessor {
+ (my $self, local $_) = (shift, shift);
+ my $p = $self->{properties};
+ $p->{$_} = [@_] if @_;
+ $p->{$_} = [] unless exists $p->{$_};
+ return ref($p->{$_}) ? $p->{$_} : [$p->{$_}];
+}
+
+# XXX Problem - if Module::Build is loaded from a different directory,
+# it'll look for (and perhaps destroy/create) a _build directory.
+sub subclass {
+ my ($pack, %opts) = @_;
+
+ my $build_dir = '_build'; # XXX The _build directory is ostensibly settable by the user. Shouldn't hard-code here.
+ $pack->delete_filetree($build_dir) if -e $build_dir;
+
+ die "Must provide 'code' or 'class' option to subclass()\n"
+ unless $opts{code} or $opts{class};
+
+ $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");
+
+ 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};
+use $pack;
+\@ISA = qw($pack);
+$opts{code}
+1;
+EOF
+ close $fh;
+
+ unshift @INC, File::Spec->catdir(File::Spec->rel2abs($build_dir), 'lib');
+ eval "use $opts{class}";
+
+ return $opts{class};
+}
+
+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};
+}
+
+sub dist_version_from {
+ my ($self) = @_;
+ my $p = $self->{properties};
+ if ($self->module_name) {
+ $p->{dist_version_from} ||=
+ join( '/', 'lib', split(/::/, $self->module_name) ) . '.pm';
+ }
+ return $p->{dist_version_from} || undef;
+}
+
+sub dist_version {
+ my ($self) = @_;
+ my $p = $self->{properties};
+
+ return $p->{dist_version} if defined $p->{dist_version};
+
+ if ( my $dist_version_from = $self->dist_version_from ) {
+ my $version_from = File::Spec->catfile( split( qr{/}, $dist_version_from ) );
+ my $pm_info = Module::Build::ModuleInfo->new_from_file( $version_from )
+ or die "Can't find file $version_from to determine version";
+ $p->{dist_version} = $self->normalize_version( $pm_info->version() );
+ }
+
+ die ("Can't determine distribution version, must supply either 'dist_version',\n".
+ "'dist_version_from', or 'module_name' parameter")
+ unless defined $p->{dist_version};
+
+ return $p->{dist_version};
+}
+
+sub dist_author { shift->_pod_parse('author') }
+sub dist_abstract { shift->_pod_parse('abstract') }
+
+sub _pod_parse {
+ my ($self, $part) = @_;
+ 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";
+ return $p->{$member} = $parser->$method();
+}
+
+sub version_from_file { # Method provided for backwards compatibility
+ return Module::Build::ModuleInfo->new_from_file($_[1])->version();
+}
+
+sub find_module_by_name { # Method provided for backwards compatibility
+ return Module::Build::ModuleInfo->find_module_by_name(@_[1,2]);
+}
+
+sub add_to_cleanup {
+ my $self = shift;
+ my %files = map {$self->localize_file_path($_), 1} @_;
+ $self->{phash}{cleanup}->write(\%files);
+}
+
+sub cleanup {
+ my $self = shift;
+ my $all = $self->{phash}{cleanup}->read;
+ return keys %$all;
+}
+
+sub config_file {
+ my $self = shift;
+ return unless -d $self->config_dir;
+ return File::Spec->catfile($self->config_dir, @_);
+}
+
+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': $!";
+ my $ref = eval do {local $/; <$fh>};
+ my $c;
+ ($self->{args}, $c, $self->{properties}) = @$ref;
+ $self->{config} = Module::Build::Config->new(values => $c);
+ close $fh;
+}
+
+sub has_config_data {
+ my $self = shift;
+ return scalar grep $self->{phash}{$_}->has_data(), qw(config_data features auto_features);
+}
+
+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
+ print $fh $data;
+ return;
+ }
+
+ print {$fh} Module::Build::Dumper->_data_dump($data);
+}
+
+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}]);
+
+ # Set a new magic number and write it to a file
+ $self->_write_data('magicnum', $self->magic_number(int rand 1_000_000));
+
+ $self->{phash}{$_}->write() foreach qw(notes cleanup features auto_features config_data runtime_params);
+}
+
+sub check_autofeatures {
+ my ($self) = @_;
+ my $features = $self->auto_features;
+
+ return unless %$features;
+
+ $self->log_info("Checking features:\n");
+
+ # TODO refactor into ::Util
+ my $longest = sub {
+ my @str = @_ or croak("no strings given");
+
+ my @len = map({length($_)} @str);
+ my $max = 0;
+ my $longest;
+ for my $i (0..$#len) {
+ ($max, $longest) = ($len[$i], $str[$i]) if($len[$i] > $max);
+ }
+ return($longest);
+ };
+ 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));
+
+ 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";
+ }
+ }
+ $self->log_warn("$log_text") unless $self->quiet;
+ } else {
+ $self->log_info("enabled\n");
+ }
+ }
+
+ $self->log_warn("\n") unless $self->quiet;
+}
+
+sub prereq_failures {
+ my ($self, $info) = @_;
+
+ my @types = @{ $self->prereq_action_types };
+ $info ||= {map {$_, $self->$_()} @types};
+
+ my $out;
+
+ foreach my $type (@types) {
+ my $prereqs = $info->{$type};
+ while ( my ($modname, $spec) = each %$prereqs ) {
+ my $status = $self->check_installed_status($modname, $spec);
+
+ if ($type =~ /^(?:\w+_)?conflicts$/) {
+ next if !$status->{ok};
+ $status->{conflicts} = delete $status->{need};
+ $status->{message} = "$modname ($status->{have}) conflicts with this distribution";
+
+ } elsif ($type =~ /^(?:\w+_)?recommends$/) {
+ next if $status->{ok};
+ $status->{message} = (!ref($status->{have}) && $status->{have} eq '<none>'
+ ? "Optional prerequisite $modname is not installed"
+ : "$modname ($status->{have}) is installed, but we prefer to have $spec");
+ } else {
+ next if $status->{ok};
+ }
+
+ $out->{$type}{$modname} = $status;
+ }
+ }
+
+ return $out;
+}
+
+# returns a hash of defined prerequisites; i.e. only prereq types with values
+sub _enum_prereqs {
+ my $self = shift;
+ my %prereqs;
+ foreach my $type ( @{ $self->prereq_action_types } ) {
+ if ( $self->can( $type ) ) {
+ my $prereq = $self->$type() || {};
+ $prereqs{$type} = $prereq if %$prereq;
+ }
+ }
+ return \%prereqs;
+}
+
+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 $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(<<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");
+ return 1;
+
+ }
+}
+
+sub perl_version {
+ my ($self) = @_;
+ # Check the current perl interpreter
+ # It's much more convenient to use $] here than $^V, but 'man
+ # perlvar' says I'm not supposed to. Bloody tyrant.
+ return $^V ? $self->perl_version_to_float(sprintf "%vd", $^V) : $];
+}
+
+sub perl_version_to_float {
+ my ($self, $version) = @_;
+ return $version if grep( /\./, $version ) < 2;
+ $version =~ s/\./../;
+ $version =~ s/\.(\d+)/sprintf '%03d', $1/eg;
+ return $version;
+}
+
+sub _parse_conditions {
+ my ($self, $spec) = @_;
+
+ if ($spec =~ /^\s*([\w.]+)\s*$/) { # A plain number, maybe with dots, letters, and underscores
+ return (">= $spec");
+ } else {
+ return split /\s*,\s*/, $spec;
+ }
+}
+
+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)
+ unless UNIVERSAL::isa($v1,'Module::Build::Version');
+
+ my $eval_str = "\$v1 $op \$v2";
+ my $result = eval $eval_str;
+ $self->log_warn("error comparing versions: '$eval_str' [email protected]") if [email protected];
+
+ return $result;
+}
+
+# I wish I could set $! to a string, but I can't, so I use [email protected]
+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';
+ }
+
+ [email protected] = $status->{message};
+ return 0;
+}
+
+sub make_executable {
+ # Perl's chmod() is mapped to useful things on various non-Unix
+ # platforms, so we use it in the base class even though it looks
+ # Unixish.
+
+ my $self = shift;
+ foreach (@_) {
+ my $current_mode = (stat $_)[2];
+ chmod $current_mode | oct(111), $_;
+ }
+}
+
+sub is_executable {
+ # We assume this does the right thing on generic platforms, though
+ # we do some other more specific stuff on Unixish platforms.
+ my ($self, $file) = @_;
+ return -x $file;
+}
+
+sub _startperl { shift()->config('startperl') }
+
+# Return any directories in @INC which are not in the default @INC for
+# this perl. For example, stuff passed in with -I or loaded with "use lib".
+sub _added_to_INC {
+ my $self = shift;
+
+ my %seen;
+ $seen{$_}++ foreach $self->_default_INC;
+ return grep !$seen{$_}++, @INC;
+}
+
+# Determine the default @INC for this Perl
+{
+ my @default_inc; # Memoize
+ 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);
+
+ my $case_tolerant = 0+(File::Spec->can('case_tolerant')
+ && File::Spec->case_tolerant);
+ $q{base_dir} = uc $q{base_dir} if $case_tolerant;
+ $q{base_dir} = Win32::GetShortPathName($q{base_dir}) if $self->is_windowsish;
+
+ $q{magic_numfile} = $self->config_file('magicnum');
+
+ my @myINC = $self->_added_to_INC;
+ for (@myINC, values %q) {
+ $_ = File::Spec->canonpath( $_ );
+ s/([\\\'])/\\$1/g;
+ }
+
+ my $quoted_INC = join ",\n", map " '$_'", @myINC;
+ my $shebang = $self->_startperl;
+ my $magic_number = $self->magic_number;
+
+ print $fh <<EOF;
+$shebang
+
+use strict;
+use Cwd;
+use File::Basename;
+use File::Spec;
+
+sub magic_number_matches {
+ return 0 unless -e '$q{magic_numfile}';
+ local *FH;
+ open FH, '$q{magic_numfile}' or return 0;
+ my \$filenum = <FH>;
+ close FH;
+ return \$filenum == $magic_number;
+}
+
+my \$progname;
+my \$orig_dir;
+BEGIN {
+ \$^W = 1; # Use warnings
+ \$progname = basename(\$0);
+ \$orig_dir = Cwd::cwd();
+ my \$base_dir = '$q{base_dir}';
+ if (!magic_number_matches()) {
+ unless (chdir(\$base_dir)) {
+ die ("Couldn't chdir(\$base_dir), aborting\\n");
+ }
+ unless (magic_number_matches()) {
+ die ("Configuration seems to be out of date, please re-run 'perl Build.PL' again.\\n");
+ }
+ }
+ unshift \@INC,
+ (
+$quoted_INC
+ );
+}
+
+close(*DATA) unless eof(*DATA); # ensure no open handles to this script
+
+use $build_package;
+
+# Some platforms have problems setting \$^X in shebang contexts, fix it up here
+\$^X = Module::Build->find_perl_interpreter;
+
+if (-e 'Build.PL' and not $build_package->up_to_date('Build.PL', \$progname)) {
+ warn "Warning: Build.PL has been altered. You may need to run 'perl Build.PL' again.\\n";
+}
+
+# This should have just enough arguments to be able to bootstrap the rest.
+my \$build = $build_package->resume (
+ properties => {
+ config_dir => '$q{config_dir}',
+ orig_dir => \$orig_dir,
+ },
+);
+
+\$build->dispatch;
+EOF
+}
+
+sub create_build_script {
+ my ($self) = @_;
+ $self->write_config;
+
+ 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_info("Creating new '$build_script' script for ",
+ "'$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");
+ 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");
+ }
+}
+
+sub dispatch {
+ my $self = shift;
+ local $self->{_completed_actions} = {};
+
+ if (@_) {
+ my ($action, %p) = @_;
+ my $args = $p{args} ? delete($p{args}) : {};
+
+ local $self->{invoked_action} = $action;
+ local $self->{args} = {%{$self->{args}}, %$args};
+ local $self->{properties} = {%{$self->{properties}}, %p};
+ return $self->_call_action($action);
+ }
+
+ die "No build action specified" unless $self->{action};
+ local $self->{invoked_action} = $self->{action};
+ $self->_call_action($self->{action});
+}
+
+sub _call_action {
+ my ($self, $action) = @_;
+
+ return if $self->{_completed_actions}{$action}++;
+
+ local $self->{action} = $action;
+ my $method = $self->can_action( $action );
+ die "No action '$action' defined, try running the 'help' action.\n" unless $method;
+ return $self->$method();
+}
+
+sub can_action {
+ my ($self, $action) = @_;
+ return $self->can( "ACTION_$action" );
+}
+
+# cuts the user-specified options out of the command-line args
+sub cull_options {
+ my $self = shift;
+ my (@argv) = @_;
+
+ # XXX is it even valid to call this as a class method?
+ return({}, @argv) unless(ref($self)); # no object
+
+ my $specs = $self->get_options;
+ return({}, @argv) unless($specs and %$specs); # no user options
+
+ require Getopt::Long;
+ # XXX Should we let Getopt::Long handle M::B's options? That would
+ # be easy-ish to add to @specs right here, but wouldn't handle options
+ # passed without "--" as M::B currently allows. We might be able to
+ # get around this by setting the "prefix_pattern" Configure option.
+ my @specs;
+ my $args = {};
+ # Construct the specifications for GetOptions.
+ while (my ($k, $v) = each %$specs) {
+ # Throw an error if specs conflict with our own.
+ die "Option specification '$k' conflicts with a " . ref $self
+ . " option of the same name"
+ if $self->valid_property($k);
+ push @specs, $k . (defined $v->{type} ? $v->{type} : '');
+ push @specs, $v->{store} if exists $v->{store};
+ $args->{$k} = $v->{default} if exists $v->{default};
+ }
+
+ local @ARGV = @argv; # No other way to dupe Getopt::Long
+
+ # Get the options values and return them.
+ # XXX Add option to allow users to set options?
+ if ( @specs ) {
+ Getopt::Long::Configure('pass_through');
+ Getopt::Long::GetOptions($args, @specs);
+ }
+
+ return $args, @ARGV;
+}
+
+sub unparse_args {
+ my ($self, $args) = @_;
+ my @out;
+ while (my ($k, $v) = each %$args) {
+ push @out, (UNIVERSAL::isa($v, 'HASH') ? map {+"--$k", "$_=$v->{$_}"} keys %$v :
+ UNIVERSAL::isa($v, 'ARRAY') ? map {+"--$k", $_} @$v :
+ ("--$k", $v));
+ }
+ return @out;
+}
+
+sub args {
+ my $self = shift;
+ return wantarray ? %{ $self->{args} } : $self->{args} unless @_;
+ my $key = shift;
+ $self->{args}{$key} = shift if @_;
+ return $self->{args}{$key};
+}
+
+# allows select parameters (with underscores) to be spoken with dashes
+# when used as command-line options
+sub _translate_option {
+ my $self = shift;
+ my $opt = shift;
+
+ (my $tr_opt = $opt) =~ tr/-/_/;
+
+ return $tr_opt if grep $tr_opt =~ /^(?:no_?)?$_$/, qw(
+ create_license
+ create_makefile_pl
+ create_readme
+ extra_compiler_flags
+ extra_linker_flags
+ html_css
+ install_base
+ install_path
+ meta_add
+ meta_merge
+ test_files
+ use_rcfile
+ use_tap_harness
+ tap_harness_args
+ ); # normalize only selected option names
+
+ return $opt;
+}
+
+sub _read_arg {
+ my ($self, $args, $key, $val) = @_;
+
+ $key = $self->_translate_option($key);
+
+ if ( exists $args->{$key} ) {
+ $args->{$key} = [ $args->{$key} ] unless ref $args->{$key};
+ push @{$args->{$key}}, $val;
+ } else {
+ $args->{$key} = $val;
+ }
+}
+
+# decide whether or not an option requires/has an operand
+sub _optional_arg {
+ my $self = shift;
+ my $opt = shift;
+ my $argv = shift;
+
+ $opt = $self->_translate_option($opt);
+
+ my @bool_opts = qw(
+ build_bat
+ create_license
+ create_readme
+ pollute
+ quiet
+ uninst
+ use_rcfile
+ verbose
+ sign
+ use_tap_harness
+ );
+
+ # inverted boolean options; eg --noverbose or --no-verbose
+ # converted to proper name & returned with false value (verbose, 0)
+ if ( grep $opt =~ /^no[-_]?$_$/, @bool_opts ) {
+ $opt =~ s/^no-?//;
+ return ($opt, 0);
+ }
+
+ # non-boolean option; return option unchanged along with its argument
+ return ($opt, shift(@$argv)) unless grep $_ eq $opt, @bool_opts;
+
+ # we're punting a bit here, if an option appears followed by a digit
+ # we take the digit as the argument for the option. If there is
+ # nothing that looks like a digit, we pretend the option is a flag
+ # that is being set and has no argument.
+ my $arg = 1;
+ $arg = shift(@$argv) if @$argv && $argv->[0] =~ /^\d+$/;
+
+ return ($opt, $arg);
+}
+
+sub read_args {
+ my $self = shift;
+
+ (my $args, @_) = $self->cull_options(@_);
+ my %args = %$args;
+
+ my $opt_re = qr/[\w\-]+/;
+
+ my ($action, @argv);
+ while (@_) {
+ local $_ = shift;
+ if ( /^(?:--)?($opt_re)=(.*)$/ ) {
+ $self->_read_arg(\%args, $1, $2);
+ } elsif ( /^--($opt_re)$/ ) {
+ my($opt, $arg) = $self->_optional_arg($1, \@_);
+ $self->_read_arg(\%args, $opt, $arg);
+ } elsif ( /^($opt_re)$/ and !defined($action)) {
+ $action = $1;
+ } else {
+ push @argv, $_;
+ }
+ }
+ $args{ARGV} = \@argv;
+
+ for ('extra_compiler_flags', 'extra_linker_flags') {
+ $args{$_} = [ $self->split_like_shell($args{$_}) ] if exists $args{$_};
+ }
+
+ # Convert to arrays
+ for ('include_dirs') {
+ $args{$_} = [ $args{$_} ] if exists $args{$_} && !ref $args{$_}
+ }
+
+ # Hashify these parameters
+ for ($self->hash_properties, 'config') {
+ next unless exists $args{$_};
+ my %hash;
+ $args{$_} ||= [];
+ $args{$_} = [ $args{$_} ] unless ref $args{$_};
+ foreach my $arg ( @{$args{$_}} ) {
+ $arg =~ /(\w+)=(.*)/
+ or die "Malformed '$_' argument: '$arg' should be something like 'foo=bar'";
+ $hash{$1} = $2;
+ }
+ $args{$_} = \%hash;
+ }
+
+ # De-tilde-ify any path parameters
+ for my $key (qw(prefix install_base destdir)) {
+ next if !defined $args{$key};
+ $args{$key} = $self->_detildefy($args{$key});
+ }
+
+ for my $key (qw(install_path)) {
+ next if !defined $args{$key};
+
+ for my $subkey (keys %{$args{$key}}) {
+ next if !defined $args{$key}{$subkey};
+ my $subkey_ext = $self->_detildefy($args{$key}{$subkey});
+ if ( $subkey eq 'html' ) { # translate for compatibility
+ $args{$key}{binhtml} = $subkey_ext;
+ $args{$key}{libhtml} = $subkey_ext;
+ } else {
+ $args{$key}{$subkey} = $subkey_ext;
+ }
+ }
+ }
+
+ if ($args{makefile_env_macros}) {
+ require Module::Build::Compat;
+ %args = (%args, Module::Build::Compat->makefile_to_build_macros);
+ }
+
+ return \%args, $action;
+}
+
+# Default: do nothing. Overridden for Unix & Windows.
+sub _detildefy {}
+
+
+# merge Module::Build argument lists that have already been parsed
+# by read_args(). Takes two references to option hashes and merges
+# the contents, giving priority to the first.
+sub _merge_arglist {
+ my( $self, $opts1, $opts2 ) = @_;
+
+ my %new_opts = %$opts1;
+ while (my ($key, $val) = each %$opts2) {
+ if ( exists( $opts1->{$key} ) ) {
+ if ( ref( $val ) eq 'HASH' ) {
+ while (my ($k, $v) = each %$val) {
+ $new_opts{$key}{$k} = $v unless exists( $opts1->{$key}{$k} );
+ }
+ }
+ } else {
+ $new_opts{$key} = $val
+ }
+ }
+
+ return %new_opts;
+}
+
+# Look for a home directory on various systems.
+sub _home_dir {
+ my @home_dirs;
+ push( @home_dirs, $ENV{HOME} ) if $ENV{HOME};
+
+ push( @home_dirs, File::Spec->catpath($ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '') )
+ if $ENV{HOMEDRIVE} && $ENV{HOMEPATH};
+
+ my @other_home_envs = qw( USERPROFILE APPDATA WINDIR SYS$LOGIN );
+ push( @home_dirs, map $ENV{$_}, grep $ENV{$_}, @other_home_envs );
+
+ my @real_home_dirs = grep -d, @home_dirs;
+
+ return wantarray ? @real_home_dirs : shift( @real_home_dirs );
+}
+
+sub _find_user_config {
+ my $self = shift;
+ my $file = shift;
+ foreach my $dir ( $self->_home_dir ) {
+ my $path = File::Spec->catfile( $dir, $file );
+ return $path if -e $path;
+ }
+ return undef;
+}
+
+# read ~/.modulebuildrc returning global options '*' and
+# options specific to the currently executing $action.
+sub read_modulebuildrc {
+ my( $self, $action ) = @_;
+
+ return () unless $self->use_rcfile;
+
+ my $modulebuildrc;
+ if ( exists($ENV{MODULEBUILDRC}) && $ENV{MODULEBUILDRC} eq 'NONE' ) {
+ return ();
+ } elsif ( exists($ENV{MODULEBUILDRC}) && -e $ENV{MODULEBUILDRC} ) {
+ $modulebuildrc = $ENV{MODULEBUILDRC};
+ } elsif ( exists($ENV{MODULEBUILDRC}) ) {
+ $self->log_warn("WARNING: Can't find resource file " .
+ "'$ENV{MODULEBUILDRC}' defined in environment.\n" .
+ "No options loaded\n");
+ return ();
+ } else {
+ $modulebuildrc = $self->_find_user_config( '.modulebuildrc' );
+ return () unless $modulebuildrc;
+ }
+
+ my $fh = IO::File->new( $modulebuildrc )
+ or die "Can't open $modulebuildrc: $!";
+
+ my %options; my $buffer = '';
+ while (defined( my $line = <$fh> )) {
+ chomp( $line );
+ $line =~ s/#.*$//;
+ next unless length( $line );
+
+ if ( $line =~ /^\S/ ) {
+ if ( $buffer ) {
+ my( $action, $options ) = split( /\s+/, $buffer, 2 );
+ $options{$action} .= $options . ' ';
+ $buffer = '';
+ }
+ $buffer = $line;
+ } else {
+ $buffer .= $line;
+ }
+ }
+
+ if ( $buffer ) { # anything left in $buffer ?
+ my( $action, $options ) = split( /\s+/, $buffer, 2 );
+ $options{$action} .= $options . ' '; # merge if more than one line
+ }
+
+ my ($global_opts) =
+ $self->read_args( $self->split_like_shell( $options{'*'} || '' ) );
+ my ($action_opts) =
+ $self->read_args( $self->split_like_shell( $options{$action} || '' ) );
+
+ # specific $action options take priority over global options '*'
+ return $self->_merge_arglist( $action_opts, $global_opts );
+}
+
+# merge the relevant options in ~/.modulebuildrc into Module::Build's
+# option list where they do not conflict with commandline options.
+sub merge_modulebuildrc {
+ my( $self, $action, %cmdline_opts ) = @_;
+ my %rc_opts = $self->read_modulebuildrc( $action || $self->{action} || 'build' );
+ my %new_opts = $self->_merge_arglist( \%cmdline_opts, \%rc_opts );
+ $self->merge_args( $action, %new_opts );
+}
+
+sub merge_args {
+ my ($self, $action, %args) = @_;
+ $self->{action} = $action if defined $action;
+
+ my %additive = map { $_ => 1 } $self->hash_properties;
+
+ # Extract our 'properties' from $cmd_args, the rest are put in 'args'.
+ while (my ($key, $val) = each %args) {
+ $self->{phash}{runtime_params}->access( $key => $val )
+ if $self->valid_property($key);
+
+ if ($key eq 'config') {
+ $self->config($_ => $val->{$_}) foreach keys %$val;
+ } else {
+ my $add_to = $additive{$key} ? $self->{properties}{$key} :
+ $self->valid_property($key) ? $self->{properties} :
+ $self->{args} ;
+
+ if ($additive{$key}) {
+ $add_to->{$_} = $val->{$_} foreach keys %$val;
+ } else {
+ $add_to->{$key} = $val;
+ }
+ }
+ }
+}
+
+sub cull_args {
+ my $self = shift;
+ my ($args, $action) = $self->read_args(@_);
+ $self->merge_args($action, %$args);
+ $self->merge_modulebuildrc( $action, %$args );
+}
+
+sub super_classes {
+ 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;
+}
+
+sub known_actions {
+ my ($self) = @_;
+
+ my %actions;
+ no strict 'refs';
+
+ foreach my $class ($self->super_classes) {
+ foreach ( keys %{ $class . '::' } ) {
+ $actions{$1}++ if /^ACTION_(\w+)/;
+ }
+ }
+
+ return wantarray ? sort keys %actions : \%actions;
+}
+
+sub get_action_docs {
+ my ($self, $action) = @_;
+ my $actions = $self->known_actions;
+ die "No known action '$action'" unless $actions->{$action};
+
+ my ($files_found, @docs) = (0);
+ foreach my $class ($self->super_classes) {
+ (my $file = $class) =~ s{::}{/}g;
+ # NOTE: silently skipping relative paths if any chdir() happened
+ $file = $INC{$file . '.pm'} or next;
+ my $fh = IO::File->new("< $file") or next;
+ $files_found++;
+
+ # Code below modified from /usr/bin/perldoc
+
+ # Skip to ACTIONS section
+ local $_;
+ while (<$fh>) {
+ last if /^=head1 ACTIONS\s/;
+ }
+
+ # Look for our action and determine the style
+ my $style;
+ while (<$fh>) {
+ last if /^=head1 /;
+
+ # only item and head2 are allowed (3&4 are not in 5.005)
+ if(/^=(item|head2)\s+\Q$action\E\b/) {
+ $style = $1;
+ push @docs, $_;
+ last;
+ }
+ }
+ $style or next; # not here
+
+ # and the content
+ if($style eq 'item') {
+ my ($found, $inlist) = (0, 0);
+ while (<$fh>) {
+ if (/^=(item|back)/) {
+ last unless $inlist;
+ }
+ push @docs, $_;
+ ++$inlist if /^=over/;
+ --$inlist if /^=back/;
+ }
+ }
+ else { # head2 style
+ # stop at anything equal or greater than the found level
+ while (<$fh>) {
+ last if(/^=(?:head[12]|cut)/);
+ push @docs, $_;
+ }
+ }
+ # TODO maybe disallow overriding just pod for an action
+ # TODO and possibly: @docs and last;
+ }
+
+ unless ($files_found) {
+ [email protected] = "Couldn't find any documentation to search";
+ return;
+ }
+ unless (@docs) {
+ [email protected] = "Couldn't find any docs for action '$action'";
+ return;
+ }
+
+ return join '', @docs;
+}
+
+sub ACTION_prereq_report {
+ my $self = shift;
+ $self->log_info( $self->prereq_report );
+}
+
+sub ACTION_prereq_data {
+ my $self = shift;
+ $self->log_info( Module::Build::Dumper->_data_dump( $self->prereq_data ) );
+}
+
+sub prereq_data {
+ my $self = shift;
+ my @types = ('configure_requires', @{ $self->prereq_action_types } );
+ my $info = { map { $_ => $self->$_() } grep { %{$self->$_()} } @types };
+ return $info;
+}
+
+sub prereq_report {
+ my $self = shift;
+ my $info = $self->prereq_data;
+
+ my $output = '';
+ foreach my $type (keys %$info) {
+ my $prereqs = $info->{$type};
+ $output .= "\n$type:\n";
+ my $mod_len = 2;
+ my $ver_len = 4;
+ my %mods;
+ while ( my ($modname, $spec) = each %$prereqs ) {
+ my $len = length $modname;
+ $mod_len = $len if $len > $mod_len;
+ $spec ||= '0';
+ $len = length $spec;
+ $ver_len = $len if $len > $ver_len;
+
+ my $mod = $self->check_installed_status($modname, $spec);
+ $mod->{name} = $modname;
+ $mod->{ok} ||= 0;
+ $mod->{ok} = ! $mod->{ok} if $type =~ /^(\w+_)?conflicts$/;
+
+ $mods{lc $modname} = $mod;
+ }
+
+ my $space = q{ } x ($mod_len - 3);
+ my $vspace = q{ } x ($ver_len - 3);
+ my $sline = q{-} x ($mod_len - 3);
+ my $vline = q{-} x ($ver_len - 3);
+ my $disposition = ($type =~ /^(\w+_)?conflicts$/) ?
+ 'Clash' : 'Need';
+ $output .=
+ " Module $space $disposition $vspace Have\n".
+ " ------$sline+------$vline-+----------\n";
+
+
+ for my $k (sort keys %mods) {
+ my $mod = $mods{$k};
+ my $space = q{ } x ($mod_len - length $k);
+ my $vspace = q{ } x ($ver_len - length $mod->{need});
+ my $f = $mod->{ok} ? ' ' : '!';
+ $output .=
+ " $f $mod->{name} $space $mod->{need} $vspace ".
+ (defined($mod->{have}) ? $mod->{have} : "")."\n";
+ }
+ }
+ return $output;
+}
+
+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)};
+ return;
+ }
+
+ print <<EOF;
+
+ 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";
+ print "See `perldoc Module::Build` for complete documentation.\n";
+}
+
+sub _action_listing {
+ my ($self, $actions) = @_;
+
+ # 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||'');
+ }
+ return $out;
+}
+
+sub ACTION_retest {
+ my ($self) = @_;
+
+ # Protect others against our @INC changes
+ local @INC = @INC;
+
+ # Filter out nonsensical @INC entries - some versions of
+ # Test::Harness will really explode the number of entries here
+ @INC = grep {ref() || -d} @INC if @INC > 100;
+
+ $self->do_tests;
+}
+
+sub ACTION_testall {
+ my ($self) = @_;
+
+ my @types;
+ for my $action (grep { $_ ne 'all' } $self->get_test_types) {
+ # XXX We can't just dispatch because we get multiple summaries but
+ # we'll need to dispatch to support custom setup/teardown in the
+ # action. To support that, we'll need to call something besides
+ # Harness::runtests() because we'll need to collect the results in
+ # parts, then run the summary.
+ push(@types, $action);
+ #$self->_call_action( "test$action" );
+ }
+ $self->generic_test(types => ['default', @types]);
+}
+
+sub get_test_types {
+ my ($self) = @_;
+
+ my $t = $self->{properties}->{test_types};
+ return ( defined $t ? ( keys %$t ) : () );
+}
+
+
+sub ACTION_test {
+ my ($self) = @_;
+ $self->generic_test(type => 'default');
+}
+
+sub generic_test {
+ my $self = shift;
+ (@_ % 2) and croak('Odd number of elements in argument hash');
+ my %args = @_;
+
+ my $p = $self->{properties};
+
+ my @types = (
+ (exists($args{type}) ? $args{type} : ()),
+ (exists($args{types}) ? @{$args{types}} : ()),
+ );
+ @types or croak "need some types of tests to check";
+
+ my %test_types = (
+ default => $p->{test_file_exts},
+ (defined($p->{test_types}) ? %{$p->{test_types}} : ()),
+ );
+
+ for my $type (@types) {
+ croak "$type not defined in test_types!"
+ unless defined $test_types{ $type };
+ }
+
+ # we use local here because it ends up two method calls deep
+ local $p->{test_file_exts} = [ map { ref $_ ? @$_ : $_ } @test_types{@types} ];
+ $self->depends_on('code');
+
+ # Protect others against our @INC changes
+ local @INC = @INC;
+
+ # Make sure we test the module in blib/
+ unshift @INC, (File::Spec->catdir($p->{base_dir}, $self->blib, 'lib'),
+ File::Spec->catdir($p->{base_dir}, $self->blib, 'arch'));
+
+ # Filter out nonsensical @INC entries - some versions of
+ # Test::Harness will really explode the number of entries here
+ @INC = grep {ref() || -d} @INC if @INC > 100;
+
+ $self->do_tests;
+}
+
+sub do_tests {
+ my $self = shift;
+
+ my $tests = $self->find_test_files;
+
+ if(@$tests) {
+ my $args = $self->tap_harness_args;
+ if($self->use_tap_harness or ($args and %$args)) {
+ $self->run_tap_harness($tests);
+ }
+ else {
+ $self->run_test_harness($tests);
+ }
+ }
+ else {
+ $self->log_info("No tests defined.\n");
+ }
+
+ $self->run_visual_script;
+}
+
+sub run_tap_harness {
+ my ($self, $tests) = @_;
+
+ require TAP::Harness;
+
+ # TODO allow the test @INC to be set via our API?
+
+ TAP::Harness->new({
+ lib => [@INC],
+ verbosity => $self->{properties}{verbose},
+ switches => [ $self->harness_switches ],
+ %{ $self->tap_harness_args },
+ })->runtests(@$tests);
+}
+
+sub run_test_harness {
+ my ($self, $tests) = @_;
+ require Test::Harness;
+ my $p = $self->{properties};
+ my @harness_switches = $self->harness_switches;
+
+ # Work around a Test::Harness bug that loses the particular perl
+ # we're running under. $self->perl is trustworthy, but $^X isn't.
+ local $^X = $self->perl;
+
+ # Do everything in our power to work with all versions of Test::Harness
+ local $Test::Harness::switches = join ' ', grep defined, $Test::Harness::switches, @harness_switches;
+ local $Test::Harness::Switches = join ' ', grep defined, $Test::Harness::Switches, @harness_switches;
+ local $ENV{HARNESS_PERL_SWITCHES} = join ' ', grep defined, $ENV{HARNESS_PERL_SWITCHES}, @harness_switches;
+
+ $Test::Harness::switches = undef unless length $Test::Harness::switches;
+ $Test::Harness::Switches = undef unless length $Test::Harness::Switches;
+ delete $ENV{HARNESS_PERL_SWITCHES} unless length $ENV{HARNESS_PERL_SWITCHES};
+
+ local ($Test::Harness::verbose,
+ $Test::Harness::Verbose,
+ $ENV{TEST_VERBOSE},
+ $ENV{HARNESS_VERBOSE}) = ($p->{verbose} || 0) x 4;
+
+ Test::Harness::runtests(@$tests);
+}
+
+sub run_visual_script {
+ my $self = shift;
+ # This will get run and the user will see the output. It doesn't
+ # emit Test::Harness-style output.
+ $self->run_perl_script('visual.pl', '-Mblib='.$self->blib)
+ if -e 'visual.pl';
+}
+
+sub harness_switches {
+ shift->{properties}{debugger} ? qw(-w -d) : ();
+}
+
+sub test_files {
+ my $self = shift;
+ my $p = $self->{properties};
+ if (@_) {
+ return $p->{test_files} = (@_ == 1 ? shift : [@_]);
+ }
+ return $self->find_test_files;
+}
+
+sub expand_test_dir {
+ my ($self, $dir) = @_;
+ my $exts = $self->{properties}{test_file_exts};
+
+ return sort map { @{$self->rscan_dir($dir, qr{^[^.].*\Q$_\E$})} } @$exts
+ if $self->recursive_test_files;
+
+ return sort map { glob File::Spec->catfile($dir, "*$_") } @$exts;
+}
+
+sub ACTION_testdb {
+ my ($self) = @_;
+ local $self->{properties}{debugger} = 1;
+ $self->depends_on('test');
+}
+
+sub ACTION_testcover {
+ my ($self) = @_;
+
+ unless (Module::Build::ModuleInfo->find_module_by_name('Devel::Cover')) {
+ warn("Cannot run testcover action unless Devel::Cover is installed.\n");
+ return;
+ }
+
+ $self->add_to_cleanup('coverage', 'cover_db');
+ $self->depends_on('code');
+
+ # See whether any of the *.pm files have changed since last time
+ # testcover was run. If so, start over.
+ if (-e 'cover_db') {
+ 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 $ENV{HARNESS_PERL_SWITCHES} = "-MDevel::Cover";
+
+ $self->depends_on('test');
+ $self->do_system('cover');
+}
+
+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);
+ $self->$method($element);
+ }
+
+ $self->depends_on('config_data');
+}
+
+sub ACTION_build {
+ my $self = shift;
+ $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) );
+ }
+}
+
+sub process_support_files {
+ 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)?$'));
+ foreach my $file (@$files) {
+ push @{$p->{objects}}, $self->compile_c($file);
+ }
+}
+
+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";
+ $self->add_to_cleanup(@$to);
+ }
+ }
+}
+
+sub process_xs_files {
+ my $self = shift;
+ my $files = $self->find_xs_files;
+ while (my ($from, $to) = each %$files) {
+ unless ($from eq $to) {
+ $self->add_to_cleanup($to);
+ $self->copy_if_modified( from => $from, to => $to );
+ }
+ $self->process_xs($to);
+ }
+}
+
+sub process_pod_files { shift()->process_files_by_extension(shift()) }
+sub process_pm_files { shift()->process_files_by_extension(shift()) }
+
+sub process_script_files {
+ my $self = shift;
+ my $files = $self->find_script_files;
+ return unless keys %$files;
+
+ 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;
+ $self->make_executable($result);
+ }
+}
+
+sub find_PL_files {
+ 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($_),
+ @$files };
+
+ } elsif (UNIVERSAL::isa($files, 'HASH')) {
+ my %out;
+ while (my ($file, $to) = each %$files) {
+ $out{ $self->localize_file_path($file) } = [ map $self->localize_file_path($_),
+ ref $to ? @$to : ($to) ];
+ }
+ return \%out;
+
+ } else {
+ 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$')) } };
+}
+
+sub find_pm_files { shift->_find_file_by_type('pm', 'lib') }
+sub find_pod_files { shift->_find_file_by_type('pod', 'lib') }
+sub find_xs_files { shift->_find_file_by_type('xs', 'lib') }
+
+sub find_script_files {
+ my $self = shift;
+ if (my $files = $self->script_files) {
+ # Always given as a Unix file spec. Values in the hash are
+ # meaningless, but we preserve if present.
+ return { map {$self->localize_file_path($_), $files->{$_}} keys %$files };
+ }
+
+ # No default location for script files
+ return {};
+}
+
+sub find_test_files {
+ my $self = shift;
+ my $p = $self->{properties};
+
+ if (my $files = $p->{test_files}) {
+ $files = [keys %$files] if UNIVERSAL::isa($files, 'HASH');
+ $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;
+ push @tests, 'test.pl' if -e 'test.pl';
+ push @tests, $self->expand_test_dir('t') if -e 't' and -d _;
+ return \@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($_),
+ grep !/\.\#/,
+ @{ $self->rscan_dir($dir, file_qr("\\.$type\$")) } };
+}
+
+sub localize_file_path {
+ my ($self, $path) = @_;
+ return File::Spec->catfile( split m{/}, $path );
+}
+
+sub localize_dir_path {
+ my ($self, $path) = @_;
+ return File::Spec->catdir( split m{/}, $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 @files = sort keys %{$self->_find_pods($self->libdoc_dirs)},
+ keys %{$self->_find_pods
+ ($self->bindoc_dirs,
+ exclude => [ file_qr('\.bat$') ])}
+ or die "Couldn't find any POD files to test\n";
+
+ { package # hide from PAUSE
+ Module::Build::PodTester; # Don't want to pollute the main namespace
+ Test::Pod->import( tests => scalar @files );
+ pod_file_ok($_) foreach @files;
+ }
+}
+
+sub ACTION_testpodcoverage {
+ 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";
+
+ # TODO this needs test coverage!
+
+ # XXX work-around a bug in Test::Pod::Coverage previous to v1.09
+ # Make sure we test the module in blib/
+ local @INC = @INC;
+ my $p = $self->{properties};
+ unshift(@INC,
+ # XXX any reason to include arch?
+ File::Spec->catdir($p->{base_dir}, $self->blib, 'lib'),
+ #File::Spec->catdir($p->{base_dir}, $self->blib, 'arch')
+ );
+
+ all_pod_coverage_ok();
+}
+
+sub ACTION_docs {
+ my $self = shift;
+
+ $self->depends_on('code');
+ $self->depends_on('manpages', 'html');
+}
+
+# Given a file type, will return true if the file type would normally
+# be installed when neither install-base nor prefix has been set.
+# I.e. it will be true only if the path is set from Config.pm or
+# set explicitly by the user via install-path.
+sub _is_default_installable {
+ my $self = shift;
+ my $type = shift;
+ return ( $self->install_destination($type) &&
+ ( $self->install_path($type) ||
+ $self->install_sets($self->installdirs)->{$type} )
+ ) ? 1 : 0;
+}
+
+sub ACTION_manpages {
+ my $self = shift;
+
+ return unless $self->_mb_feature('manpage_support');
+
+ $self->depends_on('code');
+
+ foreach my $type ( qw(bin lib) ) {
+ my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
+ exclude => [ file_qr('\.bat$') ] );
+ next unless %$files;
+
+ my $sub = $self->can("manify_${type}_pods");
+ next unless defined( $sub );
+
+ if ( $self->invoked_action eq 'manpages' ) {
+ $self->$sub();
+ } elsif ( $self->_is_default_installable("${type}doc") ) {
+ $self->$sub();
+ }
+ }
+
+}
+
+sub manify_bin_pods {
+ my $self = shift;
+
+ my $files = $self->_find_pods( $self->{properties}{bindoc_dirs},
+ exclude => [ file_qr('\.bat$') ] );
+ return unless keys %$files;
+
+ my $mandir = File::Spec->catdir( $self->blib, 'bindoc' );
+ File::Path::mkpath( $mandir, 0, oct(777) );
+
+ require Pod::Man;
+ foreach my $file (keys %$files) {
+ # Pod::Simple based parsers only support one document per instance.
+ # This is expected to change in a future version (Pod::Simple > 3.03).
+ my $parser = Pod::Man->new( section => 1 ); # binaries go in section 1
+ my $manpage = $self->man1page_name( $file ) . '.' .
+ $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");
+ $parser->parse_from_file( $file, $outfile );
+ $files->{$file} = $outfile;
+ }
+}
+
+sub manify_lib_pods {
+ my $self = shift;
+
+ my $files = $self->_find_pods($self->{properties}{libdoc_dirs});
+ return unless keys %$files;
+
+ my $mandir = File::Spec->catdir( $self->blib, 'libdoc' );
+ File::Path::mkpath( $mandir, 0, oct(777) );
+
+ require Pod::Man;
+ while (my ($file, $relfile) = each %$files) {
+ # Pod::Simple based parsers only support one document per instance.
+ # This is expected to change in a future version (Pod::Simple > 3.03).
+ my $parser = Pod::Man->new( section => 3 ); # libraries go in section 3
+ my $manpage = $self->man3page_name( $relfile ) . '.' .
+ $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");
+ $parser->parse_from_file( $file, $outfile );
+ $files->{$file} = $outfile;
+ }
+}
+
+sub _find_pods {
+ my ($self, $dirs, %args) = @_;
+ my %files;
+ foreach my $spec (@$dirs) {
+ my $dir = $self->localize_dir_path($spec);
+ next unless -e $dir;
+
+ FILE: foreach my $file ( @{ $self->rscan_dir( $dir ) } ) {
+ foreach my $regexp ( @{ $args{exclude} } ) {
+ next FILE if $file =~ $regexp;
+ }
+ $files{$file} = File::Spec->abs2rel($file, $dir) if $self->contains_pod( $file )
+ }
+ }
+ return \%files;
+}
+
+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 '';
+}
+
+sub ACTION_html {
+ my $self = shift;
+
+ return unless $self->_mb_feature('HTML_support');
+
+ $self->depends_on('code');
+
+ foreach my $type ( qw(bin lib) ) {
+ my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
+ exclude =>
+ [ file_qr('\.(?:bat|com|html)$') ] );
+ next unless %$files;
+
+ if ( $self->invoked_action eq 'html' ) {
+ $self->htmlify_pods( $type );
+ } elsif ( $self->_is_default_installable("${type}html") ) {
+ $self->htmlify_pods( $type );
+ }
+ }
+
+}
+
+
+# 1) If it's an ActiveState perl install, we need to run
+# ActivePerl::DocTools->UpdateTOC;
+# 2) Links to other modules are not being generated
+sub htmlify_pods {
+ my $self = shift;
+ my $type = shift;
+ my $htmldir = shift || File::Spec->catdir($self->blib, "${type}html");
+
+ require Module::Build::PodParser;
+ require Pod::Html;
+
+ $self->add_to_cleanup('pod2htm*');
+
+ my $pods = $self->_find_pods( $self->{properties}{"${type}doc_dirs"},
+ exclude => [ file_qr('\.(?:bat|com|html)$') ] );
+ return unless %$pods; # nothing to do
+
+ unless ( -d $htmldir ) {
+ File::Path::mkpath($htmldir, 0, oct(755))
+ or die "Couldn't mkdir $htmldir: $!";
+ }
+
+ my @rootdirs = ($type eq 'bin') ? qw(bin) :
+ $self->installdirs eq 'core' ? qw(lib) : qw(site lib);
+
+ my $podpath = join ':',
+ map $_->[1],
+ grep -e $_->[0],
+ map [File::Spec->catdir($self->blib, $_), $_],
+ qw( script lib );
+
+ foreach my $pod ( keys %$pods ) {
+
+ my ($name, $path) = File::Basename::fileparse($pods->{$pod},
+ file_qr('\.(?:pm|plx?|pod)$'));
+ my @dirs = File::Spec->splitdir( File::Spec->canonpath( $path ) );
+ pop( @dirs ) if scalar(@dirs) && $dirs[-1] eq File::Spec->curdir;
+
+ my $fulldir = File::Spec->catfile($htmldir, @rootdirs, @dirs);
+ my $outfile = File::Spec->catfile($fulldir, "${name}.html");
+ my $infile = File::Spec->abs2rel($pod);
+
+ next if $self->up_to_date($infile, $outfile);
+
+ unless ( -d $fulldir ){
+ File::Path::mkpath($fulldir, 0, oct(755))
+ or die "Couldn't mkdir $fulldir: $!";
+ }
+
+ my $path2root = join( '/', ('..') x (@[email protected]) );
+ my $htmlroot = join( '/',
+ ($path2root,
+ $self->installdirs eq 'core' ? () : qw(site) ) );
+
+ my $fh = IO::File->new($infile) or die "Can't read $infile: $!";
+ my $abstract = Module::Build::PodParser->new(fh => $fh)->get_abstract();
+
+ my $title = join( '::', (@dirs, $name) );
+ $title .= " - $abstract" if $abstract;
+
+ my @opts = (
+ '--flush',
+ "--title=$title",
+ "--podpath=$podpath",
+ "--infile=$infile",
+ "--outfile=$outfile",
+ '--podroot=' . $self->blib,
+ "--htmlroot=$htmlroot",
+ );
+
+ if ( eval{Pod::Html->VERSION(1.03)} ) {
+ push( @opts, ('--header', '--backlink=Back to Top') );
+ push( @opts, "--css=$path2root/" . $self->html_css) if $self->html_css;
+ }
+
+ $self->log_info("HTMLifying $infile -> $outfile\n");
+ $self->log_verbose("pod2html @opts\n");
+ eval { Pod::Html::pod2html(@opts); 1 }
+ or $self->log_warn("pod2html @opts failed: [email protected]");
+ }
+
+}
+
+# Adapted from ExtUtils::MM_Unix
+sub man1page_name {
+ my $self = shift;
+ return File::Basename::basename( shift );
+}
+
+# Adapted from ExtUtils::MM_Unix and Pod::Man
+# Depending on M::B's dependency policy, it might make more sense to refactor
+# Pod::Man::begin_pod() to extract a name() methods, and use them...
+# -spurkis
+sub man3page_name {
+ 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 );
+}
+
+sub manpage_separator {
+ return '::';
+}
+
+# For systems that don't have 'diff' executable, should use Algorithm::Diff
+sub ACTION_diff {
+ my $self = shift;
+ $self->depends_on('build');
+ my $local_lib = File::Spec->rel2abs('lib');
+ my @myINC = grep {$_ ne $local_lib} @INC;
+
+ # The actual install destination might not be in @INC, so check there too.
+ push @myINC, map $self->install_destination($_), qw(lib arch);
+
+ 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};
+
+ my $text_suffix = file_qr('\.(pm|pod)$');
+
+ 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 {
+ print "Binary files $file and $installed differ\n";
+ }
+ }
+ }
+}
+
+sub ACTION_pure_install {
+ shift()->depends_on('install');
+}
+
+sub ACTION_install {
+ my ($self) = @_;
+ require ExtUtils::Install;
+ $self->depends_on('build');
+ ExtUtils::Install::install($self->install_map, !$self->quiet, 0, $self->{args}{uninst}||0);
+}
+
+sub ACTION_fakeinstall {
+ my ($self) = @_;
+ require ExtUtils::Install;
+ my $eui_version = ExtUtils::Install->VERSION;
+ if ( $eui_version < 1.32 ) {
+ $self->log_warn(
+ "The 'fakeinstall' action requires Extutils::Install 1.32 or later.\n"
+ . "(You only have version $eui_version)."
+ );
+ return;
+ }
+ $self->depends_on('build');
+ ExtUtils::Install::install($self->install_map, !$self->quiet, 1, $self->{args}{uninst}||0);
+}
+
+sub ACTION_versioninstall {
+ my ($self) = @_;
+
+ die "You must have only.pm 0.25 or greater installed for this operation: [email protected]\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_clean {
+ my ($self) = @_;
+ 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);
+}
+
+sub ACTION_ppd {
+ my ($self) = @_;
+ require Module::Build::PPMMaker;
+ my $ppd = Module::Build::PPMMaker->new();
+ my $file = $ppd->make_ppd(%{$self->{args}}, build => $self);
+ $self->add_to_cleanup($file);
+}
+
+sub ACTION_ppmdist {
+ my ($self) = @_;
+
+ $self->depends_on( 'build' );
+
+ my $ppm = $self->ppm_name;
+ $self->delete_filetree( $ppm );
+ $self->log_info( "Creating $ppm\n" );
+ $self->add_to_cleanup( $ppm, "$ppm.tar.gz" );
+
+ my %types = ( # translate types/dirs to those expected by ppm
+ lib => 'lib',
+ arch => 'arch',
+ bin => 'bin',
+ script => 'script',
+ bindoc => 'man1',
+ libdoc => 'man3',
+ binhtml => undef,
+ libhtml => undef,
+ );
+
+ foreach my $type ($self->install_types) {
+ next if exists( $types{$type} ) && !defined( $types{$type} );
+
+ my $dir = File::Spec->catdir( $self->blib, $type );
+ next unless -e $dir;
+
+ my $files = $self->rscan_dir( $dir );
+ foreach my $file ( @$files ) {
+ next unless -f $file;
+ my $rel_file =
+ File::Spec->abs2rel( File::Spec->rel2abs( $file ),
+ File::Spec->rel2abs( $dir ) );
+ my $to_file =
+ File::Spec->catfile( $ppm, 'blib',
+ exists( $types{$type} ) ? $types{$type} : $type,
+ $rel_file );
+ $self->copy_if_modified( from => $file, to => $to_file );
+ }
+ }
+
+ foreach my $type ( qw(bin lib) ) {
+ local $self->{properties}{html_css} = 'Active.css';
+ $self->htmlify_pods( $type, File::Spec->catdir($ppm, 'blib', 'html') );
+ }
+
+ # create a tarball;
+ # the directory tar'ed must be blib so we need to do a chdir first
+ my $target = File::Spec->catfile( File::Spec->updir, $ppm );
+ $self->_do_in_dir( $ppm, sub { $self->make_tarball( 'blib', $target ) } );
+
+ $self->depends_on( 'ppd' );
+
+ $self->delete_filetree( $ppm );
+}
+
+sub ACTION_pardist {
+ my ($self) = @_;
+
+ # Need PAR::Dist
+ if ( not eval { require PAR::Dist; PAR::Dist->VERSION(0.17) } ) {
+ $self->log_warn(
+ "In order to create .par distributions, you need to\n"
+ . "install PAR::Dist first."
+ );
+ return();
+ }
+
+ $self->depends_on( 'build' );
+
+ return PAR::Dist::blib_to_par(
+ name => $self->dist_name,
+ version => $self->dist_version,
+ );
+}
+
+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) = @_;
+
+ require ExtUtils::Manifest;
+ local $^W; # ExtUtils::Manifest is not warnings clean.
+ my ($missing, $extra) = ExtUtils::Manifest::fullcheck();
+
+ return unless @$missing || @$extra;
+
+ my $msg = "MANIFEST appears to be out of sync with the distribution\n";
+ if ( $self->invoked_action eq 'distcheck' ) {
+ die $msg;
+ } else {
+ warn $msg;
+ }
+}
+
+sub _add_to_manifest {
+ my ($self, $manifest, $lines) = @_;
+ $lines = [$lines] unless ref $lines;
+
+ my $existing_files = $self->_read_manifest($manifest);
+ return unless defined( $existing_files );
+
+ @$lines = grep {!exists $existing_files->{$_}} @$lines
+ or return;
+
+ 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$/;
+ $fh->close;
+
+ $fh = IO::File->new(">> $manifest") or die "Can't write to $manifest: $!";
+ print $fh "\n" unless $has_newline;
+ print $fh map "$_\n", @$lines;
+ close $fh;
+ chmod($mode, $manifest);
+
+ $self->log_info(map "Added to $manifest: $_\n", @$lines);
+}
+
+sub _sign_dir {
+ my ($self, $dir) = @_;
+
+ unless (eval { require Module::Signature; 1 }) {
+ $self->log_warn("Couldn't load Module::Signature for 'distsign' action:\n [email protected]\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 _do_in_dir {
+ my ($self, $dir, $do) = @_;
+
+ my $start_dir = $self->cwd;
+ chdir $dir or die "Can't chdir() to $dir: $!";
+ eval {$do->()};
+ chdir $start_dir or push @err, "Can't chdir() back to $start_dir: $!";
+ die join "\n", @err if @err;
+}
+
+sub ACTION_distsign {
+ my ($self) = @_;
+ {
+ local $self->{properties}{sign} = 0; # We'll sign it ourselves
+ $self->depends_on('distdir') unless -d $self->dist_dir;
+ }
+ $self->_sign_dir($self->dist_dir);
+}
+
+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');
+}
+
+sub do_create_makefile_pl {
+ my $self = shift;
+ require Module::Build::Compat;
+ $self->log_info("Creating Makefile.PL\n");
+ Module::Build::Compat->create_makefile_pl($self->create_makefile_pl, $self, @_);
+ $self->_add_to_manifest('MANIFEST', 'Makefile.PL');
+}
+
+sub do_create_license {
+ my $self = shift;
+ $self->log_info("Creating LICENSE file\n");
+
+ my $l = $self->license
+ or die "No license specified";
+
+ my $key = $self->valid_licenses->{$l}
+ or die "'$l' isn't a license key we know about";
+ my $class = "Software::License::$key";
+
+ eval "use $class; 1"
+ or die "Can't load Software::License to create LICENSE file: [email protected]";
+
+ $self->delete_filetree('LICENSE');
+
+ my $author = join " & ", @{ $self->dist_author };
+ my $license = $class->new({holder => $author});
+ my $fh = IO::File->new('> LICENSE')
+ or die "Can't write LICENSE file: $!";
+ print $fh $license->fulltext;
+ close $fh;
+
+ $self->_add_to_manifest('MANIFEST', 'LICENSE');
+}
+
+sub do_create_readme {
+ my $self = shift;
+ $self->delete_filetree('README');
+
+ my $docfile = $self->_main_docfile;
+ unless ( $docfile ) {
+ $self->log_warn(<<EOF);
+Cannot create README: can't determine which file contains documentation;
+Must supply either 'dist_version_from', or 'module_name' parameter.
+EOF
+ return;
+ }
+
+ if ( eval {require Pod::Readme; 1} ) {
+ $self->log_info("Creating README using Pod::Readme\n");
+
+ my $parser = Pod::Readme->new;
+ $parser->parse_from_file($docfile, 'README', @_);
+
+ } elsif ( eval {require Pod::Text; 1} ) {
+ $self->log_info("Creating README using Pod::Text\n");
+
+ my $fh = IO::File->new('> README');
+ if ( defined($fh) ) {
+ local $^W = 0;
+ no strict "refs";
+
+ # work around bug in Pod::Text 3.01, which expects
+ # Pod::Simple::parse_file to take input and output filehandles
+ # when it actually only takes an input filehandle
+
+ my $old_parse_file;
+ $old_parse_file = \&{"Pod::Simple::parse_file"}
+ and
+ local *{"Pod::Simple::parse_file"} = sub {
+ my $self = shift;
+ $self->output_fh($_[1]) if $_[1];
+ $self->$old_parse_file($_[0]);
+ }
+ if $Pod::Text::VERSION
+ == 3.01; # Split line to avoid evil version-finder
+
+ Pod::Text::pod2text( $docfile, $fh );
+
+ $fh->close;
+ } else {
+ $self->log_warn(
+ "Cannot create 'README' file: Can't open file for writing\n" );
+ return;
+ }
+
+ } else {
+ $self->log_warn("Can't load Pod::Readme or Pod::Text to create README\n");
+ return;
+ }
+
+ $self->_add_to_manifest('MANIFEST', 'README');
+}
+
+sub _main_docfile {
+ my $self = shift;
+ if ( my $pm_file = $self->dist_version_from ) {
+ (my $pod_file = $pm_file) =~ s/.pm$/.pod/;
+ return (-e $pod_file ? $pod_file : $pm_file);
+ } else {
+ return undef;
+ }
+}
+
+sub ACTION_distdir {
+ my ($self) = @_;
+
+ $self->depends_on('distmeta');
+
+ my $dist_files = $self->_read_manifest('MANIFEST')
+ or die "Can't create distdir without a MANIFEST file - run 'manifest' action first";
+ 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->_sign_dir($dist_dir) if $self->{properties}{sign};
+}
+
+sub ACTION_disttest {
+ my ($self) = @_;
+
+ $self->depends_on('distdir');
+
+ $self->_do_in_dir
+ ( $self->dist_dir,
+ sub {
+ # XXX could be different names for scripts
+
+ $self->run_perl_script('Build.PL') # XXX Should this be run w/ --nouse-rcfile
+ or die "Error executing 'Build.PL' in dist directory: $!";
+ $self->run_perl_script('Build')
+ or die "Error executing 'Build' in dist directory: $!";
+ $self->run_perl_script('Build', [], ['test'])
+ or die "Error executing 'Build test' in dist directory";
+ });
+}
+
+
+=begin private
+
+ my $has_include = $build->_eumanifest_has_include;
+
+Returns true if the installed version of ExtUtils::Manifest supports
+#include and #include_default directives. False otherwise.
+
+=end private
+
+=cut
+
+# #!include and #!include_default were added in 1.50
+sub _eumanifest_has_include {
+ my $self = shift;
+
+ require ExtUtils::Manifest;
+ return ExtUtils::Manifest->VERSION >= 1.50 ? 1 : 0;
+ return 0;
+}
+
+
+=begin private
+
+ my $maniskip_file = $build->_default_maniskip;
+
+Returns the location of the installed MANIFEST.SKIP file used by
+default.
+
+=end private
+
+=cut
+
+sub _default_maniskip {
+ my $self = shift;
+
+ my $default_maniskip;
+ for my $dir (@INC) {
+ $default_maniskip = File::Spec->catfile($dir, "ExtUtils", "MANIFEST.SKIP");
+ last if -r $default_maniskip;
+ }
+
+ return $default_maniskip;
+}
+
+
+=begin private
+
+ my $content = $build->_slurp($file);
+
+Reads $file and returns the $content.
+
+=end private
+
+=cut
+
+sub _slurp {
+ my $self = shift;
+ my $file = shift;
+ open my $fh, "<", $file or croak "Can't open $file: $!";
+ local $/;
+ return <$fh>;
+}
+
+
+sub _write_default_maniskip {
+ my $self = shift;
+ my $file = shift || 'MANIFEST.SKIP';
+ my $fh = IO::File->new("> $file")
+ or die "Can't open $file: $!";
+
+ my $content = $self->_eumanifest_has_include ? "#!include_default\n"
+ : $self->_slurp( $self->_default_maniskip );
+
+ $content .= <<'EOF';
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\bBuild.bat$
+\b_build
+\bBuild.COM$
+\bBUILD.COM$
+\bbuild.com$
+
+# Avoid archives of this distribution
+EOF
+
+ # Skip, for example, 'Module-Build-0.27.tar.gz'
+ $content .= '\b'.$self->dist_name.'-[\d\.\_]+'."\n";
+
+ print $fh $content;
+
+ return;
+}
+
+sub ACTION_manifest {
+ my ($self) = @_;
+
+ my $maniskip = 'MANIFEST.SKIP';
+ unless ( -e 'MANIFEST' || -e $maniskip ) {
+ $self->log_warn("File '$maniskip' does not exist: Creating a default '$maniskip'\n");
+ $self->_write_default_maniskip($maniskip);
+ }
+
+ require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean.
+ local ($^W, $ExtUtils::Manifest::Quiet) = (0,1);
+ ExtUtils::Manifest::mkmanifest();
+}
+
+# Case insensitive regex for files
+sub file_qr {
+ return File::Spec->case_tolerant ? qr($_[0])i : qr($_[0]);
+}
+
+sub dist_dir {
+ my ($self) = @_;
+ return join "-", $self->dist_name, $self->dist_version;
+}
+
+sub ppm_name {
+ my $self = shift;
+ return 'PPM-' . $self->dist_dir;
+}
+
+sub _files_in {
+ my ($self, $dir) = @_;
+ return unless -d $dir;
+
+ local *DH;
+ opendir DH, $dir or die "Can't read directory $dir: $!";
+
+ my @files;
+ while (defined (my $file = readdir DH)) {
+ my $full_path = File::Spec->catfile($dir, $file);
+ next if -d $full_path;
+ push @files, $full_path;
+ }
+ return @files;
+}
+
+sub script_files {
+ my $self = shift;
+
+ for ($self->{properties}{script_files}) {
+ $_ = shift if @_;
+ next unless $_;
+
+ # Always coerce into a hash
+ return $_ if UNIVERSAL::isa($_, 'HASH');
+ return $_ = { map {$_,1} @$_ } if UNIVERSAL::isa($_, 'ARRAY');
+
+ die "'script_files' must be a hashref, arrayref, or string" if ref();
+
+ return $_ = { map {$_,1} $self->_files_in( $_ ) } if -d $_;
+ return $_ = {$_ => 1};
+ }
+
+ my %pl_files = map {
+ 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 $_ : $_ )
+ } @bin_files;
+
+ return $_ = { map {$_ => 1} grep !$pl_files{$bin_map{$_}}, @bin_files };
+}
+BEGIN { *scripts = \&script_files; }
+
+{
+ my %licenses = (
+ perl => 'Perl_5',
+ apache => 'Apache_2_0',
+ artistic => 'Artistic_1_0',
+ artistic_2 => 'Artistic_2_0',
+ lgpl => 'LGPL_2_1',
+ lgpl2 => 'LGPL_2_1',
+ lgpl3 => 'LGPL_3_0',
+ bsd => 'BSD',
+ gpl => 'GPL_1',
+ gpl2 => 'GPL_2',
+ gpl3 => 'GPL_3',
+ mit => 'MIT',
+ mozilla => 'Mozilla_1_1',
+ open_source => undef,
+ unrestricted => undef,
+ restrictive => undef,
+ unknown => undef,
+ );
+
+ # TODO - would be nice to not have these here, since they're more
+ # properly stored only in Software::License
+ my %license_urls = (
+ perl => 'http://dev.perl.org/licenses/',
+ apache => 'http://apache.org/licenses/LICENSE-2.0',
+ 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',
+ lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php',
+ lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html',
+ bsd => 'http://opensource.org/licenses/bsd-license.php',
+ gpl => 'http://opensource.org/licenses/gpl-license.php',
+ gpl2 => 'http://opensource.org/licenses/gpl-2.0.php',
+ gpl3 => 'http://opensource.org/licenses/gpl-3.0.html',
+ mit => 'http://opensource.org/licenses/mit-license.php',
+ mozilla => 'http://opensource.org/licenses/mozilla1.1.php',
+ open_source => undef,
+ unrestricted => undef,
+ restrictive => undef,
+ unknown => undef,
+ );
+ sub valid_licenses {
+ return \%licenses;
+ }
+ sub _license_url {
+ return $license_urls{$_[1]};
+ }
+}
+
+sub _hash_merge {
+ my ($self, $h, $k, $v) = @_;
+ if (ref $h->{$k} eq 'ARRAY') {
+ push @{$h->{$k}}, ref $v ? @$v : $v;
+ } elsif (ref $h->{$k} eq 'HASH') {
+ $h->{$k}{$_} = $v->{$_} foreach keys %$v;
+ } else {
+ $h->{$k} = $v;
+ }
+}
+
+sub ACTION_distmeta {
+ my ($self) = @_;
+
+ $self->do_create_makefile_pl if $self->create_makefile_pl;
+ $self->do_create_readme if $self->create_readme;
+ $self->do_create_license if $self->create_license;
+ $self->do_create_metafile;
+}
+
+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';
+ }
+ unless (exists $self->valid_licenses->{ $p->{license} }) {
+ die "Unknown license type '$p->{license}'";
+ }
+
+ # If we're in the distdir, the metafile may exist and be non-writable.
+ $self->delete_filetree($metafile);
+ $self->log_info("Creating $metafile\n");
+
+ # Since we're building ourself, we have to do some special stuff
+ # here: the ConfigData module is found in blib/lib.
+ local @INC = @INC;
+ if (($self->module_name || '') eq 'Module::Build') {
+ $self->depends_on('config_data');
+ push @INC, File::Spec->catdir($self->blib, 'lib');
+ }
+
+ if ( $self->write_metafile( $self->metafile, $self->generate_metadata ) ) {
+ $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) = @_;
+
+ 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 );
+ } else {
+ # XXX probably redundant
+ require Module::Build::YAML;
+ &Module::Build::YAML::DumpFile($metafile, $node);
+ }
+ return 1;
+}
+
+sub normalize_version {
+ my ($self, $version) = @_;
+ if ( $version =~ /[=<>!,]/ ) { # logic, not just version
+ # take as is without modification
+ }
+ elsif ( ref $version eq 'version' ||
+ ref $version eq 'Module::Build::Version' ) { # version objects
+ $version = $version->is_qv ? $version->normal : $version->stringify;
+ }
+ elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
+ # normalize string tuples without "v": "1.2.3" -> "v1.2.3"
+ $version = "v$version";
+ }
+ else {
+ # leave alone
+ }
+ return $version;
+}
+
+sub prepare_metadata {
+ my ($self, $node, $keys) = @_;
+ my $p = $self->{properties};
+
+ # 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});
+ }
+ $node->{version} = $self->normalize_version($node->{version});
+
+ if (defined( my $l = $self->license )) {
+ die "Unknown license string '$l'"
+ unless exists $self->valid_licenses->{ $l };
+
+ if (my $key = $self->valid_licenses->{ $l }) {
+ my $class = "Software::License::$key";
+ if (eval "use $class; 1") {
+ # S::L requires a 'holder' key
+ $node->{resources}{license} = $class->new({holder=>"nobody"})->url;
+ }
+ else {
+ $node->{resources}{license} = $self->_license_url($l);
+ }
+ }
+ # XXX we are silently omitting the url for any unknown license
+ }
+
+ # 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}) {
+ for my $mod ( keys %{ $p->{$type} } ) {
+ $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'}
+ ) {
+ $prereq_types{configure_requires}{'Module::Build'} = $VERSION;
+ }
+
+ for my $t ( keys %prereq_types ) {
+ $add_node->($t, $prereq_types{$t});
+ }
+
+ if (exists $p->{dynamic_config}) {
+ $add_node->('dynamic_config', $p->{dynamic_config});
+ }
+ my $pkgs = eval { $self->find_dist_packages };
+ $self->log_warn("[email protected]\nWARNING: Possible missing or corrupt 'MANIFEST' file.\n" .
+ "Nothing to enter for 'provides' field in META.yml\n");
+ } else {
+ $node->{provides} = $pkgs if %$pkgs;
+ }
+;
+ if (exists $p->{no_index}) {
+ $add_node->('no_index', $p->{no_index});
+ }
+
+ $add_node->('generated_by', "Module::Build version $Module::Build::VERSION");
+
+ $add_node->('meta-spec',
+ {version => '1.4',
+ url => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
+ });
+
+ while (my($k, $v) = each %{$self->meta_add}) {
+ $add_node->($k, $v);
+ }
+
+ while (my($k, $v) = each %{$self->meta_merge}) {
+ $self->_hash_merge($node, $k, $v);
+ }
+
+ return $node;
+}
+
+sub _read_manifest {
+ my ($self, $file) = @_;
+ return undef unless -e $file;
+
+ require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean.
+ local ($^W, $ExtUtils::Manifest::Quiet) = (0,1);
+ return scalar ExtUtils::Manifest::maniread($file);
+}
+
+sub find_dist_packages {
+ my $self = shift;
+
+ # Only packages in .pm files are candidates for inclusion here.
+ # Only include things in the MANIFEST, not things in developer's
+ # private stock.
+
+ my $manifest = $self->_read_manifest('MANIFEST')
+ or die "Can't find dist packages without a MANIFEST file - run 'manifest' action first";
+
+ # Localize
+ my %dist_files = map { $self->localize_file_path($_) => $_ }
+ keys %$manifest;
+
+ my @pm_files = grep {exists $dist_files{$_}} keys %{ $self->find_pm_files };
+
+ # 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} );
+ (my $prime_package = join( '::', @path[1..$#path] )) =~ s/\.pm$//;
+
+ my $pm_info = Module::Build::ModuleInfo->new_from_file( $file );
+
+ foreach my $package ( $pm_info->packages_inside ) {
+ next if $package eq 'main'; # main can appear numerous times, ignore
+ next if grep /^_/, split( /::/, $package ); # private package, ignore
+
+ 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};
+ $prime{$package}{version} = $version if defined( $version );
+ }
+ } else {
+ push( @{$alt{$package}}, {
+ file => $dist_files{$file},
+ version => $version,
+ } );
+ }
+ }
+ }
+
+ # Then we iterate over all the packages found above, identifying conflicts
+ # and selecting the "best" candidate for recording the file & version
+ # for each package.
+ foreach my $package ( keys( %alt ) ) {
+ my $result = $self->_resolve_module_versions( $alt{$package} );
+
+ if ( exists( $prime{$package} ) ) { # primary package selected
+
+ if ( $result->{err} ) {
+ # Use the selected primary package, but there are conflicting
+ # errors among multiple alternative packages that need to be
+ # reported
+ $self->log_warn(
+ "Found conflicting versions for package '$package'\n" .
+ " $prime{$package}{file} ($prime{$package}{version})\n" .
+ $result->{err}
+ );
+
+ } elsif ( defined( $result->{version} ) ) {
+ # There is a primary package selected, and exactly one
+ # alternative package
+
+ if ( exists( $prime{$package}{version} ) &&
+ defined( $prime{$package}{version} ) ) {
+ # Unless the version of the primary package agrees with the
+ # version of the alternative package, report a conflict
+ if ( $self->compare_versions( $prime{$package}{version}, '!=',
+ $result->{version} ) ) {
+ $self->log_warn(
+ "Found conflicting versions for package '$package'\n" .
+ " $prime{$package}{file} ($prime{$package}{version})\n" .
+ " $result->{file} ($result->{version})\n"
+ );
+ }
+
+ } else {
+ # The prime package selected has no version so, we choose to
+ # use any alternative package that does have a version
+ $prime{$package}{file} = $result->{file};
+ $prime{$package}{version} = $result->{version};
+ }
+
+ } else {
+ # no alt package found with a version, but we have a prime
+ # package so we use it whether it has a version or not
+ }
+
+ } else { # No primary package was selected, use the best alternative
+
+ if ( $result->{err} ) {
+ $self->log_warn(
+ "Found conflicting versions for package '$package'\n" .
+ $result->{err}
+ );
+ }
+
+ # Despite possible conflicting versions, we choose to record
+ # something rather than nothing
+ $prime{$package}{file} = $result->{file};
+ $prime{$package}{version} = $result->{version}
+ if defined( $result->{version} );
+ }
+ }
+
+ # Normalize versions. Can't use exists() here because of bug in YAML::Node.
+ # XXX "bug in YAML::Node" comment seems irrelvant -- dagolden, 2009-05-18
+ for (grep defined $_->{version}, values %prime) {
+ $_->{version} = $self->normalize_version( $_->{version} );
+ }
+
+ return \%prime;
+}
+
+# separate out some of the conflict resolution logic from
+# $self->find_dist_packages(), above, into a helper function.
+#
+sub _resolve_module_versions {
+ my $self = shift;
+
+ my $packages = shift;
+
+ my( $file, $version );
+ my $err = '';
+ foreach my $p ( @$packages ) {
+ if ( defined( $p->{version} ) ) {
+ if ( defined( $version ) ) {
+ if ( $self->compare_versions( $version, '!=', $p->{version} ) ) {
+ $err .= " $p->{file} ($p->{version})\n";
+ } else {
+ # same version declared multiple times, ignore
+ }
+ } else {
+ $file = $p->{file};
+ $version = $p->{version};
+ }
+ }
+ $file ||= $p->{file} if defined( $p->{file} );
+ }
+
+ if ( $err ) {
+ $err = " $file ($version)\n" . $err;
+ }
+
+ my %result = (
+ file => $file,
+ version => $version,
+ err => $err
+ );
+
+ return \%result;
+}
+
+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 {
+ require Archive::Tar;
+
+ # 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;
+
+ my $files = $self->rscan_dir($dir);
+ my $tar = Archive::Tar->new;
+ $tar->add_files(@$files);
+ for my $f ($tar->get_files) {
+ $f->mode($f->mode & ~022); # chmod go-w
+ }
+ $tar->write("$file.tar.gz", 1);
+ }
+}
+
+sub install_path {
+ my $self = shift;
+ my( $type, $value ) = ( @_, '<empty>' );
+
+ Carp::croak( 'Type argument missing' )
+ unless defined( $type );
+
+ my $map = $self->{properties}{install_path};
+ return $map unless @_;
+
+ # delete existing value if $value is literal undef()
+ unless ( defined( $value ) ) {
+ delete( $map->{$type} );
+ return undef;
+ }
+
+ # return existing value if no new $value is given
+ if ( $value eq '<empty>' ) {
+ return undef unless exists $map->{$type};
+ return $map->{$type};
+ }
+
+ # set value if $value is a valid relative path
+ return $map->{$type} = $value;
+}
+
+sub install_base_relpaths {
+ # Usage: install_base_relpaths(), install_base_relpaths('lib'),
+ # or install_base_relpaths('lib' => $value);
+ my $self = shift;
+ my $map = $self->{properties}{install_base_relpaths};
+ return $map unless @_;
+ return $self->_relpaths($map, @_);
+}
+
+
+# Translated from ExtUtils::MM_Any::init_INSTALL_from_PREFIX
+sub prefix_relative {
+ my ($self, $type) = @_;
+ my $installdirs = $self->installdirs;
+
+ my $relpath = $self->install_sets($installdirs)->{$type};
+
+ return $self->_prefixify($relpath,
+ $self->original_prefix($installdirs),
+ $type,
+ );
+}
+
+sub _relpaths {
+ my $self = shift;
+ my( $map, $type, $value ) = ( @_, '<empty>' );
+
+ Carp::croak( 'Type argument missing' )
+ unless defined( $type );
+
+ my @value = ();
+
+ # delete existing value if $value is literal undef()
+ unless ( defined( $value ) ) {
+ delete( $map->{$type} );
+ return undef;
+ }
+
+ # return existing value if no new $value is given
+ elsif ( $value eq '<empty>' ) {
+ return undef unless exists $map->{$type};
+ @value = @{ $map->{$type} };
+ }
+
+ # set value if $value is a valid relative path
+ else {
+ Carp::croak( "Value must be a relative path" )
+ if File::Spec::Unix->file_name_is_absolute($value);
+
+ @value = split( /\//, $value );
+ $map->{$type} = \@value;
+ }
+
+ return File::Spec->catdir( @value );
+}
+
+# Defaults to use in case the config install paths cannot be prefixified.
+sub prefix_relpaths {
+ # Usage: prefix_relpaths('site'), prefix_relpaths('site', 'lib'),
+ # or prefix_relpaths('site', 'lib' => $value);
+ my $self = shift;
+ my $installdirs = shift || $self->installdirs;
+ my $map = $self->{properties}{prefix_relpaths}{$installdirs};
+ return $map unless @_;
+ return $self->_relpaths($map, @_);
+}
+
+
+# Translated from ExtUtils::MM_Unix::prefixify()
+sub _prefixify {
+ my($self, $path, $sprefix, $type) = @_;
+
+ my $rprefix = $self->prefix;
+ $rprefix .= '/' if $sprefix =~ m|/$|;
+
+ $self->log_verbose(" prefixify $path from $sprefix to $rprefix\n")
+ if defined( $path ) && length( $path );
+
+ if( !defined( $path ) || ( length( $path ) == 0 ) ) {
+ $self->log_verbose(" no path to prefixify, falling back to default.\n");
+ return $self->_prefixify_default( $type, $rprefix );
+ } elsif( !File::Spec->file_name_is_absolute($path) ) {
+ $self->log_verbose(" path is relative, not prefixifying.\n");
+ } elsif( $path !~ s{^\Q$sprefix\E\b}{}s ) {
+ $self->log_verbose(" cannot prefixify, falling back to default.\n");
+ return $self->_prefixify_default( $type, $rprefix );
+ }
+
+ $self->log_verbose(" now $path in $rprefix\n");
+
+ return $path;
+}
+
+sub _prefixify_default {
+ my $self = shift;
+ my $type = shift;
+ my $rprefix = shift;
+
+ my $default = $self->prefix_relpaths($self->installdirs, $type);
+ if( !$default ) {
+ $self->log_verbose(" no default install location for type '$type', using prefix '$rprefix'.\n");
+ return $rprefix;
+ } else {
+ return $default;
+ }
+}
+
+sub install_destination {
+ my ($self, $type) = @_;
+
+ return $self->install_path($type) if $self->install_path($type);
+
+ if ( $self->install_base ) {
+ my $relpath = $self->install_base_relpaths($type);
+ return $relpath ? File::Spec->catdir($self->install_base, $relpath) : undef;
+ }
+
+ if ( $self->prefix ) {
+ my $relpath = $self->prefix_relative($type);
+ return $relpath ? File::Spec->catdir($self->prefix, $relpath) : undef;
+ }
+
+ return $self->install_sets($self->installdirs)->{$type};
+}
+
+sub install_types {
+ my $self = shift;
+
+ my %types;
+ if ( $self->install_base ) {
+ %types = %{$self->install_base_relpaths};
+ } elsif ( $self->prefix ) {
+ %types = %{$self->prefix_relpaths};
+ } else {
+ %types = %{$self->install_sets($self->installdirs)};
+ }
+
+ %types = (%types, %{$self->install_path});
+
+ return sort keys %types;
+}
+
+sub install_map {
+ my ($self, $blib) = @_;
+ $blib ||= $self->blib;
+
+ my( %map, @skipping );
+ foreach my $type ($self->install_types) {
+ my $localdir = File::Spec->catdir( $blib, $type );
+ next unless -e $localdir;
+
+ if (my $dest = $self->install_destination($type)) {
+ $map{$localdir} = $dest;
+ } else {
+ push( @skipping, $type );
+ }
+ }
+
+ $self->log_warn(
+ "WARNING: Can't figure out install path for types: @skipping\n" .
+ "Files will not be installed.\n"
+ ) if @skipping;
+
+ # Write the packlist into the same place as ExtUtils::MakeMaker.
+ if ($self->create_packlist and my $module_name = $self->module_name) {
+ my $archdir = $self->install_destination('arch');
+ my @ext = split /::/, $module_name;
+ $map{write} = File::Spec->catfile($archdir, 'auto', @ext, '.packlist');
+ }
+
+ # Handle destdir
+ if (length(my $destdir = $self->destdir || '')) {
+ foreach (keys %map) {
+ # Need to remove volume from $map{$_} using splitpath, or else
+ # we'll create something crazy like C:\Foo\Bar\E:\Baz\Quux
+ # VMS will always have the file separate than the path.
+ my ($volume, $path, $file) = File::Spec->splitpath( $map{$_}, 0 );
+
+ # catdir needs a list of directories, or it will create something
+ # crazy like volume:[Foo.Bar.volume.Baz.Quux]
+ my @dirs = File::Spec->splitdir($path);
+
+ # First merge the directories
+ $path = File::Spec->catdir($destdir, @dirs);
+
+ # Then put the file back on if there is one.
+ if ($file ne '') {
+ $map{$_} = File::Spec->catfile($path, $file)
+ } else {
+ $map{$_} = $path;
+ }
+ }
+ }
+
+ $map{read} = ''; # To keep ExtUtils::Install quiet
+
+ return \%map;
+}
+
+sub depends_on {
+ my $self = shift;
+ foreach my $action (@_) {
+ $self->_call_action($action);
+ }
+}
+
+sub rscan_dir {
+ my ($self, $dir, $pattern) = @_;
+ my @result;
+ local $_; # find() can overwrite $_, so protect ourselves
+ my $subr = !$pattern ? sub {push @result, $File::Find::name} :
+ !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;
+}
+
+sub delete_filetree {
+ my $self = shift;
+ my $deleted = 0;
+ foreach (@_) {
+ next unless -e $_;
+ $self->log_info("Deleting $_\n");
+ File::Path::rmtree($_, 0, 0);
+ die "Couldn't remove '$_': $!\n" if -e $_;
+ $deleted++;
+ }
+ return $deleted;
+}
+
+sub autosplit_file {
+ my ($self, $file, $to) = @_;
+ require AutoSplit;
+ my $dir = File::Spec->catdir($to, 'lib', 'auto');
+ AutoSplit::autosplit($file, $dir);
+}
+
+sub cbuilder {
+ # Returns a CBuilder object
+
+ my $self = shift;
+ my $s = $self->{stash};
+ return $s->{_cbuilder} if $s->{_cbuilder};
+ die "Module::Build is not configured with C_support"
+ unless $self->_mb_feature('C_support');
+
+ require ExtUtils::CBuilder;
+ return $s->{_cbuilder} = ExtUtils::CBuilder->new(
+ config => $self->config,
+ ($self->quiet ? (quiet => 1 ) : ()),
+ );
+}
+
+sub have_c_compiler {
+ my ($self) = @_;
+
+ my $p = $self->{properties};
+ return $p->{have_compiler} if defined $p->{have_compiler};
+
+ $self->log_verbose("Checking if compiler tools configured... ");
+ my $b = eval { $self->cbuilder };
+ my $have = $b && $b->have_compiler;
+ $self->log_verbose($have ? "ok.\n" : "failed.\n");
+ return $p->{have_compiler} = $have;
+}
+
+sub compile_c {
+ my ($self, $file, %args) = @_;
+ 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);
+
+ $b->compile(source => $file,
+ defines => $args{defines},
+ object_file => $obj_file,
+ include_dirs => $self->include_dirs,
+ extra_compiler_flags => $self->extra_compiler_flags,
+ );
+
+ return $obj_file;
+}
+
+sub link_c {
+ my ($self, $spec) = @_;
+ my $p = $self->{properties}; # For convenience
+
+ $self->add_to_cleanup($spec->{lib_file});
+
+ my $objects = $p->{objects} || [];
+
+ return $spec->{lib_file}
+ if $self->up_to_date([$spec->{obj_file}, @$objects],
+ $spec->{lib_file});
+
+ my $module_name = $spec->{module_name} || $self->module_name;
+
+ $self->cbuilder->link(
+ module_name => $module_name,
+ objects => [$spec->{obj_file}, @$objects],
+ lib_file => $spec->{lib_file},
+ extra_linker_flags => $p->{extra_linker_flags} );
+
+ return $spec->{lib_file};
+}
+
+sub compile_xs {
+ my ($self, $file, %args) = @_;
+
+ $self->log_info("$file -> $args{outfile}\n");
+
+ if (eval {require ExtUtils::ParseXS; 1}) {
+
+ ExtUtils::ParseXS::process_file(
+ filename => $file,
+ prototypes => 0,
+ output => $args{outfile},
+ );
+ } 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)]
+ );
+ 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);
+ close $fh;
+ }
+}
+
+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 oneliner {
+ # Returns a string that the shell can evaluate as a perl command.
+ # This should be avoided whenever possible, since "the shell" really
+ # means zillions of shells on zillions of platforms and it's really
+ # hard to get it right all the time.
+
+ # Some of this code is stolen with permission from ExtUtils::MakeMaker.
+
+ my($self, $cmd, $switches, $args) = @_;
+ $switches = [] unless defined $switches;
+ $args = [] unless defined $args;
+
+ # Strip leading and trailing newlines
+ $cmd =~ s{^\n+}{};
+ $cmd =~ s{\n+$}{};
+
+ my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter;
+ return $self->_quote_args($perl, @$switches, '-e', $cmd, @$args);
+}
+
+sub run_perl_script {
+ my ($self, $script, $preargs, $postargs) = @_;
+ foreach ($preargs, $postargs) {
+ $_ = [ $self->split_like_shell($_) ] unless ref();
+ }
+ return $self->run_perl_command([@$preargs, $script, @$postargs]);
+}
+
+sub run_perl_command {
+ # XXX Maybe we should accept @args instead of $args? Must resolve
+ # this before documenting.
+ my ($self, $args) = @_;
+ $args = [ $self->split_like_shell($args) ] unless ref($args);
+ my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter;
+
+ # Make sure our local additions to @INC are propagated to the subprocess
+ local $ENV{PERL5LIB} = join $self->config('path_sep'), $self->_added_to_INC;
+
+ return $self->do_system($perl, @$args);
+}
+
+# Infer various data from the path of the input filename
+# that is needed to create output files.
+# The input filename is expected to be of the form:
+# lib/Module/Name.ext or Module/Name.ext
+sub _infer_xs_spec {
+ my $self = shift;
+ my $file = shift;
+
+ my $cf = $self->{config};
+
+ my %spec;
+
+ my( $v, $d, $f ) = File::Spec->splitpath( $file );
+ my @d = File::Spec->splitdir( $d );
+ (my $file_base = $f) =~ s/\.[^.]+$//i;
+
+ $spec{base_name} = $file_base;
+
+ $spec{src_dir} = File::Spec->catpath( $v, $d, '' );
+
+ # the module name
+ shift( @d ) while @d && ($d[0] eq 'lib' || $d[0] eq '');
+ pop( @d ) while @d && $d[-1] eq '';
+ $spec{module_name} = join( '::', (@d, $file_base) );
+
+ $spec{archdir} = File::Spec->catdir($self->blib, 'arch', 'auto',
+ @d, $file_base);
+
+ $spec{bs_file} = File::Spec->catfile($spec{archdir}, "${file_base}.bs");
+
+ $spec{lib_file} = File::Spec->catfile($spec{archdir},
+ "${file_base}.".$cf->get('dlext'));
+
+ $spec{c_file} = File::Spec->catfile( $spec{src_dir},
+ "${file_base}.c" );
+
+ $spec{obj_file} = File::Spec->catfile( $spec{src_dir},
+ "${file_base}".$cf->get('obj_ext') );
+
+ return \%spec;
+}
+
+sub process_xs {
+ my ($self, $file) = @_;
+
+ my $spec = $self->_infer_xs_spec($file);
+
+ # File name, minus the suffix
+ (my $file_base = $file) =~ s/\.[^.]+$//;
+
+ # .xs -> .c
+ $self->add_to_cleanup($spec->{c_file});
+
+ unless ($self->up_to_date($file, $spec->{c_file})) {
+ $self->compile_xs($file, outfile => $spec->{c_file});
+ }
+
+ # .c -> .o
+ my $v = $self->dist_version;
+ $self->compile_c($spec->{c_file},
+ defines => {VERSION => qq{"$v"}, XS_VERSION => qq{"$v"}});
+
+ # archdir
+ File::Path::mkpath($spec->{archdir}, 0, oct(777)) unless -d $spec->{archdir};
+
+ # .xs -> .bs
+ $self->add_to_cleanup($spec->{bs_file});
+ unless ($self->up_to_date($file, $spec->{bs_file})) {
+ require ExtUtils::Mkbootstrap;
+ $self->log_info("ExtUtils::Mkbootstrap::Mkbootstrap('$spec->{bs_file}')\n");
+ ExtUtils::Mkbootstrap::Mkbootstrap($spec->{bs_file}); # Original had $BSLOADLIBS - what's that?
+ {my $fh = IO::File->new(">> $spec->{bs_file}")} # create
+ utime((time)x2, $spec->{bs_file}); # touch
+ }
+
+ # .o -> .(a|bundle)
+ $self->link_c($spec);
+}
+
+sub do_system {
+ my ($self, @cmd) = @_;
+ $self->log_info("@cmd\n");
+
+ # Some systems proliferate huge PERL5LIBs, try to ameliorate:
+ my %seen;
+ my $sep = $self->config('path_sep');
+ local $ENV{PERL5LIB} =
+ ( !exists($ENV{PERL5LIB}) ? '' :
+ length($ENV{PERL5LIB}) < 500
+ ? $ENV{PERL5LIB}
+ : join $sep, grep { ! $seen{$_}++ and -d $_ } split($sep, $ENV{PERL5LIB})
+ );
+
+ my $status = system(@cmd);
+ if ($status and $! =~ /Argument list too long/i) {
+ my $env_entries = '';
+ foreach (sort keys %ENV) { $env_entries .= "$_=>".length($ENV{$_})."; " }
+ warn "'Argument list' was 'too long', env lengths are $env_entries";
+ }
+ return !$status;
+}
+
+sub copy_if_modified {
+ my $self = shift;
+ my %args = (@_ > 3
+ ? ( @_ )
+ : ( from => shift, to_dir => shift, flatten => shift )
+ );
+ $args{verbose} = !$self->quiet
+ unless exists $args{verbose};
+
+ my $file = $args{from};
+ 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
+ $args{flatten} = 1 if File::Spec->file_name_is_absolute( $file );
+
+ my $to_path;
+ if (defined $args{to} and length $args{to}) {
+ $to_path = $args{to};
+ } elsif (defined $args{to_dir} and length $args{to_dir}) {
+ $to_path = File::Spec->catfile( $args{to_dir}, $args{flatten}
+ ? File::Basename::basename($file)
+ : $file );
+ } else {
+ die "No 'to' or 'to_dir' parameter given to copy_if_modified";
+ }
+
+ return if $self->up_to_date($file, $to_path); # Already fresh
+
+ {
+ local $self->{properties}{quiet} = 1;
+ $self->delete_filetree($to_path); # delete destination if exists
+ }
+
+ # 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};
+
+ 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'): $!";
+ } else {
+ File::Copy::copy($file, $to_path) or die "Can't copy('$file', '$to_path'): $!";
+ }
+
+ # mode is read-only + (executable if source is executable)
+ my $mode = oct(444) | ( $self->is_executable($file) ? oct(111) : 0 );
+ chmod( $mode, $to_path );
+
+ return $to_path;
+}
+
+sub up_to_date {
+ my ($self, $source, $derived) = @_;
+ $source = [$source] unless ref $source;
+ $derived = [$derived] unless ref $derived;
+
+ # empty $derived means $source should always run
+ return 0 if @$source && [email protected]$derived || grep {not -e} @$derived;
+
+ my $most_recent_source = time / (24*60*60);
+ foreach my $file (@$source) {
+ unless (-e $file) {
+ $self->log_warn("Can't find source file $file for up-to-date check");
+ next;
+ }
+ $most_recent_source = -M _ if -M _ < $most_recent_source;
+ }
+
+ foreach my $derived (@$derived) {
+ return 0 if -M $derived > $most_recent_source;
+ }
+ return 1;
+}
+
+sub dir_contains {
+ 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;
+}
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Base - Default methods for Module::Build
+
+=head1 SYNOPSIS
+
+ Please see the Module::Build documentation.
+
+=head1 DESCRIPTION
+
+The C<Module::Build::Base> module defines the core functionality of
+C<Module::Build>. Its methods may be overridden by any of the
+platform-dependent modules in the C<Module::Build::Platform::>
+namespace, but the intention here is to make this base module as
+platform-neutral as possible. Nicely enough, Perl has several core
+tools available in the C<File::> namespace for doing this, so the task
+isn't very difficult.
+
+Please see the C<Module::Build> documentation for more details.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=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), Module::Build(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Compat.pm b/inc/Module-Build/Module/Build/Compat.pm
new file mode 100644
index 0000000..6b606ae
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Compat.pm
@@ -0,0 +1,578 @@
+package Module::Build::Compat;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+
+use File::Basename ();
+use File::Spec;
+use IO::File;
+use Config;
+use Module::Build;
+use Module::Build::ModuleInfo;
+use Data::Dumper;
+
+my %convert_installdirs = (
+ PERL => 'core',
+ SITE => 'site',
+ VENDOR => 'vendor',
+);
+
+my %makefile_to_build =
+ (
+ TEST_VERBOSE => 'verbose',
+ VERBINST => 'verbose',
+ INC => sub { map {(extra_compiler_flags => $_)} Module::Build->split_like_shell(shift) },
+ POLLUTE => sub { (extra_compiler_flags => '-DPERL_POLLUTE') },
+ INSTALLDIRS => sub { (installdirs => $convert_installdirs{uc shift()}) },
+ LIB => sub {
+ my $lib = shift;
+ my %config = (
+ installprivlib => $lib,
+ installsitelib => $lib,
+ installarchlib => "$lib/$Config{archname}",
+ installsitearch => "$lib/$Config{archname}"
+ );
+ return map { (config => "$_=$config{$_}") } keys %config;
+ },
+
+ # Convert INSTALLVENDORLIB and friends.
+ (
+ map {
+ my $name = $_;
+ $name => sub {
+ my @ret = (config => lc($name) . "=" . shift );
+ print STDERR "# Converted to @ret\n";
+
+ return @ret;
+ }
+ } qw(
+ INSTALLARCHLIB INSTALLSITEARCH INSTALLVENDORARCH
+ INSTALLPRIVLIB INSTALLSITELIB INSTALLVENDORLIB
+ INSTALLBIN INSTALLSITEBIN INSTALLVENDORBIN
+ INSTALLSCRIPT INSTALLSITESCRIPT INSTALLVENDORSCRIPT
+ INSTALLMAN1DIR INSTALLSITEMAN1DIR INSTALLVENDORMAN1DIR
+ INSTALLMAN3DIR INSTALLSITEMAN3DIR INSTALLVENDORMAN3DIR
+ )
+ ),
+
+ # Some names they have in common
+ map {$_, lc($_)} qw(DESTDIR PREFIX INSTALL_BASE UNINST),
+ );
+
+my %macro_to_build = %makefile_to_build;
+# "LIB=foo make" is not the same as "perl Makefile.PL LIB=foo"
+delete $macro_to_build{LIB};
+
+
+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)$/;
+
+ my $fh;
+ if ($args{fh}) {
+ $fh = $args{fh};
+ } else {
+ $args{file} ||= 'Makefile.PL';
+ local $build->{properties}{quiet} = 1;
+ $build->delete_filetree($args{file});
+ $fh = IO::File->new("> $args{file}") or die "Can't write $args{file}: $!";
+ }
+
+ print {$fh} "# Note: this file was auto-generated by ", __PACKAGE__, " version $VERSION\n";
+
+ # Minimum perl version should be specified as "require 5.XXXXXX" in
+ # Makefile.PL
+ my $requires = $build->requires;
+ if ( my $minimum_perl = $requires->{perl} ) {
+ print {$fh} "require $minimum_perl;\n";
+ }
+
+ # If a *bundled* custom subclass is being used, make sure we add its
+ # directory to @INC. Also, lib.pm always needs paths in Unix format.
+ my $subclass_load = '';
+ if (ref($build) ne "Module::Build") {
+ my $subclass_dir = $package->subclass_dir($build);
+
+ if (File::Spec->file_name_is_absolute($subclass_dir)) {
+ my $base_dir = $build->base_dir;
+
+ if ($build->dir_contains($base_dir, $subclass_dir)) {
+ $subclass_dir = File::Spec->abs2rel($subclass_dir, $base_dir);
+ $subclass_dir = $package->unixify_dir($subclass_dir);
+ $subclass_load = "use lib '$subclass_dir';";
+ }
+ # Otherwise, leave it the empty string
+
+ } else {
+ $subclass_dir = $package->unixify_dir($subclass_dir);
+ $subclass_load = "use lib '$subclass_dir';";
+ }
+ }
+
+ if ($type eq 'small') {
+ printf {$fh} <<'EOF', $subclass_load, ref($build), ref($build);
+ use Module::Build::Compat 0.02;
+ %s
+ Module::Build::Compat->run_build_pl(args => \@ARGV);
+ require %s;
+ Module::Build::Compat->write_makefile(build_class => '%s');
+EOF
+
+ } 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 [email protected];
+ %s
+ Module::Build::Compat->run_build_pl(args => \@ARGV);
+ 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);
+ if (eval "use Tie::IxHash; 1") {
+ 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 = 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);
+ }
+
+ local $Data::Dumper::Terse = 1;
+ my $args = Data::Dumper::Dumper(\%MM_Args);
+ $args =~ s/\{(.*)\}/($1)/s;
+
+ print $fh <<"EOF";
+use ExtUtils::MakeMaker;
+WriteMakefile
+$args;
+EOF
+ }
+}
+
+sub _test_globs {
+ my ($self, $build) = @_;
+
+ return map { File::Spec->catfile($_, '*.t') }
+ @{$build->rscan_dir('t', sub { -d $File::Find::name })};
+}
+
+sub subclass_dir {
+ my ($self, $build) = @_;
+
+ return (Module::Build::ModuleInfo->find_module_dir_by_name(ref $build)
+ || File::Spec->catdir($build->config_dir, 'lib'));
+}
+
+sub unixify_dir {
+ my ($self, $path) = @_;
+ return join '/', File::Spec->splitdir($path);
+}
+
+sub makefile_to_build_args {
+ my $class = shift;
+ my @out;
+ foreach my $arg (@_) {
+ next if $arg eq '';
+
+ my ($key, $val) = ($arg =~ /^(\w+)=(.+)/ ? ($1, $2) :
+ die "Malformed argument '$arg'");
+
+ # Do tilde-expansion if it looks like a tilde prefixed path
+ ( $val ) = Module::Build->_detildefy( $val ) if $val =~ /^~/;
+
+ if (exists $makefile_to_build{$key}) {
+ my $trans = $makefile_to_build{$key};
+ push @out, $class->_argvify( ref($trans) ? $trans->($val) : ($trans => $val) );
+ } elsif (exists $Config{lc($key)}) {
+ push @out, $class->_argvify( config => lc($key) . "=$val" );
+ } else {
+ # Assume M::B can handle it in lowercase form
+ push @out, $class->_argvify("\L$key" => $val);
+ }
+ }
+ return @out;
+}
+
+sub _argvify {
+ my ($self, @pairs) = @_;
+ my @out;
+ while (@pairs) {
+ my ($k, $v) = splice @pairs, 0, 2;
+ push @out, ("--$k", $v);
+ }
+ return @out;
+}
+
+sub makefile_to_build_macros {
+ my @out;
+ my %config; # must accumulate and return as a hashref
+ while (my ($macro, $trans) = each %macro_to_build) {
+ # On some platforms (e.g. Cygwin with 'make'), the mere presence
+ # of "EXPORT: FOO" in the Makefile will make $ENV{FOO} defined.
+ # Therefore we check length() too.
+ next unless exists $ENV{$macro} && length $ENV{$macro};
+ my $val = $ENV{$macro};
+ my @args = ref($trans) ? $trans->($val) : ($trans => $val);
+ while (@args) {
+ my ($k, $v) = splice(@args, 0, 2);
+ if ( $k eq 'config' ) {
+ if ( $v =~ /^([^=]+)=(.*)$/ ) {
+ $config{$1} = $2;
+ }
+ else {
+ warn "Couldn't parse config '$v'\n";
+ }
+ }
+ else {
+ push @out, ($k => $v);
+ }
+ }
+ }
+ push @out, (config => \%config) if %config;
+ return @out;
+}
+
+sub run_build_pl {
+ my ($pack, %in) = @_;
+ $in{script} ||= 'Build.PL';
+ my @args = $in{args} ? $pack->makefile_to_build_args(@{$in{args}}) : ();
+ print "# running $in{script} @args\n";
+ Module::Build->run_perl_script($in{script}, [], \@args) or die "Couldn't run $in{script}: $!";
+}
+
+sub fake_makefile {
+ my ($self, %args) = @_;
+ unless (exists $args{build_class}) {
+ warn "Unknown 'build_class', defaulting to 'Module::Build'\n";
+ $args{build_class} = 'Module::Build';
+ }
+ my $class = $args{build_class};
+
+ my $perl = $class->find_perl_interpreter;
+
+ # VMS MMS/MMK need to use MCR to run the Perl image.
+ $perl = 'MCR ' . $perl if $self->_is_vms_mms;
+
+ my $noop = ($class->is_windowsish ? 'rem>nul' :
+ $self->_is_vms_mms ? 'Continue' :
+ 'true');
+
+ my $filetype = $class->is_vmsish ? '.COM' : '';
+
+ my $Build = 'Build' . $filetype . ' --makefile_env_macros 1';
+ my $unlink = $class->oneliner('1 while unlink $ARGV[0]', [], [$args{makefile}]);
+ $unlink =~ s/\$/\$\$/g unless $class->is_vmsish;
+
+ my $maketext = <<"EOF";
+all : force_do_it
+ $perl $Build
+realclean : force_do_it
+ $perl $Build realclean
+ $unlink
+distclean : force_do_it
+ $perl $Build distclean
+ $unlink
+
+
+force_do_it :
+ @ $noop
+EOF
+
+ foreach my $action ($class->known_actions) {
+ next if $action =~ /^(all|distclean|realclean|force_do_it)$/; # Don't double-define
+ $maketext .= <<"EOF";
+$action : force_do_it
+ $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";
+ for my $macro (keys %macro_to_build) {
+ $maketext .= ".IFDEF $macro\n\tDEFINE $macro \"\$($macro)\"\n.ENDIF\n";
+ }
+ $maketext .= "\n";
+ }
+ else {
+ $maketext .= "\n.EXPORT : " . join(' ', keys %macro_to_build) . "\n\n";
+ }
+
+ return $maketext;
+}
+
+sub fake_prereqs {
+ my $file = File::Spec->catfile('_build', 'prereqs');
+ my $fh = IO::File->new("< $file") or die "Can't read $file: $!";
+ my $prereqs = eval do {local $/; <$fh>};
+ close $fh;
+
+ my @prereq;
+ foreach my $section (qw/build_requires requires/) {
+ foreach (keys %{$prereqs->{$section}}) {
+ next if $_ eq 'perl';
+ push @prereq, "$_=>q[$prereqs->{$section}{$_}]";
+ }
+ }
+
+ return unless @prereq;
+ return "# PREREQ_PM => { " . join(", ", @prereq) . " }\n\n";
+}
+
+
+sub write_makefile {
+ my ($pack, %in) = @_;
+
+ unless (exists $in{build_class}) {
+ warn "Unknown 'build_class', defaulting to 'Module::Build'\n";
+ $in{build_class} = 'Module::Build';
+ }
+ my $class = $in{build_class};
+ $in{makefile} ||= $pack->_is_vms_mms ? 'Descrip.MMS' : 'Makefile';
+
+ open MAKE, "> $in{makefile}" or die "Cannot write $in{makefile}: $!";
+ print MAKE $pack->fake_prereqs;
+ print MAKE $pack->fake_makefile(%in);
+ close MAKE;
+}
+
+sub _is_vms_mms {
+ return Module::Build->is_vmsish && ($Config{make} =~ m/MM[SK]/i);
+}
+
+1;
+__END__
+
+=for :stopwords passthrough
+
+=head1 NAME
+
+Module::Build::Compat - Compatibility with ExtUtils::MakeMaker
+
+
+=head1 SYNOPSIS
+
+ # In a Build.PL :
+ use Module::Build;
+ my $build = Module::Build->new
+ ( module_name => 'Foo::Bar',
+ license => 'perl',
+ create_makefile_pl => 'passthrough' );
+ ...
+
+
+=head1 DESCRIPTION
+
+Because C<ExtUtils::MakeMaker> has been the standard way to distribute
+modules for a long time, many tools (CPAN.pm, or your system
+administrator) may expect to find a working F<Makefile.PL> in every
+distribution they download from CPAN. If you want to throw them a
+bone, you can use C<Module::Build::Compat> to automatically generate a
+F<Makefile.PL> for you, in one of several different styles.
+
+C<Module::Build::Compat> also provides some code that helps out the
+F<Makefile.PL> at runtime.
+
+
+=head1 METHODS
+
+=over 4
+
+=item create_makefile_pl($style, $build)
+
+Creates a F<Makefile.PL> in the current directory in one of several
+styles, based on the supplied C<Module::Build> object C<$build>. This is
+typically controlled by passing the desired style as the
+C<create_makefile_pl> parameter to C<Module::Build>'s C<new()> method;
+the F<Makefile.PL> will then be automatically created during the
+C<distdir> action.
+
+The currently supported styles are:
+
+=over 4
+
+=item small
+
+A small F<Makefile.PL> will be created that passes all functionality
+through to the F<Build.PL> script in the same directory. The user must
+already have C<Module::Build> installed in order to use this, or else
+they'll get a module-not-found error.
+
+=item passthrough
+
+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.
+
+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.
+
+=back
+
+=item run_build_pl(args => \@ARGV)
+
+This method runs the F<Build.PL> script, passing it any arguments the
+user may have supplied to the C<perl Makefile.PL> command. Because
+C<ExtUtils::MakeMaker> and C<Module::Build> accept different arguments, this
+method also performs some translation between the two.
+
+C<run_build_pl()> accepts the following named parameters:
+
+=over 4
+
+=item args
+
+The C<args> parameter specifies the parameters that would usually
+appear on the command line of the C<perl Makefile.PL> command -
+typically you'll just pass a reference to C<@ARGV>.
+
+=item script
+
+This is the filename of the script to run - it defaults to C<Build.PL>.
+
+=back
+
+=item write_makefile()
+
+This method writes a 'dummy' F<Makefile> that will pass all commands
+through to the corresponding C<Module::Build> actions.
+
+C<write_makefile()> accepts the following named parameters:
+
+=over 4
+
+=item makefile
+
+The name of the file to write - defaults to the string C<Makefile>.
+
+=back
+
+=back
+
+
+=head1 SCENARIOS
+
+So, some common scenarios are:
+
+=over 4
+
+=item 1.
+
+Just include a F<Build.PL> script (without a F<Makefile.PL>
+script), and give installation directions in a F<README> or F<INSTALL>
+document explaining how to install the module. In particular, explain
+that the user must install C<Module::Build> before installing your
+module.
+
+Note that if you do this, you may make things easier for yourself, but
+harder for people with older versions of CPAN or CPANPLUS on their
+system, because those tools generally only understand the
+F<Makefile.PL>/C<ExtUtils::MakeMaker> way of doing things.
+
+=item 2.
+
+Include a F<Build.PL> script and a "traditional" F<Makefile.PL>,
+created either manually or with C<create_makefile_pl()>. Users won't
+ever have to install C<Module::Build> if they use the F<Makefile.PL>, but
+they won't get to take advantage of C<Module::Build>'s extra features
+either.
+
+For good measure, of course, test both the F<Makefile.PL> and the
+F<Build.PL> before shipping.
+
+=item 3.
+
+Include a F<Build.PL> script and a "pass-through" F<Makefile.PL>
+built using C<Module::Build::Compat>. This will mean that people can
+continue to use the "old" installation commands, and they may never
+notice that it's actually doing something else behind the scenes. It
+will also mean that your installation process is compatible with older
+versions of tools like CPAN and CPANPLUS.
+
+=back
+
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+
+=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
+
+L<Module::Build>(3), L<ExtUtils::MakeMaker>(3)
+
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Config.pm b/inc/Module-Build/Module/Build/Config.pm
new file mode 100644
index 0000000..a18a97f
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Config.pm
@@ -0,0 +1,59 @@
+package Module::Build::Config;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Config;
+
+sub new {
+ my ($pack, %args) = @_;
+ return bless {
+ stack => {},
+ values => $args{values} || {},
+ }, $pack;
+}
+
+sub get {
+ my ($self, $key) = @_;
+ return $self->{values}{$key} if ref($self) && exists $self->{values}{$key};
+ return $Config{$key};
+}
+
+sub set {
+ my ($self, $key, $val) = @_;
+ $self->{values}{$key} = $val;
+}
+
+sub push {
+ my ($self, $key, $val) = @_;
+ push @{$self->{stack}{$key}}, $self->{values}{$key}
+ if exists $self->{values}{$key};
+ $self->{values}{$key} = $val;
+}
+
+sub pop {
+ my ($self, $key) = @_;
+
+ my $val = delete $self->{values}{$key};
+ if ( exists $self->{stack}{$key} ) {
+ $self->{values}{$key} = pop @{$self->{stack}{$key}};
+ delete $self->{stack}{$key} unless @{$self->{stack}{$key}};
+ }
+
+ return $val;
+}
+
+sub values_set {
+ my $self = shift;
+ return undef unless ref($self);
+ return $self->{values};
+}
+
+sub all_config {
+ my $self = shift;
+ my $v = ref($self) ? $self->{values} : {};
+ return {%Config, %$v};
+}
+
+1;
diff --git a/inc/Module-Build/Module/Build/ConfigData.pm b/inc/Module-Build/Module/Build/ConfigData.pm
new file mode 100644
index 0000000..76956bc
--- /dev/null
+++ b/inc/Module-Build/Module/Build/ConfigData.pm
@@ -0,0 +1,201 @@
+package Module::Build::ConfigData;
+use strict;
+my $arrayref = eval do {local $/; <DATA>}
+ or die "Couldn't load ConfigData data: [email protected]";
+close DATA;
+my ($config, $features, $auto_features) = @$arrayref;
+
+sub config { $config->{$_[1]} }
+
+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 feature_names {
+ my @features = (keys %$features, auto_feature_names());
+ @features;
+}
+
+sub config_names { keys %$config }
+
+sub write {
+ my $me = __FILE__;
+ require IO::File;
+
+ # Can't use Module::Build::Dumper here because M::B is only a
+ # build-time prereq of this module
+ require Data::Dumper;
+
+ my $mode_orig = (stat $me)[2] & 07777;
+ chmod($mode_orig | 0222, $me); # Make it writeable
+ my $fh = IO::File->new($me, 'r+') or die "Can't rewrite $me: $!";
+ seek($fh, 0, 0);
+ while (<$fh>) {
+ last if /^__DATA__$/;
+ }
+ die "Couldn't find __DATA__ token in $me" if eof($fh);
+
+ seek($fh, tell($fh), 0);
+ my $data = [$config, $features, $auto_features];
+ $fh->print( 'do{ my '
+ . Data::Dumper->new([$data],['x'])->Purity(1)->Dump()
+ . '$x; }' );
+ truncate($fh, tell($fh));
+ $fh->close;
+
+ chmod($mode_orig, $me)
+ or warn "Couldn't restore permissions on $me: $!";
+}
+
+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
+ # was reanimated with Data::Dumper and eval(). Not sure why, but
+ # copying to a new hash seems to solve it.
+ my %info = %$info;
+
+ require Module::Build; # XXX should get rid of this
+ 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 $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;
+}
+
+
+=head1 NAME
+
+Module::Build::ConfigData - Configuration for Module::Build
+
+
+=head1 SYNOPSIS
+
+ use Module::Build::ConfigData;
+ $value = Module::Build::ConfigData->config('foo');
+ $value = Module::Build::ConfigData->feature('bar');
+
+ @names = Module::Build::ConfigData->config_names;
+ @names = Module::Build::ConfigData->feature_names;
+
+ Module::Build::ConfigData->set_config(foo => $new_value);
+ Module::Build::ConfigData->set_feature(bar => $new_value);
+ Module::Build::ConfigData->write; # Save changes
+
+
+=head1 DESCRIPTION
+
+This module holds the configuration data for the C<Module::Build>
+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<Module::Build::ConfigData>
+module, and you should attempt to understand the repercussions of your
+actions.
+
+
+=head1 METHODS
+
+=over 4
+
+=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)
+
+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)
+
+Sets the configuration item with the given name to the given value.
+The value may be any Perl scalar that will serialize correctly using
+C<Data::Dumper>. This includes references, objects (usually), and
+complex data structures. It probably does not include transient
+things like filehandles or sockets.
+
+=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<Module::Build::ConfigData>, or in scalar context the number of items.
+
+=item feature_names()
+
+Returns a list of all the names of features currently defined in
+C<Module::Build::ConfigData>, or in scalar context the number of features.
+
+=item auto_feature_names()
+
+Returns a list of all the names of features whose availability is
+dynamically determined, or in scalar context the number of such
+features. Does not include such features that have later been set to
+a fixed value.
+
+=item write()
+
+Commits any changes from C<set_config()> and C<set_feature()> to disk.
+Requires write access to the C<Module::Build::ConfigData> module.
+
+=back
+
+
+=head1 AUTHOR
+
+C<Module::Build::ConfigData> 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<Module::Build::ConfigData>.
+
+=cut
+
+__DATA__
+
+do{ my $x = [
+ {},
+ {},
+ {
+ 'YAML_support' => {
+ 'requires' => {
+ 'YAML' => ' >= 0.35, != 0.49_01 '
+ },
+ 'description' => 'Use YAML.pm to write META.yml files'
+ },
+ 'manpage_support' => {
+ 'requires' => {
+ 'Pod::Man' => 0
+ },
+ 'description' => 'Create Unix man pages'
+ },
+ 'C_support' => {
+ 'requires' => {
+ 'ExtUtils::CBuilder' => '0.15'
+ },
+ 'recommends' => {
+ 'ExtUtils::ParseXS' => '1.02'
+ },
+ 'description' => 'Compile/link C & XS code'
+ },
+ 'HTML_support' => {
+ 'requires' => {
+ 'Pod::Html' => 0
+ },
+ 'description' => 'Create HTML documentation'
+ }
+ }
+ ];
+$x; } \ No newline at end of file
diff --git a/inc/Module-Build/Module/Build/Cookbook.pm b/inc/Module-Build/Module/Build/Cookbook.pm
new file mode 100644
index 0000000..0a50977
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Cookbook.pm
@@ -0,0 +1,529 @@
+package Module::Build::Cookbook;
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+
+
+=head1 NAME
+
+Module::Build::Cookbook - Examples of Module::Build Usage
+
+=head1 DESCRIPTION
+
+C<Module::Build> isn't conceptually very complicated, but examples are
+always helpful. The following recipes should help developers and/or
+installers put together the pieces from the other parts of the
+documentation.
+
+
+=head1 BASIC RECIPES
+
+
+=head2 Installing modules that use Module::Build
+
+In most cases, you can just issue the following commands:
+
+ perl Build.PL
+ ./Build
+ ./Build test
+ ./Build install
+
+There's nothing complicated here - first you're running a script
+called F<Build.PL>, then you're running a (newly-generated) script
+called F<Build> and passing it various arguments.
+
+The exact commands may vary a bit depending on how you invoke perl
+scripts on your system. For instance, if you have multiple versions
+of perl installed, you can install to one particular perl's library
+directories like so:
+
+ /usr/bin/perl5.8.1 Build.PL
+ ./Build
+ ./Build test
+ ./Build install
+
+If you're on Windows where the current directory is always searched
+first for scripts, you'll probably do something like this:
+
+ perl Build.PL
+ Build
+ Build test
+ Build install
+
+On the old Mac OS (version 9 or lower) using MacPerl, you can
+double-click on the F<Build.PL> script to create the F<Build> script,
+then double-click on the F<Build> script to run its C<build>, C<test>,
+and C<install> actions.
+
+The F<Build> script knows what perl was used to run F<Build.PL>, so
+you don't need to re-invoke the F<Build> script with the complete perl
+path each time. If you invoke it with the I<wrong> perl path, you'll
+get a warning or a fatal error.
+
+=head2 Modifying Config.pm values
+
+C<Module::Build> relies heavily on various values from perl's
+C<Config.pm> to do its work. For example, default installation paths
+are given by C<installsitelib> and C<installvendorman3dir> and
+friends, C linker & compiler settings are given by C<ld>,
+C<lddlflags>, C<cc>, C<ccflags>, and so on. I<If you're pretty sure
+you know what you're doing>, you can tell C<Module::Build> to pretend
+there are different values in F<Config.pm> than what's really there,
+by passing arguments for the C<--config> parameter on the command
+line:
+
+ perl Build.PL --config cc=gcc --config ld=gcc
+
+Inside the C<Build.PL> script the same thing can be accomplished by
+passing values for the C<config> parameter to C<new()>:
+
+ my $build = Module::Build->new
+ (
+ ...
+ config => { cc => 'gcc', ld => 'gcc' },
+ ...
+ );
+
+In custom build code, the same thing can be accomplished by calling
+the L<Module::Build/config> method:
+
+ $build->config( cc => 'gcc' ); # Set
+ $build->config( ld => 'gcc' ); # Set
+ ...
+ my $linker = $build->config('ld'); # Get
+
+
+=head2 Installing modules using the programmatic interface
+
+If you need to build, test, and/or install modules from within some
+other perl code (as opposed to having the user type installation
+commands at the shell), you can use the programmatic interface.
+Create a Module::Build object (or an object of a custom Module::Build
+subclass) and then invoke its C<dispatch()> method to run various
+actions.
+
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ license => 'perl',
+ requires => { 'Some::Module' => '1.23' },
+ );
+ $build->dispatch('build');
+ $build->dispatch('test', verbose => 1);
+ $build->dispatch('install');
+
+The first argument to C<dispatch()> is the name of the action, and any
+following arguments are named parameters.
+
+This is the interface we use to test Module::Build itself in the
+regression tests.
+
+
+=head2 Installing to a temporary directory
+
+To create packages for package managers like RedHat's C<rpm> or
+Debian's C<deb>, you may need to install to a temporary directory
+first and then create the package from that temporary installation.
+To do this, specify the C<destdir> parameter to the C<install> action:
+
+ ./Build install --destdir /tmp/my-package-1.003
+
+This essentially just prepends all the installation paths with the
+F</tmp/my-package-1.003> directory.
+
+
+=head2 Installing to a non-standard directory
+
+To install to a non-standard directory (for example, if you don't have
+permission to install in the system-wide directories), you can use the
+C<install_base> or C<prefix> parameters:
+
+ ./Build install --install_base /foo/bar
+
+See L<Module::Build/"INSTALL PATHS"> for a much more complete
+discussion of how installation paths are determined.
+
+
+=head2 Installing in the same location as ExtUtils::MakeMaker
+
+With the introduction of C<--prefix> in Module::Build 0.28 and
+C<INSTALL_BASE> in C<ExtUtils::MakeMaker> 6.31 its easy to get them both
+to install to the same locations.
+
+First, ensure you have at least version 0.28 of Module::Build
+installed and 6.31 of C<ExtUtils::MakeMaker>. Prior versions have
+differing (and in some cases quite strange) installation behaviors.
+
+The following installation flags are equivalent between
+C<ExtUtils::MakeMaker> and C<Module::Build>.
+
+ MakeMaker Module::Build
+ PREFIX=... --prefix ...
+ INSTALL_BASE=... --install_base ...
+ DESTDIR=... --destdir ...
+ LIB=... --install_path lib=...
+ INSTALLDIRS=... --installdirs ...
+ INSTALLDIRS=perl --installdirs core
+ UNINST=... --uninst ...
+ INC=... --extra_compiler_flags ...
+ POLLUTE=1 --extra_compiler_flags -DPERL_POLLUTE
+
+For example, if you are currently installing C<MakeMaker> modules with
+this command:
+
+ perl Makefile.PL PREFIX=~
+ make test
+ make install UNINST=1
+
+You can install into the same location with Module::Build using this:
+
+ perl Build.PL --prefix ~
+ ./Build test
+ ./Build install --uninst 1
+
+=head3 C<prefix> vs C<install_base>
+
+The behavior of C<prefix> is complicated and depends on
+how your Perl is configured. The resulting installation locations
+will vary from machine to machine and even different installations of
+Perl on the same machine. Because of this, it's difficult to document
+where C<prefix> will place your modules.
+
+In contrast, C<install_base> has predictable, easy to explain
+installation locations. Now that C<Module::Build> and C<MakeMaker> both
+have C<install_base> there is little reason to use C<prefix> other
+than to preserve your existing installation locations. If you are
+starting a fresh Perl installation we encourage you to use
+C<install_base>. If you have an existing installation installed via
+C<prefix>, consider moving it to an installation structure matching
+C<install_base> and using that instead.
+
+
+=head2 Running a single test file
+
+C<Module::Build> supports running a single test, which enables you to
+track down errors more quickly. Use the following format:
+
+ ./Build test --test_files t/mytest.t
+
+In addition, you may want to run the test in verbose mode to get more
+informative output:
+
+ ./Build test --test_files t/mytest.t --verbose 1
+
+I run this so frequently that I define the following shell alias:
+
+ alias t './Build test --verbose 1 --test_files'
+
+So then I can just execute C<t t/mytest.t> to run a single test.
+
+
+=head1 ADVANCED RECIPES
+
+
+=head2 Making a CPAN.pm-compatible distribution
+
+New versions of CPAN.pm understand how to use a F<Build.PL> script,
+but old versions don't. If authors want to help users who have old
+versions, some form of F<Makefile.PL> should be supplied. The easiest
+way to accomplish this is to use the C<create_makefile_pl> parameter to
+C<< Module::Build->new() >> in the C<Build.PL> script, which can
+create various flavors of F<Makefile.PL> during the C<dist> action.
+
+As a best practice, we recommend using the "traditional" style of
+F<Makefile.PL> unless your distribution has needs that can't be
+accomplished that way.
+
+The C<Module::Build::Compat> module, which is part of
+C<Module::Build>'s distribution, is responsible for creating these
+F<Makefile.PL>s. Please see L<Module::Build::Compat> for the details.
+
+
+=head2 Changing the order of the build process
+
+The C<build_elements> property specifies the steps C<Module::Build>
+will take when building a distribution. To change the build order,
+change the order of the entries in that property:
+
+ # Process pod files first
+ my @e = @{$build->build_elements};
+ my ($i) = grep {$e[$_] eq 'pod'} 0..$#e;
+ unshift @e, splice @e, $i, 1;
+
+Currently, C<build_elements> has the following default value:
+
+ [qw( PL support pm xs pod script )]
+
+Do take care when altering this property, since there may be
+non-obvious (and non-documented!) ordering dependencies in the
+C<Module::Build> code.
+
+
+=head2 Adding new file types to the build process
+
+Sometimes you might have extra types of files that you want to install
+alongside the standard types like F<.pm> and F<.pod> files. For
+instance, you might have a F<Bar.dat> file containing some data
+related to the C<Foo::Bar> module and you'd like for it to end up as
+F<Foo/Bar.dat> somewhere in perl's C<@INC> path so C<Foo::Bar> can
+access it easily at runtime. The following code from a sample
+C<Build.PL> file demonstrates how to accomplish this:
+
+ use Module::Build;
+ my $build = Module::Build->new
+ (
+ module_name => 'Foo::Bar',
+ ...other stuff here...
+ );
+ $build->add_build_element('dat');
+ $build->create_build_script;
+
+This will find all F<.dat> files in the F<lib/> directory, copy them
+to the F<blib/lib/> directory during the C<build> action, and install
+them during the C<install> action.
+
+If your extra files aren't located in the C<lib/> directory in your
+distribution, you can explicitly say where they are, just as you'd do
+with F<.pm> or F<.pod> files:
+
+ use Module::Build;
+ my $build = new Module::Build
+ (
+ module_name => 'Foo::Bar',
+ dat_files => {'some/dir/Bar.dat' => 'lib/Foo/Bar.dat'},
+ ...other stuff here...
+ );
+ $build->add_build_element('dat');
+ $build->create_build_script;
+
+If your extra files actually need to be created on the user's machine,
+or if they need some other kind of special processing, you'll probably
+want to subclass C<Module::Build> and create a special method to
+process them, named C<process_${kind}_files()>:
+
+ use Module::Build;
+ my $class = Module::Build->subclass(code => <<'EOF');
+ sub process_dat_files {
+ my $self = shift;
+ ... locate and process *.dat files,
+ ... and create something in blib/lib/
+ }
+ EOF
+ my $build = $class->new
+ (
+ module_name => 'Foo::Bar',
+ ...other stuff here...
+ );
+ $build->add_build_element('dat');
+ $build->create_build_script;
+
+If your extra files don't go in F<lib/> but in some other place, see
+L<"Adding new elements to the install process"> for how to actually
+get them installed.
+
+Please note that these examples use some capabilities of Module::Build
+that first appeared in version 0.26. Before that it could
+still be done, but the simple cases took a bit more work.
+
+
+=head2 Adding new elements to the install process
+
+By default, Module::Build creates seven subdirectories of the F<blib>
+directory during the build process: F<lib>, F<arch>, F<bin>,
+F<script>, F<bindoc>, F<libdoc>, and F<html> (some of these may be
+missing or empty if there's nothing to go in them). Anything copied
+to these directories during the build will eventually be installed
+during the C<install> action (see L<Module::Build/"INSTALL PATHS">.
+
+If you need to create a new custom type of installable element, e.g. C<conf>,
+then you need to tell Module::Build where things in F<blib/conf/>
+should be installed. To do this, use the C<install_path> parameter to
+the C<new()> method:
+
+ my $build = Module::Build->new
+ (
+ ...other stuff here...
+ install_path => { conf => $installation_path }
+ );
+
+Or you can call the C<install_path()> method later:
+
+ $build->install_path(conf => $installation_path);
+
+The user may also specify the path on the command line:
+
+ perl Build.PL --install_path conf=/foo/path/etc
+
+The important part, though, is that I<somehow> the install path needs
+to be set, or else nothing in the F<blib/conf/> directory will get
+installed, and a runtime error during the C<install> action will
+result.
+
+See also L<"Adding new file types to the build process"> for how to
+create the stuff in F<blib/conf/> in the first place.
+
+
+=head1 EXAMPLES ON CPAN
+
+Several distributions on CPAN are making good use of various features
+of Module::Build. They can serve as real-world examples for others.
+
+
+=head2 SVN-Notify-Mirror
+
+L<http://search.cpan.org/~jpeacock/SVN-Notify-Mirror/>
+
+John Peacock, author of the C<SVN-Notify-Mirror> distribution, says:
+
+=over 4
+
+=item 1. Using C<auto_features>, I check to see whether two optional
+modules are available - SVN::Notify::Config and Net::SSH;
+
+=item 2. If the S::N::Config module is loaded, I automatically
+generate test files for it during Build (using the C<PL_files>
+property).
+
+=item 3. If the C<ssh_feature> is available, I ask if the user wishes
+to perform the ssh tests (since it requires a little preliminary
+setup);
+
+=item 4. Only if the user has C<ssh_feature> and answers yes to the
+testing, do I generate a test file.
+
+I'm sure I could not have handled this complexity with EU::MM, but it
+was very easy to do with M::B.
+
+=back
+
+
+=head2 Modifying an action
+
+Sometimes you might need an to have an action, say C<./Build install>,
+do something unusual. For instance, you might need to change the
+ownership of a file or do something else peculiar to your application.
+
+You can subclass C<Module::Build> on the fly using the C<subclass()>
+method and override the methods that perform the actions. You may
+need to read through C<Module::Build::Authoring> and
+C<Module::Build::API> to find the methods you want to override. All
+"action" methods are implemented by a method called "ACTION_" followed
+by the action's name, so here's an example of how it would work for
+the C<install> action:
+
+ # Build.PL
+ use Module::Build;
+ my $class = Module::Build->subclass(
+ class => "Module::Build::Custom",
+ code => <<'SUBCLASS' );
+
+ sub ACTION_install {
+ my $self = shift;
+ # YOUR CODE HERE
+ $self->SUPER::ACTION_install;
+ }
+ SUBCLASS
+
+ $class->new(
+ module_name => 'Your::Module',
+ # rest of the usual Module::Build parameters
+ )->create_build_script;
+
+
+=head2 Adding an action
+
+You can add a new C<./Build> action simply by writing the method for
+it in your subclass. Use C<depends_on> to declare that another action
+must have been run before your action.
+
+For example, let's say you wanted to be able to write C<./Build
+commit> to test your code and commit it to Subversion.
+
+ # Build.PL
+ use Module::Build;
+ my $class = Module::Build->subclass(
+ class => "Module::Build::Custom",
+ code => <<'SUBCLASS' );
+
+ sub ACTION_commit {
+ my $self = shift;
+
+ $self->depends_on("test");
+ $self->do_system(qw(svn commit));
+ }
+ SUBCLASS
+
+
+=head2 Bundling Module::Build
+
+Note: This section probably needs an update as the technology improves
+(see contrib/bundle.pl in the distribution).
+
+Suppose you want to use some new-ish features of Module::Build,
+e.g. newer than the version of Module::Build your users are likely to
+already have installed on their systems. The first thing you should
+do is set C<configure_requires> to your minimum version of
+Module::Build. See L<Module::Build::Authoring>.
+
+But not every build system honors C<configure_requires> yet. Here's
+how you can ship a copy of Module::Build, but still use a newer
+installed version to take advantage of any bug fixes and upgrades.
+
+First, install Module::Build into F<Your-Project/inc/Module-Build>.
+CPAN will not index anything in the F<inc> directory so this copy will
+not show up in CPAN searches.
+
+ cd Module-Build
+ perl Build.PL --install_base /path/to/Your-Project/inc/Module-Build
+ ./Build test
+ ./Build install
+
+You should now have all the Module::Build .pm files in
+F<Your-Project/inc/Module-Build/lib/perl5>.
+
+Next, add this to the top of your F<Build.PL>.
+
+ my $Bundled_MB = 0.30; # or whatever version it was.
+
+ # Find out what version of Module::Build is installed or fail quietly.
+ # This should be cross-platform.
+ 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.
+ chomp $Installed_MB;
+
+ $Installed_MB = 0 if $?;
+
+ # Use our bundled copy of Module::Build if it's newer than the installed.
+ unshift @INC, "inc/Module-Build/lib/perl5" if $Bundled_MB > $Installed_MB;
+
+ require Module::Build;
+
+And write the rest of your F<Build.PL> normally. Module::Build will
+remember your change to C<@INC> and use it when you run F<./Build>.
+
+In the future, we hope to provide a more automated solution for this
+scenario; see C<inc/latest.pm> in the Module::Build distribution for
+one indication of the direction we're moving.
+
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001-2008 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), L<Module::Build::Authoring>(3),
+L<Module::Build::API>(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Dumper.pm b/inc/Module-Build/Module/Build/Dumper.pm
new file mode 100644
index 0000000..74426b4
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Dumper.pm
@@ -0,0 +1,19 @@
+package Module::Build::Dumper;
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+
+# This is just a split-out of a wrapper function to do Data::Dumper
+# stuff "the right way". See:
+# http://groups.google.com/group/perl.module.build/browse_thread/thread/c8065052b2e0d741
+
+use Data::Dumper;
+
+sub _data_dump {
+ my ($self, $data) = @_;
+ return ("do{ my "
+ . Data::Dumper->new([$data],['x'])->Purity(1)->Terse(0)->Dump()
+ . '$x; }')
+}
+
+1;
diff --git a/inc/Module-Build/Module/Build/ModuleInfo.pm b/inc/Module-Build/Module/Build/ModuleInfo.pm
new file mode 100644
index 0000000..191b9e9
--- /dev/null
+++ b/inc/Module-Build/Module/Build/ModuleInfo.pm
@@ -0,0 +1,471 @@
+# -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
+# vim:ts=8:sw=2:et:sta:sts=2
+package Module::Build::ModuleInfo;
+
+# This module provides routines to gather information about
+# perl modules (assuming this may be expanded in the distant
+# parrot future to look at other types of modules).
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+
+use File::Spec;
+use IO::File;
+use Module::Build::Version;
+
+
+my $PKG_REGEXP = qr{ # match a package declaration
+ ^[\s\{;]* # intro chars on a line
+ package # the word 'package'
+ \s+ # whitespace
+ ([\w:]+) # a package name
+ \s* # optional whitespace
+ ; # semicolon line terminator
+}x;
+
+my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
+ ([\$*]) # sigil - $ or *
+ (
+ ( # optional leading package name
+ (?:::|\')? # possibly starting like just :: ( la $::VERSION)
+ (?:\w+(?:::|\'))* # Foo::Bar:: ...
+ )?
+ VERSION
+ )\b
+}x;
+
+my $VERS_REGEXP = qr{ # match a VERSION definition
+ (?:
+ \(\s*$VARNAME_REGEXP\s*\) # with parens
+ |
+ $VARNAME_REGEXP # without parens
+ )
+ \s*
+ =[^=~] # = but not ==, nor =~
+}x;
+
+
+sub new_from_file {
+ my $class = shift;
+ my $filename = File::Spec->rel2abs( shift );
+
+ return undef unless defined( $filename ) && -f $filename;
+ return $class->_init(undef, $filename, @_);
+}
+
+sub new_from_module {
+ my $class = shift;
+ my $module = shift;
+ my %props = @_;
+
+ $props{inc} ||= \@INC;
+ my $filename = $class->find_module_by_name( $module, $props{inc} );
+ return undef unless defined( $filename ) && -f $filename;
+ return $class->_init($module, $filename, %props);
+}
+
+sub _init {
+ my $class = shift;
+ my $module = shift;
+ my $filename = shift;
+ my %props = @_;
+
+ my( %valid_props, @valid_props );
+ @valid_props = qw( collect_pod inc );
+ @valid_props{@valid_props} = delete( @props{@valid_props} );
+ warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
+
+ my %data = (
+ module => $module,
+ filename => $filename,
+ version => undef,
+ packages => [],
+ versions => {},
+ pod => {},
+ pod_headings => [],
+ collect_pod => 0,
+
+ %valid_props,
+ );
+
+ my $self = bless(\%data, $class);
+
+ $self->_parse_file();
+
+ unless($self->{module} and length($self->{module})) {
+ my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
+ if($f =~ /\.pm$/) {
+ $f =~ s/\..+$//;
+ my @candidates = grep /$f$/, @{$self->{packages}};
+ $self->{module} = shift(@candidates); # punt
+ }
+ else {
+ if(grep /main/, @{$self->{packages}}) {
+ $self->{module} = 'main';
+ }
+ else {
+ $self->{module} = $self->{packages}[0] || '';
+ }
+ }
+ }
+
+ $self->{version} = $self->{versions}{$self->{module}}
+ if defined( $self->{module} );
+
+ return $self;
+}
+
+# class method
+sub _do_find_module {
+ my $class = shift;
+ my $module = shift || die 'find_module_by_name() requires a package name';
+ my $dirs = shift || \@INC;
+
+ my $file = File::Spec->catfile(split( /::/, $module));
+ foreach my $dir ( @$dirs ) {
+ my $testfile = File::Spec->catfile($dir, $file);
+ return [ File::Spec->rel2abs( $testfile ), $dir ]
+ if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp
+ return [ File::Spec->rel2abs( "$testfile.pm" ), $dir ]
+ if -e "$testfile.pm";
+ }
+ return;
+}
+
+# class method
+sub find_module_by_name {
+ my $found = shift()->_do_find_module(@_) or return;
+ return $found->[0];
+}
+
+# class method
+sub find_module_dir_by_name {
+ my $found = shift()->_do_find_module(@_) or return;
+ return $found->[1];
+}
+
+
+# given a line of perl code, attempt to parse it if it looks like a
+# $VERSION assignment, returning sigil, full name, & package name
+sub _parse_version_expression {
+ my $self = shift;
+ my $line = shift;
+
+ my( $sig, $var, $pkg );
+ if ( $line =~ $VERS_REGEXP ) {
+ ( $sig, $var, $pkg ) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
+ if ( $pkg ) {
+ $pkg = ($pkg eq '::') ? 'main' : $pkg;
+ $pkg =~ s/::$//;
+ }
+ }
+
+ return ( $sig, $var, $pkg );
+}
+
+sub _parse_file {
+ my $self = shift;
+
+ my $filename = $self->{filename};
+ my $fh = IO::File->new( $filename )
+ or die( "Can't open '$filename': $!" );
+
+ $self->_parse_fh($fh);
+}
+
+sub _parse_fh {
+ my ($self, $fh) = @_;
+
+ my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
+ my( @pkgs, %vers, %pod, @pod );
+ my $pkg = 'main';
+ my $pod_sect = '';
+ my $pod_data = '';
+
+ while (defined( my $line = <$fh> )) {
+ my $line_num = $.;
+
+ chomp( $line );
+ next if $line =~ /^\s*#/;
+
+ $in_pod = ($line =~ /^=(?!cut)/) ? 1 : ($line =~ /^=cut/) ? 0 : $in_pod;
+
+ # Would be nice if we could also check $in_string or something too
+ last if !$in_pod && $line =~ /^__(?:DATA|END)__$/;
+
+ if ( $in_pod || $line =~ /^=cut/ ) {
+
+ if ( $line =~ /^=head\d\s+(.+)\s*$/ ) {
+ push( @pod, $1 );
+ if ( $self->{collect_pod} && length( $pod_data ) ) {
+ $pod{$pod_sect} = $pod_data;
+ $pod_data = '';
+ }
+ $pod_sect = $1;
+
+
+ } elsif ( $self->{collect_pod} ) {
+ $pod_data .= "$line\n";
+
+ }
+
+ } else {
+
+ $pod_sect = '';
+ $pod_data = '';
+
+ # parse $line to see if it's a $VERSION declaration
+ my( $vers_sig, $vers_fullname, $vers_pkg ) =
+ $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;
+
+ # VERSION defined with full package spec, i.e. $Module::VERSION
+ } elsif ( $vers_fullname && $vers_pkg ) {
+ push( @pkgs, $vers_pkg ) unless grep( $vers_pkg eq $_, @pkgs );
+ $need_vers = 0 if $vers_pkg eq $pkg;
+
+ unless ( defined $vers{$vers_pkg} && length $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
+ # $VERSION" idiom (though there are probably other idioms
+ # that we should watch out for...)
+ warn <<"EOM" unless $line =~ /=\s*eval/;
+Package '$vers_pkg' already declared with version '$vers{$vers_pkg}',
+ignoring subsequent declaration on line $line_num.
+EOM
+ }
+
+ # first non-comment line in undeclared package main is VERSION
+ } elsif ( !exists($vers{main}) && $pkg eq 'main' && $vers_fullname ) {
+ $need_vers = 0;
+ my $v =
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
+ $vers{$pkg} = $v;
+ push( @pkgs, 'main' );
+
+ # first non-comment line in undeclared package defines package main
+ } elsif ( !exists($vers{main}) && $pkg eq 'main' && $line =~ /\w+/ ) {
+ $need_vers = 1;
+ $vers{main} = '';
+ push( @pkgs, 'main' );
+
+ # only keep if this is the first $VERSION seen
+ } elsif ( $vers_fullname && $need_vers ) {
+ $need_vers = 0;
+ my $v =
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
+
+
+ unless ( defined $vers{$pkg} && length $vers{$pkg} ) {
+ $vers{$pkg} = $v;
+ } else {
+ warn <<"EOM";
+Package '$pkg' already declared with version '$vers{$pkg}'
+ignoring new version '$v' on line $line_num.
+EOM
+ }
+
+ }
+
+ }
+
+ }
+
+ if ( $self->{collect_pod} && length($pod_data) ) {
+ $pod{$pod_sect} = $pod_data;
+ }
+
+ $self->{versions} = \%vers;
+ $self->{packages} = \@pkgs;
+ $self->{pod} = \%pod;
+ $self->{pod_headings} = \@pod;
+}
+
+{
+my $pn = 0;
+sub _evaluate_version_line {
+ my $self = shift;
+ my( $sigil, $var, $line ) = @_;
+
+ # Some of this code came from the ExtUtils:: hierarchy.
+
+ # We compile into $vsub because 'use version' would cause
+ # compiletime/runtime issues with local()
+ my $vsub;
+ $pn++; # everybody gets their own package
+ my $eval = qq{BEGIN { q# Hide from _packages_inside()
+ #; package Module::Build::ModuleInfo::_version::p$pn;
+ use Module::Build::Version;
+ no strict;
+
+ local $sigil$var;
+ \$$var=undef;
+ \$vsub = sub {
+ $line;
+ \$$var
+ };
+ }};
+
+ local $^W;
+ # Try to get the $VERSION
+ eval $eval;
+ warn "Error evaling version line '$eval' in $self->{filename}: [email protected]\n"
+ (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: [email protected]\n" if [email protected];
+
+ # Bless it into our own version class
+ $result = Module::Build::Version->new($result);
+
+ return $result;
+}
+}
+
+
+############################################################
+
+# accessors
+sub name { $_[0]->{module} }
+
+sub filename { $_[0]->{filename} }
+sub packages_inside { @{$_[0]->{packages}} }
+sub pod_inside { @{$_[0]->{pod_headings}} }
+sub contains_pod { $#{$_[0]->{pod_headings}} }
+
+sub version {
+ my $self = shift;
+ my $mod = shift || $self->{module};
+ my $vers;
+ if ( defined( $mod ) && length( $mod ) &&
+ exists( $self->{versions}{$mod} ) ) {
+ return $self->{versions}{$mod};
+ } else {
+ return undef;
+ }
+}
+
+sub pod {
+ my $self = shift;
+ my $sect = shift;
+ if ( defined( $sect ) && length( $sect ) &&
+ exists( $self->{pod}{$sect} ) ) {
+ return $self->{pod}{$sect};
+ } else {
+ return undef;
+ }
+}
+
+1;
+
+__END__
+
+=for :stopwords ModuleInfo
+
+=head1 NAME
+
+ModuleInfo - Gather package and POD information from a perl module file
+
+
+=head1 DESCRIPTION
+
+=over 4
+
+=item new_from_file($filename, collect_pod => 1)
+
+Construct a C<ModuleInfo> object given the path to a file. Takes an optional
+argument C<collect_pod> which is a boolean that determines whether
+POD data is collected and stored for reference. POD data is not
+collected by default. POD headings are always collected.
+
+=item new_from_module($module, collect_pod => 1, inc => \@dirs)
+
+Construct a C<ModuleInfo> object given a module or package name. In addition
+to accepting the C<collect_pod> argument as described above, this
+method accepts a C<inc> argument which is a reference to an array of
+of directories to search for the module. If none are given, the
+default is @INC.
+
+=item name()
+
+Returns the name of the package represented by this module. If there
+are more than one packages, it makes a best guess based on the
+filename. If it's a script (i.e. not a *.pm) the package name is
+'main'.
+
+=item version($package)
+
+Returns the version as defined by the $VERSION variable for the
+package as returned by the C<name> method if no arguments are
+given. If given the name of a package it will attempt to return the
+version of that package if it is specified in the file.
+
+=item filename()
+
+Returns the absolute path to the file.
+
+=item packages_inside()
+
+Returns a list of packages.
+
+=item pod_inside()
+
+Returns a list of POD sections.
+
+=item contains_pod()
+
+Returns true if there is any POD in the file.
+
+=item pod($section)
+
+Returns the POD data in the given section.
+
+=item find_module_by_name($module, \@dirs)
+
+Returns the path to a module given the module or package name. A list
+of directories can be passed in as an optional parameter, otherwise
[email protected] is searched.
+
+Can be called as either an object or a class method.
+
+=item find_module_dir_by_name($module, \@dirs)
+
+Returns the entry in C<@dirs> (or C<@INC> by default) that contains
+the module C<$module>. A list of directories can be passed in as an
+optional parameter, otherwise @INC is searched.
+
+Can be called as either an object or a class method.
+
+=back
+
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>, Randy W. Sims <[email protected]>
+
+
+=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
+
diff --git a/inc/Module-Build/Module/Build/Notes.pm b/inc/Module-Build/Module/Build/Notes.pm
new file mode 100644
index 0000000..c51610e
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Notes.pm
@@ -0,0 +1,296 @@
+package Module::Build::Notes;
+
+# A class for persistent hashes
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Data::Dumper;
+use IO::File;
+use Module::Build::Dumper;
+
+sub new {
+ my ($class, %args) = @_;
+ my $file = delete $args{file} or die "Missing required parameter 'file' to new()";
+ my $self = bless {
+ disk => {},
+ new => {},
+ file => $file,
+ %args,
+ }, $class;
+}
+
+sub restore {
+ my $self = shift;
+
+ my $fh = IO::File->new("< $self->{file}") or die "Can't read $self->{file}: $!";
+ $self->{disk} = eval do {local $/; <$fh>};
+ $self->{new} = {};
+}
+
+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);
+}
+
+sub has_data {
+ my $self = shift;
+ return keys %{$self->read()} > 0;
+}
+
+sub exists {
+ my ($self, $key) = @_;
+ return exists($self->{new}{$key}) || exists($self->{disk}{$key});
+}
+
+sub read {
+ my $self = shift;
+
+ if (@_) {
+ # Return 1 key as a scalar
+ my $key = shift;
+ 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}}}
+ : $self->{disk});
+ return wantarray ? %$out : $out;
+}
+
+sub _same {
+ my ($self, $x, $y) = @_;
+ return 1 if !defined($x) and !defined($y);
+ return 0 if !defined($x) or !defined($y);
+ return $x eq $y;
+}
+
+sub write {
+ my ($self, $href) = @_;
+ $href ||= {};
+
+ @{$self->{new}}{ keys %$href } = values %$href; # Merge
+
+ # Do some optimization to avoid unnecessary writes
+ foreach my $key (keys %{ $self->{new} }) {
+ next if ref $self->{new}{$key};
+ 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->_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);
+}
+
+sub write_config_data {
+ my ($self, %args) = @_;
+
+ my $fh = IO::File->new("> $args{file}") or die "Can't create '$args{file}': $!";
+
+ printf $fh <<'EOF', $args{config_module};
+package %s;
+use strict;
+my $arrayref = eval do {local $/; <DATA>}
+ or die "Couldn't load ConfigData data: [email protected]";
+close DATA;
+my ($config, $features, $auto_features) = @$arrayref;
+
+sub config { $config->{$_[1]} }
+
+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 feature_names {
+ my @features = (keys %%$features, auto_feature_names());
+ @features;
+}
+
+sub config_names { keys %%$config }
+
+sub write {
+ my $me = __FILE__;
+ require IO::File;
+
+ # Can't use Module::Build::Dumper here because M::B is only a
+ # build-time prereq of this module
+ require Data::Dumper;
+
+ my $mode_orig = (stat $me)[2] & 07777;
+ chmod($mode_orig | 0222, $me); # Make it writeable
+ my $fh = IO::File->new($me, 'r+') or die "Can't rewrite $me: $!";
+ seek($fh, 0, 0);
+ while (<$fh>) {
+ last if /^__DATA__$/;
+ }
+ die "Couldn't find __DATA__ token in $me" if eof($fh);
+
+ seek($fh, tell($fh), 0);
+ my $data = [$config, $features, $auto_features];
+ $fh->print( 'do{ my '
+ . Data::Dumper->new([$data],['x'])->Purity(1)->Dump()
+ . '$x; }' );
+ truncate($fh, tell($fh));
+ $fh->close;
+
+ chmod($mode_orig, $me)
+ or warn "Couldn't restore permissions on $me: $!";
+}
+
+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
+ # was reanimated with Data::Dumper and eval(). Not sure why, but
+ # copying to a new hash seems to solve it.
+ my %%info = %%$info;
+
+ require Module::Build; # XXX should get rid of this
+ 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 $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;
+
+=head1 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
+
+
+=head1 DESCRIPTION
+
+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>
+module, and you should attempt to understand the repercussions of your
+actions.
+
+
+=head1 METHODS
+
+=over 4
+
+=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)
+
+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)
+
+Sets the configuration item with the given name to the given value.
+The value may be any Perl scalar that will serialize correctly using
+C<Data::Dumper>. This includes references, objects (usually), and
+complex data structures. It probably does not include transient
+things like filehandles or sockets.
+
+=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.
+
+=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.
+
+=item auto_feature_names()
+
+Returns a list of all the names of features whose availability is
+dynamically determined, or in scalar context the number of such
+features. Does not include such features that have later been set to
+a fixed value.
+
+=item write()
+
+Commits any changes from C<set_config()> and C<set_feature()> to disk.
+Requires write access to the C<$notes_name> module.
+
+=back
+
+
+=head1 AUTHOR
+
+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
+
+__DATA__
+
+EOF
+
+ print {$fh} Module::Build::Dumper->_data_dump([$args{config_data}, $args{feature}, $args{auto_features}]);
+}
+
+1;
diff --git a/inc/Module-Build/Module/Build/PPMMaker.pm b/inc/Module-Build/Module/Build/PPMMaker.pm
new file mode 100644
index 0000000..709ba1f
--- /dev/null
+++ b/inc/Module-Build/Module/Build/PPMMaker.pm
@@ -0,0 +1,196 @@
+package Module::Build::PPMMaker;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+
+# This code is mostly borrowed from ExtUtils::MM_Unix 6.10_03, with a
+# few tweaks based on the PPD spec at
+# http://www.xav.com/perl/site/lib/XML/PPD.html
+
+# The PPD spec is based on <http://www.w3.org/TR/NOTE-OSD>
+
+sub new {
+ my $package = shift;
+ return bless {@_}, $package;
+}
+
+sub make_ppd {
+ my ($self, %args) = @_;
+ my $build = delete $args{build};
+
+ my @codebase;
+ if (exists $args{codebase}) {
+ @codebase = ref $args{codebase} ? @{$args{codebase}} : ($args{codebase});
+ } else {
+ my $distfile = $build->ppm_name . '.tar.gz';
+ print "Using default codebase '$distfile'\n";
+ @codebase = ($distfile);
+ }
+
+ my %dist;
+ foreach my $info (qw(name author abstract version)) {
+ 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}};
+
+ # TODO: could add <LICENSE HREF=...> tag if we knew what the URLs were for
+ # various licenses
+ my $ppd = <<"PPD";
+<SOFTPKG NAME=\"$dist{name}\" VERSION=\"$dist{version}\">
+ <TITLE>$dist{name}</TITLE>
+ <ABSTRACT>$dist{abstract}</ABSTRACT>
[email protected]{[ 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) {
+ next if $modname eq 'perl';
+
+ my $min_version = '0.0';
+ foreach my $c ($build->_parse_conditions($spec)) {
+ my ($op, $version) = $c =~ /^\s* (<=?|>=?|==|!=) \s* ([\w.]+) \s*$/x;
+
+ # This is a nasty hack because it fails if there is no >= op
+ if ($op eq '>=') {
+ $min_version = $version;
+ last;
+ }
+ }
+
+ # 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
+
+ }
+ }
+
+ # 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.
+ 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" />
+ <ARCHITECTURE NAME="%s" />
+EOF
+ }
+
+ foreach my $codebase (@codebase) {
+ $self->_simple_xml_escape($codebase);
+ $ppd .= sprintf(<<'EOF', $codebase);
+ <CODEBASE HREF="%s" />
+EOF
+ }
+
+ $ppd .= <<'EOF';
+ </IMPLEMENTATION>
+</SOFTPKG>
+EOF
+
+ my $ppd_file = "$dist{name}.ppd";
+ my $fh = IO::File->new(">$ppd_file")
+ or die "Cannot write to $ppd_file: $!";
+ print $fh $ppd;
+ close $fh;
+
+ return $ppd_file;
+}
+
+sub _ppd_version {
+ my ($self, $version) = @_;
+
+ # generates something like "0,18,0,0"
+ return join ',', (split(/\./, $version), (0)x4)[0..3];
+}
+
+sub _varchname { # Copied from PPM.pm
+ my ($self, $config) = @_;
+ my $varchname = $config->{archname};
+ # Append "-5.8" to architecture name for Perl 5.8 and later
+ if ($] >= 5.008) {
+ my $vstring = sprintf "%vd", $^V;
+ $vstring =~ s/\.\d+$//;
+ $varchname .= "-$vstring";
+ }
+ return $varchname;
+}
+
+{
+ my %escapes = (
+ "\n" => "\\n",
+ '"' => '&quot;',
+ '&' => '&amp;',
+ '>' => '&gt;',
+ '<' => '&lt;',
+ );
+ my $rx = join '|', keys %escapes;
+
+ sub _simple_xml_escape {
+ $_[1] =~ s/($rx)/$escapes{$1}/go;
+ }
+}
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::PPMMaker - Perl Package Manager file creation
+
+
+=head1 SYNOPSIS
+
+ On the command line, builds a .ppd file:
+ ./Build ppd
+
+
+=head1 DESCRIPTION
+
+This package contains the code that builds F<.ppd> "Perl Package
+Description" files, in support of ActiveState's "Perl Package
+Manager". Details are here:
+L<http://aspn.activestate.com/ASPN/Downloads/ActivePerl/PPM/>
+
+
+=head1 AUTHOR
+
+Dave Rolsky <[email protected]>, Ken Williams <[email protected]>
+
+
+=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), Module::Build(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/Amiga.pm b/inc/Module-Build/Module/Build/Platform/Amiga.pm
new file mode 100644
index 0000000..3b85eeb
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/Amiga.pm
@@ -0,0 +1,34 @@
+package Module::Build::Platform::Amiga;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::Amiga - Builder class for Amiga platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/Default.pm b/inc/Module-Build/Module/Build/Platform/Default.pm
new file mode 100644
index 0000000..f2b732a
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/Default.pm
@@ -0,0 +1,33 @@
+package Module::Build::Platform::Default;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::Default - Stub class for unknown platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/EBCDIC.pm b/inc/Module-Build/Module/Build/Platform/EBCDIC.pm
new file mode 100644
index 0000000..0e7488e
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/EBCDIC.pm
@@ -0,0 +1,34 @@
+package Module::Build::Platform::EBCDIC;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::EBCDIC - Builder class for EBCDIC platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/MPEiX.pm b/inc/Module-Build/Module/Build/Platform/MPEiX.pm
new file mode 100644
index 0000000..4351b70
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/MPEiX.pm
@@ -0,0 +1,34 @@
+package Module::Build::Platform::MPEiX;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::MPEiX - Builder class for MPEiX platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/MacOS.pm b/inc/Module-Build/Module/Build/Platform/MacOS.pm
new file mode 100644
index 0000000..7dbd619
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/MacOS.pm
@@ -0,0 +1,152 @@
+package Module::Build::Platform::MacOS;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+use ExtUtils::Install;
+
+sub have_forkpipe { 0 }
+
+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;
+}
+
+sub make_executable {
+ my $self = shift;
+ require MacPerl;
+ foreach (@_) {
+ MacPerl::SetFileInfo('McPL', 'TEXT', $_);
+ }
+}
+
+sub dispatch {
+ my $self = shift;
+
+ require MacPerl;
+
+ # What comes first in the action list.
+ my @action_list = qw(build test install);
+ my %actions = map {+($_, 1)} $self->known_actions;
+ delete @actions{@action_list};
+ push @action_list, sort { $a cmp $b } keys %actions;
+
+ my %toolserver = map {+$_ => 1} qw(test disttest diff testdb);
+ 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_realclean {
+ my $self = shift;
+ chmod 0666, $self->{properties}{build_script};
+ $self->SUPER::ACTION_realclean;
+}
+
+# ExtUtils::Install has a hard-coded '.' directory in versions less
+# than 1.30. We use a sneaky trick to turn that into ':'.
+#
+# Note that we do it here in a cross-platform way, so this code could
+# actually go in Module::Build::Base. But we put it here to be less
+# intrusive for other platforms.
+
+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) = @_;
+
+ @dirs = map { $_ eq '.' ? File::Spec->curdir : $_ } @dirs;
+
+ return File::Find::find($code, @dirs);
+ };
+
+ return $self->SUPER::ACTION_install(@_);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Module::Build::Platform::MacOS - Builder class for MacOS platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base> and override a few methods. Please see
+L<Module::Build> for the docs.
+
+=head2 Overridden Methods
+
+=over 4
+
+=item new()
+
+MacPerl doesn't define $Config{sitelib} or $Config{sitearch} for some
+reason, but $Config{installsitelib} and $Config{installsitearch} are
+there. So we copy the install variables to the other location
+
+=item make_executable()
+
+On MacOS we set the file type and creator to MacPerl so it will run
+with a double-click.
+
+=item dispatch()
+
+Because there's no easy way to say "./Build test" on MacOS, if
+dispatch is called with no arguments and no @ARGV a dialog box will
+pop up asking what action to take and any extra arguments.
+
+Default action is "test".
+
+=item ACTION_realclean()
+
+Need to unlock the Build program before deleting.
+
+=back
+
+=head1 AUTHOR
+
+Michael G Schwern <[email protected]>
+
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/RiscOS.pm b/inc/Module-Build/Module/Build/Platform/RiscOS.pm
new file mode 100644
index 0000000..eb22a7f
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/RiscOS.pm
@@ -0,0 +1,34 @@
+package Module::Build::Platform::RiscOS;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::RiscOS - Builder class for RiscOS platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/Unix.pm b/inc/Module-Build/Module/Build/Platform/Unix.pm
new file mode 100644
index 0000000..60f7371
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/Unix.pm
@@ -0,0 +1,73 @@
+package Module::Build::Platform::Unix;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+sub is_executable {
+ # We consider the owner bit to be authoritative on a file, because
+ # -x will always return true if the user is root and *any*
+ # executable bit is set. The -x test seems to try to answer the
+ # question "can I execute this file", but I think we want "is this
+ # file executable".
+
+ my ($self, $file) = @_;
+ return +(stat $file)[2] & 0100;
+}
+
+sub _startperl { "#! " . shift()->perl }
+
+sub _construct {
+ my $self = shift()->SUPER::_construct(@_);
+
+ # perl 5.8.1-RC[1-3] had some broken %Config entries, and
+ # unfortunately Red Hat 9 shipped it like that. Fix 'em up here.
+ my $c = $self->{config};
+ for (qw(siteman1 siteman3 vendorman1 vendorman3)) {
+ $c->{"install${_}dir"} ||= $c->{"install${_}"};
+ }
+
+ return $self;
+}
+
+# Open group says username should be portable filename characters,
+# but some Unix OS working with ActiveDirectory wind up with user-names
+# with back-slashes in the name. The new code below is very liberal
+# in what it accepts.
+sub _detildefy {
+ my ($self, $value) = @_;
+ $value =~ s[^~([^/]+)?(?=/|$)] # tilde with optional username
+ [$1 ?
+ ((getpwnam $1)[7] || "~$1") :
+ ($ENV{HOME} || (getpwuid $>)[7])
+ ]ex;
+ return $value;
+}
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::Unix - Builder class for Unix platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/VMS.pm b/inc/Module-Build/Module/Build/Platform/VMS.pm
new file mode 100644
index 0000000..b9c2da0
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/VMS.pm
@@ -0,0 +1,482 @@
+package Module::Build::Platform::VMS;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+
+=head1 NAME
+
+Module::Build::Platform::VMS - Builder class for VMS platforms
+
+=head1 DESCRIPTION
+
+This module inherits from C<Module::Build::Base> and alters a few
+minor details of its functionality. Please see L<Module::Build> for
+the general docs.
+
+=head2 Overridden Methods
+
+=over 4
+
+=item _set_defaults
+
+Change $self->{build_script} to 'Build.com' so @Build works.
+
+=cut
+
+sub _set_defaults {
+ my $self = shift;
+ $self->SUPER::_set_defaults(@_);
+
+ $self->{properties}{build_script} = 'Build.com';
+}
+
+
+=item cull_args
+
+'@Build foo' on VMS will not preserve the case of 'foo'. Rather than forcing
+people to write '@Build "foo"' we'll dispatch case-insensitively.
+
+=cut
+
+sub cull_args {
+ my $self = shift;
+ my($action, $args) = $self->SUPER::cull_args(@_);
+ my @possible_actions = grep { lc $_ eq lc $action } $self->known_actions;
+
+ die "Ambiguous action '$action'. Could be one of @possible_actions"
+ if @possible_actions > 1;
+
+ return ($possible_actions[0], $args);
+}
+
+
+=item manpage_separator
+
+Use '__' instead of '::'.
+
+=cut
+
+sub manpage_separator {
+ return '__';
+}
+
+
+=item prefixify
+
+Prefixify taking into account VMS' filepath syntax.
+
+=cut
+
+# Translated from ExtUtils::MM_VMS::prefixify()
+sub _prefixify {
+ my($self, $path, $sprefix, $type) = @_;
+ my $rprefix = $self->prefix;
+
+ $self->log_verbose(" prefixify $path from $sprefix to $rprefix\n");
+
+ # Translate $(PERLPREFIX) to a real path.
+ $rprefix = VMS::Filespec::vmspath($rprefix) if $rprefix;
+ $sprefix = VMS::Filespec::vmspath($sprefix) if $sprefix;
+
+ $self->log_verbose(" rprefix translated to $rprefix\n".
+ " sprefix translated to $sprefix\n");
+
+ if( length $path == 0 ) {
+ $self->log_verbose(" no path to prefixify.\n")
+ }
+ elsif( !File::Spec->file_name_is_absolute($path) ) {
+ $self->log_verbose(" path is relative, not prefixifying.\n");
+ }
+ elsif( $sprefix eq $rprefix ) {
+ $self->log_verbose(" no new prefix.\n");
+ }
+ else {
+ my($path_vol, $path_dirs) = File::Spec->splitpath( $path );
+ my $vms_prefix = $self->config('vms_prefix');
+ if( $path_vol eq $vms_prefix.':' ) {
+ $self->log_verbose(" $vms_prefix: seen\n");
+
+ $path_dirs =~ s{^\[}{\[.} unless $path_dirs =~ m{^\[\.};
+ $path = $self->_catprefix($rprefix, $path_dirs);
+ }
+ else {
+ $self->log_verbose(" cannot prefixify.\n");
+ return $self->prefix_relpaths($self->installdirs, $type);
+ }
+ }
+
+ $self->log_verbose(" now $path\n");
+
+ return $path;
+}
+
+=item _quote_args
+
+Command-line arguments (but not the command itself) must be quoted
+to ensure case preservation.
+
+=cut
+
+sub _quote_args {
+ # Returns a string that can become [part of] a command line with
+ # proper quoting so that the subprocess sees this same list of args,
+ # 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
+ : 0;
+
+ # Do not quote qualifiers that begin with '/'.
+ map { if (!/^\//) {
+ $_ =~ s/\"/""/g; # escape C<"> by doubling
+ $_ = q(").$_.q(");
+ }
+ }
+ ($got_arrayref ? @{$args[0]}
+ : @args
+ );
+
+ return $got_arrayref ? $args[0]
+ : join(' ', @args);
+}
+
+=item have_forkpipe
+
+There is no native fork(), so some constructs depending on it are not
+available.
+
+=cut
+
+sub have_forkpipe { 0 }
+
+=item _backticks
+
+Override to ensure that we quote the arguments but not the command.
+
+=cut
+
+sub _backticks {
+ # The command must not be quoted but the arguments to it must be.
+ my ($self, @cmd) = @_;
+ my $cmd = shift @cmd;
+ my $args = $self->_quote_args(@cmd);
+ return `$cmd $args`;
+}
+
+=item do_system
+
+Override to ensure that we quote the arguments but not the command.
+
+=cut
+
+sub do_system {
+ # The command must not be quoted but the arguments to it must be.
+ my ($self, @cmd) = @_;
+ $self->log_info("@cmd\n");
+ my $cmd = shift @cmd;
+ my $args = $self->_quote_args(@cmd);
+ return !system("$cmd $args");
+}
+
+=item oneliner
+
+Override to ensure that we do not quote the command.
+
+=cut
+
+sub oneliner {
+ my $self = shift;
+ my $oneliner = $self->SUPER::oneliner(@_);
+
+ $oneliner =~ s/^\"\S+\"//;
+
+ return "MCR $^X $oneliner";
+}
+
+=item _infer_xs_spec
+
+Inherit the standard version but tweak the library file name to be
+something Dynaloader can find.
+
+=cut
+
+sub _infer_xs_spec {
+ my $self = shift;
+ my $file = shift;
+
+ my $spec = $self->SUPER::_infer_xs_spec($file);
+
+ # Need to create with the same name as DynaLoader will load with.
+ if (defined &DynaLoader::mod2fname) {
+ my $file = $$spec{module_name} . '.' . $self->{config}->get('dlext');
+ $file =~ tr/:/_/;
+ $file = DynaLoader::mod2fname([$file]);
+ $$spec{lib_file} = File::Spec->catfile($$spec{archdir}, $file);
+ }
+
+ return $spec;
+}
+
+=item rscan_dir
+
+Inherit the standard version but remove dots at end of name.
+If the extended character set is in effect, do not remove dots from filenames
+with Unix path delimiters.
+
+=cut
+
+sub rscan_dir {
+ my ($self, $dir, $pattern) = @_;
+
+ my $result = $self->SUPER::rscan_dir( $dir, $pattern );
+
+ for my $file (@$result) {
+ if (!_efs() && ($file =~ m#/#)) {
+ $file =~ s/\.$//;
+ }
+ }
+ return $result;
+}
+
+=item dist_dir
+
+Inherit the standard version but replace embedded dots with underscores because
+a dot is the directory delimiter on VMS.
+
+=cut
+
+sub dist_dir {
+ my $self = shift;
+
+ my $dist_dir = $self->SUPER::dist_dir;
+ $dist_dir =~ s/\./_/g unless _efs();
+ return $dist_dir;
+}
+
+=item man3page_name
+
+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
+
+sub man3page_name {
+ my $self = shift;
+
+ my $mpname = $self->SUPER::man3page_name( shift );
+ my $sep = $self->manpage_separator;
+ $mpname =~ s/^$sep//;
+ return $mpname;
+}
+
+=item expand_test_dir
+
+Inherit the standard version but relativize the paths as the native glob() doesn't
+do that for us.
+
+=cut
+
+sub expand_test_dir {
+ my ($self, $dir) = @_;
+
+ my @reldirs = $self->SUPER::expand_test_dir( $dir );
+
+ for my $eachdir (@reldirs) {
+ my ($v,$d,$f) = File::Spec->splitpath( $eachdir );
+ my $reldir = File::Spec->abs2rel( File::Spec->catpath( $v, $d, '' ) );
+ $eachdir = File::Spec->catfile( $reldir, $f );
+ }
+ return @reldirs;
+}
+
+=item _detildefy
+
+The home-grown glob() does not currently handle tildes, so provide limited support
+here. Expect only UNIX format file specifications for now.
+
+=cut
+
+sub _detildefy {
+ my ($self, $arg) = @_;
+
+ # Apparently double ~ are not translated.
+ return $arg if ($arg =~ /^~~/);
+
+ # Apparently ~ followed by whitespace are not translated.
+ return $arg if ($arg =~ /^~ /);
+
+ if ($arg =~ /^~/) {
+ my $spec = $arg;
+
+ # Remove the tilde
+ $spec =~ s/^~//;
+
+ # Remove any slash following the tilde if present.
+ $spec =~ s#^/##;
+
+ # break up the paths for the merge
+ my $home = VMS::Filespec::unixify($ENV{HOME});
+
+ # In the default VMS mode, the trailing slash is present.
+ # In Unix report mode it is not. The parsing logic assumes that
+ # it is present.
+ $home .= '/' unless $home =~ m#/$#;
+
+ # Trivial case of just ~ by it self
+ if ($spec eq '') {
+ $home =~ s#/$##;
+ return $home;
+ }
+
+ my ($hvol, $hdir, $hfile) = File::Spec::Unix->splitpath($home);
+ if ($hdir eq '') {
+ # Someone has tampered with $ENV{HOME}
+ # So hfile is probably the directory since this should be
+ # a path.
+ $hdir = $hfile;
+ }
+
+ my ($vol, $dir, $file) = File::Spec::Unix->splitpath($spec);
+
+ my @hdirs = File::Spec::Unix->splitdir($hdir);
+ my @dirs = File::Spec::Unix->splitdir($dir);
+
+ my $newdirs;
+
+ # Two cases of tilde handling
+ if ($arg =~ m#^~/#) {
+
+ # Simple case, just merge together
+ $newdirs = File::Spec::Unix->catdir(@hdirs, @dirs);
+
+ } else {
+
+ # Complex case, need to add an updir - No delimiters
+ my @backup = File::Spec::Unix->splitdir(File::Spec::Unix->updir);
+
+ $newdirs = File::Spec::Unix->catdir(@hdirs, @backup, @dirs);
+
+ }
+
+ # Now put the two cases back together
+ $arg = File::Spec::Unix->catpath($hvol, $newdirs, $file);
+
+ }
+ return $arg;
+
+}
+
+=item find_perl_interpreter
+
+On VMS, $^X returns the fully qualified absolute path including version
+number. It's logically impossible to improve on it for getting the perl
+we're currently running, and attempting to manipulate it is usually
+lossy.
+
+=cut
+
+sub find_perl_interpreter {
+ return VMS::Filespec::vmsify($^X);
+}
+
+=item localize_file_path
+
+Convert the file path to the local syntax
+
+=cut
+
+sub localize_file_path {
+ my ($self, $path) = @_;
+ $path = VMS::Filespec::vmsify($path);
+ $path =~ s/\.\z//;
+ return $path;
+}
+
+=item localize_dir_path
+
+Convert the directory path to the local syntax
+
+=cut
+
+sub localize_dir_path {
+ my ($self, $path) = @_;
+ return VMS::Filespec::vmspath($path);
+}
+
+=item ACTION_clean
+
+The home-grown glob() expands a bit too aggressively when given a bare name,
+so default in a zero-length extension.
+
+=cut
+
+sub ACTION_clean {
+ my ($self) = @_;
+ foreach my $item (map glob(VMS::Filespec::rmsexpand($_, '.;0')), $self->cleanup) {
+ $self->delete_filetree($item);
+ }
+}
+
+
+# Need to look up the feature settings. The preferred way is to use the
+# VMS::Feature module, but that may not be available to dual life modules.
+
+my $use_feature;
+BEGIN {
+ if (eval { local $SIG{__DIE__}; require VMS::Feature; }) {
+ $use_feature = 1;
+ }
+}
+
+# Need to look up the UNIX report mode. This may become a dynamic mode
+# in the future.
+sub _unix_rpt {
+ my $unix_rpt;
+ if ($use_feature) {
+ $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;
+ }
+ return $unix_rpt;
+}
+
+# Need to look up the EFS character set mode. This may become a dynamic
+# mode in the future.
+sub _efs {
+ my $efs;
+ if ($use_feature) {
+ $efs = VMS::Feature::current("efs_charset");
+ } else {
+ my $env_efs = $ENV{'DECC$EFS_CHARSET'} || '';
+ $efs = $env_efs =~ /^[ET1]/i;
+ }
+ return $efs;
+}
+
+=back
+
+=head1 AUTHOR
+
+Michael G Schwern <[email protected]>
+Ken Williams <[email protected]>
+Craig A. Berry <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
+
+1;
+__END__
diff --git a/inc/Module-Build/Module/Build/Platform/VOS.pm b/inc/Module-Build/Module/Build/Platform/VOS.pm
new file mode 100644
index 0000000..3099cc5
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/VOS.pm
@@ -0,0 +1,34 @@
+package Module::Build::Platform::VOS;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::VOS - Builder class for VOS platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base>. Please see the L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/Windows.pm b/inc/Module-Build/Module/Build/Platform/Windows.pm
new file mode 100644
index 0000000..8dcf52c
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/Windows.pm
@@ -0,0 +1,299 @@
+package Module::Build::Platform::Windows;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+
+use Config;
+use File::Basename;
+use File::Spec;
+use IO::File;
+
+use Module::Build::Base;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Base);
+
+
+sub manpage_separator {
+ return '.';
+}
+
+sub have_forkpipe { 0 }
+
+sub _detildefy {
+ my ($self, $value) = @_;
+ $value =~ s,^~(?= [/\\] | $ ),$ENV{HOME},x
+ if $ENV{HOME};
+ return $value;
+}
+
+sub ACTION_realclean {
+ my ($self) = @_;
+
+ $self->SUPER::ACTION_realclean();
+
+ my $basename = basename($0);
+ $basename =~ s/(?:\.bat)?$//i;
+
+ if ( lc $basename eq lc $self->build_script ) {
+ if ( $self->build_bat ) {
+ $self->log_info("Deleting $basename.bat\n");
+ my $full_progname = $0;
+ $full_progname =~ s/(?:\.bat)?$/.bat/i;
+
+ # Voodoo required to have a batch file delete itself without error;
+ # Syntax differs between 9x & NT: the later requires a null arg (???)
+ require Win32;
+ my $null_arg = (Win32::IsWinNT()) ? '""' : '';
+ my $cmd = qq(start $null_arg /min "\%comspec\%" /c del "$full_progname");
+
+ my $fh = IO::File->new(">> $basename.bat")
+ or die "Can't create $basename.bat: $!";
+ print $fh $cmd;
+ close $fh ;
+ } else {
+ $self->delete_filetree($self->build_script . '.bat');
+ }
+ }
+}
+
+sub make_executable {
+ my $self = shift;
+
+ $self->SUPER::make_executable(@_);
+
+ foreach my $script (@_) {
+
+ # Native batch script
+ if ( $script =~ /\.(bat|cmd)$/ ) {
+ $self->SUPER::make_executable($script);
+ next;
+
+ # Perl script that needs to be wrapped in a batch script
+ } else {
+ my %opts = ();
+ if ( $script eq $self->build_script ) {
+ $opts{ntargs} = q(-x -S %0 --build_bat %*);
+ $opts{otherargs} = q(-x -S "%0" --build_bat %1 %2 %3 %4 %5 %6 %7 %8 %9);
+ }
+
+ my $out = eval {$self->pl2bat(in => $script, update => 1, %opts)};
+ $self->log_warn("WARNING: Unable to convert file '$script' to an executable script:\[email protected]");
+ } else {
+ $self->SUPER::make_executable($out);
+ }
+ }
+ }
+}
+
+# This routine was copied almost verbatim from the 'pl2bat' utility
+# distributed with perl. It requires too much voodoo with shell quoting
+# differences and shortcomings between the various flavors of Windows
+# to reliably shell out
+sub pl2bat {
+ my $self = shift;
+ my %opts = @_;
+
+ # NOTE: %0 is already enclosed in doublequotes by cmd.exe, as appropriate
+ $opts{ntargs} = '-x -S %0 %*' unless exists $opts{ntargs};
+ $opts{otherargs} = '-x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9' unless exists $opts{otherargs};
+
+ $opts{stripsuffix} = '/\\.plx?/' unless exists $opts{stripsuffix};
+ $opts{stripsuffix} = ($opts{stripsuffix} =~ m{^/([^/]*[^/\$]|)\$?/?$} ? $1 : "\Q$opts{stripsuffix}\E");
+
+ unless (exists $opts{out}) {
+ $opts{out} = $opts{in};
+ $opts{out} =~ s/$opts{stripsuffix}$//oi;
+ $opts{out} .= '.bat' unless $opts{in} =~ /\.bat$/i or $opts{in} =~ /^-$/;
+ }
+
+ my $head = <<EOT;
+ \@rem = '--*-Perl-*--
+ \@echo off
+ if "%OS%" == "Windows_NT" goto WinNT
+ perl $opts{otherargs}
+ goto endofperl
+ :WinNT
+ perl $opts{ntargs}
+ if NOT "%COMSPEC%" == "%SystemRoot%\\system32\\cmd.exe" goto endofperl
+ if %errorlevel% == 9009 echo You do not have Perl in your PATH.
+ if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
+ goto endofperl
+ \@rem ';
+EOT
+
+ $head =~ s/^\s+//gm;
+ my $headlines = 2 + ($head =~ tr/\n/\n/);
+ my $tail = "\n__END__\n:endofperl\n";
+
+ my $linedone = 0;
+ my $taildone = 0;
+ my $linenum = 0;
+ my $skiplines = 0;
+
+ my $start = $Config{startperl};
+ $start = "#!perl" unless $start =~ /^#!.*perl/;
+
+ my $in = IO::File->new("< $opts{in}") or die "Can't open $opts{in}: $!";
+ my @file = <$in>;
+ $in->close;
+
+ foreach my $line ( @file ) {
+ $linenum++;
+ if ( $line =~ /^:endofperl\b/ ) {
+ if (!exists $opts{update}) {
+ warn "$opts{in} has already been converted to a batch file!\n";
+ return;
+ }
+ $taildone++;
+ }
+ if ( not $linedone and $line =~ /^#!.*perl/ ) {
+ if (exists $opts{update}) {
+ $skiplines = $linenum - 1;
+ $line .= "#line ".(1+$headlines)."\n";
+ } else {
+ $line .= "#line ".($linenum+$headlines)."\n";
+ }
+ $linedone++;
+ }
+ if ( $line =~ /^#\s*line\b/ and $linenum == 2 + $skiplines ) {
+ $line = "";
+ }
+ }
+
+ my $out = IO::File->new("> $opts{out}") or die "Can't open $opts{out}: $!";
+ print $out $head;
+ print $out $start, ( $opts{usewarnings} ? " -w" : "" ),
+ "\n#line ", ($headlines+1), "\n" unless $linedone;
+ print $out @file[$skiplines..$#file];
+ print $out $tail unless $taildone;
+ $out->close;
+
+ return $opts{out};
+}
+
+
+sub _quote_args {
+ # Returns a string that can become [part of] a command line with
+ # proper quoting so that the subprocess sees this same list of args.
+ my ($self, @args) = @_;
+
+ my @quoted;
+
+ for (@args) {
+ if ( /^[^\s*?!\$<>;|'"\[\]\{\}]+$/ ) {
+ # Looks pretty safe
+ push @quoted, $_;
+ } else {
+ # XXX this will obviously have to improve - is there already a
+ # core module lying around that does proper quoting?
+ s/"/\\"/g;
+ push @quoted, qq("$_");
+ }
+ }
+
+ return join " ", @quoted;
+}
+
+
+sub split_like_shell {
+ # As it turns out, Windows command-parsing is very different from
+ # Unix command-parsing. Double-quotes mean different things,
+ # backslashes don't necessarily mean escapes, and so on. So we
+ # can't use Text::ParseWords::shellwords() to break a command string
+ # 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++;
+ } elsif ( $ch eq '\\' && $next_ch eq '\\' ) {
+ $arg .= '\\';
+ $i++;
+ } elsif ( $ch eq '"' && $next_ch eq '"' && $quote_mode ) {
+ $quote_mode = !$quote_mode;
+ $arg .= '"';
+ $i++;
+ } elsif ( $ch eq '"' && $next_ch eq '"' && !$quote_mode &&
+ ( $i + 2 == length() ||
+ substr( $_, $i + 2, 1 ) eq ' ' )
+ ) { # for cases like: a"" => [ 'a' ]
+ push( @argv, $arg );
+ $arg = '';
+ $i += 2;
+ } elsif ( $ch eq '"' ) {
+ $quote_mode = !$quote_mode;
+ } elsif ( $ch eq ' ' && !$quote_mode ) {
+ push( @argv, $arg ) if $arg;
+ $arg = '';
+ ++$i while substr( $_, $i + 1, 1 ) eq ' ';
+ } else {
+ $arg .= $ch;
+ }
+
+ $i++;
+ }
+
+ push( @argv, $arg ) if defined( $arg ) && length( $arg );
+ return @argv;
+}
+
+
+# system(@cmd) does not like having double-quotes in it on Windows.
+# So we quote them and run it as a single command.
+sub do_system {
+ my ($self, @cmd) = @_;
+
+ my $cmd = $self->_quote_args(@cmd);
+ my $status = system($cmd);
+ if ($status and $! =~ /Argument list too long/i) {
+ my $env_entries = '';
+ foreach (sort keys %ENV) { $env_entries .= "$_=>".length($ENV{$_})."; " }
+ warn "'Argument list' was 'too long', env lengths are $env_entries";
+ }
+ return !$status;
+}
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Module::Build::Platform::Windows - Builder class for Windows platforms
+
+=head1 DESCRIPTION
+
+The sole purpose of this module is to inherit from
+C<Module::Build::Base> and override a few methods. Please see
+L<Module::Build> for the docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>, Randy W. Sims <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/aix.pm b/inc/Module-Build/Module/Build/Platform/aix.pm
new file mode 100644
index 0000000..dc685c7
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/aix.pm
@@ -0,0 +1,40 @@
+package Module::Build::Platform::aix;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Platform::Unix;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Platform::Unix);
+
+# This class isn't necessary anymore, but we can't delete it, because
+# some people might still have the old copy in their @INC, containing
+# code we don't want to execute, so we have to make sure an upgrade
+# will replace it with this empty subclass.
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::aix - Builder class for AIX platform
+
+=head1 DESCRIPTION
+
+This module provides some routines very specific to the AIX
+platform.
+
+Please see the L<Module::Build> for the general docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/cygwin.pm b/inc/Module-Build/Module/Build/Platform/cygwin.pm
new file mode 100644
index 0000000..8842db4
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/cygwin.pm
@@ -0,0 +1,39 @@
+package Module::Build::Platform::cygwin;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Platform::Unix;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Platform::Unix);
+
+sub manpage_separator {
+ '.'
+}
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::cygwin - Builder class for Cygwin platform
+
+=head1 DESCRIPTION
+
+This module provides some routines very specific to the cygwin
+platform.
+
+Please see the L<Module::Build> for the general docs.
+
+=head1 AUTHOR
+
+Initial stub by Yitzchak Scott-Thoennes <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/darwin.pm b/inc/Module-Build/Module/Build/Platform/darwin.pm
new file mode 100644
index 0000000..cb1449a
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/darwin.pm
@@ -0,0 +1,40 @@
+package Module::Build::Platform::darwin;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Platform::Unix;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Platform::Unix);
+
+# This class isn't necessary anymore, but we can't delete it, because
+# some people might still have the old copy in their @INC, containing
+# code we don't want to execute, so we have to make sure an upgrade
+# will replace it with this empty subclass.
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::darwin - Builder class for Mac OS X platform
+
+=head1 DESCRIPTION
+
+This module provides some routines very specific to the Mac OS X
+platform.
+
+Please see the L<Module::Build> for the general docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/Platform/os2.pm b/inc/Module-Build/Module/Build/Platform/os2.pm
new file mode 100644
index 0000000..9940a08
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Platform/os2.pm
@@ -0,0 +1,39 @@
+package Module::Build::Platform::os2;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use Module::Build::Platform::Unix;
+
+use vars qw(@ISA);
[email protected] = qw(Module::Build::Platform::Unix);
+
+sub manpage_separator { '.' }
+
+sub have_forkpipe { 0 }
+
+1;
+__END__
+
+
+=head1 NAME
+
+Module::Build::Platform::os2 - Builder class for OS/2 platform
+
+=head1 DESCRIPTION
+
+This module provides some routines very specific to the OS/2
+platform.
+
+Please see the L<Module::Build> for the general docs.
+
+=head1 AUTHOR
+
+Ken Williams <[email protected]>
+
+=head1 SEE ALSO
+
+perl(1), Module::Build(3), ExtUtils::MakeMaker(3)
+
+=cut
diff --git a/inc/Module-Build/Module/Build/PodParser.pm b/inc/Module-Build/Module/Build/PodParser.pm
new file mode 100644
index 0000000..171d2c4
--- /dev/null
+++ b/inc/Module-Build/Module/Build/PodParser.pm
@@ -0,0 +1,106 @@
+package Module::Build::PodParser;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = '0.34';
+$VERSION = eval $VERSION;
+use vars qw(@ISA);
+
+sub new {
+ # Perl is so fun.
+ my $package = shift;
+
+ my $self;
+
+ # Try using Pod::Parser first
+ if (eval{ require Pod::Parser; 1; }) {
+ @ISA = qw(Pod::Parser);
+ $self = $package->SUPER::new(@_);
+ $self->{have_pod_parser} = 1;
+ } else {
+ @ISA = ();
+ *parse_from_filehandle = \&_myparse_from_filehandle;
+ $self = bless {have_pod_parser => 0, @_}, $package;
+ }
+
+ unless ($self->{fh}) {
+ die "No 'file' or 'fh' parameter given" unless $self->{file};
+ $self->{fh} = IO::File->new($self->{file}) or die "Couldn't open $self->{file}: $!";
+ }
+
+ return $self;
+}
+
+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 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} || [];
+}
+
+################## Pod::Parser overrides ###########
+sub initialize {
+ my $self = shift;
+ $self->{_head} = '';
+ $self->SUPER::initialize();
+}
+
+sub command {
+ my ($self, $cmd, $text) = @_;
+ if ( $cmd eq 'head1' ) {
+ $text =~ s/^\s+//;
+ $text =~ s/\s+$//;
+ $self->{_head} = $text;
+ }
+}
+
+sub textblock {
+ my ($self, $text) = @_;
+ $text =~ s/^\s+//;
+ $text =~ s/\s+$//;
+ if ($self->{_head} eq 'NAME') {
+ my ($name, $abstract) = split( /\s+-\s+/, $text, 2 );
+ $self->{abstract} = $abstract;
+ } elsif ($self->{_head} =~ /^AUTHORS?$/) {
+ push @{$self->{author}}, $text if $text =~ /\@/;
+ }
+}
+
+sub verbatim {}
+sub interior_sequence {}
+
+1;
diff --git a/inc/Module-Build/Module/Build/Version.pm b/inc/Module-Build/Module/Build/Version.pm
new file mode 100644
index 0000000..ac48cdd
--- /dev/null
+++ b/inc/Module-Build/Module/Build/Version.pm
@@ -0,0 +1,686 @@
+package Module::Build::Version;
+use strict;
+
+use vars qw($VERSION);
+$VERSION = 0.77;
+
+eval "use version $VERSION";
+if ([email protected]) { # can't locate version files, use our own
+
+ # Avoid redefined warnings if an old version.pm was available
+ delete $version::{$_} foreach keys %version::;
+
+ # first we get the stub version module
+ my $version;
+ while (<DATA>) {
+ s/(\$VERSION)\s=\s\d+/\$VERSION = 0/;
+ $version .= $_ if $_;
+ last if /^1;$/;
+ }
+
+ # and now get the current version::vpp code
+ my $vpp;
+ while (<DATA>) {
+ s/(\$VERSION)\s=\s\d+/\$VERSION = 0/;
+ $vpp .= $_ if $_;
+ last if /^1;$/;
+ }
+
+ # but we eval them in reverse order since version depends on
+ # version::vpp to already exist
+ $INC{'version/vpp.pm'} = 'inside Module::Build::Version';
+ eval $version; die [email protected] if [email protected];
+ $INC{'version.pm'} = 'inside Module::Build::Version';
+}
+
+# now we can safely subclass version, installed or not
+use vars qw(@ISA);
[email protected] = qw(version);
+
+1;
+__DATA__
+# stub version module to make everything else happy
+package version;
+
+use 5.005_04;
+use strict;
+
+use vars qw(@ISA $VERSION $CLASS *declare *qv);
+
+$VERSION = 0.77;
+
+$CLASS = 'version';
+
+push @ISA, "version::vpp";
+local $^W;
+*version::qv = \&version::vpp::qv;
+*version::declare = \&version::vpp::declare;
+*version::_VERSION = \&version::vpp::_VERSION;
+if ($] > 5.009001 && $] <= 5.010000) {
+ no strict 'refs';
+ *{'version::stringify'} = \*version::vpp::stringify;
+ *{'version::(""'} = \*version::vpp::stringify;
+ *{'version::new'} = \*version::vpp::new;
+}
+
+# Preloaded methods go here.
+sub import {
+ no strict 'refs';
+ my ($class) = shift;
+
+ # Set up any derived class
+ unless ($class eq 'version') {
+ local $^W;
+ *{$class.'::declare'} = \&version::declare;
+ *{$class.'::qv'} = \&version::qv;
+ }
+
+ my %args;
+ if (@_) { # any remaining terms are arguments
+ map { $args{$_} = 1 } @_
+ }
+ else { # no parameters at all on use line
+ %args =
+ (
+ qv => 1,
+ 'UNIVERSAL::VERSION' => 1,
+ );
+ }
+
+ my $callpkg = caller();
+
+ if (exists($args{declare})) {
+ *{$callpkg."::declare"} =
+ sub {return $class->declare(shift) }
+ unless defined(&{$callpkg.'::declare'});
+ }
+
+ if (exists($args{qv})) {
+ *{$callpkg."::qv"} =
+ sub {return $class->qv(shift) }
+ unless defined(&{"$callpkg\::qv"});
+ }
+
+ if (exists($args{'UNIVERSAL::VERSION'})) {
+ local $^W;
+ *UNIVERSAL::VERSION = \&version::_VERSION;
+ }
+
+ if (exists($args{'VERSION'})) {
+ *{$callpkg."::VERSION"} = \&version::_VERSION;
+ }
+}
+
+1;
+
+# replace everything from here to the end with the current version/vpp.pm
+package version::vpp;
+use strict;
+
+use POSIX qw/locale_h/;
+use locale;
+use vars qw ($VERSION @ISA @REGEXS);
+$VERSION = '0.77';
+$VERSION = eval $VERSION;
+
+push @REGEXS, qr/
+ ^v? # optional leading 'v'
+ (\d*) # major revision not required
+ \. # requires at least one decimal
+ (?:(\d+)\.?){1,}
+ /x;
+
+use overload (
+ '""' => \&stringify,
+ '0+' => \&numify,
+ 'cmp' => \&vcmp,
+ '<=>' => \&vcmp,
+ 'bool' => \&vbool,
+ 'nomethod' => \&vnoop,
+);
+
+my $VERSION_MAX = 0x7FFFFFFF;
+
+eval "use warnings";
+ eval '
+ package warnings;
+ sub enabled {return $^W;}
+ 1;
+ ';
+}
+
+sub new
+{
+ 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} } ];
+ $self->{qv} = 1 if $value->{qv};
+ $self->{alpha} = 1 if $value->{alpha};
+ $self->{original} = ''.$value->{original};
+ return $self;
+ }
+
+ my $currlocale = setlocale(LC_ALL);
+
+ # if the current locale uses commas for decimal points, we
+ # just replace commas with decimal places, rather than changing
+ # locales
+ if ( localeconv()->{decimal_point} eq ',' ) {
+ $value =~ tr/,/./;
+ }
+
+ if ( not defined $value or $value =~ /^undef$/ ) {
+ # RT #19517 - special case for undef comparison
+ # or someone forgot to pass a value
+ push @{$self->{version}}, 0;
+ $self->{original} = "0";
+ return ($self);
+ }
+
+ if ( $#_ == 2 ) { # must be CVS-style
+ $value = 'v'.$_[2];
+ }
+
+ $value = _un_vstring($value);
+
+ # exponential notation
+ if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
+ $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
+ my $qv = 0;
+ my $alpha = 0;
+ my $width = 3;
+ my $saw_period = 0;
+ my $vinf = 0;
+ my ($start, $last, $pos, $s);
+ $s = 0;
+
+ while ( substr($value,$s,1) =~ /\s/ ) { # leading whitespace is OK
+ $s++;
+ }
+
+ if (substr($value,$s,1) eq 'v') {
+ $s++; # get past 'v'
+ $qv = 1; # force quoted version processing
+ }
+
+ $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 '.' ) {
+ if ($alpha) {
+ Carp::croak("Invalid version format ".
+ "(underscores before decimal)");
+ }
+ $saw_period++;
+ $last = $pos;
+ }
+ elsif ( substr($value,$pos,1) eq '_' ) {
+ if ($alpha) {
+ require Carp;
+ Carp::croak("Invalid version format ".
+ "(multiple underscores)");
+ }
+ $alpha = 1;
+ $width = $pos - $last - 1; # natural width of sub-version
+ }
+ elsif ( substr($value,$pos,1) eq ','
+ and substr($value,$pos+1,1) =~ /[0-9]/ ) {
+ # looks like an unhandled locale
+ $saw_period++;
+ $last = $pos;
+ }
+ $pos++;
+ }
+
+ if ( $alpha && !$saw_period ) {
+ require Carp;
+ Carp::croak("Invalid version format ".
+ "(alpha without decimal)");
+ }
+
+ if ( $alpha && $saw_period && $width == 0 ) {
+ require Carp;
+ Carp::croak("Invalid version format ".
+ "(misplaced _ in number)");
+ }
+
+ if ( $saw_period > 1 ) {
+ $qv = 1; # force quoted version processing
+ }
+
+ $last = $pos;
+ $pos = $s;
+
+ if ( $qv ) {
+ $self->{qv} = 1;
+ }
+
+ if ( $alpha ) {
+ $self->{alpha} = 1;
+ }
+
+ if ( !$qv && $width < 3 ) {
+ $self->{width} = $width;
+ }
+
+ while ( substr($value,$pos,1) =~ /\d/ ) {
+ $pos++;
+ }
+
+ if ( substr($value,$pos,1) !~ /[a-z]/ ) { ### FIX THIS ###
+ my $rev;
+
+ while (1) {
+ $rev = 0;
+ {
+
+ # this is atoi() that delimits on underscores
+ my $end = $pos;
+ my $mult = 1;
+ my $orev;
+
+ # the following if() will only be true after the decimal
+ # point of a version originally created with a bare
+ # floating point number, i.e. not quoted in any way
+ if ( !$qv && $s > $start && $saw_period == 1 ) {
+ $mult *= 100;
+ while ( $s < $end ) {
+ $orev = $rev;
+ $rev += substr($value,$s,1) * $mult;
+ $mult /= 10;
+ if ( abs($orev) > abs($rev)
+ || abs($rev) > abs($VERSION_MAX) ) {
+ if ( warnings::enabled("overflow") ) {
+ require Carp;
+ Carp::carp("Integer overflow in version");
+ }
+ $s = $end - 1;
+ $rev = $VERSION_MAX;
+ }
+ $s++;
+ if ( substr($value,$s,1) eq '_' ) {
+ $s++;
+ }
+ }
+ }
+ else {
+ while (--$end >= $s) {
+ $orev = $rev;
+ $rev += substr($value,$end,1) * $mult;
+ $mult *= 10;
+ if ( abs($orev) > abs($rev)
+ || abs($rev) > abs($VERSION_MAX) ) {
+ if ( warnings::enabled("overflow") ) {
+ require Carp;
+ Carp::carp("Integer overflow in version");
+ }
+ $end = $s - 1;
+ $rev = $VERSION_MAX;
+ }
+ }
+ }
+ }
+
+ # Append revision
+ push @{$self->{version}}, $rev;
+ if ( substr($value,$pos,1) eq '.'
+ && substr($value,$pos+1,1) =~ /\d/ ) {
+ $s = ++$pos;
+ }
+ elsif ( substr($value,$pos,1) eq '_'
+ && substr($value,$pos+1,1) =~ /\d/ ) {
+ $s = ++$pos;
+ }
+ elsif ( substr($value,$pos,1) eq ','
+ && substr($value,$pos+1,1) =~ /\d/ ) {
+ $s = ++$pos;
+ }
+ elsif ( substr($value,$pos,1) =~ /\d/ ) {
+ $s = $pos;
+ }
+ else {
+ $s = $pos;
+ last;
+ }
+ if ( $qv ) {
+ while ( substr($value,$pos,1) =~ /\d/ ) {
+ $pos++;
+ }
+ }
+ else {
+ my $digits = 0;
+ while (substr($value,$pos,1) =~ /[\d_]/ && $digits < 3) {
+ if ( substr($value,$pos,1) ne '_' ) {
+ $digits++;
+ }
+ $pos++;
+ }
+ }
+ }
+ }
+ if ( $qv ) { # quoted versions always get at least three terms
+ my $len = scalar @{$self->{version}};
+ $len = 3 - $len;
+ while ($len-- > 0) {
+ push @{$self->{version}}, 0;
+ }
+ }
+
+ if ( substr($value,$pos) ) { # any remaining text
+ if ( warnings::enabled("misc") ) {
+ require Carp;
+ Carp::carp("Version string '$value' contains invalid data; ".
+ "ignoring: '".substr($value,$pos)."'");
+ }
+ }
+
+ # cache the original value for use when stringification
+ if ( $vinf ) {
+ $self->{vinf} = 1;
+ $self->{original} = 'v.Inf';
+ }
+ else {
+ $self->{original} = substr($value,0,$pos);
+ }
+
+ return ($self);
+}
+
+*parse = \&new;
+
+sub numify
+{
+ my ($self) = @_;
+ unless (_verify($self)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ my $width = $self->{width} || 3;
+ my $alpha = $self->{alpha} || "";
+ my $len = $#{$self->{version}};
+ my $digit = $self->{version}[0];
+ my $string = sprintf("%d.", $digit );
+
+ for ( my $i = 1 ; $i < $len ; $i++ ) {
+ $digit = $self->{version}[$i];
+ if ( $width < 3 ) {
+ my $denom = 10**(3-$width);
+ my $quot = int($digit/$denom);
+ my $rem = $digit - ($quot * $denom);
+ $string .= sprintf("%0".$width."d_%d", $quot, $rem);
+ }
+ else {
+ $string .= sprintf("%03d", $digit);
+ }
+ }
+
+ if ( $len > 0 ) {
+ $digit = $self->{version}[$len];
+ if ( $alpha && $width == 3 ) {
+ $string .= "_";
+ }
+ $string .= sprintf("%0".$width."d", $digit);
+ }
+ else # $len = 0
+ {
+ $string .= sprintf("000");
+ }
+
+ return $string;
+}
+
+sub normal
+{
+ my ($self) = @_;
+ unless (_verify($self)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ my $alpha = $self->{alpha} || "";
+ my $len = $#{$self->{version}};
+ my $digit = $self->{version}[0];
+ my $string = sprintf("v%d", $digit );
+
+ for ( my $i = 1 ; $i < $len ; $i++ ) {
+ $digit = $self->{version}[$i];
+ $string .= sprintf(".%d", $digit);
+ }
+
+ if ( $len > 0 ) {
+ $digit = $self->{version}[$len];
+ if ( $alpha ) {
+ $string .= sprintf("_%0d", $digit);
+ }
+ else {
+ $string .= sprintf(".%0d", $digit);
+ }
+ }
+
+ if ( $len <= 2 ) {
+ for ( $len = 2 - $len; $len != 0; $len-- ) {
+ $string .= sprintf(".%0d", 0);
+ }
+ }
+
+ return $string;
+}
+
+sub stringify
+{
+ my ($self) = @_;
+ unless (_verify($self)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ return exists $self->{original}
+ ? $self->{original}
+ : exists $self->{qv}
+ ? $self->normal
+ : $self->numify;
+}
+
+sub vcmp
+{
+ require UNIVERSAL;
+ my ($left,$right,$swap) = @_;
+ my $class = ref($left);
+ unless ( UNIVERSAL::isa($right, $class) ) {
+ $right = $class->new($right);
+ }
+
+ if ( $swap ) {
+ ($left, $right) = ($right, $left);
+ }
+ unless (_verify($left)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ unless (_verify($right)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ my $l = $#{$left->{version}};
+ my $r = $#{$right->{version}};
+ my $m = $l < $r ? $l : $r;
+ my $lalpha = $left->is_alpha;
+ my $ralpha = $right->is_alpha;
+ my $retval = 0;
+ my $i = 0;
+ while ( $i <= $m && $retval == 0 ) {
+ $retval = $left->{version}[$i] <=> $right->{version}[$i];
+ $i++;
+ }
+
+ # tiebreaker for alpha with identical terms
+ if ( $retval == 0
+ && $l == $r
+ && $left->{version}[$m] == $right->{version}[$m]
+ && ( $lalpha || $ralpha ) ) {
+
+ if ( $lalpha && !$ralpha ) {
+ $retval = -1;
+ }
+ elsif ( $ralpha && !$lalpha) {
+ $retval = +1;
+ }
+ }
+
+ # possible match except for trailing 0's
+ if ( $retval == 0 && $l != $r ) {
+ if ( $l < $r ) {
+ while ( $i <= $r && $retval == 0 ) {
+ if ( $right->{version}[$i] != 0 ) {
+ $retval = -1; # not a match after all
+ }
+ $i++;
+ }
+ }
+ else {
+ while ( $i <= $l && $retval == 0 ) {
+ if ( $left->{version}[$i] != 0 ) {
+ $retval = +1; # not a match after all
+ }
+ $i++;
+ }
+ }
+ }
+
+ return $retval;
+}
+
+sub vbool {
+ my ($self) = @_;
+ return vcmp($self,$self->new("0"),1);
+}
+
+sub vnoop {
+ require Carp;
+ Carp::croak("operation not supported with version object");
+}
+
+sub is_alpha {
+ my ($self) = @_;
+ return (exists $self->{alpha});
+}
+
+sub qv {
+ my $value = shift;
+ my $class = 'version';
+ if (@_) {
+ $class = ref($value) || $value;
+ $value = shift;
+ }
+
+ $value = _un_vstring($value);
+ $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
+ my $version = $class->new($value);
+ return $version;
+}
+
+*declare = \&qv;
+
+sub is_qv {
+ my ($self) = @_;
+ return (exists $self->{qv});
+}
+
+
+sub _verify {
+ my ($self) = @_;
+ if ( ref($self)
+ && eval { exists $self->{version} }
+ && ref($self->{version}) eq 'ARRAY'
+ ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+sub _un_vstring {
+ my $value = shift;
+ # may be a v-string
+ if ( $] >= 5.006_000 && length($value) >= 3 && $value !~ /[._]/ ) {
+ my $tvalue = sprintf("v%vd",$value);
+ if ( $tvalue =~ /^v\d+\.\d+\.\d+$/ ) {
+ # must be a v-string
+ $value = $tvalue;
+ }
+ }
+ return $value;
+}
+
+sub _VERSION {
+ my ($obj, $req) = @_;
+ my $class = ref($obj) || $obj;
+
+ no strict 'refs';
+ eval "require $class" unless %{"$class\::"}; # already existing
+ return undef if [email protected] =~ /Can't locate/ and not defined $req;
+
+ if ( not %{"$class\::"} and $] >= 5.008) { # file but no package
+ require Carp;
+ Carp::croak( "$class defines neither package nor VERSION"
+ ."--version check failed");
+ }
+
+ my $version = eval "\$$class\::VERSION";
+ if ( defined $version ) {
+ local $^W if $] <= 5.008;
+ $version = version::vpp->new($version);
+ }
+
+ if ( defined $req ) {
+ unless ( defined $version ) {
+ require Carp;
+ my $msg = $] < 5.006
+ ? "$class version $req required--this is only version "
+ : "$class does not define \$$class\::VERSION"
+ ."--version check failed";
+
+ if ( $ENV{VERSION_DEBUG} ) {
+ Carp::confess($msg);
+ }
+ else {
+ Carp::croak($msg);
+ }
+ }
+
+ $req = version::vpp->new($req);
+
+ if ( $req > $version ) {
+ require Carp;
+ if ( $req->is_qv ) {
+ Carp::croak(
+ sprintf ("%s version %s required--".
+ "this is only version %s", $class,
+ $req->normal, $version->normal)
+ );
+ }
+ else {
+ Carp::croak(
+ sprintf ("%s version %s required--".
+ "this is only version %s", $class,
+ $req->stringify, $version->stringify)
+ );
+ }
+ }
+ }
+
+ return defined $version ? $version->stringify : undef;
+}
+
+1; #this line is important and will help the module return a true value
diff --git a/inc/Module-Build/Module/Build/YAML.pm b/inc/Module-Build/Module/Build/YAML.pm
new file mode 100644
index 0000000..4a181ad
--- /dev/null
+++ b/inc/Module-Build/Module/Build/YAML.pm
@@ -0,0 +1,161 @@
+package Module::Build::YAML;
+
+use strict;
+use vars qw($VERSION @EXPORT @EXPORT_OK);
+$VERSION = "0.50";
[email protected]_OK = qw(Dump Load DumpFile LoadFile);
+
+sub new {
+ my $this = shift;
+ my $class = ref($this) || $this;
+ my $self = {};
+ bless $self, $class;
+ return($self);
+}
+
+sub Dump {
+ shift if ($_[0] eq __PACKAGE__ || ref($_[0]) eq __PACKAGE__);
+ my $yaml = "";
+ foreach my $item (@_) {
+ $yaml .= "---\n";
+ $yaml .= &_yaml_chunk("", $item);
+ }
+ return $yaml;
+}
+
+sub Load {
+ shift if ($_[0] eq __PACKAGE__ || ref($_[0]) eq __PACKAGE__);
+ die "not yet implemented";
+}
+
+# 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"};
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Module::Build::YAML - Provides just enough YAML support so that Module::Build works even if YAML.pm is not installed
+
+=head1 SYNOPSIS
+
+ use Module::Build::YAML;
+
+ ...
+
+=head1 DESCRIPTION
+
+Provides just enough YAML support so that Module::Build works even if YAML.pm is not installed.
+
+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.
+
+=head1 AUTHOR
+
+Stephen Adkins <[email protected]>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2006. Stephen Adkins. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
+
diff --git a/it/check_jmx4perl/base.cfg b/it/check_jmx4perl/base.cfg
new file mode 100644
index 0000000..87a3d12
--- /dev/null
+++ b/it/check_jmx4perl/base.cfg
@@ -0,0 +1,19 @@
+# ================================================
+# Base definitions:
+
+# Check for relative memory checks
+<Check base_memory_relative>
+ Use = base_relative_threshold($0,$1)
+ Use = base_relative_label
+ Label = (base) $BASE
+ Unit = B
+</Check>
+
+<Check base_relative_threshold>
+ Critical = ${0:90}
+ Warning = ${1:80}
+</Check>
+
+<Check base_relative_label>
+ Label = (grandpa) %.2r% used (%.2v %u / %.2b %w)
+</Check> \ No newline at end of file
diff --git a/it/check_jmx4perl/base.pl b/it/check_jmx4perl/base.pl
new file mode 100644
index 0000000..17d1717
--- /dev/null
+++ b/it/check_jmx4perl/base.pl
@@ -0,0 +1,50 @@
+# Base functions for various check_jmx4perl checks
+
+use strict;
+use FindBin;
+use JMX::Jmx4Perl::Alias;
+use JMX::Jmx4Perl::Request;
+use JMX::Jmx4Perl::Response;
+
+sub exec_check_perl4jmx {
+ my @args;
+ for (@_) {
+ push @args,split;
+ }
+ my ($url,$user,$password,$product,$target,$target_user,$target_password) =
+ @ENV{"JMX4PERL_GATEWAY","JMX4PERL_USER",
+ "JMX4PERL_PASSWORD","JMX4PERL_PRODUCT","JMX4PERL_TARGET_URL","JMX4PERL_TARGET_USER","JMX4PERL_TARGET_PASSWORD"};
+ push @args,("--user",$user,"--password",$password) if $user;
+ push @args,("--product",$product) if $product;
+ push @args,("--url",$url);
+ push @args,("--target",$target) if $target;
+ push @args,("--target-user",$target_user,"--target-password",$target_password) if $target_user;
+ #push @args,"--legacy-escape";
+ #push @args,("--verbose");
+
+ my $cmd = "perl $FindBin::Bin/../../scripts/check_jmx4perl "
+ .join(" ",map { '"' . $_ . '"' } @args);
+ #print $cmd,"\n";
+ open (F,"$cmd 2>&1 |")
+ || die "Cannot open check_jmx4perl: $!";
+ my $content = join "",<F>;
+ close F;
+
+ if ($? == -1) {
+ die "check_jmx4perl: failed to execute: $!\n";
+ }
+ elsif ($? & 127) {
+ die "check_jmx4perl child died with signal %d, %s coredump\n",
+ ($? & 127), ($? & 128) ? 'with' : 'without';
+ }
+ return ($? >> 8,$content);
+}
+
+sub reset_history {
+ my $jmx = shift;
+ my ($mbean,$operation) = $jmx->resolve_alias(JMX4PERL_HISTORY_RESET);
+ my $req = new JMX::Jmx4Perl::Request(EXEC,$mbean,$operation,{target => undef});
+ my $resp = $jmx->request($req);
+}
+
+1;
diff --git a/it/check_jmx4perl/checks.cfg b/it/check_jmx4perl/checks.cfg
new file mode 100644
index 0000000..59cfc57
--- /dev/null
+++ b/it/check_jmx4perl/checks.cfg
@@ -0,0 +1,195 @@
+
+# Include base configuration
+include base.cfg
+
+# ==================================================================
+# Various parameterized checks
+<Check outer_arg>
+ Use = memory_heap
+ Critical = 90
+
+ Label = $0 $BASE (Warning: %.2y, Critical: %.2z)
+</Check>
+
+# ==================================================================
+# Predefined Checks
+
+# Heap Memory
+<Check memory_heap>
+ Use = base_memory_relative
+ Value = java.lang:type=Memory/HeapMemoryUsage/used
+ Base = java.lang:type=Memory/HeapMemoryUsage/max
+ Name = Heap Memory ${0:default_name}
+ Label = Heap-Memory: $BASE
+</Check>
+
+<Check memory_heap2>
+ Use = base_memory_relative
+ MBean = java.lang:type=Memory
+ Attribute = HeapMemoryUsage
+ Path = used
+ BaseMBean = java.lang:type=Memory
+ BaseAttribute = HeapMemoryUsage
+ BasePath = max
+ Name = Heap Memory ${0:default_name}
+ Label = Heap-Memory: $BASE
+</Check>
+
+<Check memory_heap_with_label>
+ Value = java.lang:type=Memory/HeapMemoryUsage/used
+ Name = $1
+ Label = $0
+ Critical = 1:
+</Check>
+
+# Perm Gen Memory (used for class definitions)
+<Check memory_non_heap>
+ Use = base_memory_relative($0,$1)
+ Value = java.lang:type=Memory/NonHeapMemoryUsage/used
+ Base = java.lang:type=Memory/HeapMemoryUsage/max
+ Label = NonHeap Memory: $BASE
+</Check>
+
+# ===============================================
+# Thread count
+<Check thread_count>
+ Value = java.lang:type=Threading/ThreadCount
+ Name = ${0} $1 $2
+ Label = "thread_count: $0 $1 $2 : Value %f in range"
+ Critical = ${0}
+ Warning = $1
+ Method = POST
+</Check>
+
+<Check invalid_method>
+ Value = java.lang:type=Threading/ThreadCount
+ Name = $0 $1 $2
+ Critical = $0
+ Warning = $1
+ Method = Bla
+</Check>
+
+# Child
+<Check def_placeholder_1>
+ Use thread_count(,2)
+</Check>
+
+<Check def_placeholder_2>
+ Use thread_count(${0},2)
+</Check>
+
+<Check def_placeholder_3>
+ Use thread_count
+</Check>
+
+# =========================================================
+# Operation checks
+
+<Check overloaded_operation>
+ MBean = jolokia.it:type=operation
+ Operation = overloadedMethod(java.lang.String)
+ Argument = ${0}
+ Critical = 5
+ Warning = :1
+</Check>
+
+# =========================================================
+# Bug specific checks
+
+# MBean with '#'
+
+<Check hash_check>
+ MBean = jolokia/it:pid=[ServiceRegistryProvider\#(null)],type=ParticipantMonitor,id=*
+ Attribute = Ok
+ String = 1
+ Label = ServiceRegistryProvider is running
+ Name = Running
+ Critic