<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package Module::Build::Tiny;
$Module::Build::Tiny::VERSION = '0.051';
use strict;
use warnings;
use Exporter 5.57 'import';
our @EXPORT = qw/Build Build_PL/;

use CPAN::Meta;
use ExtUtils::Config 0.003;
use ExtUtils::Helpers 0.020 qw/make_executable split_like_shell man1_pagename man3_pagename detildefy/;
use ExtUtils::Install qw/pm_to_blib install/;
use ExtUtils::InstallPaths 0.002;
use File::Basename qw/basename dirname/;
use File::Find ();
use File::Path qw/mkpath rmtree/;
use File::Spec::Functions qw/catfile catdir rel2abs abs2rel splitdir curdir/;
use Getopt::Long 2.36 qw/GetOptionsFromArray/;
use JSON::PP 2 qw/encode_json decode_json/;

sub write_file {
	my ($filename, $content) = @_;
	open my $fh, '&gt;', $filename or die "Could not open $filename: $!\n";
	print $fh $content;
}
sub read_file {
	my ($filename) = @_;
	open my $fh, '&lt;', $filename or die "Could not open $filename: $!\n";
	return do { local $/; &lt;$fh&gt; };
}

sub get_meta {
	my ($metafile) = grep { -e $_ } qw/META.json META.yml/ or die "No META information provided\n";
	return CPAN::Meta-&gt;load_file($metafile);
}

sub manify {
	my ($input_file, $output_file, $section, $opts) = @_;
	return if -e $output_file &amp;&amp; -M $input_file &lt;= -M $output_file;
	my $dirname = dirname($output_file);
	mkpath($dirname, $opts-&gt;{verbose}) if not -d $dirname;
	require Pod::Man;
	Pod::Man-&gt;new(section =&gt; $section)-&gt;parse_from_file($input_file, $output_file);
	print "Manifying $output_file\n" if $opts-&gt;{verbose} &amp;&amp; $opts-&gt;{verbose} &gt; 0;
	return;
}

sub process_xs {
	my ($source, $options, $c_files) = @_;

	die "Can't build xs files under --pureperl-only\n" if $options-&gt;{'pureperl-only'};
	my (undef, @parts) = splitdir(dirname($source));
	push @parts, my $file_base = basename($source, '.xs');
	my $archdir = catdir(qw/blib arch auto/, @parts);
	my $tempdir = 'temp';

	my $c_file = catfile($tempdir, "$file_base.c");
	require ExtUtils::ParseXS;
	mkpath($tempdir, $options-&gt;{verbose}, oct '755');
	ExtUtils::ParseXS::process_file(filename =&gt; $source, prototypes =&gt; 0, output =&gt; $c_file);

	my $version = $options-&gt;{meta}-&gt;version;
	require ExtUtils::CBuilder;
	my $builder = ExtUtils::CBuilder-&gt;new(config =&gt; $options-&gt;{config}-&gt;values_set);
	my @objects = $builder-&gt;compile(source =&gt; $c_file, defines =&gt; { VERSION =&gt; qq/"$version"/, XS_VERSION =&gt; qq/"$version"/ }, include_dirs =&gt; [ curdir, 'include', 'src', dirname($source) ]);

	my $o = $options-&gt;{config}-&gt;get('_o');
	for my $c_source (@{ $c_files }) {
		my $o_file = catfile($tempdir, basename($c_source, '.c') . $o);
		push @objects, $builder-&gt;compile(source =&gt; $c_source, include_dirs =&gt; [ curdir, 'include', 'src', dirname($c_source) ])
	}

	require DynaLoader;
	my $mod2fname = defined &amp;DynaLoader::mod2fname ? \&amp;DynaLoader::mod2fname : sub { return $_[0][-1] };

	mkpath($archdir, $options-&gt;{verbose}, oct '755') unless -d $archdir;
	my $lib_file = catfile($archdir, $mod2fname-&gt;(\@parts) . '.' . $options-&gt;{config}-&gt;get('dlext'));
	return $builder-&gt;link(objects =&gt; \@objects, lib_file =&gt; $lib_file, module_name =&gt; join '::', @parts);
}

sub find {
	my ($pattern, $dir) = @_;
	my @ret;
	File::Find::find(sub { push @ret, $File::Find::name if /$pattern/ &amp;&amp; -f }, $dir) if -d $dir;
	return @ret;
}

sub contains_pod {
	my ($file) = @_;
	return unless -T $file;
	return read_file($file) =~ /^\=(?:head|pod|item)/m;
}

