File Coverage

blib/lib/Shipwright/Script/Import.pm
Criterion Covered Total %
statement 30 198 15.1
branch 0 96 0.0
condition 0 56 0.0
subroutine 10 14 71.4
pod 0 2 0.0
total 40 366 10.9


line stmt bran cond sub pod time code
1             package Shipwright::Script::Import;
2              
3 1     1   1194 use strict;
  1         2  
  1         33  
4 1     1   5 use warnings;
  1         1  
  1         30  
5              
6 1     1   5 use base qw/App::CLI::Command Shipwright::Base Shipwright::Script/;
  1         2  
  1         144  
7             __PACKAGE__->mk_accessors(
8             qw/comment no_follow build_script require_yml include_dual_lifed
9             name test_script extra_tests overwrite min_perl_version skip version as
10             skip_recommends skip_all_test_requires skip_all_recommends skip_installed
11             no_default_build skip_all_build_requires
12             /
13             );
14              
15 1     1   5 use Shipwright;
  1         2  
  1         7  
16 1     1   23 use File::Spec::Functions qw/catfile catdir splitdir/;
  1         5  
  1         50  
17 1     1   5 use Shipwright::Util;
  1         1  
  1         92  
18 1     1   10 use File::Copy qw/copy move/;
  1         1  
  1         48  
19 1     1   4 use File::Temp qw/tempdir/;
  1         1  
  1         38  
20 1     1   6 use Config;
  1         2  
  1         33  
21 1     1   5 use List::MoreUtils qw/firstidx/;
  1         2  
  1         9  
