From 15daaf123635c1e734dfe6e3aed60f52df0364ce Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 27 Mar 2017 09:56:14 -0400 Subject: Bug 1350466 - Uplift bug 1342832 to bmo for performance and other reasons --- Bugzilla/CPAN.pm | 142 +++++++++++++++++++++++++++++++++++++++ Bugzilla/Constants.pm | 13 ---- Bugzilla/Install/Requirements.pm | 27 +------- Bugzilla/Install/Util.pm | 2 +- 4 files changed, 144 insertions(+), 40 deletions(-) create mode 100644 Bugzilla/CPAN.pm (limited to 'Bugzilla') diff --git a/Bugzilla/CPAN.pm b/Bugzilla/CPAN.pm new file mode 100644 index 000000000..f044e45bc --- /dev/null +++ b/Bugzilla/CPAN.pm @@ -0,0 +1,142 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::CPAN; + +use 5.10.1; +use strict; +use warnings; + +use Bugzilla::Constants qw(bz_locations); +use Bugzilla::Install::Requirements qw(check_cpan_feature); + +BEGIN { + my $json_xs_ok = eval { + require JSON::XS; + require JSON; + JSON->VERSION("2.5"); + 1; + }; + if ($json_xs_ok) { + $ENV{PERL_JSON_BACKEND} = 'JSON::XS'; + } +} + +use constant _CAN_HAS_FEATURE => eval { + require CPAN::Meta::Prereqs; + require CPAN::Meta::Requirements; + require Module::Metadata; + require Module::Runtime; + CPAN::Meta::Prereqs->VERSION('2.132830'); + CPAN::Meta::Requirements->VERSION('2.121'); + Module::Metadata->VERSION('1.000019'); + 1; +}; + +my (%FEATURE, %FEATURE_LOADED); + +sub cpan_meta { + my ($class) = @_; + my $dir = bz_locations()->{libpath}; + my $file = File::Spec->catfile($dir, 'MYMETA.json'); + state $CPAN_META; + + return $CPAN_META if $CPAN_META; + + if (-f $file) { + open my $meta_fh, '<', $file or die "unable to open $file: $!"; + my $str = do { local $/ = undef; scalar <$meta_fh> }; + # detaint + $str =~ /^(.+)$/s; $str = $1; + close $meta_fh; + + return $CPAN_META = CPAN::Meta->load_json_string($str); + } + else { + require Bugzilla::Error; + Bugzilla::Error::ThrowCodeError('cpan_meta_missing'); + } +} + +sub cpan_requirements { + my ($class, $prereqs) = @_; + if ($prereqs->can('merged_requirements')) { + return $prereqs->merged_requirements( [ 'configure', 'runtime' ], ['requires'] ); + } + else { + my $req = CPAN::Meta::Requirements->new; + $req->add_requirements( $prereqs->requirements_for('configure', 'requires') ); + $req->add_requirements( $prereqs->requirements_for('runtime', 'requires') ); + return $req; + } +} + +sub has_feature { + my ($class, $feature_name) = @_; + + return 0 unless _CAN_HAS_FEATURE; + return $FEATURE{$feature_name} if exists $FEATURE{ $feature_name }; + + my $meta = $class->cpan_meta; + my $feature = eval { $meta->feature($feature_name) } + or ThrowCodeError('invalid_feature', { feature => $feature_name }); + + return $FEATURE{$feature_name} = check_cpan_feature($feature)->{ok}; +} + + +# Bugzilla expects this will also load all the modules.. so we have to do that. +# Later we should put a deprecation warning here, and favor calling has_feature(). +sub feature { + my ($class, $feature_name) = @_; + return 0 unless _CAN_HAS_FEATURE; + return 1 if $FEATURE_LOADED{$feature_name}; + return 0 unless $class->has_feature($feature_name); + + my $meta = $class->cpan_meta; + my $feature = $meta->feature($feature_name); + my @modules = $feature->prereqs->merged_requirements->required_modules; + Module::Runtime::require_module($_) foreach @modules; + return $FEATURE_LOADED{$feature_name} = 1; +} + +1; + +__END__ + + +=head1 NAME + +Bugzilla::CPAN - Methods relating to Bugzilla's CPAN metadata (including features) + +=head1 SYNOPSIS + + use Bugzilla; + Bugzilla->cpan_meta; + Bugzilla->feature('psgi'); + Bugzilla->has_feature('psgi'); + +=head1 DESCRIPTION + +You most likely never need to use this module directly, as the Bugzilla factory class inherits all of these class methods. +It exists so that cpan metadata can be read in before the rest of Bugzilla.pm is loaded in checksetup.pl + +=head1 CLASS METHODS + +=head2 C + +Wrapper around C that also loads all of required modules into the runtime. + +=head2 C + +Consults F for optional Bugzilla features and returns true if all the requirements +are installed. + +=head2 C + +Returns a L from the contents of MYMETA.json in the bugzilla directory. + diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 94d60999b..852c9c362 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -26,8 +26,6 @@ use Memoize; bz_locations - CAN_HAS_FEATURE - CONCATENATE_ASSETS IS_NULL @@ -221,17 +219,6 @@ use constant REST_DOC => "http://www.bugzilla.org/docs/tip/en/html/api/"; use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml'; use constant LOCAL_FILE => 'bugzilla-update.xml'; # Relative to datadir. -use constant CAN_HAS_FEATURE => eval { - require CPAN::Meta::Prereqs; - require CPAN::Meta::Requirements; - require Module::Metadata; - require Module::Runtime; - CPAN::Meta::Prereqs->VERSION('2.132830'); - CPAN::Meta::Requirements->VERSION('2.121'); - Module::Metadata->VERSION('1.000019'); - 1; -}; - # When true CSS and JavaScript assets will be concatanted and minified at # run-time, to reduce the number of requests required to render a page. # Setting this to a false value can help debugging. diff --git a/Bugzilla/Install/Requirements.pm b/Bugzilla/Install/Requirements.pm index da06ab70c..54a45fd18 100644 --- a/Bugzilla/Install/Requirements.pm +++ b/Bugzilla/Install/Requirements.pm @@ -33,7 +33,6 @@ use autodie; our @EXPORT = qw( FEATURE_FILES - load_cpan_meta check_cpan_requirements check_cpan_feature check_all_cpan_features @@ -104,25 +103,6 @@ use constant FEATURE_FILES => ( s3 => ['Bugzilla/S3.pm', 'Bugzilla/S3/Bucket.pm', 'Bugzilla/Attachment/S3.pm'] ); -sub load_cpan_meta { - my $dir = bz_locations()->{libpath}; - my @meta_json = map { File::Spec->catfile($dir, $_) } qw( MYMETA.json META.json ); - my ($file) = grep { -f $_ } @meta_json; - - if ($file) { - open my $meta_fh, '<', $file or die "unable to open $file: $!"; - my $str = do { local $/ = undef; scalar <$meta_fh> }; - # detaint - $str =~ /^(.+)$/s; $str = $1; - close $meta_fh; - - return CPAN::Meta->load_json_string($str); - } - else { - ThrowCodeError('cpan_meta_missing'); - } -} - sub check_all_cpan_features { my ($meta, $dirs, $output) = @_; my %report; @@ -163,7 +143,7 @@ sub check_cpan_requirements { sub _check_prereqs { my ($prereqs, $dirs, $output) = @_; $dirs //= \@INC; - my $reqs = $prereqs->merged_requirements(['configure', 'runtime'], ['requires']); + my $reqs = Bugzilla::CPAN->cpan_requirements($prereqs); my @found; my @missing; @@ -484,11 +464,6 @@ Returns: C<1> if the check was successful, C<0> otherwise. Returns a hashref where file names are the keys and the value is the feature that must be enabled in order to compile that file. -=item C - -Load MYMETA.json or META.json from the bugzilla directory, and a return a L object. - =back =back - diff --git a/Bugzilla/Install/Util.pm b/Bugzilla/Install/Util.pm index c65c0e061..33b3d43d3 100644 --- a/Bugzilla/Install/Util.pm +++ b/Bugzilla/Install/Util.pm @@ -268,7 +268,7 @@ sub indicate_progress { sub feature_description { my ($feature_name) = @_; eval { - my $meta = _cache()->{cpan_meta} //= Bugzilla::Install::Requirements::load_cpan_meta(); + my $meta = Bugzilla::CPAN->cpan_meta; return $meta->feature($feature_name)->description } or warn $@; -- cgit v1.2.3-24-g4f1b