File Coverage

blib/lib/Perl/Dist/Inno.pm
Criterion Covered Total %
statement 79 81 97.5
branch n/a
condition n/a
subroutine 27 27 100.0
pod n/a
total 106 108 98.1


line stmt bran cond sub pod time code
1             package Perl::Dist::Inno;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Perl::Dist::Inno - 3rd Generation Distribution Builder using Inno Setup
8              
9             =head1 SYNOPSIS
10              
11             Creating a custom distribution
12              
13             package My::Perl::Dist;
14            
15             use strict;
16             use base 'Perl::Dist::Strawberry';
17            
18             1;
19              
20             Building that distribution...
21              
22             > perldist --cpan "file://c|/minicpan/" Strawberry
23              
24             =head1 DESCRIPTION
25              
26             B is a Win32 Perl distribution builder that targets
27             the Inno Setup 5 installer creation program.
28              
29             It provides a rich set of functionality that allows a distribution
30             developer to specify either Perl 5.8.8 or Perl 5.10.0, specify
31             additional C libraries and CPAN modules to be installed, and then
32             set start menu entries to websites and programs as needed.
33              
34             A distribution directory and a matching .iss script is
35             generated, which is then handed off to Inno Setup 5 to create the
36             final distribution .exe installer.
37              
38             Alternatively, B can generate a .zip file for
39             the distribution without the installer.
40              
41             Because the API for B is extremely rich and fairly
42             complex (and a moving target) the documentation is unfortunately
43             a bit less complete than it should be.
44              
45             As parts of the API solidify I hope to document them better.
46              
47             =head2 API Structure
48              
49             The L API is separated into 2 layers, and a series
50             of objects.
51              
52             L provides the direct mapping to the Inno
53             Setup 5 .iss script, and has no logical understand of Perl Distribution.
54              
55             It stores the values that will ultimately be written into the .iss
56             files as attributes, and contains a series of collections of
57             L, L and
58             L>Perl::Dist::Inno::Icon> objects, which map directly to entries
59             in the .iss script's [Files], [Icons] and [Registry] sections.
60              
61             To the extent that it does interact with actual distributions, it is
62             only to the extent of validating some directories exist, and
63             triggering the actual execution of the Inno Setup 5 compiler.
64              
65             B (this class) is a sub-class of
66             L and represents the layer at which
67             the understanding of the Perl distribution itself is implemented.
68              
69             L and its various subclasses provides the internal
70             representation of the logical elements of a Perl distribution.
71              
72             These assets are mostly transient and are destroyed once the asset
73             has been added to the distribution (this may change).
74              
75             In the process of adding the asset to the distribution, various
76             files may be created and objects added to the script object that
77             will result in .iss keys being created where the installer builder
78             needs to know about that asset explicitly.
79              
80             L itself provides both many levels of abstraction
81             with sensible default implementations of high level concept methods,
82             as well as multiple levels of submethods.
83              
84             Strong separation of concerns in this manner allows people creating
85             distribution sub-classes to add hooks to the build process in many
86             places, for maximum customisability.
87              
88             The main Perl::Dist::Inno B method implements the basic flow
89             for the creation of a Perl distribution. The order is rougly as
90             follows:
91              
92             =over 4
93              
94             =item 1. Install a C toolchain
95              
96             =item 2. Install additional C libraries
97              
98             =item 3. Install Perl itself
99              
100             =item 4. Install/Upgrade the CPAN toolchain
101              
102             =item 5. Install additional CPAN modules
103              
104             =item 6. Optionally install Portability support
105              
106             =item 7. Install Win32-specific things such as start menu entries
107              
108             =item 8. Remove any files we don't need in the final distribution
109              
110             =item 9. Generate the zip, exe or msi files.
111              
112             =back
113              
114             =head2 Creating Your Own Distribution
115              
116             Rather than building directly on top of Perl::Dist::Inno, it is probably
117             better to build on top of a particular distribution, probably Strawberry.
118              
119             For more information, see the L documentation
120             which details how to sub-class the distribution.
121              
122             =head1 METHODS
123              
124             =cut
125              
126 1     1   22 use 5.006;
  1         3  
  1         35  
127 1     1   5 use strict;
  1         1  
  1         23  
128 1     1   5 use warnings;
  1         1  
  1         27  
129 1     1   5 use Carp ();
  1         2  
  1         18  
130 1     1   1150 use Archive::Tar 1.42 ();
  1         168044  
  1         32  
131 1     1   1213 use Archive::Zip 1.26 ();
  1         52122  
  1         30  
132 1     1   9 use File::Temp 0.21 ();
  1         25  
  1         19  
133 1     1   6 use File::Spec 3.29 ();
  1         21  
  1         17  
134 1     1   5 use File::Spec::Unix ();
  1         2  
  1         16  
135 1     1   8560 use File::Spec::Win32 ();
  1         3469  
  1         63  
136 1     1   17 use File::Copy ();
  1         4  
  1         106  
137 1     1   26945 use File::Copy::Recursive 0.38 ();
  1         5252  
  1         38  
138 1     1   13 use File::Path 2.07 ();
  1         26  
  1         27  
139 1     1   1315 use File::PathList 1.04 ();
  1         4371  
  1         28  
140 1     1   1387 use File::pushd 1.00 ();
  1         2451  
  1         32  
141 1     1   1440 use File::Remove 1.42 ();
  1         1872  
  1         28  
142 1     1   865 use File::HomeDir 0.82 ();
  1         6941  
  1         25  
143 1     1   8 use File::Basename ();
  1         3  
  1         19  
144 1     1   873 use File::ShareDir 1.00 ();
  1         6239  
  1         33  
145 1     1   3082 use File::Find::Rule 0.30 ();
  1         8621  
  1         65  
146 1     1   10 use IPC::Run3 0.042 ();
  1         43  
  1         88  
147 1     1   1085 use YAML::Tiny 1.36 ();
  1         6427  
  1         26  
148 1     1   961 use IO::Capture 0.05 ();
  1         881  
  1         24  
149 1     1   6 use Params::Util 0.35 ();
  1         21  
  1         20  
150 1     1   885 use HTTP::Status 5.817 ();
  1         3303  
  1         30  
151 1     1   934 use LWP::UserAgent 5.823 ();
  1         37401  
  1         27  
152 1     1   463 use LWP::UserAgent::WithCache 0.06 ();
  0            
  0            
