diff options
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/extension-convert.pl | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/contrib/extension-convert.pl b/contrib/extension-convert.pl new file mode 100644 index 000000000..1f29db3da --- /dev/null +++ b/contrib/extension-convert.pl @@ -0,0 +1,304 @@ +#!/usr/bin/perl -w +# +# 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 the Initial Developer are Copyright (C) 2009 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Max Kanat-Alexander <mkanat@bugzilla.org> + +use strict; +use warnings; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Util qw(trim); + +use File::Basename; +use File::Copy qw(move); +use File::Find; +use File::Path qw(mkpath rmtree); + +my $from = $ARGV[0] + or die <<END; +You must specify the name of the extension you are converting from, +as the first argument. +END +my $extension_name = ucfirst($from); + +my $extdir = bz_locations()->{'extensionsdir'}; + +my $from_dir = "$extdir/$from"; +if (!-d $from_dir) { + die "$from_dir does not exist.\n"; +} + +my $to_dir = "$extdir/$extension_name"; +if (-d $to_dir) { + die "$to_dir already exists, not converting.\n"; +} + +if (ON_WINDOWS) { + # There's no easy way to recursively copy a directory on Windows. + print "WARNING: This will modify the contents of $from_dir.\n", + "Press Ctrl-C to stop or any other key to continue...\n"; + getc; + move($from_dir, $to_dir) + || die "rename of $from_dir to $to_dir failed: $!"; +} +else { + print "Copying $from_dir to $to_dir...\n"; + system("cp", "-r", $from_dir, $to_dir); +} + +# Make sure we don't accidentally modify the $from_dir anywhere else +# in this script. +undef $from_dir; + +if (!-d $to_dir) { + die "$to_dir was not created.\n"; +} + +my $version = get_version($to_dir); +move_template_hooks($to_dir); +rename_module_packages($to_dir, $extension_name); +my $install_requirements = get_install_requirements($to_dir); +my ($modules, $subs) = code_files_to_subroutines($to_dir); + +my $config_pm = <<END; +package Bugzilla::Extension::$extension_name; +use strict; +use constant NAME => '$extension_name'; +$install_requirements +__PACKAGE__->NAME; +END + +my $extension_pm = <<END; +package Bugzilla::Extension::$extension_name; +use strict; +use base qw(Bugzilla::Extension); + +$modules + +our \$VERSION = '$version'; + +$subs + +__PACKAGE__->NAME; +END + +open(my $config_fh, '>', "$to_dir/Config.pm") || die "$to_dir/Config.pm: $!"; +print $config_fh $config_pm; +close($config_fh); +open(my $extension_fh, '>', "$to_dir/Extension.pm") + || die "$to_dir/Extension.pm: $!"; +print $extension_fh $extension_pm; +close($extension_fh); + +rmtree("$to_dir/code"); +unlink("$to_dir/info.pl"); + +############### +# Subroutines # +############### + +sub rename_module_packages { + my ($dir, $name) = @_; + my $lib_dir = "$dir/lib"; + + # We don't want things like Bugzilla::Extension::Testopia::Testopia. + if (-d "$lib_dir/$name") { + print "Moving contents of $lib_dir/$name into $lib_dir...\n"; + foreach my $file (glob("$lib_dir/$name/*")) { + my $dirname = dirname($file); + my $basename = basename($file); + rename($file, "$dirname/../$basename") || warn "$file: $!\n"; + } + } + + my @modules; + find({ wanted => sub { $_ =~ /\.pm$/i and push(@modules, $_) }, + no_chdir => 1 }, $lib_dir); + my %module_rename; + foreach my $file (@modules) { + open(my $fh, '<', $file) || die "$file: $!"; + my $content = do { local $/ = undef; <$fh> }; + close($fh); + if ($content =~ /^package (\S+);/m) { + my $package = $1; + my $new_name = $file; + $new_name =~ s/^$lib_dir\///; + $new_name =~ s/\.pm$//; + $new_name = join('::', File::Spec->splitdir($new_name)); + $new_name = "Bugzilla::Extension::${name}::$new_name"; + print "Renaming $package to $new_name...\n"; + $content =~ s/^package \Q$package\E;/package \Q$new_name\E;/; + open(my $write_fh, '>', $file) || die "$file: $!"; + print $write_fh $content; + close($write_fh); + $module_rename{$package} = $new_name; + } + } + + print "Renaming module names inside of library and code files...\n"; + my @code_files = glob("$dir/code/*.pl"); + rename_modules_internally(\%module_rename, [@modules, @code_files]); +} + +sub rename_modules_internally { + my ($rename, $files) = @_; + + # We can't use \b because :: matches \b. + my $break = qr/^|[^\w:]|$/; + foreach my $file (@$files) { + open(my $fh, '<', $file) || die "$file: $!"; + my $content = do { local $/ = undef; <$fh> }; + close($fh); + foreach my $old_name (keys %$rename) { + my $new_name = $rename->{$old_name}; + $content =~ s/($break)\Q$old_name\E($break)/$1$new_name$2/gms; + } + open(my $write_fh, '>', $file) || die "$file: $!"; + print $write_fh $content; + close($write_fh); + } +} + +sub get_version { + my ($dir) = @_; + print "Getting version info from info.pl...\n"; + my $info; + { + local @INC = ("$dir/lib", @INC); + $info = do "$dir/info.pl"; die $@ if $@; + } + return $info->{version}; +} + +sub get_install_requirements { + my ($dir) = @_; + my $file = "$dir/code/install-requirements.pl"; + return '' if !-f $file; + + print "Moving install-requirements.pl code into Config.pm...\n"; + my ($modules, $code) = process_code_file($file); + $modules = join('', @$modules); + $code = join('', @$code); + if ($modules) { + return "$modules\n\n$code"; + } + return $code; +} + +sub process_code_file { + my ($file) = @_; + open(my $fh, '<', $file) || die "$file: $!"; + my $stuff_started; + my (@modules, @code); + foreach my $line (<$fh>) { + $stuff_started = 1 if $line !~ /^#/; + next if !$stuff_started; + next if $line =~ /^use (warnings|strict|lib|Bugzilla)[^\w:]/; + if ($line =~ /^(?:use|require)\b/) { + push(@modules, $line); + } + else { + push(@code, $line); + } + } + close $fh; + return (\@modules, \@code); +} + +sub code_files_to_subroutines { + my ($dir) = @_; + + my @dir_files = glob("$dir/code/*.pl"); + my (@all_modules, @subroutines); + foreach my $file (@dir_files) { + next if $file =~ /install-requirements/; + print "Moving $file code into Extension.pm...\n"; + my ($modules, $code) = process_code_file($file); + my @code_lines = map { " $_" } @$code; + my $code_string = join('', @code_lines); + $code_string =~ s/Bugzilla->hook_args/\$args/g; + $code_string =~ s/my\s+\$args\s+=\s+\$args;//gs; + chomp($code_string); + push(@all_modules, @$modules); + my $name = basename($file); + $name =~ s/-/_/; + $name =~ s/\.pl$//; + + my $subroutine = <<END; +sub $name { + my (\$self, \$args) = \@_; +$code_string +} +END + push(@subroutines, $subroutine); + } + + my %seen_modules = map { trim($_) => 1 } @all_modules; + my $module_string = join("\n", sort keys %seen_modules); + my $subroutine_string = join("\n", @subroutines); + return ($module_string, $subroutine_string); +} + +sub move_template_hooks { + my ($dir) = @_; + foreach my $lang (glob("$dir/template/*")) { + next if !_file_matters($lang); + mkpath("$lang/hook") || die "$lang/hook: $!"; + # Hooks can be in all sorts of weird places, including + # template/default/hook. + foreach my $hooks_container ($lang, "$lang/default/hook") { + foreach my $file (glob("$hooks_container/*")) { + next if !_file_matters($file, 1); + my $dirname = basename($file); + print "Moving $file to $lang/hook/$dirname...\n"; + rename($file, "$lang/hook/$dirname") || die "move failed: $!"; + } + } + } +} + +sub _file_matters { + my ($path, $tmpl) = @_; + my @ignore = qw(default custom CVS hook); + my $file = basename($path); + return 0 if grep(lc($_) eq lc($file), @ignore); + # Hidden files + return 0 if $file =~ /^\./; + if ($tmpl) { + return 1 if $file =~ /\.tmpl$/; + } + return 0 if !-d $path; + return 1; +} + +__END__ + +=head1 NAME + +extension-convert.pl - Convert extensions from the pre-3.6 format to the +3.6 format. + +=head1 SYNOPSIS + + contrib/extension-convert.pl name + + Converts an extension in the F<extensions/> directory into the new + extension layout for Bugzilla 3.6. |