22              
23             sub options {
24             (
25 0     0 0   'm|comment=s' => 'comment',
26             'name=s' => 'name',
27             'no-follow' => 'no_follow',
28             'build-script=s' => 'build_script',
29             'require-yml=s' => 'require_yml',
30             'test-script' => 'test_script',
31             'extra-tests' => 'extra_tests',
32             'overwrite' => 'overwrite',
33             'min-perl-version=s' => 'min_perl_version',
34             'skip=s' => 'skip',
35             'version=s' => 'version',
36             'as=s' => 'as',
37             'skip-recommends=s' => 'skip_recommends',
38             'skip-all-recommends' => 'skip_all_recommends',
39             'skip-all-test-requires' => 'skip_all_test_requires',
40             'skip-all-build-requires' => 'skip_all_build_requires',
41             'skip-installed' => 'skip_installed',
42             'include-dual-lifed' => 'include_dual_lifed',
43             'no-default-build' => 'no_default_build',
44             );
45             }
46              
47             my ( %imported, $version );
48              
49             sub run {
50 0     0 0   my $self = shift;
51 0           my @sources = @_;
52 0           my $source;
53 0           $source = $sources[0];
54 0 0 0       confess_or_die "--name and --as args are not supported when importing multiple sources"
55             if @sources > 1 && $self->name;
56              
57             {
58 0           require version;
  0            
59 0   0       my $version =
60             version->new( $self->min_perl_version || $Config{version} );
61 0           $self->min_perl_version( $version->numify );
62             }
63              
64 0           my $shipwright = Shipwright->new( repository => $self->repository, );
65              
66 0   0       my $order = $shipwright->backend->order || [];
67 0           my $installed = { map { $_ => 1 } @$order };
  0            
68              
69 0 0 0       if ( $self->name && !$source ) {
70              
71             # don't have source specified, use the one in repo
72 0   0       my $map = $shipwright->backend->map || {};
73              
74 0   0       my $source_yml = $shipwright->backend->source || {};
75 0           my $branches = $shipwright->backend->branches;
76              
77 0           my $r_map = { reverse %$map };
78 0 0         if ( $r_map->{ $self->name } ) {
    0          
79 0           $source = 'cpan:' . $r_map->{ $self->name };
80             }
81             elsif ($branches) {
82 0           $source = $source_yml->{ $self->name };
83 0 0         if ( ref $source ) {
84 0   0       $source = $source->{ $self->as || $branches->{ $self->name }[0] };
85             }
86             }
87             else {
88 0           $source = $source_yml->{$self->name};
89             }
90              
91 0           @sources = $source;
92             }
93              
94 0 0         confess_or_die "we need source arg\n" unless $source;
95              
96 0 0         if ( $self->extra_tests ) {
    0          
97              
98 0           $self->log->info( 'going to import extra_tests' );
99 0           $shipwright->backend->import(
100             source => $source,
101             comment => 'import extra tests',
102             _extra_tests => 1,
103             );
104             }
105             elsif ( $self->test_script ) {
106 0           $self->log->info('going to import test_script');
107 0           $shipwright->backend->test_script( source => $source );
108             }
109             else {
110 0   0       $self->skip( { map { $_ => 1 } split /\s*,\s*/, $self->skip || '' } );
  0            
111 0           $self->skip_recommends(
112 0   0       { map { $_ => 1 } split /\s*,\s*/, $self->skip_recommends || '' } );
113              
114 0 0         if ( $self->name ) {
115 0 0         if ( $self->name =~ /::/ ) {
116 0           my $name = $self->name;
117 0           $self->log->warn(
118             "$name contains '::', will treat it as '-'");
119 0           $name =~ s/::/-/g;
120 0           $self->name($name);
121             }
122 0 0         if ( $self->name !~ /^[-.\w]+$/ ) {
123 0           confess_or_die
124             qq{name can only have alphanumeric characters, "." and "-"\n};
125             }
126             }
127              
128 0           for my $source (@sources) {
129 0 0         if ( $source =~ /^(perl-[\d.]+)(?:\.tar\.gz)?$/ ) {
    0          
130 0           $source = "http://www.cpan.org/src/$1.tar.gz";
131             }
132             elsif ( $source eq 'perl' ) {
133 0 0         if ( $self->version ) {
134 0           $source =
135             "http://www.cpan.org/src/perl-"
136             . $self->version
137             . '.tar.gz';
138             }
139             else {
140 0           confess_or_die
141             "unknown perl version, please specify --version";
142             }
143             }
144              
145 0           my $shipwright = Shipwright->new(
146             repository => $self->repository,
147             source => $source,
148             name => $self->name,
149             follow => !$self->no_follow,
150             min_perl_version => $self->min_perl_version,
151             include_dual_lifed => $self->include_dual_lifed,
152             skip => $self->skip,
153             version => $self->version,
154             installed => $installed,
155             skip_recommends => $self->skip_recommends,
156             skip_all_recommends => $self->skip_all_recommends,
157             skip_all_test_requires => $self->skip_all_test_requires,
158             skip_all_build_requires => $self->skip_all_build_requires,
159             skip_installed => $self->skip_installed,
160             );
161              
162 0 0 0       confess_or_die "cpan dists can't be branched"
163             if $shipwright->source->isa('Shipwright::Source::CPAN')
164             && $self->as;
165              
166 0 0         unless ( $self->overwrite ) {
167              
168             # skip already imported dists
169 0           $shipwright->source->skip(
170 0 0         { %{ $self->skip }, %{ $shipwright->backend->map || {} } }
  0            
171             );
172             }
173              
174             dump_yaml_file(
175 0   0       $shipwright->source->map_path,
176             $shipwright->backend->map || {},
177             );
178              
179 0   0       dump_yaml_file(
180             $shipwright->source->url_path,
181             $shipwright->backend->source || {},
182             );
183              
184 0           $source = $shipwright->source->run(
185             copy => { '__require.yml' => $self->require_yml }, );
186              
187 0 0         next unless $source; # if running the source returned undef, we should skip
188              
189 0           $version =
190             load_yaml_file( $shipwright->source->version_path );
191 0           my $name = ( splitdir( $source ) )[-1];
192              
193 0           my $base = parent_dir($source);
194              
195 0           my $script_dir;
196 0 0         if ( -e catdir( $base, '__scripts', $name ) ) {
197 0           $script_dir = catdir( $base, '__scripts', $name );
198             }
199             else {
200              
201             # Source part doesn't have script stuff, so we need to create by ourselves.
202 0           $script_dir = tempdir(
203             'shipwright_script_import_XXXXXX',
204             CLEANUP => 1,
205             TMPDIR => 1,
206             );
207              
208 0 0         if ( my $script = $self->build_script ) {
    0          
209 0 0         if ( $script =~ /\.pl$/ ) {
210 0           copy( $script, catfile( $script_dir, 'build.pl' ) );
211             }
212             else {
213 0           copy( $script, catfile( $script_dir, 'build' ) );
214             }
215             }
216             elsif ( ! $self->no_default_build ) {
217 0           $self->_generate_build( $source, $script_dir, $shipwright );
218             }
219             }
220              
221 0 0         if ( $self->no_follow ) {
222 0 0         open my $fh, '>', catfile( $script_dir, 'require.yml' ) or
223             confess_or_die "can't write to $script_dir/require.yml: $!\n";
224 0           print $fh "---\n";
225 0           close $fh;
226             }
227             else {
228 0           $self->_import_req( $source, $shipwright, $script_dir );
229              
230 0 0         if ( -e catfile( $source, '__require.yml' ) ) {
231 0 0         move(
232             catfile( $source, '__require.yml' ),
233             catfile( $script_dir, 'require.yml' )
234             ) or confess_or_die "move __require.yml failed: $!\n";
235             }
236             }
237              
238 0           my $branches =
239             load_yaml_file( $shipwright->source->branches_path );
240 0   0       $branches ||= {};
241              
242 0           $self->log->fatal( "importing $name" );
243 0 0 0       $shipwright->backend->import(
    0 0        
244             source => $source,
245             comment => $self->comment || 'import ' . $source,
246              
247             # import anyway for the main dist, unless it's already imported in this run
248             overwrite => $imported{$name} ? 0 : 1,
249             version => $version->{$name},
250             as => $self->as,
251             branches =>
252             $shipwright->source->isa('Shipwright::Source::Shipyard')
253             ? ( $branches->{$name} || [] )
254             : (undef),
255             );
256              
257 0 0         $shipwright->backend->import(
258             source => $source,
259             comment => 'import scripts for ' . $source,
260             build_script => $script_dir,
261             overwrite => $imported{$name} ? 0 : 1,
262             );
263 0           $imported{$name}++;
264              
265             # merge new map into map.yml in repo
266 0   0       my $new_map =
267             load_yaml_file( $shipwright->source->map_path )
268             || {};
269 0 0         $shipwright->backend->map(
270 0           { %{ $shipwright->backend->map || {} }, %$new_map } );
271              
272 0   0       my $new_url =
273             load_yaml_file( $shipwright->source->url_path )
274             || {};
275 0           my $source_url = delete $new_url->{$name};
276              
277 0 0 0       if ( $name !~ /^cpan-/
278             || $shipwright->source->isa('Shipwright::Source::Shipyard') )
279             {
280 0   0       my $source = $shipwright->backend->source || {};
281 0 0         if ( $shipwright->source->isa('Shipwright::Source::Shipyard') )
282             {
283 0           $source->{$name} = $source_url;
284             }
285             else {
286 0   0       $source->{$name}{ $self->as || 'vendor' } = $source_url;
287             }
288 0           $shipwright->backend->source($source);
289             }
290             }
291              
292             }
293              
294 0           $self->log->fatal( 'successfully imported' );
295              
296             }
297              
298             # _import_req: import required dists for a dist
299              
300             sub _import_req {
301 0     0     my $self = shift;
302 0           my $source = shift;
303 0           my $shipwright = shift;
304 0           my $script_dir = shift;
305              
306 0           my $name = (splitdir( $source ))[-1];
307 0           $self->log->info( "going to import requirements for $name" );
308              
309 0           my $require_file = catfile( $source, '__require.yml' );
310 0 0         $require_file = catfile( $script_dir, 'require.yml' )
311             unless -e catfile( $source, '__require.yml' );
312              
313 0           my $dir = parent_dir($source);
314              
315 0           my $map_file = catfile( $dir, 'map.yml' );
316              
317 0 0         if ( -e $require_file ) {
318 0           my $req = load_yaml_file($require_file);
319 0           my $map = {};
320              
321 0 0         if ( -e $map_file ) {
322 0           $map = load_yaml_file($map_file);
323              
324             }
325              
326 0           opendir my ($d), $dir;
327 0           my @sources = readdir $d;
328 0           close $d;
329              
330 0           for my $type (qw/requires configure_requires recommends build_requires test_requires/) {
331 0           for my $module ( keys %{ $req->{$type} } ) {
  0            
332 0   0       my $dist = $map->{$module} || $module;
333 0           $dist =~ s/::/-/g;
334              
335 0 0         unless ( $imported{$dist}++ ) {
336              
337 0           my ($name) = grep { $_ eq $dist } @sources;
  0            
338 0 0         unless ($name) {
339 0           $self->log->warn(
340             "missing $dist in source which is for "
341             . $source );
342 0           next;
343             }
344              
345 0           $self->log->fatal( "import $name" );
346 0           my $s = catdir( $dir, $name );
347              
348 0           my $script_dir;
349 0 0         if ( -e catdir( $dir, '__scripts', $dist ) ) {
350 0           $script_dir = catdir( $dir, '__scripts', $dist );
351             }
352             else {
353 0           $script_dir = tempdir(
354             'shipwright_script_import_XXXXXX',
355             CLEANUP => 1,
356             TMPDIR => 1,
357             );
358 0 0         if ( -e catfile( $s, '__require.yml' ) ) {
359 0 0         move(
360             catfile( $s, '__require.yml' ),
361             catfile( $script_dir, 'require.yml' )
362             ) or confess_or_die "move $s/__require.yml failed: $!\n";
363             }
364              
365 0           $self->_generate_build( $s, $script_dir, $shipwright );
366             }
367              
368 0           $self->_import_req( $s, $shipwright, $script_dir );
369              
370 0           my $branches = load_yaml_file(
371             $shipwright->source->branches_path );
372 0 0 0       $shipwright->backend->import(
373             comment => 'deps for ' . $source,
374             source => $s,
375             overwrite => $self->overwrite,
376             version => $version->{$dist},
377             branches => $shipwright->source->isa(
378             'Shipwright::Source::Shipyard')
379             ? ( $branches->{$dist} || [] )
380             : (undef),
381             );
382 0           $shipwright->backend->import(
383             source => $s,
384             comment => 'import scripts for ' . $s,
385             build_script => $script_dir,
386             overwrite => $self->overwrite,
387             );
388 0 0         if (
389             $shipwright->source->isa(
390             'Shipwright::Source::Shipyard')
391             )
392             {
393 0   0       my $new_url =
394             load_yaml_file( $shipwright->source->url_path )
395             || {};
396 0           my $source_url = delete $new_url->{$dist};
397 0   0       my $source = $shipwright->backend->source || {};
398 0           $source->{$dist} = $source_url;
399 0           $shipwright->backend->source($source);
400             }
401             }
402             }
403             }
404             }
405              
406             }
407              
408             # _generate_build:
409             # automatically generate build script if not provided
410              
411             sub _generate_build {
412 0     0     my $self = shift;
413 0           my $source_dir = shift;
414 0           my $script_dir = shift;
415 0           my $shipwright = shift;
416              
417 0           my ($name) = $source_dir =~ /([-\w.]+)$/;
418              
419 0           my @commands;
420 0 0         if ( $name eq 'perl' ) {
    0          
    0          
    0          
    0          
421 0           $self->log->info( 'detected perl source' );
422 0           @commands = (
423             'configure: sh Configure -de -Dprefix=%%INSTALL_BASE%% -Dinstallstyle=lib/perl5',
424             'make: %%MAKE%%',
425             'test: %%MAKE%% test',
426             'install: %%MAKE%% install',
427             'clean: %%MAKE%% clean'
428             );
429             }
430             elsif ( -f catfile( $source_dir, 'Build.PL' ) ) {
431 0           $self->log->info( 'detected Module::Build build system' );
432 0           @commands = (
433             'configure: %%PERL%% %%MODULE_BUILD_BEFORE_BUILD_PL%% Build.PL --install_base=%%INSTALL_BASE%% --install_path lib=%%INSTALL_BASE%%/lib/perl5 --install_path arch=%%INSTALL_BASE%%/lib/perl5',
434             'make: %%PERL%% %%MODULE_BUILD_BEFORE_BUILD%% Build',
435             'test: %%PERL%% %%MODULE_BUILD_BEFORE_BUILD%% Build test',
436             'install: %%PERL%% %%MODULE_BUILD_BEFORE_BUILD%% Build install',
437             'clean: %%PERL%% %%MODULE_BUILD_BEFORE_BUILD%% Build realclean',
438             );
439             }
440             elsif ( -f catfile( $source_dir, 'Makefile.PL' ) ) {
441 0           $self->log->info( 'detected ExtUtils::MakeMaker build system or alike' );
442             # XXX when only support 5.8.9+, we can change it to INSTALL_BASE=%%INSTALL_BASE%%
443             # because LIB=.../lib/perl5 is so ugly and not so right
444 0           @commands = (
445             'configure: %%PERL%% Makefile.PL LIB=%%INSTALL_BASE%%/lib/perl5/ PREFIX=%%INSTALL_BASE%% INSTALLSITEARCH=%%INSTALL_BASE%%/lib/perl5 INSTALLARCHLIB=%%INSTALL_BASE%%/lib/perl5 %%MAKEMAKER_CONFIGURE_EXTRA%%',
446             'make: %%MAKE%%',
447             'test: %%MAKE%% test',
448             'install: %%MAKE%% install',
449             'clean: %%MAKE%% clean',
450             );
451             }
452             elsif ( -f catfile( $source_dir, 'configure' ) ) {
453 0           $self->log->info( 'detected autoconf build system' );
454 0           @commands = (
455             'configure: ./configure --prefix=%%INSTALL_BASE%%',
456             'make: %%MAKE%%',
457             'install: %%MAKE%% install',
458             'clean: %%MAKE%% clean',
459             );
460             }
461             elsif ( -f catfile( $source_dir, 'configure.cmake' ) ) {
462 0           $self->log->info( 'detected cmake build system' );
463 0           @commands = (
464             'configure: cmake . -DCMAKE_INSTALL_PREFIX=%%INSTALL_BASE%%',
465             'make: %%MAKE%%',
466             'install: %%MAKE%% install',
467             'clean: %%MAKE%% clean',
468             );
469             }
470             else {
471 0           $self->log->warn(<
472             unknown build system for this dist; you MUST manually edit /scripts/$name/build
473             or provide a build.pl file or this dist will not be built!
474             EOF
475 0           $self->log->warn("no idea how to build $source_dir");
476              
477             # stub build file to provide the user something to go from
478 0           @commands = (
479             '# Edit this file to specify commands for building this dist.',
480             '# See the perldoc for Shipwright::Manual::CustomizeBuild for more',
481             '# info.',
482             'configure: ',
483             'make: ',
484             'test: ',
485             'install: ',
486             'clean: ',
487             );
488             }
489              
490 0 0         open my $fh, '>', catfile( $script_dir, 'build' ) or confess_or_die $@;
491 0           print $fh $_, "\n" for @commands;
492 0           close $fh;
493             }
494              
495             1;
496              
497             __END__