my %actions = (
	build =&gt; sub {
		my %opt = @_;
		for my $pl_file (find(qr/\.PL$/, 'lib')) {
			(my $pm = $pl_file) =~ s/\.PL$//;
			system $^X, $pl_file, $pm and die "$pl_file returned $?\n";
		}
		my %modules = map { $_ =&gt; catfile('blib', $_) } find(qr/\.pm$/, 'lib');
		my %docs    = map { $_ =&gt; catfile('blib', $_) } find(qr/\.pod$/, 'lib');
		my %scripts = map { $_ =&gt; catfile('blib', $_) } find(qr/(?:)/, 'script');
		my %sdocs   = map { $_ =&gt; delete $scripts{$_} } grep { /.pod$/ } keys %scripts;
		my %dist_shared    = map { $_ =&gt; catfile(qw/blib lib auto share dist/, $opt{meta}-&gt;name, abs2rel($_, 'share')) } find(qr/(?:)/, 'share');
		my %module_shared  = map { $_ =&gt; catfile(qw/blib lib auto share module/, abs2rel($_, 'module-share')) } find(qr/(?:)/, 'module-share');
		pm_to_blib({ %modules, %docs, %scripts, %dist_shared, %module_shared }, catdir(qw/blib lib auto/));
		make_executable($_) for values %scripts;
		mkpath(catdir(qw/blib arch/), $opt{verbose});
		my $main_xs = join('/', 'lib', split /-/, $opt{meta}-&gt;name) . '.xs';
		for my $xs (find(qr/.xs$/, 'lib')) {
			my @c_files = $xs eq $main_xs ? find(qr/\.c$/, 'src') : ();
			process_xs($xs, \%opt, \@c_files);
		}

		if ($opt{install_paths}-&gt;install_destination('bindoc') &amp;&amp; $opt{install_paths}-&gt;is_default_installable('bindoc')) {
			my $section = $opt{config}-&gt;get('man1ext');
			for my $input (keys %scripts, keys %sdocs) {
				next unless contains_pod($input);
				my $output = catfile('blib', 'bindoc', man1_pagename($input));
				manify($input, $output, $section, \%opt);
			}
		}
		if ($opt{install_paths}-&gt;install_destination('libdoc') &amp;&amp; $opt{install_paths}-&gt;is_default_installable('libdoc')) {
			my $section = $opt{config}-&gt;get('man3ext');
			for my $input (keys %modules, keys %docs) {
				next unless contains_pod($input);
				my $output = catfile('blib', 'libdoc', man3_pagename($input));
				manify($input, $output, $section, \%opt);
			}
		}
		return 0;
	},
	test =&gt; sub {
		my %opt = @_;
		die "Must run `./Build build` first\n" if not -d 'blib';
		require TAP::Harness::Env;
		my %test_args = (
			(verbosity =&gt; $opt{verbose}) x!! exists $opt{verbose},
			(jobs =&gt; $opt{jobs}) x!! exists $opt{jobs},
			(color =&gt; 1) x !!-t STDOUT,
			lib =&gt; [ map { rel2abs(catdir(qw/blib/, $_)) } qw/arch lib/ ],
		);
		my $tester = TAP::Harness::Env-&gt;create(\%test_args);
		return $tester-&gt;runtests(sort +find(qr/\.t$/, 't'))-&gt;has_errors;
	},
	install =&gt; sub {
		my %opt = @_;
		die "Must run `./Build build` first\n" if not -d 'blib';
		install($opt{install_paths}-&gt;install_map, @opt{qw/verbose dry_run uninst/});
		return 0;
	},
	clean =&gt; sub {
		my %opt = @_;
		rmtree($_, $opt{verbose}) for qw/blib temp/;
		return 0;
	},
	realclean =&gt; sub {
		my %opt = @_;
		rmtree($_, $opt{verbose}) for qw/blib temp Build _build_params MYMETA.yml MYMETA.json/;
		return 0;
	},
);

sub get_arguments {
	my @sources = @_;
	my %opt;
	GetOptionsFromArray($_, \%opt, qw/install_base=s install_path=s% installdirs=s destdir=s prefix=s config=s% uninst:1 verbose:1 dry_run:1 pureperl-only:1 create_packlist=i jobs=i/) for (@sources);
	$_ = detildefy($_) for grep { defined } @opt{qw/install_base destdir prefix/}, values %{ $opt{install_path} };
	$opt{config} = ExtUtils::Config-&gt;new($opt{config});
	$opt{meta} = get_meta();
	$opt{install_paths} = ExtUtils::InstallPaths-&gt;new(%opt, dist_name =&gt; $opt{meta}-&gt;name);
	return %opt;
}

sub Build {
	my $action = @ARGV &amp;&amp; $ARGV[0] =~ /\A\w+\z/ ? shift @ARGV : 'build';
	die "No such action '$action'\n" if not $actions{$action};
	my($env, $bargv) = @{ decode_json(read_file('_build_params')) };
	my %opt = get_arguments($env, $bargv, \@ARGV);
	exit $actions{$action}-&gt;(%opt);
}

