diff options
Diffstat (limited to 'Bugzilla/Install')
-rw-r--r-- | Bugzilla/Install/CPAN.pm | 351 | ||||
-rw-r--r-- | Bugzilla/Install/DB.pm | 22 | ||||
-rw-r--r-- | Bugzilla/Install/Filesystem.pm | 26 | ||||
-rw-r--r-- | Bugzilla/Install/Localconfig.pm | 39 | ||||
-rw-r--r-- | Bugzilla/Install/Requirements.pm | 990 | ||||
-rw-r--r-- | Bugzilla/Install/Util.pm | 52 |
6 files changed, 290 insertions, 1190 deletions
diff --git a/Bugzilla/Install/CPAN.pm b/Bugzilla/Install/CPAN.pm deleted file mode 100644 index e7556526c..000000000 --- a/Bugzilla/Install/CPAN.pm +++ /dev/null @@ -1,351 +0,0 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- -# -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# The Initial Developer of the Original Code is Everything Solved, Inc. -# Portions created by Everything Solved are Copyright (C) 2007 -# Everything Solved, Inc. All Rights Reserved. -# -# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> - -package Bugzilla::Install::CPAN; -use strict; -use base qw(Exporter); -our @EXPORT = qw( - BZ_LIB - - check_cpan_requirements - set_cpan_config - install_module -); - -use Bugzilla::Constants; -use Bugzilla::Install::Requirements qw(have_vers); -use Bugzilla::Install::Util qw(bin_loc install_string); - -use Config; -use CPAN; -use Cwd qw(abs_path); -use File::Path qw(rmtree); - -# These are required for install-module.pl to be able to install -# all modules properly. -use constant REQUIREMENTS => ( - { - module => 'CPAN', - package => 'CPAN', - version => '1.81', - }, - { - # When Module::Build isn't installed, the YAML module allows - # CPAN to read META.yml to determine that Module::Build first - # needs to be installed to compile a module. - module => 'YAML', - package => 'YAML', - version => 0, - }, - { - # Many modules on CPAN are now built with Dist::Zilla, which - # unfortunately means they require this version of EU::MM to install. - module => 'ExtUtils::MakeMaker', - package => 'ExtUtils-MakeMaker', - version => '6.31', - }, -); - -# We need the absolute path of ext_libpath, because CPAN chdirs around -# and so we can't use a relative directory. -# -# We need it often enough (and at compile time, in install-module.pl) so -# we make it a constant. -use constant BZ_LIB => abs_path(bz_locations()->{ext_libpath}); - -# CPAN requires nearly all of its parameters to be set, or it will start -# asking questions to the user. We want to avoid that, so we have -# defaults here for most of the required parameters we know about, in case -# any of them aren't set. The rest are handled by set_cpan_defaults(). -use constant CPAN_DEFAULTS => { - auto_commit => 0, - # We always force builds, so there's no reason to cache them. - build_cache => 0, - build_requires_install_policy => 'yes', - cache_metadata => 1, - colorize_output => 1, - colorize_print => 'bold', - index_expire => 1, - scan_cache => 'atstart', - - inhibit_startup_message => 1, - - bzip2 => bin_loc('bzip2'), - curl => bin_loc('curl'), - gzip => bin_loc('gzip'), - links => bin_loc('links'), - lynx => bin_loc('lynx'), - make => bin_loc('make'), - pager => bin_loc('less'), - tar => bin_loc('tar'), - unzip => bin_loc('unzip'), - wget => bin_loc('wget'), - - urllist => ['http://www.cpan.org/'], -}; - -sub check_cpan_requirements { - my ($original_dir, $original_args) = @_; - - _require_compiler(); - - my @install; - foreach my $module (REQUIREMENTS) { - my $installed = have_vers($module, 1); - push(@install, $module) if !$installed; - } - - return if !@install; - - my $restart_required; - foreach my $module (@install) { - $restart_required = 1 if $module->{module} eq 'CPAN'; - install_module($module->{module}, 1); - } - - if ($restart_required) { - chdir $original_dir; - exec($^X, $0, @$original_args); - } -} - -sub _require_compiler { - my @errors; - - my $cc_name = $Config{cc}; - my $cc_exists = bin_loc($cc_name); - - if (!$cc_exists) { - push(@errors, install_string('install_no_compiler')); - } - - my $make_name = $CPAN::Config->{make}; - my $make_exists = bin_loc($make_name); - - if (!$make_exists) { - push(@errors, install_string('install_no_make')); - } - - die @errors if @errors; -} - -sub install_module { - my ($name, $test) = @_; - my $bzlib = BZ_LIB; - - # Make Module::AutoInstall install all dependencies and never prompt. - local $ENV{PERL_AUTOINSTALL} = '--alldeps'; - # This makes Net::SSLeay not prompt the user, if it gets installed. - # It also makes any other MakeMaker prompts accept their defaults. - local $ENV{PERL_MM_USE_DEFAULT} = 1; - - # Certain modules require special stuff in order to not prompt us. - my $original_makepl = $CPAN::Config->{makepl_arg}; - # This one's a regex in case we're doing Template::Plugin::GD and it - # pulls in Template-Toolkit as a dependency. - if ($name =~ /^Template/) { - $CPAN::Config->{makepl_arg} .= " TT_ACCEPT=y TT_EXTRAS=n"; - } - elsif ($name eq 'XML::Twig') { - $CPAN::Config->{makepl_arg} = "-n $original_makepl"; - } - elsif ($name eq 'SOAP::Lite') { - $CPAN::Config->{makepl_arg} .= " --noprompt"; - } - - my $module = CPAN::Shell->expand('Module', $name); - if (!$module) { - die install_string('no_such_module', { module => $name }) . "\n"; - } - my $version = $module->cpan_version; - my $module_name = $name; - - if ($name eq 'LWP::UserAgent' && $^V lt v5.8.8) { - # LWP 6.x requires Perl 5.8.8 or newer. - # As PAUSE only indexes the very last version of each module, - # we have to specify the path to the tarball ourselves. - $name = 'GAAS/libwww-perl-5.837.tar.gz'; - # This tarball contains LWP::UserAgent 5.835. - $version = '5.835'; - } - - print install_string('install_module', - { module => $module_name, version => $version }) . "\n"; - - if ($test) { - CPAN::Shell->force('install', $name); - } - else { - CPAN::Shell->notest('install', $name); - } - - # If it installed any binaries in the Bugzilla directory, delete them. - if (-d "$bzlib/bin") { - File::Path::rmtree("$bzlib/bin"); - } - - $CPAN::Config->{makepl_arg} = $original_makepl; -} - -sub set_cpan_config { - my $do_global = shift; - my $bzlib = BZ_LIB; - - # We set defaults before we do anything, otherwise CPAN will - # start asking us questions as soon as we load its configuration. - eval { require CPAN::Config; }; - _set_cpan_defaults(); - - # Calling a senseless autoload that does nothing makes us - # automatically load any existing configuration. - # We want to avoid the "invalid command" message. - open(my $saveout, ">&", "STDOUT"); - open(STDOUT, '>', '/dev/null'); - eval { CPAN->ignore_this_error_message_from_bugzilla; }; - undef $@; - close(STDOUT); - open(STDOUT, '>&', $saveout); - - my $dir = $CPAN::Config->{cpan_home}; - if (!defined $dir || !-w $dir) { - # If we can't use the standard CPAN build dir, we try to make one. - $dir = "$ENV{HOME}/.cpan"; - mkdir $dir; - - # If we can't make one, we finally try to use the Bugzilla directory. - if (!-w $dir) { - print STDERR install_string('cpan_bugzilla_home'), "\n"; - $dir = "$bzlib/.cpan"; - } - } - $CPAN::Config->{cpan_home} = $dir; - $CPAN::Config->{build_dir} = "$dir/build"; - # We always force builds, so there's no reason to cache them. - $CPAN::Config->{keep_source_where} = "$dir/source"; - # This is set both here and in defaults so that it's always true. - $CPAN::Config->{inhibit_startup_message} = 1; - # Automatically install dependencies. - $CPAN::Config->{prerequisites_policy} = 'follow'; - - # Unless specified, we install the modules into the Bugzilla directory. - if (!$do_global) { - require Config; - - $CPAN::Config->{makepl_arg} .= " LIB=\"$bzlib\"" - . " INSTALLMAN1DIR=\"$bzlib/man/man1\"" - . " INSTALLMAN3DIR=\"$bzlib/man/man3\"" - # The bindirs are here because otherwise we'll try to write to - # the system binary dirs, and that will cause CPAN to die. - . " INSTALLBIN=\"$bzlib/bin\"" - . " INSTALLSCRIPT=\"$bzlib/bin\"" - # INSTALLDIRS=perl is set because that makes sure that MakeMaker - # always uses the directories we've specified here. - . " INSTALLDIRS=perl"; - $CPAN::Config->{mbuild_arg} = " --install_base \"$bzlib\"" - . " --install_path lib=\"$bzlib\"" - . " --install_path arch=\"$bzlib/$Config::Config{archname}\""; - $CPAN::Config->{mbuild_install_arg} = $CPAN::Config->{mbuild_arg}; - - # When we're not root, sometimes newer versions of CPAN will - # try to read/modify things that belong to root, unless we set - # certain config variables. - $CPAN::Config->{histfile} = "$dir/histfile"; - $CPAN::Config->{use_sqlite} = 0; - $CPAN::Config->{prefs_dir} = "$dir/prefs"; - - # Unless we actually set PERL5LIB, some modules can't install - # themselves, like DBD::mysql, DBD::Pg, and XML::Twig. - my $current_lib = $ENV{PERL5LIB} ? $ENV{PERL5LIB} . ':' : ''; - $ENV{PERL5LIB} = $current_lib . $bzlib; - } -} - -sub _set_cpan_defaults { - # If CPAN hasn't been configured, we try to use some reasonable defaults. - foreach my $key (keys %{CPAN_DEFAULTS()}) { - $CPAN::Config->{$key} = CPAN_DEFAULTS->{$key} - if !defined $CPAN::Config->{$key}; - } - - my @missing; - # In newer CPANs, this is in HandleConfig. In older CPANs, it's in - # Config. - if (eval { require CPAN::HandleConfig }) { - @missing = CPAN::HandleConfig->missing_config_data; - } - else { - @missing = CPAN::Config->missing_config_data; - } - - foreach my $key (@missing) { - $CPAN::Config->{$key} = ''; - } -} - -1; - -__END__ - -=head1 NAME - -Bugzilla::Install::CPAN - Routines to install Perl modules from CPAN. - -=head1 SYNOPSIS - - use Bugzilla::Install::CPAN; - - set_cpan_config(); - install_module('Module::Name'); - -=head1 DESCRIPTION - -This is primarily used by L<install-module> to do the "hard work" of -installing CPAN modules. - -=head1 SUBROUTINES - -=over - -=item C<set_cpan_config> - -Sets up the configuration of CPAN for this session. Must be called -before L</install_module>. Takes one boolean parameter. If true, -L</install_module> will install modules globally instead of to the -local F<lib/> directory. On most systems, you have to be root to do that. - -=item C<install_module> - -Installs a module from CPAN. Takes two arguments: - -=over - -=item C<$name> - The name of the module, just like you'd pass to the -C<install> command in the CPAN shell. - -=item C<$test> - If true, we run tests on this module before installing, -but we still force the install if the tests fail. This is only used -when we internally install a newer CPAN module. - -=back - -Note that calling this function prints a B<lot> of information to -STDOUT and STDERR. - -=back diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index 705cf3e37..ca59e778d 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -1,26 +1,18 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- +# 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/. # -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> -# Noel Cragg <noel@red-bean.com> +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. package Bugzilla::Install::DB; # NOTE: This package may "use" any modules that it likes, # localconfig is available, and params are up to date. +use 5.10.1; use strict; +use warnings; use Bugzilla::Constants; use Bugzilla::Hook; diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm index 4a773f511..c7e9f4554 100644 --- a/Bugzilla/Install/Filesystem.pm +++ b/Bugzilla/Install/Filesystem.pm @@ -1,19 +1,9 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- +# 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/. # -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> -# Bill Barry <after.fallout@gmail.com> +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. package Bugzilla::Install::Filesystem; @@ -25,7 +15,9 @@ package Bugzilla::Install::Filesystem; # * Files do not have the correct permissions. # * The database does not exist. +use 5.10.1; use strict; +use warnings; use Bugzilla::Constants; use Bugzilla::Error; @@ -164,10 +156,14 @@ sub FILESYSTEM { 'migrate.pl' => { perms => OWNER_EXECUTE }, 'sentry.pl' => { perms => WS_EXECUTE }, 'metrics.pl' => { perms => WS_EXECUTE }, + 'Makefile.PL' => { perms => OWNER_WRITE }, + 'gen-cpanfile.pl' => { perms => OWNER_EXECUTE }, 'clean-bug-user-last-visit.pl' => { perms => WS_EXECUTE }, 'Bugzilla.pm' => { perms => CGI_READ }, "$localconfig*" => { perms => CGI_READ }, + 'META.*' => { perms => CGI_READ }, + 'MYMETA.*' => { perms => CGI_READ }, 'bugzilla.dtd' => { perms => WS_SERVE }, 'mod_perl.pl' => { perms => WS_SERVE }, 'robots.txt' => { perms => WS_SERVE }, diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm index 7c01be37d..263d63ced 100644 --- a/Bugzilla/Install/Localconfig.pm +++ b/Bugzilla/Install/Localconfig.pm @@ -1,22 +1,9 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- +# 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/. # -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Initial Developer of the Original Code is Everything Solved. -# Portions created by Everything Solved are Copyright (C) 2006 -# Everything Solved. All Rights Reserved. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. package Bugzilla::Install::Localconfig; @@ -28,7 +15,9 @@ package Bugzilla::Install::Localconfig; # * Files do not have the correct permissions # * The database is not up to date +use 5.10.1; use strict; +use warnings; use Bugzilla::Constants; use Bugzilla::Install::Util qw(bin_loc install_string); @@ -36,17 +25,27 @@ use Bugzilla::Util qw(generate_random_password wrap_hard); use Data::Dumper; use File::Basename qw(dirname); -use IO::File; +use English qw($EGID); +use List::Util qw(first); +use Tie::Hash::NamedCapture; use Safe; use Term::ANSIColor; -use base qw(Exporter); +use parent qw(Exporter); our @EXPORT_OK = qw( read_localconfig update_localconfig ); +sub _sensible_group { + return '' if ON_WINDOWS; + my @groups = qw( apache www-data _www ); + my $sensible_group = first { return getgrnam($_) } @groups; + + return $sensible_group // getgrgid($EGID) // ''; +} + use constant LOCALCONFIG_VARS => ( { name => 'create_htaccess', diff --git a/Bugzilla/Install/Requirements.pm b/Bugzilla/Install/Requirements.pm index 27549ca41..43c441d6b 100644 --- a/Bugzilla/Install/Requirements.pm +++ b/Bugzilla/Install/Requirements.pm @@ -1,19 +1,9 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- +# 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/. # -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> -# Marc Schumann <wurblzap@gmail.com> +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. package Bugzilla::Install::Requirements; @@ -23,38 +13,37 @@ package Bugzilla::Install::Requirements; # Subroutines may "require" and "import" from modules, but they # MUST NOT "use." +use 5.10.1; use strict; -use version; +use warnings; use Bugzilla::Constants; -use Bugzilla::Install::Util qw(vers_cmp install_string bin_loc - success extension_requirement_packages); +use Bugzilla::Install::Util qw(install_string bin_loc success + extension_requirement_packages); use List::Util qw(max); -use Safe; use Term::ANSIColor; +use CPAN::Meta; +use CPAN::Meta::Prereqs; +use CPAN::Meta::Requirements; +use Module::Metadata; -# Return::Value 1.666002 pollutes the error log with warnings about this -# deprecated module. We have to set NO_CLUCK = 1 before loading Email::Send -# in have_vers() to disable these warnings. -BEGIN { - $Return::Value::NO_CLUCK = 1; -} - -use base qw(Exporter); +use parent qw(Exporter); use autodie; + our @EXPORT = qw( - REQUIRED_MODULES - OPTIONAL_MODULES FEATURE_FILES - check_requirements - check_graphviz - export_cpanfile - have_vers - install_command + load_cpan_meta + check_cpan_requirements + check_cpan_feature + check_all_cpan_features + check_webdotbase + check_font_file map_files_to_features ); +our $checking_for_indent = 0; + # This is how many *'s are in the top of each "box" message printed # by checksetup.pl. use constant TABLE_WIDTH => 71; @@ -64,10 +53,12 @@ use constant TABLE_WIDTH => 71; # # The keys are the names of the modules, the values are what the module # is called in the output of "apachectl -t -D DUMP_MODULES". -use constant APACHE_MODULES => { +use constant APACHE_MODULES => { mod_headers => 'headers_module', mod_env => 'env_module', mod_expires => 'expires_module', + mod_rewrite => 'rewrite_module', + mod_version => 'version_module' }; # These are all of the binaries that we could possibly use that can @@ -81,471 +72,131 @@ use constant APACHE => qw(apachectl httpd apache2 apache); # If we don't find any of the above binaries in the normal PATH, # these are extra places we look. use constant APACHE_PATH => [qw( - /usr/sbin + /usr/sbin /usr/local/sbin /usr/libexec /usr/local/libexec )]; -# The below two constants are subroutines so that they can implement -# a hook. Other than that they are actually constants. - -# "package" is the perl package we're checking for. "module" is the name -# of the actual module we load with "require" to see if the package is -# installed or not. "version" is the version we need, or 0 if we'll accept -# any version. -# -# "blacklist" is an arrayref of regular expressions that describe versions that -# are 'blacklisted'--that is, even if the version is high enough, Bugzilla -# will refuse to say that it's OK to run with that version. -sub REQUIRED_MODULES { - my $perl_ver = sprintf('%vd', $^V); - my @modules = ( - { - package => 'CGI.pm', - module => 'CGI', - # 3.51 fixes a security problem that affects Bugzilla. - # (bug 591165) - version => '3.51', - }, - { - package => 'Digest-SHA', - module => 'Digest::SHA', - version => 0 - }, - { - package => 'TimeDate', - module => 'Date::Format', - version => '2.21' - }, - # 0.28 fixed some important bugs in DateTime. - { - package => 'DateTime', - module => 'DateTime', - version => '0.28' - }, - # 0.79 is required to work on Windows Vista and Windows Server 2008. - # As correctly detecting the flavor of Windows is not easy, - # we require this version for all Windows installations. - # 0.71 fixes a major bug affecting all platforms. - { - package => 'DateTime-TimeZone', - module => 'DateTime::TimeZone', - version => ON_WINDOWS ? '0.79' : '0.71' - }, - { - package => 'DBI', - module => 'DBI', - version => (vers_cmp($perl_ver, '5.13.3') > -1) ? '1.614' : '1.41' - }, - # 2.22 fixes various problems related to UTF8 strings in hash keys, - # as well as line endings on Windows. - { - package => 'Template-Toolkit', - module => 'Template', - version => '2.22' - }, - { - package => 'Email-Send', - module => 'Email::Send', - version => ON_WINDOWS ? '2.16' : '2.00', - blacklist => ['^2\.196$'] - }, - { - package => 'Email-MIME', - module => 'Email::MIME', - # This fixes a memory leak in walk_parts that affected jobqueue.pl. - version => '1.904' - }, - { - package => 'URI', - module => 'URI', - # This version properly handles a semicolon as the delimiter - # in a URL query string. - version => '1.37', - }, - { - package => 'List-MoreUtils', - module => 'List::MoreUtils', - version => 0.22, - }, - { - package => 'Math-Random-ISAAC', - module => 'Math::Random::ISAAC', - version => '1.0.1', - }, - ); - - if (ON_WINDOWS) { - push(@modules, { - package => 'Win32', - module => 'Win32', - # 0.35 fixes a memory leak in GetOSVersion, which we use. - version => 0.35, - }, - { - package => 'Win32-API', - module => 'Win32::API', - # 0.55 fixes a bug with char* that might affect Bugzilla::RNG. - version => '0.55', - }); - } - - my $extra_modules = _get_extension_requirements('REQUIRED_MODULES'); - push(@modules, @$extra_modules); - return \@modules; -}; - -sub OPTIONAL_MODULES { - my $perl_ver = sprintf('%vd', $^V); - my @modules = ( - { - package => 'GD', - module => 'GD', - version => '1.20', - feature => [qw(graphical_reports new_charts old_charts)], - }, - { - package => 'Chart', - module => 'Chart::Lines', - # Versions below 2.1 cannot be detected accurately. - # There is no 2.1.0 release (it was 2.1), but .0 is required to fix - # https://rt.cpan.org/Public/Bug/Display.html?id=28218. - version => '2.1.0', - feature => [qw(new_charts old_charts)], - }, - { - package => 'Template-GD', - # This module tells us whether or not Template-GD is installed - # on Template-Toolkits after 2.14, and still works with 2.14 and lower. - module => 'Template::Plugin::GD::Image', - version => 0, - feature => ['graphical_reports'], - }, - { - package => 'GDTextUtil', - module => 'GD::Text', - version => 0, - feature => ['graphical_reports'], - }, - { - package => 'GDGraph', - module => 'GD::Graph', - version => 0, - feature => ['graphical_reports'], - }, - { - package => 'MIME-tools', - # MIME::Parser is packaged as MIME::Tools on ActiveState Perl - module => ON_WINDOWS ? 'MIME::Tools' : 'MIME::Parser', - version => '5.406', - feature => ['moving'], - }, - { - package => 'libwww-perl', - module => 'LWP::UserAgent', - version => 0, - feature => ['updates'], - }, - { - package => 'XML-Twig', - module => 'XML::Twig', - version => 0, - feature => ['moving', 'updates'], - }, - { - package => 'PatchReader', - module => 'PatchReader', - # 0.9.6 fixes two notable bugs and significantly improves the UX. - version => '0.9.6', - feature => ['patch_viewer'], - }, - { - package => 'perl-ldap', - module => 'Net::LDAP', - version => 0, - feature => ['auth_ldap'], - }, - { - package => 'Authen-SASL', - module => 'Authen::SASL', - version => 0, - feature => ['smtp_auth'], - }, - { - package => 'RadiusPerl', - module => 'Authen::Radius', - version => 0, - feature => ['auth_radius'], - }, - { - package => 'SOAP-Lite', - module => 'SOAP::Lite', - # Fixes various bugs, including 542931 and 552353 + stops - # throwing warnings with Perl 5.12. - version => '0.712', - feature => ['xmlrpc'], - }, - { - package => 'JSON-RPC', - module => 'JSON::RPC', - version => 0, - feature => ['jsonrpc', 'rest'], - }, - { - package => 'JSON-XS', - module => 'JSON::XS', - # 2.0 is the first version that will work with JSON::RPC. - version => '2.0', - feature => ['jsonrpc_faster'], - }, - { - package => 'Test-Taint', - module => 'Test::Taint', - version => 0, - feature => ['jsonrpc', 'xmlrpc', 'rest'], - }, - { - # We need the 'utf8_mode' method of HTML::Parser, for HTML::Scrubber. - package => 'HTML-Parser', - module => 'HTML::Parser', - version => (vers_cmp($perl_ver, '5.13.3') > -1) ? '3.67' : '3.40', - feature => ['html_desc'], - }, - { - package => 'HTML-Scrubber', - module => 'HTML::Scrubber', - version => 0, - feature => ['html_desc'], - }, - { - # we need version 2.21 of Encode for mime_name - package => 'Encode', - module => 'Encode', - version => 2.21, - feature => ['detect_charset'], - }, - { - package => 'Encode-Detect', - module => 'Encode::Detect', - version => 0, - feature => ['detect_charset'], - }, - - # S3 attachments - { - package => 'Class-Accessor-Fast', - module => 'Class::Accessor::Fast', - version => 0, - feature => ['s3'], - }, - { - package => 'XML-Simple', - module => 'XML::Simple', - version => 0, - feature => ['s3'], - }, - - # Inbound Email - { - package => 'Email-MIME-Attachment-Stripper', - module => 'Email::MIME::Attachment::Stripper', - version => 0, - feature => ['inbound_email'], - }, - { - package => 'Email-Reply', - module => 'Email::Reply', - version => 0, - feature => ['inbound_email'], - }, - - # Mail Queueing - { - package => 'TheSchwartz', - module => 'TheSchwartz', - # 1.10 supports declining of jobs. - version => 1.10, - feature => ['jobqueue'], - }, - { - package => 'Daemon-Generic', - module => 'Daemon::Generic', - version => 0, - feature => ['jobqueue'], - }, - - # mod_perl - { - package => 'mod_perl', - module => 'mod_perl2', - version => '1.999022', - feature => ['mod_perl'], - }, - { - package => 'Apache-SizeLimit', - module => 'Apache2::SizeLimit', - # 0.96 properly determines process size on Linux. - version => '0.96', - feature => ['mod_perl'], - }, - - # memcached - { - package => 'URI-Escape', - module => 'URI::Escape', - version => 0, - feature => ['memcached', 's3'], - }, - { - package => 'Cache-Memcached', - module => 'Cache::Memcached', - version => '0', - feature => ['memcached'], - }, - - # BMO - metrics - { - package => 'ElasticSearch', - module => 'ElasticSearch', - version => '0', - feature => ['elasticsearch'], - }, - - # multi factor auth - totp - { - package => 'Auth-GoogleAuth', - module => 'Auth::GoogleAuth', - version => '1.01', - feature => ['mfa'], - }, - { - package => 'GD-Barcode-QRcode', - module => 'GD::Barcode::QRcode', - version => '0', - feature => ['mfa'], - }, - # Documentation - { - package => 'File-Copy-Recursive', - module => 'File::Copy::Recursive', - version => 0, - feature => ['documentation'], - } - ); - - my $extra_modules = _get_extension_requirements('OPTIONAL_MODULES'); - push(@modules, @$extra_modules); - return \@modules; -}; - # This maps features to the files that require that feature in order # to compile. It is used by t/001compile.t and mod_perl.pl. use constant FEATURE_FILES => ( jsonrpc => ['Bugzilla/WebService/Server/JSONRPC.pm', 'jsonrpc.cgi'], xmlrpc => ['Bugzilla/WebService/Server/XMLRPC.pm', 'xmlrpc.cgi', 'Bugzilla/WebService.pm', 'Bugzilla/WebService/*.pm'], - rest => ['Bugzilla/WebService/Server/REST.pm', 'rest.cgi'], + rest => ['Bugzilla/API/Server.pm', 'rest.cgi', 'Bugzilla/API/*/*.pm', + 'Bugzilla/API/*/Server.pm', 'Bugzilla/API/*/Resource/*.pm'], + psgi => ['app.psgi'], moving => ['importxml.pl'], auth_ldap => ['Bugzilla/Auth/Verify/LDAP.pm'], auth_radius => ['Bugzilla/Auth/Verify/RADIUS.pm'], + documentation => ['docs/makedocs.pl'], inbound_email => ['email_in.pl'], jobqueue => ['Bugzilla/Job/*', 'Bugzilla/JobQueue.pm', 'Bugzilla/JobQueue/*', 'jobqueue.pl'], patch_viewer => ['Bugzilla/Attachment/PatchReader.pm'], updates => ['Bugzilla/Update.pm'], - memcached => ['Bugzilla/Memcache.pm'], mfa => ['Bugzilla/MFA/*.pm'], + markdown => ['Bugzilla/Markdown.pm'], + memcached => ['Bugzilla/Memcache.pm'], + auth_delegation => ['auth.cgi'], ); -# This implements the REQUIRED_MODULES and OPTIONAL_MODULES stuff -# described in in Bugzilla::Extension. -sub _get_extension_requirements { - my ($function) = @_; - - my $packages = extension_requirement_packages(); - my @modules; - foreach my $package (@$packages) { - if ($package->can($function)) { - my $extra_modules = $package->$function; - push(@modules, @$extra_modules); - } +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); } - return \@modules; -}; + else { + ThrowCodeError('cpan_meta_missing'); + } +} -sub check_requirements { - my ($output) = @_; +sub check_all_cpan_features { + my ($meta, $dirs, $output) = @_; + my %report; - print "\n", install_string('checking_modules'), "\n" if $output; - my $root = ROOT_USER; - my $missing = _check_missing(REQUIRED_MODULES, $output); + local $checking_for_indent = 2; - print "\n", install_string('checking_dbd'), "\n" if $output; - my $have_one_dbd = 0; - my $db_modules = DB_MODULE; - foreach my $db (keys %$db_modules) { - my $dbd = $db_modules->{$db}->{dbd}; - $have_one_dbd = 1 if have_vers($dbd, $output); - } + print "\nOptional features:\n" if $output; + my @features = sort { $a->identifier cmp $b->identifier } $meta->features; + foreach my $feature (@features) { + next if $feature->identifier eq 'features'; + printf "Feature '%s': %s\n", $feature->identifier, $feature->description if $output; + my $result = check_cpan_feature($feature, $dirs, $output); + print "\n" if $output; - print "\n", install_string('checking_optional'), "\n" if $output; - my $missing_optional = _check_missing(OPTIONAL_MODULES, $output); + $report{$feature->identifier} = { + description => $feature->description, + result => $result, + }; + } - my $missing_apache = _missing_apache_modules(APACHE_MODULES, $output); + return \%report; +} - # If we're running on Windows, reset the input line terminator so that - # console input works properly - loading CGI tends to mess it up - $/ = "\015\012" if ON_WINDOWS; +sub check_cpan_feature { + my ($feature, $dirs, $output) = @_; - my $pass = !scalar(@$missing) && $have_one_dbd; - return { - pass => $pass, - one_dbd => $have_one_dbd, - missing => $missing, - optional => $missing_optional, - apache => $missing_apache, - any_missing => !$pass || scalar(@$missing_optional), - }; + return _check_prereqs($feature->prereqs, $dirs, $output); } -# A helper for check_requirements -sub _check_missing { - my ($modules, $output) = @_; +sub check_cpan_requirements { + my ($meta, $dirs, $output) = @_; + my $result = _check_prereqs($meta->effective_prereqs, $dirs, $output); + print colored(install_string('installation_failed'), COLOR_ERROR), "\n" if !$result->{ok} && $output; + return $result; +} + +sub _check_prereqs { + my ($prereqs, $dirs, $output) = @_; + $dirs //= \@INC; + my $reqs = $prereqs->merged_requirements(['configure', 'runtime'], ['requires']); + my @found; my @missing; - foreach my $module (@$modules) { - unless (have_vers($module, $output)) { - push(@missing, $module); + + foreach my $module (sort $reqs->required_modules) { + my $ok = _check_module($reqs, $module, $dirs, $output); + if ($ok) { + push @found, $module; + } + else { + push @missing, $module; } } - return \@missing; + return { ok => (@missing == 0), found => \@found, missing => \@missing }; } -sub _missing_apache_modules { - my ($modules, $output) = @_; - my $apachectl = _get_apachectl(); - return [] if !$apachectl; - my $command = "$apachectl -t -D DUMP_MODULES"; - my $cmd_info = `$command 2>&1`; - # If apachectl returned a value greater than 0, then there was an - # error parsing Apache's configuration, and we can't check modules. - my $retval = $?; - if ($retval > 0) { - print STDERR install_string('apachectl_failed', - { command => $command, root => ROOT_USER }), "\n"; - return []; - } - my @missing; - foreach my $module (keys %$modules) { - my $ok = _check_apache_module($module, $modules->{$module}, - $cmd_info, $output); - push(@missing, $module) if !$ok; +sub _check_module { + my ($reqs, $module, $dirs, $output) = @_; + my $required_version = $reqs->requirements_for_module($module); + + if ($module eq 'perl') { + my $ok = $reqs->accepts_module($module, $]); + _checking_for({package => "perl", found => $], wanted => $required_version, ok => $ok}) if $output; + return $ok; + } else { + my $metadata = Module::Metadata->new_from_module($module, inc => $dirs); + my $version = eval { $metadata->version }; + my $ok = $metadata && $version && $reqs->accepts_module($module, $version || 0); + _checking_for({package => $module, $version ? ( found => $version ) : (), wanted => $required_version, ok => $ok}) if $output; + + return $ok; } - return \@missing; } + sub _get_apachectl { foreach my $bin_name (APACHE) { my $bin = bin_loc($bin_name); @@ -559,125 +210,10 @@ sub _get_apachectl { return undef; } -sub _check_apache_module { - my ($module, $config_name, $mod_info, $output) = @_; - my $ok; - if ($mod_info =~ /^\s+\Q$config_name\E\b/m) { - $ok = 1; - } - if ($output) { - _checking_for({ package => $module, ok => $ok }); - } - return $ok; -} - -sub print_module_instructions { - my ($check_results, $output) = @_; - - # First we print the long explanatory messages. - - if (scalar @{$check_results->{missing}}) { - print install_string('modules_message_required'); - } - - if (!$check_results->{one_dbd}) { - print install_string('modules_message_db'); - } - - if (my @missing = @{$check_results->{optional}} and $output) { - print install_string('modules_message_optional'); - # Now we have to determine how large the table cols will be. - my $longest_name = max(map(length($_->{package}), @missing)); - - # The first column header is at least 11 characters long. - $longest_name = 11 if $longest_name < 11; - - # The table is TABLE_WIDTH characters long. There are seven mandatory - # characters (* and space) in the string. So, we have a total - # of TABLE_WIDTH - 7 characters to work with. - my $remaining_space = (TABLE_WIDTH - 7) - $longest_name; - print '*' x TABLE_WIDTH . "\n"; - printf "* \%${longest_name}s * %-${remaining_space}s *\n", - 'MODULE NAME', 'ENABLES FEATURE(S)'; - print '*' x TABLE_WIDTH . "\n"; - foreach my $package (@missing) { - printf "* \%${longest_name}s * %-${remaining_space}s *\n", - $package->{package}, - _translate_feature($package->{feature}); - } - } - - if (my @missing = @{ $check_results->{apache} }) { - print install_string('modules_message_apache'); - my $missing_string = join(', ', @missing); - my $size = TABLE_WIDTH - 7; - printf "* \%-${size}s *\n", $missing_string; - my $spaces = TABLE_WIDTH - 2; - print "*", (' ' x $spaces), "*\n"; - } - - my $need_module_instructions = - ( (!$output and @{$check_results->{missing}}) - or ($output and $check_results->{any_missing}) ) ? 1 : 0; - - if ($need_module_instructions or @{ $check_results->{apache} }) { - # If any output was required, we want to close the "table" - print "*" x TABLE_WIDTH . "\n"; - } - - # And now we print the actual installation commands. - - if (my @missing = @{$check_results->{optional}} and $output) { - print install_string('commands_optional') . "\n\n"; - foreach my $module (@missing) { - my $command = install_command($module); - printf "%15s: $command\n", $module->{package}; - } - print "\n"; - } - - if (!$check_results->{one_dbd}) { - print install_string('commands_dbd') . "\n"; - my %db_modules = %{DB_MODULE()}; - foreach my $db (keys %db_modules) { - my $command = install_command($db_modules{$db}->{dbd}); - printf "%10s: \%s\n", $db_modules{$db}->{name}, $command; - } - print "\n"; - } - - if (my @missing = @{$check_results->{missing}}) { - print colored(install_string('commands_required'), COLOR_ERROR), "\n"; - foreach my $package (@missing) { - my $command = install_command($package); - print " $command\n"; - } - } - - if ($output && $check_results->{any_missing} && !ON_ACTIVESTATE - && !$check_results->{hide_all}) - { - print install_string('install_all', { perl => $^X }); - } - if (!$check_results->{pass}) { - print colored(install_string('installation_failed'), COLOR_ERROR), - "\n\n"; - } -} - -sub _translate_feature { - my $features = shift; - my @strings; - foreach my $feature (@$features) { - push(@strings, install_string("feature_$feature")); - } - return join(', ', @strings); -} - -sub check_graphviz { +sub check_webdotbase { my ($output) = @_; - my $webdotbase = Bugzilla->params->{'webdotbase'}; + my $webdotbase = Bugzilla->localconfig->{'webdotbase'}; return 1 if $webdotbase =~ /^https?:/; my $return; @@ -694,9 +230,9 @@ sub check_graphviz { my $webdotdir = bz_locations()->{'webdotdir'}; # Check .htaccess allows access to generated images if (-e "$webdotdir/.htaccess") { - my $htaccess = new IO::File("$webdotdir/.htaccess", 'r') + my $htaccess = new IO::File("$webdotdir/.htaccess", 'r') || die "$webdotdir/.htaccess: " . $!; - if (!grep(/png/, $htaccess->getlines)) { + if (!grep(/ \\\.png\$/, $htaccess->getlines)) { print STDERR install_string('webdot_bad_htaccess', { dir => $webdotdir }), "\n"; } @@ -706,68 +242,40 @@ sub check_graphviz { return $return; } -# This was originally clipped from the libnet Makefile.PL, adapted here for -# accurate version checking. -sub have_vers { - my ($params, $output) = @_; - my $module = $params->{module}; - my $package = $params->{package}; - if (!$package) { - $package = $module; - $package =~ s/::/-/g; +sub check_font_file { + my ($output) = @_; + + my $font_file = Bugzilla->localconfig->{'font_file'}; + + my $readable; + $readable = 1 if -r $font_file; + + my $ttf; + $ttf = 1 if $font_file =~ /\.(ttf|otf)$/; + + if ($output) { + _checking_for({ package => 'Font file', ok => $readable && $ttf}); } - my $wanted = $params->{version}; - - eval "require $module;"; - # Don't let loading a module change the output-encoding of STDOUT - # or STDERR. (CGI.pm tries to set "binmode" on these file handles when - # it's loaded, and other modules may do the same in the future.) - Bugzilla::Install::Util::set_output_encoding(); - - # VERSION is provided by UNIVERSAL::, and can be called even if - # the module isn't loaded. We eval'uate ->VERSION because it can die - # when the version is not valid (yes, this happens from time to time). - # In that case, we use an uglier method to get the version. - my $vnum = eval { $module->VERSION }; - if ($@) { - no strict 'refs'; - $vnum = ${"${module}::VERSION"}; - - # If we come here, then the version is not a valid one. - # We try to sanitize it. - if ($vnum =~ /^((\d+)(\.\d+)*)/) { - $vnum = $1; - } + + if (!$readable) { + print install_string('bad_font_file', { file => $font_file }), "\n"; } - $vnum ||= -1; - - # Must do a string comparison as $vnum may be of the form 5.10.1. - my $vok = ($vnum ne '-1' && version->new($vnum) >= version->new($wanted)) ? 1 : 0; - my $blacklisted; - if ($vok && $params->{blacklist}) { - $blacklisted = grep($vnum =~ /$_/, @{$params->{blacklist}}); - $vok = 0 if $blacklisted; + elsif (!$ttf) { + print install_string('bad_font_file_name', { file => $font_file }), "\n"; } - if ($output) { - _checking_for({ - package => $package, ok => $vok, wanted => $wanted, - found => $vnum, blacklisted => $blacklisted - }); - } - - return $vok ? 1 : 0; + return $readable && $ttf; } sub _checking_for { my ($params) = @_; - my ($package, $ok, $wanted, $blacklisted, $found) = + my ($package, $ok, $wanted, $blacklisted, $found) = @$params{qw(package ok wanted blacklisted found)}; my $ok_string = $ok ? install_string('module_ok') : ''; # If we're actually checking versions (like for Perl modules), then - # we have some rather complex logic to determine what we want to + # we have some rather complex logic to determine what we want to # show. If we're not checking versions (like for GraphViz) we just # show "ok" or "not found". if (exists $params->{found}) { @@ -790,30 +298,14 @@ sub _checking_for { } my $black_string = $blacklisted ? install_string('blacklisted') : ''; - my $want_string = $wanted ? "v$wanted" : install_string('any'); + my $want_string = $wanted ? "$wanted" : install_string('any'); my $str = sprintf "%s %20s %-11s $ok_string $black_string\n", - install_string('checking_for'), $package, "($want_string)"; + ( ' ' x $checking_for_indent ) . install_string('checking_for'), + $package, "($want_string)"; print $ok ? $str : colored($str, COLOR_ERROR); } -sub install_command { - my $module = shift; - my ($command, $package); - - if (ON_ACTIVESTATE) { - $command = 'ppm install %s'; - $package = $module->{package}; - } - else { - $command = 'cpanm %s'; - # Non-Windows installations need to use module names, because - # CPAN doesn't understand package names. - $package = $module->{module}; - } - return sprintf $command, $package; -} - # This does a reverse mapping for FEATURE_FILES. sub map_files_to_features { my %features = FEATURE_FILES; @@ -829,68 +321,6 @@ sub map_files_to_features { return \%files; } -sub export_cpanfile { - my $cpanfile; - # Required modules - foreach my $module (@{ REQUIRED_MODULES() }) { - my $requires = "requires '" . $module->{module} . "'"; - $requires .= ", '" . $module->{version} . "'" if $module->{version}; - $requires .= ";\n"; - $cpanfile .= $requires; - } - # Recommended modules - $cpanfile .= "\n# Optional\n"; - my %features; - foreach my $module (@{ OPTIONAL_MODULES() }) { - next if $module->{package} eq 'mod_perl'; # Skip mod_perl since this would be installed by distro - if (exists $module->{feature}) { - foreach my $feature (@{ $module->{feature} }) { - # cpanm requires that each feature only be defined in the cpanfile - # once, so we use an intermediate hash to consolidate/de-dupe the - # modules associated with each feature. - $features{$feature}{$module->{module}} = $module->{version}; - } - } - else { - my $recommends = ""; - $recommends .= "recommends '" . $module->{module} . "'"; - $recommends .= ", '" . $module->{version} . "'" if $module->{version}; - $recommends .= ";\n"; - $cpanfile .= $recommends; - } - } - foreach my $feature (sort keys %features) { - my $recommends = ""; - $recommends .= "feature '" . $feature . "' => sub {\n"; - foreach my $module (sort keys %{ $features{$feature} }) { - my $version = $features{$feature}{$module}; - $recommends .= " recommends '" . $module . "'"; - $recommends .= ", '$version'" if $version; - $recommends .= ";\n"; - } - $recommends .= "};\n"; - $cpanfile .= $recommends; - } - # Database modules - $cpanfile .= "\n# Database support\n"; - foreach my $db (keys %{ DB_MODULE() }) { - next if !exists DB_MODULE->{$db}->{dbd}; - my $dbd = DB_MODULE->{$db}->{dbd}; - my $recommends .= "feature '$db' => sub {\n"; - $recommends .= " recommends '" . $dbd->{module} . "'"; - $recommends .= ", '" . $dbd->{version} . "'" if $dbd->{version}; - $recommends .= ";\n};\n"; - $cpanfile .= $recommends; - } - - # Write out the cpanfile to the document root - my $file = bz_locations()->{'libpath'} . '/cpanfile'; - open(my $fh, '>', $file); - print $fh $cpanfile; - close $fh; - success(install_string('cpanfile_created', { file => $file })); -} - 1; __END__ @@ -910,59 +340,73 @@ perl modules it requires.) =over -=item C<REQUIRED_MODULES> +=item C<FEATURE_FILES> -An arrayref of hashrefs that describes the perl modules required by -Bugzilla. The hashes have three keys: +A hashref that describes what files should only be compiled if a certain +feature is enabled. The feature is the key, and the values are arrayrefs +of file names (which are passed to C<glob>, so shell patterns work). + +=back + +=head1 SUBROUTINES + +=over 4 + +=item C<check_cpan_requirements> =over -=item C<package> - The name of the Perl package that you'd find on -CPAN for this requirement. +=item B<Description> -=item C<module> - The name of a module that can be passed to the -C<install> command in C<CPAN.pm> to install this module. +This checks what required perl modules are installed, like +C<checksetup.pl> does. -=item C<version> - The version of this module that we require, or C<0> -if any version is acceptable. +=item B<Params> + +=over + +=item C<$meta> - A C<CPAN::Meta> object. + +=item C<$dirs> - the include dirs to search for modules, defaults to @INC. + +=item C<$output> - C<true> if you want the function to print out information +about what it's doing, and the versions of everything installed. =back -=item C<OPTIONAL_MODULES> +=item B<Returns> -An arrayref of hashrefs that describes the perl modules that add -additional features to Bugzilla if installed. Its hashes have all -the fields of L</REQUIRED_MODULES>, plus a C<feature> item--an arrayref -of strings that describe what features require this module. +A hashref containing these values: -=item C<FEATURE_FILES> +=over -A hashref that describes what files should only be compiled if a certain -feature is enabled. The feature is the key, and the values are arrayrefs -of file names (which are passed to C<glob>, so shell patterns work). +=item C<ok> - if all the requirements are met, this is true. -=back +=item C<found> - an arrayref of found modules +=item C<missing> - an arrayref of missing modules -=head1 SUBROUTINES +=back -=over 4 +=back -=item C<check_requirements> +=item C<check_cpan_feature> =over =item B<Description> -This checks what optional or required perl modules are installed, like -C<checksetup.pl> does. +This checks that the optional Perl modules required for a feature are installed. =item B<Params> =over -=item C<$output> - C<true> if you want the function to print out information -about what it's doing, and the versions of everything installed. +=item C<$feature> - A C<CPAN::Meta::Feature> object. + +=item C<$dirs> - the include dirs to search for modules, defaults to @INC. + +=item C<$output> - C<true> if you want the function to print out information about what it's doing, and the versions of everything installed. =back @@ -972,28 +416,50 @@ A hashref containing these values: =over -=item C<pass> - Whether or not we have all the mandatory requirements. +=item C<ok> - if all the requirements are met, this is true. + +=item C<found> - an arrayref of found modules + +=item C<missing> - an arrayref of missing modules -=item C<missing> - An arrayref containing any required modules that -are not installed or that are not up-to-date. Each item in the array is -a hashref in the format of items from L</REQUIRED_MODULES>. +=back -=item C<optional> - The same as C<missing>, but for optional modules. +=item C<check_all_cpan_features> -=item C<apache> - The name of each optional Apache module that is missing. +=over -=item C<have_one_dbd> - True if at least one C<DBD::> module is installed. +=item B<Description> + +This checks which optional Perl modules are currently installed which can enable optional features. + +=item B<Params> + +=over -=item C<any_missing> - True if there are any missing Perl modules, even -optional modules. +=item C<$meta> - A C<CPAN::Meta> object. + +=item C<$dirs> - the include dirs to search for modules, defaults to @INC. + +=item C<$output> - C<true> if you want the function to print out information +about what it's doing, and the versions of everything installed. =back +=item B<Returns> + +A hashref keyed on the feature name. The values +are hashrefs containing C<description> and C<result> keys. + +C<description> is the English description of the feature. + +C<result> is a hashref in the same format as the return value of C<check_cpan_requirements()>, +described previously. + =back -=item C<check_graphviz($output)> +=item C<check_webdotbase($output)> -Description: Checks if the graphviz binary specified in the +Description: Checks if the graphviz binary specified in the C<webdotbase> parameter is a valid binary, or a valid URL. Params: C<$output> - C<$true> if you want the function to @@ -1001,36 +467,26 @@ Params: C<$output> - C<$true> if you want the function to Returns: C<1> if the check was successful, C<0> otherwise. -=item C<have_vers($module, $output)> - - Description: Tells you whether or not you have the appropriate - version of the module requested. It also prints - out a message to the user explaining the check - and the result. - - Params: C<$module> - A hashref, in the format of an item from - L</REQUIRED_MODULES>. - C<$output> - Set to true if you want this function to - print information to STDOUT about what it's - doing. +=item C<check_font_file($output)> - Returns: C<1> if you have the module installed and you have the - appropriate version. C<0> otherwise. +Description: Checks if the font file specified in the C<font_type> parameter + is a valid-looking font file. -=item C<install_command($module)> - - Description: Prints out the appropriate command to install the - module specified, depending on whether you're - on Windows or Linux. - - Params: C<$module> - A hashref, in the format of an item from - L</REQUIRED_MODULES>. +Params: C<$output> - C<$true> if you want the function to + print out information about what it's doing. - Returns: nothing +Returns: C<1> if the check was successful, C<0> otherwise. =item C<map_files_to_features> 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_cpan_meta> + +Load MYMETA.json or META.json from the bugzilla directory, and a return a L<CPAN::Meta> object. + =back + +=back + diff --git a/Bugzilla/Install/Util.pm b/Bugzilla/Install/Util.pm index 5f6c8bceb..231ef8efc 100644 --- a/Bugzilla/Install/Util.pm +++ b/Bugzilla/Install/Util.pm @@ -1,22 +1,9 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- +# 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/. # -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# The Initial Developer of the Original Code is Everything Solved. -# Portions created by Everything Solved are Copyright (C) 2006 -# Everything Solved. All Rights Reserved. -# -# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. package Bugzilla::Install::Util; @@ -24,16 +11,16 @@ package Bugzilla::Install::Util; # module may require *only* Bugzilla::Constants and built-in # perl modules. +use 5.10.1; use strict; +use warnings; use Bugzilla::Constants; use Encode; -use ExtUtils::MM (); use File::Basename; use File::Spec; use POSIX qw(setlocale LC_CTYPE); -use Safe; use Scalar::Util qw(tainted); use Term::ANSIColor qw(colored); use PerlIO; @@ -47,17 +34,20 @@ our @EXPORT_OK = qw( extension_requirement_packages extension_template_directory extension_web_directory + i_am_persistent indicate_progress install_string include_languages success template_include_path - vers_cmp init_console ); sub bin_loc { my ($bin, $path) = @_; + # This module is not needed most of the time and is a bit slow, + # so we only load it when calling bin_loc(). + require ExtUtils::MM; # If the binary is a full path... if ($bin =~ m{[/\\]}) { @@ -275,6 +265,15 @@ sub indicate_progress { } } +sub feature_description { + my ($feature_name) = @_; + eval { + my $meta = _cache()->{cpan_meta} //= Bugzilla::Install::Requirements::load_cpan_meta(); + + return $meta->feature($feature_name)->description + } or warn $@; +} + sub install_string { my ($string_id, $vars) = @_; _cache()->{install_string_path} ||= template_include_path(); @@ -543,11 +542,20 @@ sub no_checksetup_from_cgi { # Used by install_string sub _get_string_from_file { my ($string_id, $file) = @_; - + # If we already loaded the file, then use its copy from the cache. + if (my $strings = _cache()->{strings_from_file}->{$file}) { + return $strings->{$string_id}; + } + + # This module is only needed by checksetup.pl, + # so only load it when needed. + require Safe; + return undef if !-e $file; my $safe = new Safe; $safe->rdo($file); my %strings = %{$safe->varglob('strings')}; + _cache()->{strings_from_file}->{$file} = \%strings; return $strings{$string_id}; } |