304 lines
8.6 KiB
Perl
304 lines
8.6 KiB
Perl
|
#!/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);
|
||
|
my $hook_container = "$lang/default/hook";
|
||
|
mkpath($hook_container) || warn "$hook_container: $!";
|
||
|
# Hooks can be in all sorts of weird places, including
|
||
|
# template/default/hook.
|
||
|
foreach my $file (glob("$lang/*")) {
|
||
|
next if !_file_matters($file, 1);
|
||
|
my $dirname = basename($file);
|
||
|
print "Moving $file to $hook_container/$dirname...\n";
|
||
|
rename($file, "$hook_container/$dirname") || die "move failed: $!";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub _file_matters {
|
||
|
my ($path, $tmpl) = @_;
|
||
|
my @ignore = qw(default custom CVS);
|
||
|
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.
|