1#line 1
2package Module::Install::Metadata;
3
4use strict 'vars';
5use Module::Install::Base;
6
7use vars qw{$VERSION $ISCORE @ISA};
8BEGIN {
9	$VERSION = '0.77';
10	$ISCORE  = 1;
11	@ISA     = qw{Module::Install::Base};
12}
13
14my @scalar_keys = qw{
15	name
16	module_name
17	abstract
18	author
19	version
20	distribution_type
21	tests
22	installdirs
23};
24
25my @tuple_keys = qw{
26	configure_requires
27	build_requires
28	requires
29	recommends
30	bundles
31	resources
32};
33
34my @resource_keys = qw{
35	homepage
36	bugtracker
37	repository
38};
39
40sub Meta              { shift          }
41sub Meta_ScalarKeys   { @scalar_keys   }
42sub Meta_TupleKeys    { @tuple_keys    }
43sub Meta_ResourceKeys { @resource_keys }
44
45foreach my $key ( @scalar_keys ) {
46	*$key = sub {
47		my $self = shift;
48		return $self->{values}{$key} if defined wantarray and !@_;
49		$self->{values}{$key} = shift;
50		return $self;
51	};
52}
53
54foreach my $key ( @resource_keys ) {
55	*$key = sub {
56		my $self = shift;
57		unless ( @_ ) {
58			return () unless $self->{values}{resources};
59			return map  { $_->[1] }
60			       grep { $_->[0] eq $key }
61			       @{ $self->{values}{resources} };
62		}
63		return $self->{values}{resources}{$key} unless @_;
64		my $uri = shift or die(
65			"Did not provide a value to $key()"
66		);
67		$self->resources( $key => $uri );
68		return 1;
69	};
70}
71
72sub requires {
73	my $self = shift;
74	while ( @_ ) {
75		my $module  = shift or last;
76		my $version = shift || 0;
77		push @{ $self->{values}{requires} }, [ $module, $version ];
78	}
79	$self->{values}{requires};
80}
81
82sub build_requires {
83	my $self = shift;
84	while ( @_ ) {
85		my $module  = shift or last;
86		my $version = shift || 0;
87		push @{ $self->{values}{build_requires} }, [ $module, $version ];
88	}
89	$self->{values}{build_requires};
90}
91
92sub configure_requires {
93	my $self = shift;
94	while ( @_ ) {
95		my $module  = shift or last;
96		my $version = shift || 0;
97		push @{ $self->{values}{configure_requires} }, [ $module, $version ];
98	}
99	$self->{values}{configure_requires};
100}
101
102sub recommends {
103	my $self = shift;
104	while ( @_ ) {
105		my $module  = shift or last;
106		my $version = shift || 0;
107		push @{ $self->{values}{recommends} }, [ $module, $version ];
108	}
109	$self->{values}{recommends};
110}
111
112sub bundles {
113	my $self = shift;
114	while ( @_ ) {
115		my $module  = shift or last;
116		my $version = shift || 0;
117		push @{ $self->{values}{bundles} }, [ $module, $version ];
118	}
119	$self->{values}{bundles};
120}
121
122# Resource handling
123my %lc_resource = map { $_ => 1 } qw{
124	homepage
125	license
126	bugtracker
127	repository
128};
129
130sub resources {
131	my $self = shift;
132	while ( @_ ) {
133		my $name  = shift or last;
134		my $value = shift or next;
135		if ( $name eq lc $name and ! $lc_resource{$name} ) {
136			die("Unsupported reserved lowercase resource '$name'");
137		}
138		$self->{values}{resources} ||= [];
139		push @{ $self->{values}{resources} }, [ $name, $value ];
140	}
141	$self->{values}{resources};
142}
143
144# Aliases for build_requires that will have alternative
145# meanings in some future version of META.yml.
146sub test_requires      { shift->build_requires(@_) }
147sub install_requires   { shift->build_requires(@_) }
148
149# Aliases for installdirs options
150sub install_as_core    { $_[0]->installdirs('perl')   }
151sub install_as_cpan    { $_[0]->installdirs('site')   }
152sub install_as_site    { $_[0]->installdirs('site')   }
153sub install_as_vendor  { $_[0]->installdirs('vendor') }
154
155sub sign {
156	my $self = shift;
157	return $self->{values}{sign} if defined wantarray and ! @_;
158	$self->{values}{sign} = ( @_ ? $_[0] : 1 );
159	return $self;
160}
161
162sub dynamic_config {
163	my $self = shift;
164	unless ( @_ ) {
165		warn "You MUST provide an explicit true/false value to dynamic_config\n";
166		return $self;
167	}
168	$self->{values}{dynamic_config} = $_[0] ? 1 : 0;
169	return 1;
170}
171
172sub perl_version {
173	my $self = shift;
174	return $self->{values}{perl_version} unless @_;
175	my $version = shift or die(
176		"Did not provide a value to perl_version()"
177	);
178
179	# Convert triple-part versions (eg, 5.6.1 or 5.8.9) to
180	# numbers (eg, 5.006001 or 5.008009).
181
182	$version =~ s/^(\d+)\.(\d+)\.(\d+)$/sprintf("%d.%03d%03d",$1,$2,$3)/e;
183
184	$version =~ s/_.+$//;
185	$version = $version + 0; # Numify
186	unless ( $version >= 5.005 ) {
187		die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n";
188	}
189	$self->{values}{perl_version} = $version;
190	return 1;
191}
192
193sub license {
194	my $self = shift;
195	return $self->{values}{license} unless @_;
196	my $license = shift or die(
197		'Did not provide a value to license()'
198	);
199	$self->{values}{license} = $license;
200
201	# Automatically fill in license URLs
202	if ( $license eq 'perl' ) {
203		$self->resources( license => 'http://dev.perl.org/licenses/' );
204	}
205
206	return 1;
207}
208
209sub all_from {
210	my ( $self, $file ) = @_;
211
212	unless ( defined($file) ) {
213		my $name = $self->name or die(
214			"all_from called with no args without setting name() first"
215		);
216		$file = join('/', 'lib', split(/-/, $name)) . '.pm';
217		$file =~ s{.*/}{} unless -e $file;
218		unless ( -e $file ) {
219			die("all_from cannot find $file from $name");
220		}
221	}
222	unless ( -f $file ) {
223		die("The path '$file' does not exist, or is not a file");
224	}
225
226	# Some methods pull from POD instead of code.
227	# If there is a matching .pod, use that instead
228	my $pod = $file;
229	$pod =~ s/\.pm$/.pod/i;
230	$pod = $file unless -e $pod;
231
232	# Pull the different values
233	$self->name_from($file)         unless $self->name;
234	$self->version_from($file)      unless $self->version;
235	$self->perl_version_from($file) unless $self->perl_version;
236	$self->author_from($pod)        unless $self->author;
237	$self->license_from($pod)       unless $self->license;
238	$self->abstract_from($pod)      unless $self->abstract;
239
240	return 1;
241}
242
243sub provides {
244	my $self     = shift;
245	my $provides = ( $self->{values}{provides} ||= {} );
246	%$provides = (%$provides, @_) if @_;
247	return $provides;
248}
249
250sub auto_provides {
251	my $self = shift;
252	return $self unless $self->is_admin;
253	unless (-e 'MANIFEST') {
254		warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
255		return $self;
256	}
257	# Avoid spurious warnings as we are not checking manifest here.
258	local $SIG{__WARN__} = sub {1};
259	require ExtUtils::Manifest;
260	local *ExtUtils::Manifest::manicheck = sub { return };
261
262	require Module::Build;
263	my $build = Module::Build->new(
264		dist_name    => $self->name,
265		dist_version => $self->version,
266		license      => $self->license,
267	);
268	$self->provides( %{ $build->find_dist_packages || {} } );
269}
270
271sub feature {
272	my $self     = shift;
273	my $name     = shift;
274	my $features = ( $self->{values}{features} ||= [] );
275	my $mods;
276
277	if ( @_ == 1 and ref( $_[0] ) ) {
278		# The user used ->feature like ->features by passing in the second
279		# argument as a reference.  Accomodate for that.
280		$mods = $_[0];
281	} else {
282		$mods = \@_;
283	}
284
285	my $count = 0;
286	push @$features, (
287		$name => [
288			map {
289				ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_
290			} @$mods
291		]
292	);
293
294	return @$features;
295}
296
297sub features {
298	my $self = shift;
299	while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
300		$self->feature( $name, @$mods );
301	}
302	return $self->{values}{features}
303		? @{ $self->{values}{features} }
304		: ();
305}
306
307sub no_index {
308	my $self = shift;
309	my $type = shift;
310	push @{ $self->{values}{no_index}{$type} }, @_ if $type;
311	return $self->{values}{no_index};
312}
313
314sub read {
315	my $self = shift;
316	$self->include_deps( 'YAML::Tiny', 0 );
317
318	require YAML::Tiny;
319	my $data = YAML::Tiny::LoadFile('META.yml');
320
321	# Call methods explicitly in case user has already set some values.
322	while ( my ( $key, $value ) = each %$data ) {
323		next unless $self->can($key);
324		if ( ref $value eq 'HASH' ) {
325			while ( my ( $module, $version ) = each %$value ) {
326				$self->can($key)->($self, $module => $version );
327			}
328		} else {
329			$self->can($key)->($self, $value);
330		}
331	}
332	return $self;
333}
334
335sub write {
336	my $self = shift;
337	return $self unless $self->is_admin;
338	$self->admin->write_meta;
339	return $self;
340}
341
342sub version_from {
343	require ExtUtils::MM_Unix;
344	my ( $self, $file ) = @_;
345	$self->version( ExtUtils::MM_Unix->parse_version($file) );
346}
347
348sub abstract_from {
349	require ExtUtils::MM_Unix;
350	my ( $self, $file ) = @_;
351	$self->abstract(
352		bless(
353			{ DISTNAME => $self->name },
354			'ExtUtils::MM_Unix'
355		)->parse_abstract($file)
356	 );
357}
358
359# Add both distribution and module name
360sub name_from {
361	my ($self, $file) = @_;
362	if (
363		Module::Install::_read($file) =~ m/
364		^ \s*
365		package \s*
366		([\w:]+)
367		\s* ;
368		/ixms
369	) {
370		my ($name, $module_name) = ($1, $1);
371		$name =~ s{::}{-}g;
372		$self->name($name);
373		unless ( $self->module_name ) {
374			$self->module_name($module_name);
375		}
376	} else {
377		die("Cannot determine name from $file\n");
378	}
379}
380
381sub perl_version_from {
382	my $self = shift;
383	if (
384		Module::Install::_read($_[0]) =~ m/
385		^
386		(?:use|require) \s*
387		v?
388		([\d_\.]+)
389		\s* ;
390		/ixms
391	) {
392		my $perl_version = $1;
393		$perl_version =~ s{_}{}g;
394		$self->perl_version($perl_version);
395	} else {
396		warn "Cannot determine perl version info from $_[0]\n";
397		return;
398	}
399}
400
401sub author_from {
402	my $self    = shift;
403	my $content = Module::Install::_read($_[0]);
404	if ($content =~ m/
405		=head \d \s+ (?:authors?)\b \s*
406		([^\n]*)
407		|
408		=head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
409		.*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
410		([^\n]*)
411	/ixms) {
412		my $author = $1 || $2;
413		$author =~ s{E<lt>}{<}g;
414		$author =~ s{E<gt>}{>}g;
415		$self->author($author);
416	} else {
417		warn "Cannot determine author info from $_[0]\n";
418	}
419}
420
421sub license_from {
422	my $self = shift;
423	if (
424		Module::Install::_read($_[0]) =~ m/
425		(
426			=head \d \s+
427			(?:licen[cs]e|licensing|copyright|legal)\b
428			.*?
429		)
430		(=head\\d.*|=cut.*|)
431		\z
432	/ixms ) {
433		my $license_text = $1;
434		my @phrases      = (
435			'under the same (?:terms|license) as perl itself' => 'perl',        1,
436			'GNU general public license'                      => 'gpl',         1,
437			'GNU public license'                              => 'gpl',         1,
438			'GNU lesser general public license'               => 'lgpl',        1,
439			'GNU lesser public license'                       => 'lgpl',        1,
440			'GNU library general public license'              => 'lgpl',        1,
441			'GNU library public license'                      => 'lgpl',        1,
442			'BSD license'                                     => 'bsd',         1,
443			'Artistic license'                                => 'artistic',    1,
444			'GPL'                                             => 'gpl',         1,
445			'LGPL'                                            => 'lgpl',        1,
446			'BSD'                                             => 'bsd',         1,
447			'Artistic'                                        => 'artistic',    1,
448			'MIT'                                             => 'mit',         1,
449			'proprietary'                                     => 'proprietary', 0,
450		);
451		while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
452			$pattern =~ s{\s+}{\\s+}g;
453			if ( $license_text =~ /\b$pattern\b/i ) {
454				if ( $osi and $license_text =~ /All rights reserved/i ) {
455					print "WARNING: 'All rights reserved' in copyright may invalidate Open Source license.\n";
456				}
457				$self->license($license);
458				return 1;
459			}
460		}
461	}
462
463	warn "Cannot determine license info from $_[0]\n";
464	return 'unknown';
465}
466
467sub bugtracker_from {
468	my $self    = shift;
469	my $content = Module::Install::_read($_[0]);
470	my @links   = $content =~ m/L\<(http\:\/\/rt\.cpan\.org\/[^>]+)\>/g;
471	unless ( @links ) {
472		warn "Cannot determine bugtracker info from $_[0]\n";
473		return 0;
474	}
475	if ( @links > 1 ) {
476		warn "Found more than on rt.cpan.org link in $_[0]\n";
477		return 0;
478	}
479
480	# Set the bugtracker
481	bugtracker( $links[0] );
482	return 1;
483}
484
485sub install_script {
486	my $self = shift;
487	my $args = $self->makemaker_args;
488	my $exe  = $args->{EXE_FILES} ||= [];
489        foreach ( @_ ) {
490		if ( -f $_ ) {
491			push @$exe, $_;
492		} elsif ( -d 'script' and -f "script/$_" ) {
493			push @$exe, "script/$_";
494		} else {
495			die("Cannot find script '$_'");
496		}
497	}
498}
499
5001;
501