sub Build_PL {
	my $meta = get_meta();
	printf "Creating new 'Build' script for '%s' version '%s'\n", $meta-&gt;name, $meta-&gt;version;
	my $dir = $meta-&gt;name eq 'Module-Build-Tiny' ? "use lib 'lib';" : '';
	write_file('Build', "#!perl\n$dir\nuse Module::Build::Tiny;\nBuild();\n");
	make_executable('Build');
	my @env = defined $ENV{PERL_MB_OPT} ? split_like_shell($ENV{PERL_MB_OPT}) : ();
	write_file('_build_params', encode_json([ \@env, \@ARGV ]));
	if (my $dynamic = $meta-&gt;custom('x_dynamic_prereqs')) {
		my %meta = (%{ $meta-&gt;as_struct }, dynamic_config =&gt; 0);
		my %opt = get_arguments(\@env, \@ARGV);
		require CPAN::Requirements::Dynamic;
		my $dynamic_parser = CPAN::Requirements::Dynamic-&gt;new(%opt);
		my $prereq = $dynamic_parser-&gt;evaluate($dynamic);
		$meta{prereqs} = $meta-&gt;effective_prereqs-&gt;with_merged_prereqs($prereq)-&gt;as_string_hash;
		$meta = CPAN::Meta-&gt;new(\%meta);
	}
	$meta-&gt;save(@$_) for ['MYMETA.json'], [ 'MYMETA.yml' =&gt; { version =&gt; 1.4 } ];
}

1;

#ABSTRACT: A tiny replacement for Module::Build


# vi:noet:sts=4:sw=4:ts=4

__END__

=pod

=encoding UTF-8

=head1 NAME

Module::Build::Tiny - A tiny replacement for Module::Build

=head1 VERSION

version 0.051

=head1 SYNOPSIS

 use Module::Build::Tiny;
 Build_PL();

=head1 DESCRIPTION

Many Perl distributions use a Build.PL file instead of a Makefile.PL file
to drive distribution configuration, build, test and installation.
Traditionally, Build.PL uses Module::Build as the underlying build system.
This module provides a simple, lightweight, drop-in replacement.

Whereas Module::Build has over 6,700 lines of code; this module has less
than 200, yet supports the features needed by most distributions.

=head2 Supported

=over 4

=item * Pure Perl distributions

=item * Building XS or C

=item * Recursive test files

=item * MYMETA

=item * Man page generation

=item * Generated code from PL files

=item * Module sharedirs

=item * Dynamic prerequisites

=back

=head2 Not Supported

=over 4

=item * HTML documentation generation

=item * Extending Module::Build::Tiny

=back

=head2 Directory structure

Your .pm, .xs and .pod files must be in F&lt;lib/&gt;.  Any executables must be in
F&lt;script/&gt;.  Test files must be in F&lt;t/&gt;. Dist sharedirs must be in F&lt;share/&gt;,
module sharedirs are under F&lt;module-share&gt; (e.g. F&lt;module-share/Foo-Bar&gt; for
module C&lt;Foo::Bar&gt;).

C&lt;.c&gt; files in the F&lt;src/&gt; are compiled together with the .xs file matching the
distribution name.

=head1 USAGE

These all work pretty much like their Module::Build equivalents.

=head2 perl Build.PL

=head2 Build [ build ] 

=head2 Build test

=head2 Build install

This supports the following options:

=over

=item * verbose

=item * install_base

=item * installdirs

=item * prefix

=item * install_path

=item * destdir

=item * uninst

=item * config

=item * pure-perl

=item * create_packlist

=back

=head1 AUTHORING

This module doesn't support authoring. To develop modules using Module::Build::Tiny, usage of L&lt;Dist::Zilla::Plugin::ModuleBuildTiny&gt; or L&lt;App::ModuleBuildTiny&gt; is recommended.

=head1 CONFIG FILE AND ENVIRONMENT

Options can be provided in the C&lt;PERL_MB_OPT&gt; environment variable the same way they can with Module::Build. This should be done during the configuration stage.

=head2 Incompatibilities

=over 4

=item * Argument parsing

Module::Build has an extremely permissive way of argument handling, Module::Build::Tiny only supports a (sane) subset of that. In particular, C&lt;./Build destdir=/foo&gt; does not work, you will need to pass it as C&lt;./Build --destdir=/foo&gt;.

=item * .modulebuildrc

Module::Build::Tiny does not support .modulebuildrc files. In particular, this means that versions of local::lib older than 1.006008 may break with C&lt;ERROR: Can't create /usr/local/somepath&gt;. If the output of C&lt;perl -Mlocal::lib&gt; contains C&lt;MODULEBUILDRC&gt; but not C&lt;PERL_MB_OPT &gt;, you will need to upgrade it to resolve this issue.

=back

=head1 SEE ALSO

L&lt;Module::Build&gt;

=head1 AUTHORS

=over 4

=item *

Leon Timmermans &lt;leont@cpan.org&gt;

=item *

David Golden &lt;dagolden@cpan.org&gt;

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Leon Timmermans, David Golden.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
</pre></body></html>