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

release-1.7release-1.8release-1.9
Last change on this file since fe7ece2 was f544216, checked in by Nelson Elhage <nelhage@mit.edu>, 11 years ago
Fix module-loading priorities. I think we actually get this right, now. Local modules are preferred to system modules, and unpacked modules are preferred to PAR modules in the same directory.
  • Property mode set to 100644
File size: 4.7 KB
Line 
1use strict;
2use warnings;
3
4package BarnOwl::ModuleLoader;
5
6use PAR;
7
8our %modules;
9
10sub load_all {
11    my $class = shift;
12    $class->rescan_modules;
13    PAR::reload_libs();
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
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
51sub rescan_modules {
52    @PAR::PAR_INC = ();
53
54    %modules = ();
55
56    my @moddirs = ();
57    push @moddirs, BarnOwl::get_config_dir() . "/modules";
58    push @moddirs, BarnOwl::get_data_dir() . "/modules";
59
60    for my $dir (@moddirs) {
61        # Strip defunct entries from @INC
62        @INC = grep {!/^\Q$dir/} @INC;
63
64        opendir(my $dh, $dir) or next;
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
72                unshift @INC, "$dir/$f/lib" unless grep m{^$dir/$f/lib$}, @INC;
73                $modules{$f} = 1;
74            }
75        }
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            }
85        }
86
87        closedir($dh);
88    }
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};
99    }
100
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
109    local $SIG{__WARN__} = \&squelch_redefine;
110
111    if(defined $parfile) {
112        PAR::reload_libs($parfile);
113        $class->run_startup_hooks();
114    } elsif(!defined eval "use BarnOwl::Module::$module") {
115        BarnOwl::error("Unable to load module $module: \n$@\n") if $@;
116    }
117}
118
119sub reload_module_cmd {
120    my $class = shift;
121    shift; # Command
122    my $module = shift;
123    unless(defined($module)) {
124        die("Usage: reload-module [MODULE]");
125    };
126    $class->reload_module($module);
127}
128
129sub register_keybindings {
130    BarnOwl::new_command('reload-modules', sub {BarnOwl::ModuleLoader->reload}, {
131                           summary => 'Reload all modules',
132                           usage   => 'reload-modules',
133                           description => q{Reloads all modules located in ~/.owl/modules and the system modules directory}
134                          });
135    BarnOwl::new_command('reload-module', sub {BarnOwl::ModuleLoader->reload_module_cmd(@_)}, {
136                           summary => 'Reload one module',
137                           usage   => 'reload-module [MODULE]',
138                           description => q{Reloads a single module located in ~/.owl/modules or the system modules directory}
139                          });
140}
141
142sub reload {
143    my $class = shift;
144    for my $m (keys %INC) {
145        delete $INC{$m} if $m =~ m{^BarnOwl/};
146    }
147    # Restore core modules from perlwrap.pm
148    $INC{$_} = 1 for (qw(BarnOwl.pm BarnOwl/Hooks.pm
149                         BarnOwl/Message.pm BarnOwl/Style.pm));
150    $class->run_startup_hooks();
151}
152
153sub run_startup_hooks {
154    my $class = shift;
155    local $SIG{__WARN__} = \&squelch_redefine;
156    $class->load_all;
157    $BarnOwl::Hooks::startup->run(1);
158    BarnOwl::startup() if *BarnOwl::startup{CODE};
159}
160
161sub squelch_redefine {
162    my $warning = shift;
163    warn $warning unless $warning =~ /^Subroutine .+ redefined at/;
164}
165
1661;
Note: See TracBrowser for help on using the repository browser.