153             use LWP::Online 1.07 ();
154             use Module::CoreList 2.17 ();
155             use Template 2.20 ();
156             use PAR::Dist 0.42 ();
157             use Portable::Dist 0.02 ();
158             use Storable 2.17 ();
159             use URI::file 1.37 ();
160             use Probe::Perl 0.01 ();
161             use Process 0.25 ();
162             use Process::Storable 0.25 ();
163             use Process::Delegatable 0.25 ();
164             use Perl::Dist::Asset ();
165             use Perl::Dist::Asset::Binary ();
166             use Perl::Dist::Asset::Library ();
167             use Perl::Dist::Asset::Perl ();
168             use Perl::Dist::Asset::Distribution ();
169             use Perl::Dist::Asset::Module ();
170             use Perl::Dist::Asset::PAR ();
171             use Perl::Dist::Asset::File ();
172             use Perl::Dist::Asset::Website ();
173             use Perl::Dist::Asset::Launcher ();
174             use Perl::Dist::Inno::Script ();
175             use Perl::Dist::Util::Toolchain ();
176              
177             use vars qw{$VERSION @ISA};
178             BEGIN {
179             $VERSION = '1.16';
180             @ISA = 'Perl::Dist::Inno::Script';
181             }
182              
183             use Object::Tiny 1.06 qw{
184             perl_version
185             portable
186             archlib
187             exe
188             zip
189             binary_root
190             offline
191             temp_dir
192             download_dir
193             image_dir
194             modules_dir
195             license_dir
196             build_dir
197             checkpoint_dir
198             iss_file
199             bin_perl
200             bin_make
201             bin_pexports
202             bin_dlltool
203             env_path
204             debug_stdout
205             debug_stderr
206             output_file
207             perl_version_corelist
208             cpan
209             force
210             checkpoint_before
211             checkpoint_after
212             };
213              
214              
215              
216              
217              
218             #####################################################################
219             # Upstream Binary Packages (Mirrored)
220              
221             my %PACKAGES = (
222             'dmake' => 'dmake-4.8-20070327-SHAY.zip',
223             'gcc-core' => 'gcc-core-3.4.5-20060117-3.tar.gz',
224             'gcc-g++' => 'gcc-g++-3.4.5-20060117-3.tar.gz',
225             'mingw-make' => 'mingw32-make-3.81-2.tar.gz',
226             'binutils' => 'binutils-2.17.50-20060824-1.tar.gz',
227             'mingw-runtime' => 'mingw-runtime-3.13.tar.gz',
228             'w32api' => 'w32api-3.10.tar.gz',
229             'libiconv-dep' => 'libiconv-1.9.2-1-dep.zip',
230             'libiconv-lib' => 'libiconv-1.9.2-1-lib.zip',
231             'libiconv-bin' => 'libiconv-1.9.2-1-bin.zip',
232             'expat' => 'expat-2.0.1-vanilla.zip',
233             'gmp' => 'gmp-4.2.1-vanilla.zip',
234             );
235              
236             sub binary_file {
237             unless ( $PACKAGES{$_[1]} ) {
238             Carp::croak("Unknown package '$_[1]'");
239             }
240             return $PACKAGES{$_[1]};
241             }
242              
243             sub binary_url {
244             my $self = shift;
245             my $file = shift;
246             unless ( $file =~ /\.(zip|gz|tgz)$/i ) {
247             # Shorthand, map to full file name
248             $file = $self->binary_file($file, @_);
249             }
250             return $self->binary_root . '/' . $file;
251             }
252              
253              
254              
255              
256              
257             #####################################################################
258             # Constructor
259              
260             =pod
261              
262             =head2 new
263              
264             The B method creates a new Perl Distribution build as an object.
265              
266             Each object is used to create a single distribution, and then should be
267             discarded.
268              
269             Although there are about 30 potential constructor arguments that can be
270             provided, most of them are automatically resolved and exist for overloading
271             puposes only, or they revert to sensible default and generally never need
272             to be modified.
273              
274             The following is an example of the most likely attributes that will be
275             specified.
276              
277             my $build = Perl::Dist::Inno->new(
278             image_dir => 'C:\vanilla',
279             temp_dir => 'C:\tmp\vp',
280             cpan => 'file://C|/minicpan/',
281             );
282              
283             =over 4
284              
285             =item image_dir
286              
287             Perl::Dist::Inno distributions can only be installed to fixed paths.
288              
289             To facilitate a correctly working CPAN setup, the files that will
290             ultimately end up in the installer must also be assembled under the
291             same path on the author's machine.
292              
293             The C method specifies the location of the Perl install,
294             both on the author's and end-user's host.
295              
296             Please note that this directory will be automatically deleted if it
297             already exists at object creation time. Trying to build a Perl
298             distribution on the SAME distribution can thus have devestating
299             results.
300              
301             =item temp_dir
302              
303             B needs a series of temporary directories while
304             it is running the build, including places to cache downloaded files,
305             somewhere to expand tarballs to build things, and somewhere to put
306             debugging output and the final installer zip and exe files.
307              
308             The C param specifies the root path for where these
309             temporary directories should be created.
310              
311             For convenience it is best to make these short paths with simple
312             names, near the root.
313              
314             =item cpan
315              
316             The C param provides a path to a CPAN or minicpan mirror that
317             the installer can use to fetch any needed files during the build
318             process.
319              
320             The param should be a L object to the root of the CPAN repository,
321             including trailing newline.
322              
323             If you are online and no C param is provided, the value will
324             default to the L repository as a
325             convenience.
326              
327             =item portable
328              
329             The optional boolean C param is used to indicate that the
330             distribution is intended for installation on a portable storable
331             device.
332              
333             =item exe
334              
335             The optional boolean C param is used to indicate that a zip
336             distribution package should be created.
337              
338             =item zip
339              
340             The optional boolean C param is used to indicate that an
341             InnoSetup executable installer should be created.
342              
343             =back
344              
345             The C constructor returns a B object, which you
346             should then call C on to generate the distribution.
347              
348             =cut
349              
350             sub new {
351             my $class = shift;
352             my %params = @_;
353              
354             # Apply some defaults
355             unless ( defined $params{binary_root} ) {
356             $params{binary_root} = 'http://strawberryperl.com/package';
357             }
358             if ( defined $params{image_dir} and ! defined $params{default_dir_name} ) {
359             $params{default_dir_name} = $params{image_dir};
360             }
361             unless ( defined $params{temp_dir} ) {
362             $params{temp_dir} = File::Spec->catdir(
363             File::Spec->tmpdir, 'perldist',
364             );
365             }
366             unless ( defined $params{download_dir} ) {
367             $params{download_dir} = File::Spec->catdir(
368             $params{temp_dir}, 'download',
369             );
370             File::Path::mkpath($params{download_dir});
371             }
372             unless ( defined $params{build_dir} ) {
373             $params{build_dir} = File::Spec->catdir(
374             $params{temp_dir}, 'build',
375             );
376             $class->remake_path( $params{build_dir} );
377             }
378             unless ( defined $params{output_dir} ) {
379             $params{output_dir} = File::Spec->catdir(
380             $params{temp_dir}, 'output',
381             );
382             $class->remake_path( $params{output_dir} );
383             }
384             if ( defined $params{image_dir} ) {
385             $class->remake_path( $params{image_dir} );
386             }
387             unless ( defined $params{perl_version} ) {
388             $params{perl_version} = '5100';
389             }
390              
391             # Hand off to the parent class
392             my $self = $class->SUPER::new(%params);
393              
394             # Check the version of Perl to build
395             unless ( $self->perl_version_literal ) {
396             Carp::croak "Failed to resolve perl_version_literal";
397             }
398             unless ( $self->perl_version_human ) {
399             Carp::croak "Failed to resolve perl_version_human";
400             }
401             unless ( $self->can('install_perl_' . $self->perl_version) ) {
402             Carp::croak("$class does not support Perl " . $self->perl_version);
403             }
404              
405             # Find the core list
406             my $corelist_version = $self->perl_version_literal+0;
407             $self->{perl_version_corelist} = $Module::CoreList::version{$corelist_version};
408             unless ( Params::Util::_HASH($self->{perl_version_corelist}) ) {
409             Carp::croak("Failed to resolve Module::CoreList hash for " . $self->perl_version_human);
410             }
411              
412             # Apply more defaults
413             unless ( defined $self->{force} ) {
414             $self->{force} = 0;
415             }
416             unless ( defined $self->{trace} ) {
417             $self->{trace} = 1;
418             }
419             unless ( defined $self->debug_stdout ) {
420             $self->{debug_stdout} = File::Spec->catfile(
421             $self->output_dir,
422             'debug.out',
423             );
424             }
425             unless ( defined $self->debug_stderr ) {
426             $self->{debug_stderr} = File::Spec->catfile(
427             $self->output_dir,
428             'debug.err',
429             );
430             }
431              
432             # Auto-detect online-ness if needed
433             unless ( defined $self->offline ) {
434             $self->{offline} = LWP::Online::offline();
435             }
436             unless ( defined $self->exe ) {
437             $self->{exe} = 1;
438             }
439             unless ( defined $self->zip ) {
440             $self->{zip} = $self->portable ? 1 : 0;
441             }
442             unless ( defined $self->checkpoint_before ) {
443             $self->{checkpoint_before} = 0;
444             }
445             unless ( defined $self->checkpoint_after ) {
446             $self->{checkpoint_after} = 0;
447             }
448              
449             # Normalize some params
450             $self->{offline} = !! $self->offline;
451             $self->{trace} = !! $self->{trace};
452             $self->{force} = !! $self->force;
453             $self->{portable} = !! $self->portable;
454             $self->{exe} = !! $self->exe;
455             $self->{zip} = !! $self->zip;
456             $self->{archlib} = !! $self->archlib;
457              
458             # Handle portable special cases
459             if ( $self->portable ) {
460             $self->{exe} = 0;
461             }
462              
463             # If we are online and don't have a cpan repository,
464             # use cpan.strawberryperl.com as a default.
465             if ( ! $self->offline and ! $self->cpan ) {
466             $self->{cpan} = URI->new('http://cpan.strawberryperl.com/');
467             }
468              
469             # Check params
470             unless ( Params::Util::_STRING($self->download_dir) ) {
471             Carp::croak("Missing or invalid download_dir param");
472             }
473             unless ( defined $self->modules_dir ) {
474             $self->{modules_dir} = File::Spec->catdir( $self->download_dir, 'modules' );
475             }
476             unless ( Params::Util::_STRING($self->modules_dir) ) {
477             Carp::croak("Invalid modules_dir param");
478             }
479             unless ( Params::Util::_STRING($self->image_dir) ) {
480             Carp::croak("Missing or invalid image_dir param");
481             }
482             if ( $self->image_dir =~ /\s/ ) {
483             Carp::croak("Spaces are not allowed in image_dir");
484             }
485             unless ( defined $self->license_dir ) {
486             $self->{license_dir} = File::Spec->catdir( $self->image_dir, 'licenses' );
487             }
488             unless ( Params::Util::_STRING($self->license_dir) ) {
489             Carp::croak("Invalid license_dir param");
490             }
491             unless ( Params::Util::_STRING($self->build_dir) ) {
492             Carp::croak("Missing or invalid build_dir param");
493             }
494             if ( $self->build_dir =~ /\s/ ) {
495             Carp::croak("Spaces are not allowed in build_dir");
496             }
497             unless ( Params::Util::_INSTANCE($self->user_agent, 'LWP::UserAgent') ) {
498             Carp::croak("Missing or invalid user_agent param");
499             }
500             unless ( Params::Util::_INSTANCE($self->cpan, 'URI') ) {
501             Carp::croak("Missing or invalid cpan param");
502             }
503             unless ( $self->cpan->as_string =~ /\/$/ ) {
504             Carp::croak("Missing trailing slash in cpan param");
505             }
506             unless ( defined $self->iss_file ) {
507             $self->{iss_file} = File::Spec->catfile(
508             $self->output_dir, $self->app_id . '.iss'
509             );
510             }
511              
512             # Clear the previous build
513             if ( -d $self->image_dir ) {
514             $self->trace("Removing previous " . $self->image_dir . "\n");
515             File::Remove::remove( \1, $self->image_dir );
516             } else {
517             $self->trace("No previous " . $self->image_dir . " found\n");
518             }
519              
520             # Initialize the build
521             for my $d (
522             $self->download_dir,
523             $self->image_dir,
524             $self->modules_dir,
525             $self->license_dir,
526             ) {
527             next if -d $d;
528             File::Path::mkpath($d);
529             }
530              
531             # More details on the tracing
532             if ( $self->{trace} ) {
533             $self->{stdout} = undef;
534             $self->{stderr} = undef;
535             } else {
536             $self->{stdout} = \undef;
537             $self->{stderr} = \undef;
538             }
539              
540             # Inno-Setup Initialization
541             $self->{env_path} = [];
542             $self->add_dir('c');
543             $self->add_dir('perl');
544             $self->add_dir('licenses');
545             $self->add_uninstall;
546              
547             # Set some common environment variables
548             $self->add_env( TERM => 'dumb' );
549             $self->add_env( FTP_PASSIVE => 1 );
550              
551             # Initialize the output valuse
552             $self->{output_file} = [];
553              
554             return $self;
555             }
556              
557             =pod
558              
559             =head2 offline
560              
561             The B module has limited ability to build offline, if all
562             packages have already been downloaded and cached.
563              
564             The connectedness of the Perl::Dist object is checked automatically
565             be default using L. It can be overidden by providing an
566             offline param to the constructor.
567              
568             The C accessor returns true if no connection to "the internet"
569             is available and the object will run in offline mode, or false
570             otherwise.
571              
572             =head2 download_dir
573              
574             The C accessor returns the path to the directory that
575             packages of various types will be downloaded and cached to.
576              
577             An explicit value can be provided via a C param to the
578             constructor. Otherwise the value is derived from C.
579              
580             =head2 image_dir
581              
582             The C accessor returns the path to the built distribution
583             image. That is, the directory in which the build C/Perl code and
584             modules will be installed on the build server.
585              
586             At the present time, this is also the path to which Perl will be
587             installed on the user's machine via the C accessor,
588             which is an alias to the L method
589             C. (although theoretically they can be different,
590             this is likely to break the user's Perl install)
591              
592             =cut
593              
594              
595              
596              
597              
598             #####################################################################
599             # Checkpoint Support
600              
601             sub checkpoint_task {
602             my $self = shift;
603             my $task = shift;
604             my $step = shift;
605              
606             # Are we loading at this step?
607             if ( $self->checkpoint_before == $step ) {
608             $self->checkpoint_load;
609             }
610              
611             # Skip if we are loading later on
612             unless ( $self->checkpoint_before > $step ) {
613             my $t = time;
614             $self->$task();
615             $self->trace("Completed $task in " . (time - $t) . " seconds\n");
616             }
617              
618             # Are we saving at this step
619             if ( $self->checkpoint_after == $step ) {
620             $self->checkpoint_save;
621             }
622              
623             return $self;
624             }
625              
626             sub checkpoint_file {
627             File::Spec->catfile( $_[0]->checkpoint_dir, 'self.dat' );
628             }
629              
630             sub checkpoint_self {
631             die "CODE INCOMPLETE";
632             }
633              
634             sub checkpoint_save {
635             my $self = shift;
636             unless ( $self->temp_dir ) {
637             die "Checkpoints require a temp_dir to be set";
638             }
639              
640             # Clear out any existing checkpoint
641             $self->trace("Removing old checkpoint\n");
642             $self->{checkpoint_dir} = File::Spec->catfile(
643             $self->temp_dir, 'checkpoint',
644             );
645             $self->remake_path( $self->checkpoint_dir );
646              
647             # Copy the paths into the checkpoint directory
648             $self->trace("Copying checkpoint directories...\n");
649             foreach my $dir ( qw{ build_dir download_dir image_dir output_dir } ) {
650             my $from = $self->$dir();
651             my $to = File::Spec->catdir( $self->checkpoint_dir, $dir );
652             $self->_copy( $from => $to );
653             }
654              
655             # Store the main object.
656             # Blank the checkpoint values to prevent load/save loops, and remove
657             # things we can recreate later.
658             my $copy = {
659             %$self,
660             checkpoint_before => 0,
661             checkpoint_after => 0,
662             user_agent => undef,
663             };
664             Storable::nstore( $copy, $self->checkpoint_file );
665              
666             return 1;
667             }
668              
669             sub checkpoint_load {
670             my $self = shift;
671             unless ( $self->temp_dir ) {
672             die "Checkpoints require a temp_dir to be set";
673             }
674              
675             # Does the checkpoint exist
676             $self->trace("Removing old checkpoint\n");
677             $self->{checkpoint_dir} = File::Spec->catfile(
678             $self->temp_dir, 'checkpoint',
679             );
680             unless ( -d $self->checkpoint_dir ) {
681             die "Failed to find checkpoint directory";
682             }
683              
684             # Load the stored hash over our object
685             my $stored = Storable::retrieve( $self->checkpoint_file );
686             %$self = %$stored;
687              
688             # Pull all the directories out of the storage
689             $self->trace("Restoring checkpoint directories...\n");
690             foreach my $dir ( qw{ build_dir download_dir image_dir output_dir } ) {
691             my $from = File::Spec->catdir( $self->checkpoint_dir, $dir );
692             my $to = $self->$dir();
693             File::Remove::remove( $to );
694             $self->_copy( $from => $to );
695             }
696              
697             return 1;
698             }
699              
700              
701              
702              
703              
704             #####################################################################
705             # Perl::Dist::Inno::Script Methods
706              
707             sub source_dir {
708             $_[0]->image_dir;
709             }
710              
711             # Default the versioned name to an unversioned name
712             sub app_ver_name {
713             my $self = shift;
714             if ( $self->{app_ver_name} ) {
715             return $self->{app_ver_name};
716             }
717             return $self->app_name . ' ' . $self->perl_version_human;
718             }
719              
720             # Default the output filename to the id plus the current date
721             sub output_base_filename {
722             my $self = shift;
723             if ( $self->{output_base_filename} ) {
724             return $self->{output_base_filename};
725             }
726             return $self->app_id
727             . '-' . $self->perl_version_human
728             . '-' . $self->output_date_string;
729             }
730              
731              
732              
733              
734              
735             #####################################################################
736             # Perl::Dist::Inno Main Methods
737              
738             =pod
739              
740             =head2 perl_version
741              
742             The C accessor returns the shorthand perl version
743             as a string (consisting of the three-part version with dots
744             removed).
745              
746             Thus Perl 5.8.8 will be "588" and Perl 5.10.0 will return "5100".
747              
748             =head2 perl_version_literal
749              
750             The C method returns the literal numeric Perl
751             version for the distribution.
752              
753             For Perl 5.8.8 this will be '5.008008', Perl 5.8.9 will be '5.008009',
754             and for Perl 5.10.0 this will be '5.010000'.
755              
756             =cut
757              
758             sub perl_version_literal {
759             return {
760             588 => '5.008008',
761             589 => '5.008009',
762             5100 => '5.010000',
763             }->{$_[0]->perl_version} || 0;
764             }
765              
766             =pod
767              
768             =head2 perl_version_human
769              
770             The C method returns the "marketing" form
771             of the Perl version.
772              
773             This will be either '5.8.8', '5.8.9' or '5.10.0'.
774              
775             =cut
776              
777             sub perl_version_human {
778             return {
779             588 => '5.8.8',
780             589 => '5.8.9',
781             5100 => '5.10.0',
782             }->{$_[0]->perl_version} || 0;
783             }
784              
785              
786              
787              
788              
789             #####################################################################
790             # Top Level Process Methods
791              
792             sub prepare { 1 }
793              
794             =pod
795              
796             =head1 run
797              
798             The C method is the main method for the class.
799              
800             It does a complete build of a product, spitting out an installer.
801              
802             Returns true, or throws an exception on error.
803              
804             This method may take an hour or more to run.
805              
806             =cut
807              
808             sub run {
809             my $self = shift;
810             my $start = time;
811              
812             unless ( $self->exe or $self->zip ) {
813             $self->trace("No exe or zip target, nothing to do");
814             return 1;
815             }
816              
817             # Don't buffer
818             $| = 1;
819              
820             # Install the core C toolchain
821             $self->checkpoint_task( install_c_toolchain => 1 );
822              
823             # Install any additional C libraries
824             $self->checkpoint_task( install_c_libraries => 2 );
825              
826             # Install the Perl binary
827             $self->checkpoint_task( install_perl => 3 );
828              
829             # Install additional Perl modules
830             $self->checkpoint_task( install_perl_modules => 4 );
831              
832             # Install the Win32 extras
833             $self->checkpoint_task( install_win32_extras => 5 );
834              
835             # Apply optional portability support
836             $self->checkpoint_task( install_portable => 6 ) if $self->portable;
837              
838             # Remove waste and temporary files
839             $self->checkpoint_task( remove_waste => 7 );
840              
841             # Install any extra custom non-Perl software on top of Perl.
842             # This is primarily added for the benefit of Parrot.
843             $self->checkpoint_task( install_custom => 8 );
844              
845             # Write out the distributions
846             $self->checkpoint_task( write => 9 );
847              
848             # Finished
849             $self->trace(
850             "Distribution generation completed in "
851             . (time - $start)
852             . " seconds\n"
853             );
854             foreach my $file ( @{$self->output_file} ) {
855             $self->trace("Created distribution $file\n");
856             }
857              
858             return 1;
859             }
860              
861             =pod
862              
863             =head2 install_custom
864              
865             The C method is an empty install stub provided
866             to allow sub-classed distributions to add B different
867             additional packages on top of Strawberry Perl.
868              
869             For example, this class is used by the Parrot distribution builder
870             (which needs to sit on a full Strawberry install).
871              
872             Notably, the C method AFTER C, so that the
873             file deletion logic in C won't accidntally delete files that
874             may result in a vastly more damaging effect on the custom software.
875              
876             Returns true, or throws an error on exception.
877              
878             =cut
879              
880             sub install_custom {
881             return 1;
882             }
883              
884             =pod
885              
886             =head2 install_c_toolchain
887              
888             The C method is used by C to install various
889             binary packages to provide a working C development environment.
890              
891             By default, the C toolchain consists of dmake, gcc (C/C++), binutils,
892             pexports, the mingw runtime environment, and the win32api C package.
893              
894             Although dmake is the "standard" make for Perl::Dist distributions,
895             it will also install...
896              
897             TO BE CONTINUED
898              
899             =cut
900              
901             # Install the required toolchain elements.
902             # We use separate methods for each tool to make
903             # it easier for individual distributions to customize
904             # the versions of tools they incorporate.
905             sub install_c_toolchain {
906             my $self = shift;
907              
908             # The primary make
909             $self->install_dmake;
910              
911             # Core compiler
912             $self->install_gcc;
913              
914             # C Utilities
915             $self->install_mingw_make;
916             $self->install_binutils;
917             $self->install_pexports;
918              
919             # Install support libraries
920             $self->install_mingw_runtime;
921             $self->install_win32api;
922              
923             # Set up the environment variables for the binaries
924             $self->add_env_path( 'c', 'bin' );
925              
926             return 1;
927             }
928              
929             # No additional modules by default
930             sub install_c_libraries {
931             my $class = shift;
932             if ( $class eq __PACKAGE__ ) {
933             $class->trace("install_c_libraries: Nothing to do\n");
934             }
935             return 1;
936             }
937              
938             # Install Perl 5.10.0 by default.
939             # Just hand off to the larger set of Perl install methods.
940             sub install_perl {
941             my $self = shift;
942             my $install_perl_method = "install_perl_" . $self->perl_version;
943             unless ( $self->can($install_perl_method) ) {
944             Carp::croak("Cannot generate perl, missing $install_perl_method method in " . ref($self));
945             }
946             $self->$install_perl_method(@_);
947             }
948              
949             sub install_perl_toolchain {
950             my $self = shift;
951             my $toolchain = @_
952             ? Params::Util::_INSTANCE($_[0], 'Perl::Dist::Util::Toolchain')
953             : Perl::Dist::Util::Toolchain->new(
954             perl_version => $self->perl_version_literal,
955             );
956             unless ( $toolchain ) {
957             die("Did not provide a toolchain resolver");
958             }
959              
960             # Get the regular Perl to generate the list.
961             # Run it in a separate process so we don't hold
962             # any permanent CPAN.pm locks.
963             $toolchain->delegate;
964             if ( $toolchain->{errstr} ) {
965             die("Failed to generate toolchain distributions");
966             }
967              
968             # Install the toolchain dists
969             foreach my $dist ( @{$toolchain->{dists}} ) {
970             my $automated_testing = 0;
971             my $release_testing = 0;
972             my $force = $self->force;
973             if ( $dist =~ /Scalar-List-Util/ ) {
974             # Does something weird with tainting
975             $force = 1;
976             }
977             if ( $dist =~ /URI-/ ) {
978             # Can't rely on t/heuristic.t not finding a www.perl.bv
979             # because some ISP's use DNS redirectors for unfindable
980             # sites.
981             $force = 1;
982             }
983             if ( $dist =~ /Term-ReadLine-Perl/ ) {
984             # Does evil things when testing, and
985             # so testing cannot be automated.
986             $automated_testing = 1;
987             }
988             $self->install_distribution(
989             name => $dist,
990             force => $force,
991             automated_testing => $automated_testing,
992             release_testing => $release_testing,
993             );
994             }
995              
996             return 1;
997             }
998              
999             sub install_cpan_upgrades {
1000             my $self = shift;
1001             unless ( $self->bin_perl ) {
1002             Carp::croak("Cannot install CPAN modules yet, perl is not installed");
1003             }
1004              
1005             # Generate the CPAN installation script
1006             my $cpan_string = <<"END_PERL";
1007             print "Loading CPAN...\\n";
1008             use CPAN;
1009             CPAN::HandleConfig->load unless \$CPAN::Config_loaded++;
1010             print "Upgrading all out of date CPAN modules...\\n";
1011             print "\\\$ENV{PATH} = '\$ENV{PATH}'\\n";
1012             CPAN::Shell->upgrade;
1013             print "Completed upgrade of all modules\\n";
1014             exit(0);
1015             END_PERL
1016              
1017             # Dump the CPAN script to a temp file and execute
1018             $self->trace("Running upgrade of all modules\n");
1019             my $cpan_file = File::Spec->catfile(
1020             $self->build_dir,
1021             'cpan_string.pl',
1022             );
1023             SCOPE: {
1024             open( CPAN_FILE, '>', $cpan_file ) or die "open: $!";
1025             print CPAN_FILE $cpan_string or die "print: $!";
1026             close( CPAN_FILE ) or die "close: $!";
1027             }
1028             local $ENV{PERL_MM_USE_DEFAULT} = 1;
1029             local $ENV{AUTOMATED_TESTING} = '';
1030             local $ENV{RELEASE_TESTING} = '';
1031             $self->_run3( $self->bin_perl, $cpan_file ) or die "perl failed";
1032             die "Failure detected during cpan upgrade, stopping" if $?;
1033              
1034             return 1;
1035             }
1036              
1037             # No additional modules by default
1038             sub install_perl_modules {
1039             my $self = shift;
1040              
1041             # Upgrade anything out of date,
1042             # but don't install anything extra.
1043             $self->install_cpan_upgrades;
1044              
1045             return 1;
1046             }
1047              
1048             # Portability support must be added after modules
1049             sub install_portable {
1050             my $self = shift;
1051              
1052             # Install the regular parts of Portability
1053             $self->install_module(
1054             name => 'Portable',
1055             );
1056              
1057             # Create the portability object
1058             $self->trace("Creating Portable::Dist\n");
1059             $self->{portable_dist} = Portable::Dist->new(
1060             perl_root => File::Spec->catdir(
1061             $self->image_dir => 'perl',
1062             ),
1063             );
1064             $self->trace("Running Portable::Dist\n");
1065             $self->{portable_dist}->run;
1066             $self->trace("Completed Portable::Dist\n");
1067              
1068             # Install the file that turns on Portability last
1069             $self->install_file(
1070             share => 'Perl-Dist portable.perl',
1071             install_to => 'portable.perl',
1072             );
1073              
1074             return 1;
1075             }
1076              
1077             # Install links and launchers and so on
1078             sub install_win32_extras {
1079             my $self = shift;
1080              
1081             $self->install_launcher(
1082             name => 'CPAN Client',
1083             bin => 'cpan',
1084             );
1085             $self->install_website(
1086             name => 'CPAN Search',
1087             url => 'http://search.cpan.org/',
1088             );
1089              
1090             if ( $self->perl_version_human eq '5.8.8' ) {
1091             $self->install_website(
1092             name => 'Perl 5.8.8 Documentation',
1093             url => 'http://perldoc.perl.org/5.8.8/',
1094             );
1095             }
1096             if ( $self->perl_version_human eq '5.8.9' ) {
1097             $self->install_website(
1098             name => 'Perl 5.8.9 Documentation',
1099             url => 'http://perldoc.perl.org/5.8.9/',
1100             );
1101             }
1102             if ( $self->perl_version_human eq '5.10.0' ) {
1103             $self->install_website(
1104             name => 'Perl 5.10.0 Documentation',
1105             url => 'http://perldoc.perl.org/',
1106             );
1107             }
1108              
1109             $self->install_website(
1110             name => 'Win32 Perl Wiki',
1111             url => 'http://win32.perl.org/',
1112             );
1113              
1114             return 1;
1115             }
1116              
1117             # Delete various stuff we won't be needing
1118             sub remove_waste {
1119             my $self = shift;
1120              
1121             $self->trace("Removing doc, man, info and html documentation...\n");
1122             $self->remove_dir(qw{ perl man });
1123             $self->remove_dir(qw{ perl html });
1124             $self->remove_dir(qw{ c man });
1125             $self->remove_dir(qw{ c doc });
1126             $self->remove_dir(qw{ c info });
1127             $self->remove_dir(qw{ c contrib });
1128             $self->remove_dir(qw{ c html });
1129              
1130             $self->trace("Removing C examples, manifests...\n");
1131             $self->remove_dir(qw{ c examples });
1132             $self->remove_dir(qw{ c manifest });
1133              
1134             $self->trace("Removing redundant license files...\n");
1135             $self->remove_file(qw{ c COPYING });
1136             $self->remove_file(qw{ c COPYING.LIB });
1137              
1138             $self->trace("Removing CPAN build directories and download caches...\n");
1139             $self->remove_dir(qw{ cpan sources });
1140             $self->remove_dir(qw{ cpan build });
1141              
1142             return 1;
1143             }
1144              
1145             sub remove_dir {
1146             my $self = shift;
1147             my $dir = $self->dir( @_ );
1148             File::Remove::remove( \1, $dir ) if -e $dir;
1149             return 1;
1150             }
1151              
1152             sub remove_file {
1153             my $self = shift;
1154             my $file = $self->file( @_ );
1155             File::Remove::remove( \1, $file ) if -e $file;
1156             return 1;
1157             }
1158            
1159              
1160              
1161              
1162              
1163             #####################################################################
1164             # Perl 5.8.8 Support
1165              
1166             sub install_perl_588 {
1167             my $self = shift;
1168              
1169             # Prefetch and predelegate the toolchain so that it
1170             # fails early if there's a problem
1171             $self->trace("Pregenerating toolchain...\n");
1172             my $toolchain = Perl::Dist::Util::Toolchain->new(
1173             perl_version => $self->perl_version_literal,
1174             ) or die("Failed to resolve toolchain modules");
1175             $toolchain->delegate;
1176             if ( $toolchain->{errstr} ) {
1177             die("Failed to generate toolchain distributions");
1178             }
1179              
1180             # Install the main perl distributions
1181             $self->install_perl_588_bin(
1182             name => 'perl',
1183             url => 'http://strawberryperl.com/package/perl-5.8.8.tar.gz',
1184             unpack_to => 'perl',
1185             install_to => 'perl',
1186             patch => [ qw{
1187             lib/ExtUtils/Install.pm
1188             lib/ExtUtils/Installed.pm
1189             lib/ExtUtils/Packlist.pm
1190             lib/ExtUtils/t/Install.t
1191             lib/ExtUtils/t/Installed.t
1192             lib/ExtUtils/t/Installapi2.t
1193             lib/ExtUtils/t/Packlist.t
1194             lib/ExtUtils/t/basic.t
1195             lib/ExtUtils/t/can_write_dir.t
1196             lib/CPAN/Config.pm
1197             } ],
1198             license => {
1199             'perl-5.8.8/Readme' => 'perl/Readme',
1200             'perl-5.8.8/Artistic' => 'perl/Artistic',
1201             'perl-5.8.8/Copying' => 'perl/Copying',
1202             },
1203             );
1204              
1205             # Upgrade the toolchain modules
1206             $self->install_perl_toolchain( $toolchain );
1207              
1208             return 1;
1209             }
1210              
1211             sub install_perl_588_bin {
1212             my $self = shift;
1213             my $perl = Perl::Dist::Asset::Perl->new(
1214             parent => $self,
1215             force => $self->force,
1216             @_,
1217             );
1218             unless ( $self->bin_make ) {
1219             Carp::croak("Cannot build Perl yet, no bin_make defined");
1220             }
1221              
1222             # Download the file
1223             my $tgz = $self->_mirror(
1224             $perl->url,
1225             $self->download_dir,
1226             );
1227              
1228             # Unpack to the build directory
1229             my $unpack_to = File::Spec->catdir( $self->build_dir, $perl->unpack_to );
1230             if ( -d $unpack_to ) {
1231             $self->trace("Removing previous $unpack_to\n");
1232             File::Remove::remove( \1, $unpack_to );
1233             }
1234             $self->_extract( $tgz => $unpack_to );
1235              
1236             # Get the versioned name of the directory
1237             (my $perlsrc = $tgz) =~ s{\.tar\.gz\z|\.tgz\z}{};
1238             $perlsrc = File::Basename::basename($perlsrc);
1239              
1240             # Pre-copy updated files over the top of the source
1241             my $patch = $perl->patch;
1242             if ( $patch ) {
1243             # Overwrite the appropriate files
1244             foreach my $file ( @$patch ) {
1245             $self->patch_file( "perl-5.8.8/$file" => $unpack_to );
1246             }
1247             }
1248              
1249             # Copy in licenses
1250             if ( ref $perl->license eq 'HASH' ) {
1251             my $license_dir = File::Spec->catdir( $self->image_dir, 'licenses' );
1252             $self->_extract_filemap( $tgz, $perl->license, $license_dir, 1 );
1253             }
1254              
1255             # Build win32 perl
1256             SCOPE: {
1257             my $wd = $self->_pushd($unpack_to, $perlsrc , "win32" );
1258              
1259             # Prepare to patch
1260             my $image_dir = $self->image_dir;
1261             my $INST_TOP = File::Spec->catdir( $self->image_dir, $perl->install_to );
1262             my ($INST_DRV) = File::Spec->splitpath( $INST_TOP, 1 );
1263              
1264             $self->trace("Patching makefile.mk\n");
1265             $self->patch_file( 'perl-5.8.8/win32/makefile.mk' => $unpack_to, {
1266             dist => $self,
1267             INST_DRV => $INST_DRV,
1268             INST_TOP => $INST_TOP,
1269             } );
1270              
1271             $self->trace("Building perl...\n");
1272             $self->_make;
1273              
1274             unless ( $perl->force ) {
1275             local $ENV{PERL_SKIP_TTY_TEST} = 1;
1276             $self->trace("Testing perl...\n");
1277             $self->_make('test');
1278             }
1279              
1280             $self->trace("Installing perl...\n");
1281             $self->_make( qw/install UNINST=1/ );
1282             }
1283              
1284             # Should now have a perl to use
1285             $self->{bin_perl} = File::Spec->catfile( $self->image_dir, qw/perl bin perl.exe/ );
1286             unless ( -x $self->bin_perl ) {
1287             Carp::croak("Can't execute " . $self->bin_perl);
1288             }
1289              
1290             # Add to the environment variables
1291             $self->add_env_path( 'perl', 'bin' );
1292              
1293             return 1;
1294             }
1295              
1296              
1297              
1298              
1299              
1300             #####################################################################
1301             # Perl 5.8.9 Support
1302              
1303             sub install_perl_589 {
1304             my $self = shift;
1305              
1306             # Prefetch and predelegate the toolchain so that it
1307             # fails early if there's a problem
1308             $self->trace("Pregenerating toolchain...\n");
1309             my $toolchain = Perl::Dist::Util::Toolchain->new(
1310             perl_version => $self->perl_version_literal,
1311             ) or die("Failed to resolve toolchain modules");
1312             $toolchain->delegate;
1313             if ( $toolchain->{errstr} ) {
1314             die("Failed to generate toolchain distributions");
1315             }
1316              
1317             # Install the main perl distributions
1318             $self->install_perl_589_bin(
1319             name => 'perl',
1320             url => 'http://strawberryperl.com/package/perl-5.8.9.tar.gz',
1321             unpack_to => 'perl',
1322             install_to => 'perl',
1323             patch => [ qw{
1324             lib/CPAN/Config.pm
1325             } ],
1326             license => {
1327             'perl-5.8.9/Readme' => 'perl/Readme',
1328             'perl-5.8.9/Artistic' => 'perl/Artistic',
1329             'perl-5.8.9/Copying' => 'perl/Copying',
1330             },
1331             );
1332              
1333             # Upgrade the toolchain modules
1334             $self->install_perl_toolchain( $toolchain );
1335              
1336             return 1;
1337             }
1338              
1339             sub install_perl_589_bin {
1340             my $self = shift;
1341             my $perl = Perl::Dist::Asset::Perl->new(
1342             parent => $self,
1343             force => $self->force,
1344             @_,
1345             );
1346             unless ( $self->bin_make ) {
1347             Carp::croak("Cannot build Perl yet, no bin_make defined");
1348             }
1349              
1350             # Download the file
1351             my $tgz = $self->_mirror(
1352             $perl->url,
1353             $self->download_dir,
1354             );
1355              
1356             # Unpack to the build directory
1357             my $unpack_to = File::Spec->catdir( $self->build_dir, $perl->unpack_to );
1358             if ( -d $unpack_to ) {
1359             $self->trace("Removing previous $unpack_to\n");
1360             File::Remove::remove( \1, $unpack_to );
1361             }
1362             $self->_extract( $tgz => $unpack_to );
1363              
1364             # Get the versioned name of the directory
1365             (my $perlsrc = $tgz) =~ s{\.tar\.gz\z|\.tgz\z}{};
1366             $perlsrc = File::Basename::basename($perlsrc);
1367              
1368             # Pre-copy updated files over the top of the source
1369             my $patch = $perl->patch;
1370             if ( $patch ) {
1371             # Overwrite the appropriate files
1372             foreach my $file ( @$patch ) {
1373             $self->patch_file( "perl-5.8.9/$file" => $unpack_to );
1374             }
1375             }
1376              
1377             # Copy in licenses
1378             if ( ref $perl->license eq 'HASH' ) {
1379             my $license_dir = File::Spec->catdir( $self->image_dir, 'licenses' );
1380             $self->_extract_filemap( $tgz, $perl->license, $license_dir, 1 );
1381             }
1382              
1383             # Build win32 perl
1384             SCOPE: {
1385             my $wd = $self->_pushd($unpack_to, $perlsrc , "win32" );
1386              
1387             # Prepare to patch
1388             my $image_dir = $self->image_dir;
1389             my $INST_TOP = File::Spec->catdir( $self->image_dir, $perl->install_to );
1390             my ($INST_DRV) = File::Spec->splitpath( $INST_TOP, 1 );
1391              
1392             $self->trace("Patching makefile.mk\n");
1393             $self->patch_file( 'perl-5.8.9/win32/makefile.mk' => $unpack_to, {
1394             dist => $self,
1395             INST_DRV => $INST_DRV,
1396             INST_TOP => $INST_TOP,
1397             } );
1398              
1399             $self->trace("Building perl...\n");
1400             $self->_make;
1401              
1402             unless ( $perl->force ) {
1403             local $ENV{PERL_SKIP_TTY_TEST} = 1;
1404             $self->trace("Testing perl...\n");
1405             $self->_make('test');
1406             }
1407              
1408             $self->trace("Installing perl...\n");
1409             $self->_make( qw/install UNINST=1/ );
1410             }
1411              
1412             # Should now have a perl to use
1413             $self->{bin_perl} = File::Spec->catfile( $self->image_dir, qw/perl bin perl.exe/ );
1414             unless ( -x $self->bin_perl ) {
1415             Carp::croak("Can't execute " . $self->bin_perl);
1416             }
1417              
1418             # Add to the environment variables
1419             $self->add_env_path( 'perl', 'bin' );
1420              
1421             return 1;
1422             }
1423              
1424              
1425              
1426              
1427             #####################################################################
1428             # Perl 5.10.0 Support
1429              
1430             =pod
1431              
1432             =head2 install_perl_5100
1433              
1434             The C method provides a simplified way to install
1435             Perl 5.10.0 into the distribution.
1436              
1437             It takes care of calling C with the standard
1438             params, and then calls C to set up the
1439             Perl 5.10.0 CPAN toolchain.
1440              
1441             Returns true, or throws an exception on error.
1442              
1443             =cut
1444              
1445             sub install_perl_5100 {
1446             my $self = shift;
1447              
1448             # Prefetch and predelegate the toolchain so that it
1449             # fails early if there's a problem
1450             $self->trace("Pregenerating toolchain...\n");
1451             my $toolchain = Perl::Dist::Util::Toolchain->new(
1452             perl_version => $self->perl_version_literal,
1453             ) or die("Failed to resolve toolchain modules");
1454             $toolchain->delegate;
1455             if ( $toolchain->{errstr} ) {
1456             print "Error: $toolchain->{errstr}\n";
1457             die("Failed to generate toolchain distributions");
1458             }
1459              
1460             # Install the main binary
1461             $self->install_perl_5100_bin(
1462             name => 'perl',
1463             url => 'http://strawberryperl.com/package/perl-5.10.0.tar.gz',
1464             unpack_to => 'perl',
1465             install_to => 'perl',
1466             patch => [ qw{
1467             lib/ExtUtils/Command.pm
1468             lib/CPAN/Config.pm
1469             } ],
1470             license => {
1471             'perl-5.10.0/Readme' => 'perl/Readme',
1472             'perl-5.10.0/Artistic' => 'perl/Artistic',
1473             'perl-5.10.0/Copying' => 'perl/Copying',
1474             },
1475             );
1476              
1477             # Install the toolchain
1478             $self->install_perl_toolchain( $toolchain );
1479              
1480             return 1;
1481             }
1482              
1483             =pod
1484              
1485             =head2 install_perl_5100_bin
1486              
1487             $self->install_perl_5100_bin(
1488             name => 'perl',
1489             dist => 'RGARCIA/perl-5.10.0.tar.gz',
1490             unpack_to => 'perl',
1491             license => {
1492             'perl-5.10.0/Readme' => 'perl/Readme',
1493             'perl-5.10.0/Artistic' => 'perl/Artistic',
1494             'perl-5.10.0/Copying' => 'perl/Copying',
1495             },
1496             install_to => 'perl',
1497             );
1498              
1499             The C method takes care of the detailed process
1500             of building the Perl 5.10.0 binary and installing it into the
1501             distribution.
1502              
1503             A short summary of the process would be that it downloads or otherwise
1504             fetches the named package, unpacks it, copies out any license files from
1505             the source code, then tweaks the Win32 makefile to point to the specific
1506             build directory, and then runs make/make test/make install. It also
1507             registers some environment variables for addition to the Inno Setup
1508             script.
1509              
1510             It is normally called directly by C rather than
1511             directly from the API, but is documented for completeness.
1512              
1513             It takes a number of parameters that are sufficiently detailed above.
1514              
1515             Returns true (after 20 minutes or so) or throws an exception on
1516             error.
1517              
1518             =cut
1519              
1520             sub install_perl_5100_bin {
1521             my $self = shift;
1522             my $perl = Perl::Dist::Asset::Perl->new(
1523             parent => $self,
1524             force => $self->force,
1525             @_,
1526             );
1527             unless ( $self->bin_make ) {
1528             die("Cannot build Perl yet, no bin_make defined");
1529             }
1530             $self->trace("Preparing " . $perl->name . "\n");
1531              
1532             # Download the file
1533             my $tgz = $self->_mirror(
1534             $perl->url,
1535             $self->download_dir,
1536             );
1537              
1538             # Unpack to the build directory
1539             my $unpack_to = File::Spec->catdir( $self->build_dir, $perl->unpack_to );
1540             if ( -d $unpack_to ) {
1541             $self->trace("Removing previous $unpack_to\n");
1542             File::Remove::remove( \1, $unpack_to );
1543             }
1544             $self->_extract( $tgz => $unpack_to );
1545              
1546             # Get the versioned name of the directory
1547             (my $perlsrc = $tgz) =~ s{\.tar\.gz\z|\.tgz\z}{};
1548             $perlsrc = File::Basename::basename($perlsrc);
1549              
1550             # Pre-copy updated files over the top of the source
1551             my $patch = $perl->patch;
1552             if ( $patch ) {
1553             # Overwrite the appropriate files
1554             foreach my $file ( @$patch ) {
1555             $self->patch_file( "perl-5.10.0/$file" => $unpack_to );
1556             }
1557             }
1558              
1559             # Copy in licenses
1560             if ( ref $perl->license eq 'HASH' ) {
1561             my $license_dir = File::Spec->catdir( $self->image_dir, 'licenses' );
1562             $self->_extract_filemap( $tgz, $perl->license, $license_dir, 1 );
1563             }
1564              
1565             # Build win32 perl
1566             SCOPE: {
1567             my $wd = $self->_pushd($unpack_to, $perlsrc , "win32" );
1568              
1569             # Prepare to patch
1570             my $image_dir = $self->image_dir;
1571             my $INST_TOP = File::Spec->catdir( $self->image_dir, $perl->install_to );
1572             my ($INST_DRV) = File::Spec->splitpath( $INST_TOP, 1 );
1573              
1574             $self->trace("Patching makefile.mk\n");
1575             $self->patch_file( 'perl-5.10.0/win32/makefile.mk' => $unpack_to, {
1576             dist => $self,
1577             INST_DRV => $INST_DRV,
1578             INST_TOP => $INST_TOP,
1579             } );
1580              
1581             $self->trace("Building perl...\n");
1582             $self->_make;
1583              
1584             unless ( $perl->force ) {
1585             local $ENV{PERL_SKIP_TTY_TEST} = 1;
1586             $self->trace("Testing perl...\n");
1587             $self->_make('test');
1588             }
1589              
1590             $self->trace("Installing perl...\n");
1591             $self->_make( 'install' );
1592             }
1593              
1594             # Should now have a perl to use
1595             $self->{bin_perl} = File::Spec->catfile( $self->image_dir, qw/perl bin perl.exe/ );
1596             unless ( -x $self->bin_perl ) {
1597             die "Can't execute " . $self->bin_perl;
1598             }
1599              
1600             # Add to the environment variables
1601             $self->add_env_path( 'perl', 'bin' );
1602              
1603             return 1;
1604             }
1605              
1606              
1607              
1608              
1609              
1610             #####################################################################
1611             # Installing C Toolchain and Library Packages
1612              
1613             =pod
1614              
1615             =head2 install_dmake
1616              
1617             $dist->install_dmake
1618              
1619             The C method installs the B make tool into the
1620             distribution, and is typically installed during "C toolchain" build
1621             phase.
1622              
1623             It provides the approproate arguments to C and then
1624             validates that the binary was installed correctly.
1625              
1626             Returns true or throws an exception on error.
1627              
1628             =cut
1629              
1630             sub install_dmake {
1631             my $self = shift;
1632              
1633             # Install dmake
1634             $self->install_binary(
1635             name => 'dmake',
1636             license => {
1637             'dmake/COPYING' => 'dmake/COPYING',
1638             'dmake/readme/license.txt' => 'dmake/license.txt',
1639             },
1640             install_to => {
1641             'dmake/dmake.exe' => 'c/bin/dmake.exe',
1642             'dmake/startup' => 'c/bin/startup',
1643             },
1644             );
1645              
1646             # Initialize the make location
1647             $self->{bin_make} = File::Spec->catfile(
1648             $self->image_dir, 'c', 'bin', 'dmake.exe',
1649             );
1650             unless ( -x $self->bin_make ) {
1651             Carp::croak("Can't execute make");
1652             }
1653              
1654             return 1;
1655             }
1656              
1657             =pod
1658              
1659             =head2 install_gcc
1660              
1661             $dist->install_gcc
1662              
1663             The C method installs the B into the
1664             distribution, and is typically installed during "C toolchain" build
1665             phase.
1666              
1667             It provides the appropriate arguments to several C
1668             calls. The default C method installs two binary
1669             packages, the core compiler 'gcc-core' and the C++ compiler 'gcc-c++'.
1670              
1671             Returns true or throws an exception on error.
1672              
1673             =cut
1674              
1675             sub install_gcc {
1676             my $self = shift;
1677              
1678              
1679             # Install the compilers (gcc)
1680             $self->install_binary(
1681             name => 'gcc-core',
1682             license => {
1683             'COPYING' => 'gcc/COPYING',
1684             'COPYING.lib' => 'gcc/COPYING.lib',
1685             },
1686             );
1687             $self->install_binary(
1688             name => 'gcc-g++',
1689             );
1690              
1691             return 1;
1692             }
1693              
1694             =pod
1695              
1696             =head2 install_binutils
1697              
1698             $dist->install_binutils
1699              
1700             The C method installs the C package into
1701             the distribution.
1702              
1703             The most important of these is C, which is used to extract
1704             static library files from .dll files. This is needed by some libraries
1705             to let the Perl interfaces build against them correctly.
1706              
1707             Returns true or throws an exception on error.
1708              
1709             =cut
1710              
1711             sub install_binutils {
1712             my $self = shift;
1713              
1714             $self->install_binary(
1715             name => 'binutils',
1716             license => {
1717             'Copying' => 'binutils/Copying',
1718             'Copying.lib' => 'binutils/Copying.lib',
1719             },
1720             );
1721             $self->{bin_dlltool} = File::Spec->catfile(
1722             $self->image_dir, 'c', 'bin', 'dlltool.exe',
1723             );
1724             unless ( -x $self->bin_dlltool ) {
1725             die "Can't execute dlltool";
1726             }
1727              
1728             return 1;
1729             }
1730              
1731             =pod
1732              
1733             =head2 install_pexports
1734              
1735             $dist->install_pexports
1736              
1737             The C method installs the C package
1738             into the distribution.
1739              
1740             This is needed by some libraries to let the Perl interfaces build against
1741             them correctly.
1742              
1743             Returns true or throws an exception on error.
1744              
1745             =cut
1746              
1747             sub install_pexports {
1748             my $self = shift;
1749              
1750             $self->install_binary(
1751             name => 'pexports',
1752             url => $self->binary_url('pexports-0.43-1.zip'),
1753             license => {
1754             'pexports-0.43/COPYING' => 'pexports/COPYING',
1755             },
1756             install_to => {
1757             'pexports-0.43/bin' => 'c/bin',
1758             },
1759             );
1760             $self->{bin_pexports} = File::Spec->catfile(
1761             $self->image_dir, 'c', 'bin', 'pexports.exe',
1762             );
1763             unless ( -x $self->bin_pexports ) {
1764             die "Can't execute pexports";
1765             }
1766              
1767             return 1;
1768             }
1769              
1770             =pod
1771              
1772             =head2 install_mingw_runtime
1773              
1774             $dist->install_mingw_runtime
1775              
1776             The C method installs the MinGW runtime package
1777             into the distribution, which is basically the MinGW version of libc and
1778             some other very low level libs.
1779              
1780             Returns true or throws an exception on error.
1781              
1782             =cut
1783              
1784             sub install_mingw_runtime {
1785             my $self = shift;
1786              
1787             $self->install_binary(
1788             name => 'mingw-runtime',
1789             license => {
1790             'doc/mingw-runtime/Contributors' => 'mingw/Contributors',
1791             'doc/mingw-runtime/Disclaimer' => 'mingw/Disclaimer',
1792             },
1793             );
1794              
1795             return 1;
1796             }
1797              
1798             =pod
1799              
1800             =head2 install_zlib
1801              
1802             $dist->install_zlib
1803              
1804             The C method installs the B compression library
1805             into the distribution, and is typically installed during "C toolchain"
1806             build phase.
1807              
1808             It provides the appropriate arguments to a C call that
1809             will extract the standard zlib win32 package, and generate the additional
1810             files that Perl needs.
1811              
1812             Returns true or throws an exception on error.
1813              
1814             =cut
1815              
1816             sub install_zlib {
1817             my $self = shift;
1818              
1819             # Zlib is a pexport-based lib-install
1820             $self->install_library(
1821             name => 'zlib',
1822             url => $self->binary_url('zlib-1.2.3.win32.zip'),
1823             unpack_to => 'zlib',
1824             build_a => {
1825             'dll' => 'zlib-1.2.3.win32/bin/zlib1.dll',
1826             'def' => 'zlib-1.2.3.win32/bin/zlib1.def',
1827             'a' => 'zlib-1.2.3.win32/lib/zlib1.a',
1828             },
1829             install_to => {
1830             'zlib-1.2.3.win32/bin' => 'c/bin',
1831             'zlib-1.2.3.win32/lib' => 'c/lib',
1832             'zlib-1.2.3.win32/include' => 'c/include',
1833             },
1834             );
1835              
1836             return 1;
1837             }
1838              
1839             =pod
1840              
1841             =head2 install_win32api
1842              
1843             $dist->install_win32api
1844              
1845             The C method installs C layer, to
1846             allow C code to compile against native Win32 APIs.
1847              
1848             Returns true or throws an exception on error.
1849              
1850             =cut
1851              
1852             sub install_win32api {
1853             my $self = shift;
1854              
1855             $self->install_binary(
1856             name => 'w32api',
1857             );
1858              
1859             return 1;
1860             }
1861              
1862             =pod
1863              
1864             =head2 install_mingw_make
1865              
1866             $dist->install_mingw_make
1867              
1868             The C method installs the MinGW build of the B
1869             build tool.
1870              
1871             While GNU make is not used by Perl itself, some C libraries can't be built
1872             using the normal C tool and explicitly need GNU make. So we install
1873             it as mingw-make and certain Alien:: modules will use it by that name.
1874              
1875             Returns true or throws an exception on error.
1876              
1877             =cut
1878              
1879             sub install_mingw_make {
1880             my $self = shift;
1881              
1882             $self->install_binary(
1883             name => 'mingw-make',
1884             );
1885              
1886             return 1;
1887             }
1888              
1889             =pod
1890              
1891             =head2 install_libiconv
1892              
1893             $dist->install_libiconv
1894              
1895             The C method installs the C library,
1896             which is used for various character encoding tasks, and is needed for
1897             other libraries such as C.
1898              
1899             Returns true or throws an exception on error.
1900              
1901             =cut
1902              
1903             sub install_libiconv {
1904             my $self = shift;
1905              
1906             # libiconv for win32 comes in 3 parts, install them.
1907             $self->install_binary(
1908             name => 'libiconv-dep',
1909             );
1910             $self->install_binary(
1911             name => 'libiconv-lib',
1912             );
1913             $self->install_binary(
1914             name => 'libiconv-bin',
1915             );
1916              
1917             # The dll is installed with an unexpected name,
1918             # so we correct it post-install.
1919             $self->_move(
1920             File::Spec->catfile( $self->image_dir, 'c', 'bin', 'libiconv2.dll' ),
1921             File::Spec->catfile( $self->image_dir, 'c', 'bin', 'iconv.dll' ),
1922             );
1923              
1924             return 1;
1925             }
1926              
1927             =pod
1928              
1929             =head2 install_libxml
1930              
1931             $dist->install_libxml
1932              
1933             The C method installs the C library,
1934             which is a fast, reliable, XML parsing library, and the new standard
1935             library for XML parsing.
1936              
1937             Returns true or throws an exception on error.
1938              
1939             =cut
1940              
1941             sub install_libxml {
1942             my $self = shift;
1943              
1944             # libxml is a straight forward pexport-based install
1945             $self->install_library(
1946             name => 'libxml2',
1947             url => $self->binary_url('libxml2-2.6.30.win32.zip'),
1948             unpack_to => 'libxml2',
1949             build_a => {
1950             'dll' => 'libxml2-2.6.30.win32/bin/libxml2.dll',
1951             'def' => 'libxml2-2.6.30.win32/bin/libxml2.def',
1952             'a' => 'libxml2-2.6.30.win32/lib/libxml2.a',
1953             },
1954             install_to => {
1955             'libxml2-2.6.30.win32/bin' => 'c/bin',
1956             'libxml2-2.6.30.win32/lib' => 'c/lib',
1957             'libxml2-2.6.30.win32/include' => 'c/include',
1958             },
1959             );
1960              
1961             return 1;
1962             }
1963              
1964             =pod
1965              
1966             =head2 install_expat
1967              
1968             $dist->install_expat
1969              
1970             The C method installs the C XML library,
1971             which was the first popular C XML parser. Many Perl XML libraries
1972             are based on Expat.
1973              
1974             Returns true or throws an exception on error.
1975              
1976             =cut
1977              
1978             sub install_expat {
1979             my $self = shift;
1980              
1981             # Install the PAR version of libexpat
1982             $self->install_par(
1983             name => 'libexpat',
1984             share => 'Perl-Dist vanilla/libexpat-vanilla.par',
1985             install_perl => 1,
1986             install_c => 0,
1987             );
1988              
1989             return 1;
1990             }
1991              
1992             =pod
1993              
1994             =head2 install_gmp
1995              
1996             $dist->install_gmp
1997              
1998             The C method installs the C
1999             Library>, which is used for fast and robust bignum support.
2000              
2001             Returns true or throws an exception on error.
2002              
2003             =cut
2004              
2005             sub install_gmp {
2006             my $self = shift;
2007              
2008             # Comes as a single prepackaged vanilla-specific zip file
2009             $self->install_binary(
2010             name => 'gmp',
2011             );
2012              
2013             return 1;
2014             }
2015              
2016             =pod
2017              
2018             =head2 install_pari
2019              
2020             $dist->install_pari
2021              
2022             The C method install (via a PAR package) libpari and the
2023             L module into the distribution.
2024              
2025             This method should only be called at during the install_modules phase.
2026              
2027             =cut
2028              
2029             sub install_pari {
2030             $_[0]->install_par(
2031             name => 'pari',
2032             url => 'http://strawberryperl.com/package/Math-Pari-2.010800.par',
2033             );
2034             }
2035              
2036              
2037              
2038              
2039              
2040             #####################################################################
2041             # General Installation Methods
2042              
2043             =pod
2044              
2045             =head2 install_binary
2046              
2047             $self->install_binary(
2048             name => 'gmp',
2049             );
2050              
2051             The C method is used by library-specific methods to
2052             install pre-compiled and un-modified tar.gz or zip archives into
2053             the distribution.
2054              
2055             Returns true or throws an exception on error.
2056              
2057             =cut
2058              
2059             sub install_binary {
2060             my $self = shift;
2061             my $binary = Perl::Dist::Asset::Binary->new(
2062             parent => $self,
2063             install_to => 'c', # Default to the C dir
2064             @_,
2065             );
2066             my $name = $binary->name;
2067             $self->trace("Preparing $name\n");
2068              
2069             # Download the file
2070             my $tgz = $self->_mirror(
2071             $binary->url,
2072             $self->download_dir,
2073             );
2074              
2075             # Unpack the archive
2076             my $install_to = $binary->install_to;
2077             if ( ref $binary->install_to eq 'HASH' ) {
2078             $self->_extract_filemap( $tgz, $binary->install_to, $self->image_dir );
2079              
2080             } elsif ( ! ref $binary->install_to ) {
2081             # unpack as a whole
2082             my $tgt = File::Spec->catdir( $self->image_dir, $binary->install_to );
2083             $self->_extract( $tgz => $tgt );
2084              
2085             } else {
2086             die "didn't expect install_to to be a " . ref $binary->install_to;
2087             }
2088              
2089             # Find the licenses
2090             if ( ref $binary->license eq 'HASH' ) {
2091             $self->_extract_filemap( $tgz, $binary->license, $self->license_dir, 1 );
2092             }
2093              
2094             return 1;
2095             }
2096              
2097             sub install_library {
2098             my $self = shift;
2099             my $library = Perl::Dist::Asset::Library->new(
2100             parent => $self,
2101             @_,
2102             );
2103             my $name = $library->name;
2104             $self->trace("Preparing $name\n");
2105              
2106             # Download the file
2107             my $tgz = $self->_mirror(
2108             $library->url,
2109             $self->download_dir,
2110             );
2111              
2112             # Unpack to the build directory
2113             my $unpack_to = File::Spec->catdir( $self->build_dir, $library->unpack_to );
2114             if ( -d $unpack_to ) {
2115             $self->trace("Removing previous $unpack_to\n");
2116             File::Remove::remove( \1, $unpack_to );
2117             }
2118             $self->_extract( $tgz => $unpack_to );
2119              
2120             # Build the .a file if needed
2121             if ( Params::Util::_HASH($library->build_a) ) {
2122             # Hand off for the .a generation
2123             $self->_dll_to_a(
2124             $library->build_a->{source} ?
2125             (
2126             source => File::Spec->catfile(
2127             $unpack_to, $library->build_a->{source},
2128             ),
2129             ) : (),
2130             dll => File::Spec->catfile(
2131             $unpack_to, $library->build_a->{dll},
2132             ),
2133             def => File::Spec->catfile(
2134             $unpack_to, $library->build_a->{def},
2135             ),
2136             a => File::Spec->catfile(
2137             $unpack_to, $library->build_a->{a},
2138             ),
2139             );
2140             }
2141              
2142             # Copy in the files
2143             my $install_to = $library->install_to;
2144             if ( Params::Util::_HASH($install_to) ) {
2145             foreach my $k ( sort keys %$install_to ) {
2146             my $from = File::Spec->catdir(
2147             $unpack_to, $k,
2148             );
2149             my $to = File::Spec->catdir(
2150             $self->image_dir, $install_to->{$k},
2151             );
2152             $self->_copy( $from => $to );
2153             }
2154             }
2155              
2156             # Copy in licenses
2157             if ( Params::Util::_HASH($library->license) ) {
2158             my $license_dir = File::Spec->catdir( $self->image_dir, 'licenses' );
2159             $self->_extract_filemap( $tgz, $library->license, $license_dir, 1 );
2160             }
2161              
2162             return 1;
2163             }
2164              
2165             =pod
2166              
2167             =head2 install_distribution
2168              
2169             $self->install_distribution(
2170             name => 'ADAMK/File-HomeDir-0.69.tar.gz,
2171             force => 1,
2172             automated_testing => 1,
2173             makefilepl_param => [
2174             'LIBDIR=' . File::Spec->catdir(
2175             $self->image_dir, 'c', 'lib',
2176             ),
2177             ],
2178             );
2179              
2180             The C method is used to install a single
2181             CPAN or non-CPAN distribution directly, without installing any of the
2182             dependencies for that distribution.
2183              
2184             It is used primarily during CPAN bootstrapping, to allow the
2185             installation of the toolchain modules, with the distribution install
2186             order precomputed or hard-coded.
2187              
2188             It takes a compulsory 'name' param, which should be the AUTHOR/file
2189             path within the CPAN mirror.
2190              
2191             The optional 'force' param allows the installation of distributions
2192             with spuriously failing test suites.
2193              
2194             The optional 'automated_testing' param allows for installation
2195             with the C environment flag enabled, which is
2196             used to either run more-intensive testing, or to convince certain
2197             Makefile.PL that insists on prompting that there is no human around
2198             and they REALLY need to just go with the default options.
2199              
2200             The optional 'makefilepl_param' param should be a reference to an
2201             array of additional params that should be passwd to the
2202             C. This can help with distributions that insist
2203             on taking additional options via Makefile.PL.
2204              
2205             Returns true of throws an exception on error.
2206              
2207             =cut
2208              
2209             sub install_distribution {
2210             my $self = shift;
2211             my $dist = Perl::Dist::Asset::Distribution->new(
2212             parent => $self,
2213             force => $self->force,
2214             @_,
2215             );
2216             my $name = $dist->name;
2217              
2218             # Download the file
2219             my $tgz = $self->_mirror(
2220             $dist->abs_uri( $self->cpan ),
2221             $self->download_dir,
2222             );
2223              
2224             # Where will it get extracted to
2225             my $dist_path = $name;
2226             $dist_path =~ s/\.tar\.gz//;
2227             $dist_path =~ s/\.zip//;
2228             $dist_path =~ s/.+\///;
2229             my $unpack_to = File::Spec->catdir( $self->build_dir, $dist_path );
2230              
2231             # Extract the tarball
2232             if ( -d $unpack_to ) {
2233             $self->trace("Removing previous $unpack_to\n");
2234             File::Remove::remove( \1, $unpack_to );
2235             }
2236             $self->_extract( $tgz => $self->build_dir );
2237             unless ( -d $unpack_to ) {
2238             Carp::croak("Failed to extract $unpack_to");
2239             }
2240              
2241             # Build the module
2242             SCOPE: {
2243             my $wd = $self->_pushd($unpack_to);
2244              
2245             # Enable automated_testing mode if needed
2246             # Blame Term::ReadLine::Perl for needing this ugly hack.
2247             if ( $dist->automated_testing ) {
2248             $self->trace("Installing with AUTOMATED_TESTING enabled...\n");
2249             }
2250             if ( $dist->release_testing ) {
2251             $self->trace("Installing with RELEASE_TESTING enabled...\n");
2252             }
2253             local $ENV{AUTOMATED_TESTING} = $dist->automated_testing;
2254             local $ENV{RELEASE_TESTING} = $dist->release_testing;
2255              
2256             $self->trace("Configuring $name...\n");
2257             $self->_perl( 'Makefile.PL', @{$dist->makefilepl_param} );
2258              
2259             $self->trace("Building $name...\n");
2260             $self->_make;
2261              
2262             unless ( $dist->force ) {
2263             $self->trace("Testing $name...\n");
2264             $self->_make('test');
2265             }
2266              
2267             $self->trace("Installing $name...\n");
2268             $self->_make( qw/install UNINST=1/ );
2269             }
2270              
2271             return 1;
2272             }
2273              
2274             =pod
2275              
2276             =head2 install_module
2277              
2278             $self->install_module(
2279             name => 'DBI',
2280             );
2281              
2282             The C method is a high level installation method that can
2283             be used during the C phase, once the CPAN toolchain
2284             has been been initialized.
2285              
2286             It makes the installation call using the CPAN client directly, allowing
2287             the CPAN client to both do the installation and fulfill all of the
2288             dependencies for the module, identically to if it was installed from
2289             the CPAN shell via an "install Module::Name" command.
2290              
2291             The compulsory 'name' param should be the class name of the module to
2292             be installed.
2293              
2294             The optional 'force' param can be used to force the install of module.
2295             This does not, however, force the installation of the dependencies of
2296             the module.
2297              
2298             Returns true or throws an exception on error.
2299              
2300             =cut
2301              
2302             sub install_module {
2303             my $self = shift;
2304             my $module = Perl::Dist::Asset::Module->new(
2305             force => $self->force,
2306             parent => $self,
2307             @_,
2308             );
2309             my $name = $module->name;
2310             my $force = $module->force;
2311             unless ( $self->bin_perl ) {
2312             Carp::croak("Cannot install CPAN modules yet, perl is not installed");
2313             }
2314              
2315             # Generate the CPAN installation script
2316             my $cpan_string = <<"END_PERL";
2317             print "Loading CPAN...\\n";
2318             use CPAN;
2319             CPAN::HandleConfig->load unless \$CPAN::Config_loaded++;
2320             print "Installing $name from CPAN...\\n";
2321             my \$module = CPAN::Shell->expandany( "$name" )
2322             or die "CPAN.pm couldn't locate $name";
2323             if ( \$module->uptodate ) {
2324             print "$name is up to date\\n";
2325             exit(0);
2326             }
2327             print "\\\$ENV{PATH} = '\$ENV{PATH}'\\n";
2328             if ( $force ) {
2329             CPAN::Shell->notest('install', '$name');
2330             } else {
2331             CPAN::Shell->install('$name');
2332             }
2333             print "Completed install of $name\\n";
2334             unless ( \$module->uptodate ) {
2335             die "Installation of $name appears to have failed";
2336             }
2337             exit(0);
2338             END_PERL
2339              
2340             # Dump the CPAN script to a temp file and execute
2341             $self->trace("Running install of $name\n");
2342             my $cpan_file = File::Spec->catfile(
2343             $self->build_dir,
2344             'cpan_string.pl',
2345             );
2346             SCOPE: {
2347             open( CPAN_FILE, '>', $cpan_file ) or die "open: $!";
2348             print CPAN_FILE $cpan_string or die "print: $!";
2349             close( CPAN_FILE ) or die "close: $!";
2350             }
2351             local $ENV{PERL_MM_USE_DEFAULT} = 1;
2352             local $ENV{AUTOMATED_TESTING} = '';
2353             local $ENV{RELEASE_TESTING} = '';
2354             $self->_run3( $self->bin_perl, $cpan_file ) or die "perl failed";
2355             die "Failure detected installing $name, stopping" if $?;
2356              
2357             return 1;
2358             }
2359              
2360             =pod
2361              
2362             =head2 install_modules
2363              
2364             $self->install_modules( qw{
2365             Foo::Bar
2366             This::That
2367             One::Two
2368             } );
2369              
2370             The C method is a convenience shorthand that makes it
2371             trivial to install a series of modules via C.
2372              
2373             As a convenience, it does not support any additional params to the
2374             underlying C call other than the name.
2375              
2376             =cut
2377              
2378             sub install_modules {
2379             my $self = shift;
2380             foreach my $name ( @_ ) {
2381             $self->install_module(
2382             name => $name,
2383             );
2384             }
2385             return 1;
2386             }
2387              
2388             =pod
2389              
2390             =head2 install_par
2391              
2392             The C method extends the available installation options to
2393             allow for the install of pre-compiled modules and pre-compiled C libraries
2394             via "PAR" packages.
2395              
2396             The compulsory 'name' param should be a simple identifying name, and does
2397             not have any functional use.
2398              
2399             The compulsory 'uri' param should be a URL string to the PAR package.
2400              
2401             Returns true on success or throws an exception on error.
2402              
2403             =cut
2404              
2405             sub install_par {
2406             my $self = shift;
2407             my $par = Perl::Dist::Asset::PAR->new(
2408             parent => $self,
2409             # not supported at the moment:
2410             #install_to => 'c', # Default to the C dir
2411             @_,
2412             );
2413              
2414             # Download the file.
2415             # Do it here for consistency instead of letting PAR::Dist do it
2416             $self->trace("Preparing " . $par->name . "\n");
2417             my $file = $self->_mirror(
2418             $par->url,
2419             $self->download_dir,
2420             );
2421              
2422             # Set the appropriate installation paths
2423             my $no_colon = $par->name;
2424             $no_colon =~ s/::/-/g;
2425             my $perldir = File::Spec->catdir($self->image_dir, 'perl');
2426             my $libdir = File::Spec->catdir($perldir, 'site', 'lib');
2427             my $bindir = File::Spec->catdir($perldir, 'bin');
2428             my $packlist = File::Spec->catfile($libdir, $no_colon, '.packlist');
2429             my $cdir = File::Spec->catdir($self->image_dir, 'c');
2430              
2431             # Suppress warnings for resources that don't exist
2432             local $^W = 0;
2433              
2434             # Install
2435             PAR::Dist::install_par(
2436             dist => $file,
2437             packlist_read => $packlist,
2438             packlist_write => $packlist,
2439             inst_lib => $libdir,
2440             inst_archlib => $libdir,
2441             inst_bin => $bindir,
2442             inst_script => $bindir,
2443             inst_man1dir => undef, # no man pages
2444             inst_man3dir => undef, # no man pages
2445             custom_targets => {
2446             'blib/c/lib' => File::Spec->catdir($cdir, 'lib'),
2447             'blib/c/bin' => File::Spec->catdir($cdir, 'bin'),
2448             'blib/c/include' => File::Spec->catdir($cdir, 'include'),
2449             'blib/c/share' => File::Spec->catdir($cdir, 'share'),
2450             },
2451             );
2452              
2453             return 1;
2454             }
2455              
2456             =pod
2457              
2458             =head2 install_file
2459              
2460             # Overwrite the CPAN::Config
2461             $self->install_file(
2462             share => 'Perl-Dist CPAN_Config.pm',
2463             install_to => 'perl/lib/CPAN/Config.pm',
2464             );
2465            
2466             # Install a custom icon file
2467             $self->install_file(
2468             name => 'Strawberry Perl Website Icon',
2469             url => 'http://strawberryperl.com/favicon.ico',
2470             install_to => 'Strawberry Perl Website.ico',
2471             );
2472              
2473             The C method is used to install a single specific file from
2474             various sources into the distribution.
2475              
2476             It is generally used to overwrite modules with distribution-specific
2477             customisations, or to install licenses, README files, or other
2478             miscellaneous data files which don't need to be compiled or modified.
2479              
2480             It takes a variety of different params.
2481              
2482             The optional 'name' param provides an optional plain name for the file.
2483             It does not have any functional purpose or meaning for this method.
2484              
2485             One of several alternative source methods must be provided.
2486              
2487             The 'url' method is used to provide a fully-resolved path to the
2488             source file and should be a fully-resolved URL.
2489              
2490             The 'file' method is used to provide a local path to the source file
2491             on the local system, and should be a fully-resolved filesystem path.
2492              
2493             The 'share' method is used to provide a path to a file installed as
2494             part of a CPAN distribution, and accessed via L.
2495              
2496             It should be a string containing two space-seperated value, the first
2497             of which is the distribution name, and the second is the path within
2498             the share dir of that distribution.
2499              
2500             The final compulsory method is the 'install_to' method, which provides
2501             either a destination file path, or alternatively a path to an existing
2502             directory that the file be installed below, using its source file name.
2503              
2504             Returns true or throws an exception on error.
2505              
2506             =cut
2507              
2508             sub install_file {
2509             my $self = shift;
2510             my $dist = Perl::Dist::Asset::File->new(
2511             parent => $self,
2512             @_,
2513             );
2514              
2515             # Get the file
2516             my $tgz = $self->_mirror(
2517             $dist->url,
2518             $self->download_dir
2519             );
2520              
2521             # Copy the file to the target location
2522             my $from = File::Spec->catfile( $self->download_dir, $dist->file );
2523             my $to = File::Spec->catfile( $self->image_dir, $dist->install_to );
2524             $self->_copy( $from => $to );
2525              
2526             # Clear the download file
2527             File::Remove::remove( \1, $tgz );
2528              
2529             return 1;
2530             }
2531              
2532             =pod
2533              
2534             =head2 install_launcher
2535              
2536             $self->install_launcher(
2537             name => 'CPAN Client',
2538             bin => 'cpan',
2539             );
2540              
2541             The C method is used to describe a binary program
2542             launcher that will be added to the Windows "Start" menu when the
2543             distribution is installed.
2544              
2545             It takes two compulsory param.
2546              
2547             The compulsory 'name' param is the name of the launcher, and the text
2548             that label will be displayed in the start menu (Currently this only
2549             supports ASCII, and is not language-aware in any way).
2550              
2551             The compulsory 'bin' param should be the name of a .bat script launcher
2552             in the Perl bin directory. The program itself MUST be installed before
2553             trying to add the launcher.
2554              
2555             Returns true or throws an exception on error.
2556              
2557             =cut
2558              
2559             sub install_launcher {
2560             my $self = shift;
2561             my $launcher = Perl::Dist::Asset::Launcher->new(
2562             parent => $self,
2563             @_,
2564             );
2565              
2566             # Check the script exists
2567             my $to = File::Spec->catfile( $self->image_dir, 'perl', 'bin', $launcher->bin . '.bat' );
2568             unless ( -f $to ) {
2569             die "The script '" . $launcher->bin . '" does not exist';
2570             }
2571              
2572             # Add the icon
2573             $self->add_icon(
2574             name => $launcher->name,
2575             filename => '{app}\\perl\bin\\' . $launcher->bin . '.bat',
2576             );
2577              
2578             return 1;
2579             }
2580              
2581             =pod
2582              
2583             =head2 install_website
2584              
2585             $self->install_website(
2586             name => 'Strawberry Perl Website',
2587             url => 'http://strawberryperl.com/',
2588             icon_file => 'Strawberry Perl Website.ico',
2589             icon_index => 1,
2590             );
2591              
2592             The C param is used to install a "Start" menu entry
2593             that will load a website using the default system browser.
2594              
2595             The compulsory 'name' param should be the name of the website, and will
2596             be the labelled displayed in the "Start" menu.
2597              
2598             The compulsory 'url' param is the fully resolved URL for the website.
2599              
2600             The optional 'icon_file' param should be the path to a file that contains the
2601             icon for the website.
2602              
2603             The optional 'icon_index' param should be the icon index within the icon file.
2604             This param is optional even if the 'icon_file' param has been provided, by
2605             default the first icon in the file will be used.
2606              
2607             Returns true on success, or throws an exception on error.
2608              
2609             =cut
2610              
2611             sub install_website {
2612             my $self = shift;
2613             my $website = Perl::Dist::Asset::Website->new(
2614             parent => $self,
2615             @_,
2616             );
2617              
2618             # Write the file directly to the image
2619             $website->write(
2620             File::Spec->catfile($self->image_dir, $website->file)
2621             );
2622              
2623             # Add the file to the files section of the inno script
2624             $self->add_file(
2625             source => $website->file,
2626             dest_dir => '{app}\\win32',
2627             );
2628              
2629             # Add the file to the icons section of the inno script
2630             $self->add_icon(
2631             name => $website->name,
2632             filename => '{app}\\win32\\' . $website->file,
2633             );
2634              
2635             return 1;
2636             }
2637              
2638              
2639              
2640              
2641              
2642             #####################################################################
2643             # Package Generation
2644              
2645             sub write {
2646             my $self = shift;
2647             $self->{output_file} ||= [];
2648             if ( $self->zip ) {
2649             push @{ $self->{output_file} }, $self->write_zip;
2650             }
2651             if ( $self->exe ) {
2652             push @{ $self->{output_file} }, $self->write_exe;
2653             }
2654             return 1;
2655             }
2656              
2657             =pod
2658              
2659             =head2 write_exe
2660              
2661             $self->write_exe;
2662              
2663             The C method is used to generate the compiled installer
2664             executable. It creates the entire installation file tree, and then
2665             executes InnoSetup to create the final executable.
2666              
2667             This method should only be called after all installation phases have
2668             been completed and all of the files for the distribution are in place.
2669              
2670             The executable file is written to the output directory, and the location
2671             of the file is printed to STDOUT.
2672              
2673             Returns true or throws an exception or error.
2674              
2675             =cut
2676              
2677             sub write_exe {
2678             my $self = shift;
2679              
2680             # Convert the environment to registry entries
2681             if ( @{$self->{env_path}} ) {
2682             my $value = "{olddata}";
2683             foreach my $array ( @{$self->{env_path}} ) {
2684             $value .= File::Spec::Win32->catdir(
2685             ';{app}', @$array,
2686             );
2687             }
2688             $self->add_env( PATH => $value );
2689             }
2690              
2691             $self->SUPER::write_exe(@_);
2692             }
2693              
2694             =pod
2695              
2696             =head2 write_zip
2697              
2698             The C method is used to generate a standalone .zip file
2699             containing the entire distribution, for situations in which a full
2700             installer executable is not wanted (such as for "Portable Perl"
2701             type installations).
2702              
2703             The executable file is written to the output directory, and the location
2704             of the file is printed to STDOUT.
2705              
2706             Returns true or throws an exception or error.
2707              
2708             =cut
2709              
2710             sub write_zip {
2711             my $self = shift;
2712             my $file = File::Spec->catfile(
2713             $self->output_dir, $self->output_base_filename . '.zip'
2714             );
2715             $self->trace("Generating zip at $file\n");
2716              
2717             # Create the archive
2718             my $zip = Archive::Zip->new;
2719              
2720             # Add the image directory to the root
2721             $zip->addTree( $self->image_dir, '' );
2722              
2723             # Set max compression for all members
2724             foreach my $member ( $zip->members ) {
2725             next if $member->isDirectory;
2726             $member->desiredCompressionLevel( 9 );
2727             }
2728              
2729             # Write out the file name
2730             $zip->writeToFileNamed( $file );
2731              
2732             return $file;
2733             }
2734              
2735              
2736              
2737              
2738              
2739             #####################################################################
2740             # Adding Inno-Setup Information
2741              
2742             sub add_icon {
2743             my $self = shift;
2744             my %params = @_;
2745             $params{name} = "{group}\\$params{name}";
2746             unless ( $params{filename} =~ /^\{/ ) {
2747             $params{filename} = "{app}\\$params{filename}";
2748             }
2749             $self->SUPER::add_icon(%params);
2750             }
2751              
2752             sub add_system {
2753             my $self = shift;
2754             my %params = @_;
2755             unless ( $params{filename} =~ /^\{/ ) {
2756             $params{filename} = "{app}\\$params{filename}";
2757             }
2758             $self->SUPER::add_system(%params);
2759             }
2760              
2761             sub add_run {
2762             my $self = shift;
2763             my %params = @_;
2764             unless ( $params{filename} =~ /^\{/ ) {
2765             $params{filename} = "{app}\\$params{filename}";
2766             }
2767             $self->SUPER::add_run(%params);
2768             }
2769              
2770             sub add_uninstallrun {
2771             my $self = shift;
2772             my %params = @_;
2773             unless ( $params{filename} =~ /^\{/ ) {
2774             $params{filename} = "{app}\\$params{filename}";
2775             }
2776             $self->SUPER::add_uninstallrun(%params);
2777             }
2778              
2779             sub add_env_path {
2780             my $self = shift;
2781             my @path = @_;
2782             my $dir = File::Spec->catdir(
2783             $self->image_dir, @path,
2784             );
2785             unless ( -d $dir ) {
2786             Carp::croak("PATH directory $dir does not exist");
2787             }
2788             push @{$self->{env_path}}, [ @path ];
2789             return 1;
2790             }
2791              
2792             sub get_env_path {
2793             my $self = shift;
2794             return join ';', map {
2795             File::Spec->catdir( $self->image_dir, @$_ )
2796             } @{$self->env_path};
2797             }
2798              
2799             sub get_inno_path {
2800             my $self = shift;
2801             return join ';', '{olddata}', map {
2802             File::Spec->catdir( '{app}', @$_ )
2803             } @{$self->env_path};
2804             }
2805              
2806             sub add_env_lib {
2807             my $self = shift;
2808             my @path = @_;
2809             my $dir = File::Spec->catdir(
2810             $self->image_dir, @path,
2811             );
2812             unless ( -d $dir ) {
2813             Carp::croak("INC directory $dir does not exist");
2814             }
2815             push @{$self->{env_lib}}, [ @path ];
2816             return 1;
2817             }
2818              
2819             sub get_env_lib {
2820             my $self = shift;
2821             return join ';', map {
2822             File::Spec->catdir( $self->image_dir, @$_ )
2823             } @{$self->env_lib};
2824             }
2825              
2826             sub get_inno_lib {
2827             my $self = shift;
2828             return join ';', '{olddata}', map {
2829             File::Spec->catdir( '{app}', @$_ )
2830             } @{$self->env_lib};
2831             }
2832              
2833             sub add_env_include {
2834             my $self = shift;
2835             my @path = @_;
2836             my $dir = File::Spec->catdir(
2837             $self->image_dir, @path,
2838             );
2839             unless ( -d $dir ) {
2840             Carp::croak("PATH directory $dir does not exist");
2841             }
2842             push @{$self->{env_include}}, [ @path ];
2843             return 1;
2844             }
2845              
2846             sub get_env_include {
2847             my $self = shift;
2848             return join ';', map {
2849             File::Spec->catdir( $self->image_dir, @$_ )
2850             } @{$self->env_include};
2851             }
2852              
2853             sub get_inno_include {
2854             my $self = shift;
2855             return join ';', '{olddata}', map {
2856             File::Spec->catdir( '{app}', @$_ )
2857             } @{$self->env_include};
2858             }
2859              
2860              
2861              
2862              
2863              
2864             #####################################################################
2865             # Patch Support
2866              
2867             # By default only use the default (as a default...)
2868             sub patch_include_path {
2869             my $self = shift;
2870             my $share = File::ShareDir::dist_dir('Perl-Dist');
2871             my $path = File::Spec->catdir(
2872             $share, 'default',
2873             );
2874             unless ( -d $path ) {
2875             die("Directory $path does not exist");
2876             }
2877             return [ $path ];
2878             }
2879              
2880             sub patch_pathlist {
2881             my $self = shift;
2882             return File::PathList->new(
2883             paths => $self->patch_include_path,
2884             );
2885             }
2886              
2887             # Cache this
2888             sub patch_template {
2889             $_[0]->{template_toolkit} or
2890             $_[0]->{template_toolkit} = Template->new(
2891             INCLUDE_PATH => $_[0]->patch_include_path,
2892             ABSOLUTE => 1,
2893             );
2894             }
2895              
2896             sub patch_file {
2897             my $self = shift;
2898             my $file = shift;
2899             my $file_tt = $file . '.tt';
2900             my $dir = shift;
2901             my $to = File::Spec->catfile( $dir, $file );
2902             my $pathlist = $self->patch_pathlist;
2903              
2904             # Locate the source file
2905             my $from = $pathlist->find_file( $file );
2906             my $from_tt = $pathlist->find_file( $file_tt );;
2907             unless ( defined $from and defined $from_tt ) {
2908             die "Missing or invalid file $file or $file_tt in pathlist search";
2909             }
2910              
2911             if ( $from_tt ne '' ) {
2912             # Generate the file
2913             my $hash = Params::Util::_HASH(shift) || {};
2914             my ($fh, $output) = File::Temp::tempfile();
2915             $self->trace("Generating $from_tt into temp file $output\n");
2916             $self->patch_template->process(
2917             $from_tt,
2918             { %$hash, self => $self },
2919             $fh,
2920             ) or die "Template processing failed for $from_tt";
2921              
2922             # Copy the file to the final location
2923             $fh->close;
2924             $self->_copy( $output => $to );
2925              
2926             } elsif ( $from ne '' ) {
2927             # Simple copy of the regular file to the target location
2928             $self->_copy( $from => $to );
2929              
2930             } else {
2931             die "Failed to find file $file";
2932             }
2933              
2934             return 1;
2935             }
2936              
2937             sub image_dir_url {
2938             my $self = shift;
2939             URI::file->new( $self->image_dir )->as_string;
2940             }
2941              
2942             # This is a temporary hack
2943             sub image_dir_quotemeta {
2944             my $self = shift;
2945             my $string = $self->image_dir;
2946             $string =~ s/\\/\\\\/g;
2947             return $string;
2948             }
2949              
2950              
2951              
2952              
2953              
2954             #####################################################################
2955             # Support Methods
2956              
2957             sub trace {
2958             my $self = shift;
2959             if ( $self->{trace} ) {
2960             print $_[0];
2961             }
2962             return 1;
2963             }
2964              
2965             sub dir {
2966             File::Spec->catdir( shift->image_dir, @_ );
2967             }
2968              
2969             sub file {
2970             File::Spec->catfile( shift->image_dir, @_ );
2971             }
2972              
2973             sub user_agent {
2974             my $self = shift;
2975             unless ( $self->{user_agent} ) {
2976             if ( $self->{user_agent_cache} ) {
2977             SCOPE: {
2978             # Temporarily set $ENV{HOME} to the File::HomeDir
2979             # version while loading the module.
2980             local $ENV{HOME} ||= File::HomeDir->my_home;
2981             require LWP::UserAgent::WithCache;
2982             }
2983             $self->{user_agent} = LWP::UserAgent::WithCache->new( {
2984             namespace => 'perl-dist',
2985             cache_root => $self->user_agent_directory,
2986             cache_depth => 0,
2987             default_expires_in => 86400 * 30,
2988             show_progress => 1,
2989             } );
2990             } else {
2991             $self->{user_agent} = LWP::UserAgent->new(
2992             agent => ref($self) . '/' . ($VERSION || '0.00'),
2993             timeout => 30,
2994             show_progress => 1,
2995             );
2996             }
2997             }
2998             return $self->{user_agent};
2999             }
3000              
3001             sub user_agent_cache {
3002             $_[0]->{user_agent_cache};
3003             }
3004              
3005             sub user_agent_directory {
3006             my $self = shift;
3007             my $path = ref($self);
3008             $path =~ s/::/-/g;
3009             my $dir = File::Spec->catdir(
3010             File::HomeDir->my_data,
3011             'Perl', $path,
3012             );
3013             unless ( -d $dir ) {
3014             unless ( File::Path::mkpath( $dir, { verbose => 0 } ) ) {
3015             die("Failed to create $dir");
3016             }
3017             }
3018             unless ( -w $dir ) {
3019             die("No write permissions for LWP::UserAgent cache '$dir'");
3020             }
3021             return $dir;
3022             }
3023              
3024             sub _mirror {
3025             my ($self, $url, $dir) = @_;
3026             my $file = $url;
3027             $file =~ s|.+\/||;
3028             my $target = File::Spec->catfile( $dir, $file );
3029             if ( $self->offline and -f $target ) {
3030             return $target;
3031             }
3032             if ( $self->offline and ! $url =~ m|^file://| ) {
3033             $self->trace("Error: Currently offline, cannot download.\n");
3034             exit(0);
3035             }
3036             File::Path::mkpath($dir);
3037             $| = 1;
3038              
3039             $self->trace("Downloading file $url...\n");
3040             if ( $url =~ m|^file://| ) {
3041             # Don't use WithCache for files (it generates warnings)
3042             my $ua = LWP::UserAgent->new;
3043             my $r = $ua->mirror( $url, $target );
3044             if ( $r->is_error ) {
3045             $self->trace(" Error getting $url:\n" . $r->as_string . "\n");
3046             } elsif ( $r->code == HTTP::Status::RC_NOT_MODIFIED ) {
3047             $self->trace("(already up to date)\n");
3048             }
3049             } else {
3050             # my $ua = $self->user_agent;
3051             my $ua = LWP::UserAgent->new;
3052             my $r = $ua->mirror( $url, $target );
3053             if ( $r->is_error ) {
3054             $self->trace(" Error getting $url:\n" . $r->as_string . "\n");
3055             } elsif ( $r->code == HTTP::Status::RC_NOT_MODIFIED ) {
3056             $self->trace("(already up to date)\n");
3057             }
3058             }
3059              
3060             return $target;
3061             }
3062              
3063             sub _copy {
3064             my ($self, $from, $to) = @_;
3065             my $basedir = File::Basename::dirname( $to );
3066             File::Path::mkpath($basedir) unless -e $basedir;
3067             $self->trace("Copying $from to $to\n");
3068             if ( -f $to and ! -w $to ) {
3069             require Win32::File::Object;
3070              
3071             # Make sure it isn't readonly
3072             my $file = Win32::File::Object->new( $to, 1 );
3073             my $readonly = $file->readonly;
3074             $file->readonly(0);
3075              
3076             # Do the actual copy
3077             File::Copy::Recursive::rcopy( $from, $to ) or die $!;
3078              
3079             # Set it back to what it was
3080             $file->readonly($readonly);
3081             } else {
3082             File::Copy::Recursive::rcopy( $from, $to ) or die $!;
3083             }
3084             return 1;
3085             }
3086              
3087             sub _move {
3088             my ($self, $from, $to) = @_;
3089             my $basedir = File::Basename::dirname( $to );
3090             File::Path::mkpath($basedir) unless -e $basedir;
3091             $self->trace("Moving $from to $to\n");
3092             File::Copy::Recursive::rmove( $from, $to ) or die $!;
3093             }
3094              
3095             sub _pushd {
3096             my $self = shift;
3097             my $dir = File::Spec->catdir(@_);
3098             $self->trace("Lexically changing directory to $dir...\n");
3099             return File::pushd::pushd( $dir );
3100             }
3101              
3102             sub _make {
3103             my $self = shift;
3104             my @params = @_;
3105             $self->trace(join(' ', '>', $self->bin_make, @params) . "\n");
3106             $self->_run3( $self->bin_make, @params ) or die "make failed";
3107             die "make failed (OS error)" if ( $? >> 8 );
3108             return 1;
3109             }
3110              
3111             sub _perl {
3112             my $self = shift;
3113             my @params = @_;
3114             $self->trace(join(' ', '>', $self->bin_perl, @params) . "\n");
3115             $self->_run3( $self->bin_perl, @params ) or die "perl failed";
3116             die "perl failed (OS error)" if ( $? >> 8 );
3117             return 1;
3118             }
3119              
3120             sub _run3 {
3121             my $self = shift;
3122              
3123             # Remove any Perl installs from PATH to prevent
3124             # "which" discovering stuff it shouldn't.
3125             my @path = split /;/, $ENV{PATH};
3126             my @keep = ();
3127             foreach my $p ( @path ) {
3128             # Strip any path that doesn't exist
3129             next unless -d $p;
3130              
3131             # Strip any path that contains either dmake or perl.exe.
3132             # This should remove both the ...\c\bin and ...\perl\bin
3133             # parts of the paths that Vanilla/Strawberry added.
3134             next if -f File::Spec->catfile( $p, 'dmake.exe' );
3135             next if -f File::Spec->catfile( $p, 'perl.exe' );
3136              
3137             # Strip any path that contains either unzip or gzip.exe.
3138             # These two programs cause perl to fail its own tests.
3139             next if -f File::Spec->catfile( $p, 'unzip.exe' );
3140             next if -f File::Spec->catfile( $p, 'gzip.exe' );
3141              
3142             push @keep, $p;
3143             }
3144              
3145             # Reset the environment
3146             local $ENV{LIB} = '';
3147             local $ENV{INCLUDE} = '';
3148             local $ENV{PERL5LIB} = '';
3149             local $ENV{PATH} = $self->get_env_path . ';' . join( ';', @keep );
3150              
3151             # Execute the child process
3152             return IPC::Run3::run3( [ @_ ],
3153             \undef,
3154             $self->debug_stdout,
3155             $self->debug_stderr,
3156             );
3157             }
3158              
3159             sub _extract {
3160             my ( $self, $from, $to ) = @_;
3161             File::Path::mkpath($to);
3162             my $wd = $self->_pushd($to);
3163             $self->trace("Extracting $from...\n");
3164             if ( $from =~ m{\.zip\z} ) {
3165             my $zip = Archive::Zip->new( $from );
3166             $zip->extractTree();
3167              
3168             } elsif ( $from =~ m{\.tar\.gz|\.tgz} ) {
3169             local $Archive::Tar::CHMOD = 0;
3170             Archive::Tar->extract_archive($from, 1);
3171              
3172             } else {
3173             die "Didn't recognize archive type for $from";
3174             }
3175             return 1;
3176             }
3177              
3178              
3179             sub _extract_filemap {
3180             my ( $self, $archive, $filemap, $basedir, $file_only ) = @_;
3181              
3182             if ( $archive =~ m{\.zip\z} ) {
3183             my $zip = Archive::Zip->new( $archive );
3184             my $wd = $self->_pushd($basedir);
3185             while ( my ($f, $t) = each %$filemap ) {
3186             $self->trace("Extracting $f to $t\n");
3187             my $dest = File::Spec->catfile( $basedir, $t );
3188             $zip->extractTree( $f, $dest );
3189             }
3190              
3191             } elsif ( $archive =~ m{\.tar\.gz|\.tgz} ) {
3192             local $Archive::Tar::CHMOD = 0;
3193             my $tar = Archive::Tar->new( $archive );
3194             for my $file ( $tar->get_files ) {
3195             my $f = $file->full_path;
3196             my $canon_f = File::Spec::Unix->canonpath( $f );
3197             for my $tgt ( keys %$filemap ) {
3198             my $canon_tgt = File::Spec::Unix->canonpath( $tgt );
3199             my $t;
3200              
3201             # say "matching $canon_f vs $canon_tgt";
3202             if ( $file_only ) {
3203             next unless $canon_f =~ m{\A([^/]+[/])?\Q$canon_tgt\E\z}i;
3204             ($t = $canon_f) =~ s{\A([^/]+[/])?\Q$canon_tgt\E\z}
3205             {$filemap->{$tgt}}i;
3206              
3207             } else {
3208             next unless $canon_f =~ m{\A([^/]+[/])?\Q$canon_tgt\E}i;
3209             ($t = $canon_f) =~ s{\A([^/]+[/])?\Q$canon_tgt\E}
3210             {$filemap->{$tgt}}i;
3211             }
3212             my $full_t = File::Spec->catfile( $basedir, $t );
3213             $self->trace("Extracting $f to $full_t\n");
3214             $tar->extract_file( $f, $full_t );
3215             }
3216             }
3217              
3218             } else {
3219             die "Didn't recognize archive type for $archive";
3220             }
3221              
3222             return 1;
3223             }
3224              
3225             # Convert a .dll to an .a file
3226             sub _dll_to_a {
3227             my $self = shift;
3228             my %params = @_;
3229             unless ( $self->bin_dlltool ) {
3230             Carp::croak("Required method bin_dlltool is not defined");
3231             }
3232              
3233             # Source file
3234             my $source = $params{source};
3235             if ( $source and ! $source =~ /\.dll$/ ) {
3236             Carp::croak("Missing or invalid source param");
3237             }
3238              
3239             # Target .dll file
3240             my $dll = $params{dll};
3241             unless ( $dll and $dll =~ /\.dll/ ) {
3242             Carp::croak("Missing or invalid .dll file");
3243             }
3244              
3245             # Target .def file
3246             my $def = $params{def};
3247             unless ( $def and $def =~ /\.def$/ ) {
3248             Carp::croak("Missing or invalid .def file");
3249             }
3250              
3251             # Target .a file
3252             my $_a = $params{a};
3253             unless ( $_a and $_a =~ /\.a$/ ) {
3254             Carp::croak("Missing or invalid .a file");
3255             }
3256              
3257             # Step 1 - Copy the source .dll to the target if needed
3258             unless ( ($source and -f $source) or -f $dll ) {
3259             Carp::croak("Need either a source or dll param");
3260             }
3261             if ( $source ) {
3262             $self->_move( $source => $dll );
3263             }
3264              
3265             # Step 2 - Generate the .def from the .dll
3266             SCOPE: {
3267             my $bin = $self->bin_pexports;
3268             unless ( $bin ) {
3269             Carp::croak("Required method bin_pexports is not defined");
3270             }
3271             my $ok = ! system("$bin $dll > $def");
3272             unless ( $ok and -f $def ) {
3273             Carp::croak("Failed to generate .def file");
3274             }
3275             }
3276              
3277             # Step 3 - Generate the .a from the .def
3278             SCOPE: {
3279             my $bin = $self->bin_dlltool;
3280             unless ( $bin ) {
3281             Carp::croak("Required method bin_dlltool is not defined");
3282             }
3283             my $ok = ! system("$bin -dllname $dll --def $def --output-lib $_a");
3284             unless ( $ok and -f $_a ) {
3285             Carp::croak("Failed to generate .a file");
3286             }
3287             }
3288              
3289             return 1;
3290             }
3291              
3292             sub make_path {
3293             my $class = shift;
3294             my $dir = File::Spec->rel2abs(
3295             File::Spec->catdir(
3296             File::Spec->curdir, @_,
3297             ),
3298             );
3299             File::Path::mkpath( $dir ) unless -d $dir;
3300             unless ( -d $dir ) {
3301             Carp::croak("Failed to make_path for $dir");
3302             }
3303             return $dir;
3304             }
3305              
3306             sub remake_path {
3307             my $class = shift;
3308             my $dir = File::Spec->rel2abs(
3309             File::Spec->catdir(
3310             File::Spec->curdir, @_,
3311             ),
3312             );
3313             File::Remove::remove( \1, $dir ) if -d $dir;
3314             File::Path::mkpath( $dir );
3315             unless ( -d $dir ) {
3316             Carp::croak("Failed to make_path for $dir");
3317             }
3318             return $dir;
3319             }
3320              
3321             1;
3322              
3323             =pod
3324              
3325             =head1 SUPPORT
3326              
3327             Bugs should be reported via the CPAN bug tracker
3328              
3329             L
3330              
3331             For other issues, or commercial enhancement or support, contact the author.
3332              
3333             =head1 AUTHOR
3334              
3335             Adam Kennedy Eadamk@cpan.orgE
3336              
3337             =head1 SEE ALSO
3338              
3339             L, L, L
3340              
3341             =head1 COPYRIGHT
3342              
3343             Copyright 2009 Adam Kennedy.
3344              
3345             This program is free software; you can redistribute
3346             it and/or modify it under the same terms as Perl itself.
3347              
3348             The full text of the license can be found in the
3349             LICENSE file included with this module.
3350              
3351             =cut