source: perl/lib/BarnOwl/ModuleLoader.pm @ ecd4edf

release-1.9
Last change on this file since ecd4edf was f34728b, checked in by Alex Dehnert <adehnert@mit.edu>, 11 years ago
Add completion for reload-module
  • Property mode set to 100644
File size: 4.8 KB
RevLine 
[0337203]1use strict;
2use warnings;
3
4package BarnOwl::ModuleLoader;
5
[f544216]6use PAR;
[0337203]7
[5987370]8our %modules;
9
[0337203]10sub load_all {
[5987370]11    my $class = shift;
12    $class->rescan_modules;
[5f8d8a1]13    PAR::reload_libs();
[5987370]14
15    for my $mod (keys %modules) {
16        if(!defined eval "use BarnOwl::Module::$mod") {
17            BarnOwl::error("Unable to load module $mod: \n$@\n") if $@;
18        }
19    }
20
21    $BarnOwl::Hooks::startup->add(\&register_keybindings);
22}
23
[f544216]24=h2 rescan_modules
25
26Re-compute the list of available modules, and add the necessary items to @INC
27and @PAR_INC.
28
29We load modules from two directories, the system module dir, and the user module
30directory. Modules can be in either of two forms: ${modname}.par, or else a
31${modname}/ directory containing lib/${modname}.
32
33We prefer to load modules from the user's directory, and if a module exists in
34both packed and unpacked form in the same directory, we prefer the unpacked
35module.
36
37We walk the module directories in order of ascending priority -- user directory,
38and then system directory.
39
40When we walk the directories, we first check all things that are not named
41Foo.par, add them to @INC, and add them to the module list. We then walk the
42.par files and add them to @PAR_INC and update the module list.
43
44It is important that we never add a module to @INC (or @PAR_INC) if we already
45have a source for it, in order to get priorities right. The reason is that @INC
46is processed before @PAR_INC, so if we had an unpacked system module and a
47packed local module, if we added both, the system module would take priority.
48
49=cut
50
[5987370]51sub rescan_modules {
[f544216]52    @PAR::PAR_INC = ();
[be98ba5]53
[5987370]54    %modules = ();
55
[be98ba5]56    my @moddirs = ();
57    push @moddirs, BarnOwl::get_config_dir() . "/modules";
[f544216]58    push @moddirs, BarnOwl::get_data_dir() . "/modules";
59
[be98ba5]60    for my $dir (@moddirs) {
[f544216]61        # Strip defunct entries from @INC
62        @INC = grep {!/^\Q$dir/} @INC;
63
[0337203]64        opendir(my $dh, $dir) or next;
[f544216]65        my @ents = grep {!/^\./} readdir($dh);
66
67        # Walk unpacked modules
68        for my $f (grep {!/\.par$/} @ents) {
69            if(-d "$dir/$f" && -d "$dir/$f/lib") {
70                next if $modules{$f};
71
[b4fcc06]72                unshift @INC, "$dir/$f/lib" unless grep m{^$dir/$f/lib$}, @INC;
[836e6263]73                $modules{$f} = 1;
74            }
75        }
[f544216]76
77        # Walk parfiles
78        for my $f (grep /\.par$/, @ents) {
79            if(-f "$dir/$f" && $f =~ /^(.+)\.par$/) {
80                next if $modules{$1};
81
82                PAR->import("$dir/$f");
83                $modules{$1} = 1;
84            }
[0337203]85        }
[f544216]86
87        closedir($dh);
[0337203]88    }
[5987370]89}
90
91sub reload_module {
92    my $class = shift;
93    my $module = shift;
94
95    $class->rescan_modules();
96
97    for my $m (keys %INC) {
98        delete $INC{$m} if $m =~ m{^BarnOwl/Module/$module};
[0337203]99    }
[4c2ec6c]100
[5f8d8a1]101    my $parfile;
102    for my $p (@PAR::PAR_INC) {
103        if($p =~ m/\Q$module\E[.]par$/) {
104            $parfile = $p;
105            last;
106        }
107    }
108
[5987370]109    local $SIG{__WARN__} = \&squelch_redefine;
[5f8d8a1]110
111    if(defined $parfile) {
112        PAR::reload_libs($parfile);
[41bbb8a]113        $class->run_startup_hooks();
[5f8d8a1]114    } elsif(!defined eval "use BarnOwl::Module::$module") {
[5987370]115        BarnOwl::error("Unable to load module $module: \n$@\n") if $@;
116    }
117}
118
[a82e091]119sub reload_module_cmd {
[5987370]120    my $class = shift;
121    shift; # Command
122    my $module = shift;
123    unless(defined($module)) {
[a82e091]124        die("Usage: reload-module [MODULE]");
[5987370]125    };
126    $class->reload_module($module);
[4c2ec6c]127}
128
[f34728b]129sub complete_module_name {
130    return sort(keys %modules);
131}
132
[4c2ec6c]133sub register_keybindings {
134    BarnOwl::new_command('reload-modules', sub {BarnOwl::ModuleLoader->reload}, {
135                           summary => 'Reload all modules',
[b0c8011]136                           usage   => 'reload-modules',
[4c2ec6c]137                           description => q{Reloads all modules located in ~/.owl/modules and the system modules directory}
138                          });
[a82e091]139    BarnOwl::new_command('reload-module', sub {BarnOwl::ModuleLoader->reload_module_cmd(@_)}, {
[5987370]140                           summary => 'Reload one module',
[a82e091]141                           usage   => 'reload-module [MODULE]',
[5987370]142                           description => q{Reloads a single module located in ~/.owl/modules or the system modules directory}
143                          });
[f34728b]144
145    BarnOwl::Completion::register_completer('reload-module', \&complete_module_name);
[0337203]146}
147
[836e6263]148sub reload {
149    my $class = shift;
150    for my $m (keys %INC) {
151        delete $INC{$m} if $m =~ m{^BarnOwl/};
152    }
153    # Restore core modules from perlwrap.pm
154    $INC{$_} = 1 for (qw(BarnOwl.pm BarnOwl/Hooks.pm
155                         BarnOwl/Message.pm BarnOwl/Style.pm));
[41bbb8a]156    $class->run_startup_hooks();
157}
[836e6263]158
[41bbb8a]159sub run_startup_hooks {
160    my $class = shift;
[836e6263]161    local $SIG{__WARN__} = \&squelch_redefine;
162    $class->load_all;
163    $BarnOwl::Hooks::startup->run(1);
164    BarnOwl::startup() if *BarnOwl::startup{CODE};
165}
166
167sub squelch_redefine {
168    my $warning = shift;
[9729dba]169    warn $warning unless $warning =~ /^Subroutine .+ redefined at/;
[836e6263]170}
171
[0337203]1721;
Note: See TracBrowser for help on using the repository browser.