Hi,

I'd like to propose the attached patch.

Its purpose is to make it simpler for packaging applications to work and to allow creation of binary distributions of wxWidgets dlls for Wx.

The patch is mainly pod - which explains how it would be used.

I've tested building Wx and running Wx demo on Win32 and Linux. Ive tested with PAR, PDK and Wx::Perl::Packager.

I've not written any additional tests as the changes concern running Wx::_start() which is called in various ways whenever Wx is started.

The patch does make one functional change - it restores loading plugin dlls on platforms other that Win32 (excluding MacOSX) - which I think is necessary for Wx::Html ( at least ) to work correctly.

Some background:

I'd like to produce a Wx::Binary distribution with the idea that a given Wx::Binary distribution version has a set of libraries built with the same flags and options across Linux/Win32/MacOSX as far as possible. The binary bits would live separately from Perl to make distributing a wxPerl application more straightforward - e.g. make an install of 'wxp-binary' a prerequisite of your packaged app, then you don't have to bundle the whole of wxWidgets with every exec.

I'd like remove much of the evil from Wx::Perl::Packager. PAR::Packer will be straightforward to 'adjust' for, and I'll beg Jan D to make 'more' changes to PDK.

I have PPMs for Linux and will investigate MacOSX soon.

Hopefully this will make getting started with Wx more attractive to many and provide some simple straightforward routes for distributing cross platform GUI apps


Cheers


Mark











Index: lib/Wx/Mini.pm
===================================================================
--- lib/Wx/Mini.pm      (revision 2765)
+++ lib/Wx/Mini.pm      (working copy)
@@ -20,6 +20,8 @@
 # XSLoader/DynaLoader wrapper
 #
 our( $wx_path );
+# var for loader package
+our( $wx_binary_loader );
 
 sub wxPL_STATIC();
 sub wx_boot($$) {
@@ -55,12 +57,48 @@
 _alien_path();
 
 sub _start {
-    wx_boot( 'Wx', $XS_VERSION );
+    # allow binary distributions to provide a custom loader
+    # see Wx/Loader.pod
 
+    # load the Loader
+    # a custom loader may exist as Wx::Loader::Custom
+    # or a packager may have installed a loader already
+    
+    eval{ require Wx::Loader::Custom };
+    $wx_binary_loader = 'Wx::Loader::Standard' if !$wx_binary_loader;
+    $wx_path = $wx_binary_loader->set_binary_path;
+    
+    # allow binary distrubutions and packagers to handle dll loading
+    wx_boot( 'Wx', $XS_VERSION ) if!$wx_binary_loader->boot_overload;
+    
     _boot_Constant( 'Wx', $XS_VERSION );
     _boot_GDI( 'Wx', $XS_VERSION );
 
     Load();
 }
 
+# standard loader package - it gets
+# used if nothing overrides it
+package Wx::Loader::Standard;
+
+our @ISA = qw( Exporter );
+
+sub loader_info { 'Standard Distribution'; }
+
+sub set_binary_path { $Wx::wx_path }
+
+sub load_dll {
+    # called as $package->($dllname)
+    return if $^O eq 'darwin';
+    local $ENV{PATH} = $Wx::wx_path . ';' . $ENV{PATH} if $Wx::wx_path && ( 
$^O =~ /^mswin/i );
+    return unless exists $Wx::dlls->{$_[1]} && $Wx::dlls->{$_[1]};
+    my $dll = $Wx::dlls->{$_[1]};
+    $dll = $Wx::wx_path . '/' . $dll if $Wx::wx_path;
+    Wx::_load_plugin( $dll );
+}
+
+sub unload_dll { }
+
+sub boot_overload { }
+
 1;
Index: Wx.pm
===================================================================
--- Wx.pm       (revision 2766)
+++ Wx.pm       (working copy)
@@ -103,6 +103,9 @@
 
 our( $wx_path );
 
+# var for loader package
+our( $wx_binary_loader );
+
 sub _load_file {
   Wx::wxVERSION() < 2.005 ? DynaLoader::dl_load_file( $_[0], 0 ) :
                             Wx::_load_plugin( $_[0] );
@@ -123,15 +126,14 @@
 
 END { unload_dll() }
 
-sub _unload_dll { }
+sub _unload_dll {
+  # handle in loader package
+  $wx_binary_loader->unload_dll( @_ )
+}
 
 sub _load_dll {
-  return if $^O ne 'MSWin32';
-  local $ENV{PATH} = $wx_path . ';' . $ENV{PATH} if $wx_path;
-  return unless exists $Wx::dlls->{$_[0]} && $Wx::dlls->{$_[0]};
-  my $dll = $Wx::dlls->{$_[0]};
-  $dll = $wx_path . '/' . $dll if $wx_path;
-  Wx::_load_file( $dll );
+  # handle in loader package
+  $wx_binary_loader->load_dll( @_ )
 }
 
 {
Index: lib/Wx/Loader.pod
===================================================================
--- lib/Wx/Loader.pod   (revision 0)
+++ lib/Wx/Loader.pod   (revision 0)
@@ -0,0 +1,261 @@
+=head1 NAME
+
+Wx::Loader - using custom dll loaders with Wx
+
+=head1 SYNOPSIS
+
+  #---------------------------------------
+  # For Packagers
+  #---------------------------------------
+  
+  # the order of these use()s is important
+  use MyCustomWxLoader;
+  use Wx;
+  
+  or .....
+  
+  use threads;
+  use threads::shared;
+  use MyCustomLoader;
+  use Wx;
+  
+  or - Wx::Perl::Splashfast special case
+    
+  use MyCustomWxLoader;
+  use Wx::Perl::SplashFast ("./logo.jpg",5000);
+  use Wx;
+  
+  ............. meanwhile in MyCustomWxLoader.pm
+  
+  package MyCustomWxLoader;
+  require Wx::Mini;
+  our @ISA = qw( Wx::Loader::Standard );
+    
+  $Wx::wx_binary_loader = __PACKAGE__;
+  ...... or
+  $Wx::wx_binary_loader = __PACKAGE__->new;
+  
+  #-----------------------------------------
+  # For binary distributions
+  #-----------------------------------------
+  
+  # Provide a custom Wx::Loader::Custom
+  # to be loaded by Wx::Mini
+  
+  package Wx::Loader::Custom
+  our @ISA = qw( Wx::Loader::Standard );
+  
+  # be polite if another loader is already in place
+  
+  $Wx::wx_binary_loader = __PACKAGE__ if !$Wx::wx_binary_loader;
+  ...... or
+  $Wx::wx_binary_loader = __PACKAGE__->new() if !$Wx::wx_binary_loader;
+  
+
+=head1 DESCRIPTION
+
+If you are providing binary distributions of Wx or packaging Wx
+applications to run on machines without Perl (PAR, PerlApp),you
+may need to override dll loading methods. Providing a custom
+wx_binary_loader package allows you to do this.
+
+=head1 Binary Distributions
+
+A binary distribution my provide alternative sources and loading
+methods for the wxWidgets dlls. It achieves this by providing a
+Wx::Loader::Custom module in the distribution
+
+  package Wx::Loader::Custom
+  our @ISA = qw( Wx::Loader::Standard );
+  
+  # be polite if another loader is already in place
+  
+  $Wx::wx_binary_loader = __PACKAGE__ if !$Wx::wx_binary_loader;
+  ...... or
+  $Wx::wx_binary_loader = __PACKAGE__->new() if !$Wx::wx_binary_loader;
+  
+  # remember that Wx.pm has not necessarily been loaded so only
+  # Wx::Mini is available
+  
+
+=head1 Perl Application Packagers
+
+  Applications that package perl scripts to run on machines without
+  Perl (PAR, PerlApp etc) can override dll loading methods if necessary
+  by loading a custom package before wx
+  
+  package MyCustomWxLoader;
+  require Wx::Mini;
+  our @ISA = qw( Wx::Loader::Standard );
+    
+  $Wx::wx_binary_loader = __PACKAGE__;
+  ...... or
+  $Wx::wx_binary_loader = __PACKAGE__->new;
+  
+  # remember that Wx.pm has not necessarily been loaded so only
+  # Wx::Mini is available
+
+=head1 METHODS
+
+  The following methods may be provided by custom loaders to override the
+  default behaviour
+
+=head2 loader_info
+
+  should return an information string about the loader and MUST be provided.
+  The default loader has
+  
+  sub loader_info { 'Standard Distribution'; }
+  
+  so any custom loader should return something different
+  
+  e.g.
+  
+  sub loader_info { "Mark's Broken Distribution - it's all my fault"; }
+  
+
+=head2 set_binary_path
+
+  allows setting a custom path for the wxWidgets libraries.
+  
+  sub set_binary_path {
+    my $class_or_object = shift;
+    
+    ... work out binary path
+    
+    return $newbinarypath; # method MUST return a path
+  }
+
+=head2 load_dll
+
+  Is called to load wxWidgets dlls using a key for the $Wx::dlls hash
+  ( see Wx::Mini )
+  
+  sub load_dll {
+    my ($class_or_object, $dllkey) = @_;
+    ..... load dll - or maybe not
+  }
+  
+  the default loader does:
+  
+  sub load_dll {
+    return if $^O !~ /^(mswin|linux)/i;
+    local $ENV{PATH} = $Wx::wx_path . ';' . $ENV{PATH} if $Wx::wx_path;
+    return unless exists $Wx::dlls->{$_[1]} && $Wx::dlls->{$_[1]};
+    my $dll = $Wx::dlls->{$_[1]};
+    $dll = $Wx::wx_path . '/' . $dll if $Wx::wx_path;
+    Wx::_load_plugin( $dll );
+  }
+
+=head2 unload_dll
+
+  Is called ONCE from within an END block
+  A custom loader may choose, for example to unload any dlls it has loaded
+  
+  sub unload_dll {
+    my $class_or_object = shift;
+    
+    ... carry out END actions
+  }
+  
+  The default unload_dll is a noop ( sub unload_dll {} )
+
+=head2 boot_overload
+
+  For binary distributions and packaged applications, normal shared
+  library loading semantics may not work. A custom loader may provide
+  this method to use in place of, or to supplement the standard XS load
+  of Wx.dll (Wx.so).
+  
+  The method MUST return true or false, depending on whether it has
+  loaded Wx.dll (Wx.so).
+  
+  For example, to load the core wxWidgets dlls before Wx is loaded
+  
+  sub boot_overload {
+    shift; 
+    require DynaLoader;
+    for my $dll ( qw( base core adv ) ) {
+      next unless exists $Wx::dlls->{$dll} && $Wx::dlls->{$dll};
+      my $file = ( $Wx::wx_path ) ? $Wx::wx_path . '/' . $Wx::dlls->{$dll} : 
$Wx::dlls->{$dll};
+      my $libref = DynaLoader::dl_load_file($file, 0);
+      push(@DynaLoader::dl_librefs,$libref) if $libref;
+      
+      # Dynaloader should take care of unloading
+    
+    }
+   
+    #------------ IMPORTANT ----------
+    return 0; # we have not loaded Wx
+    #---------------------------------
+  }
+  
+  
+  Some packagers extract dlls at runtime, and then may attempt to remove them
+  at application close. This may fail for Wx. For example, on MSWin the 
directory
+  cleanup fails whilst on Linux the application will seg-fault on exit.
+  Packagers may avoid this by loading Wx.dll( Wx.so) from a non standard 
location
+  ( perhaps a separate binary distribution of wx dlls ) that is not removed at
+  application exit.
+  
+  For example
+  
+  sub boot_overload {
+    my $class = shift 
+    require DynaLoader;
+    for my $dll ( qw( base core adv ) ) {
+      next unless exists $Wx::dlls->{$dll} && $Wx::dlls->{$dll};
+      my $file = ( $Wx::wx_path ) ? $Wx::wx_path . '/' . $Wx::dlls->{$dll} : 
$Wx::dlls->{$dll};
+      my $libref = DynaLoader::dl_load_file($file, 0);
+      push(@DynaLoader::dl_librefs,$libref) if $libref;
+      
+      # Dynaloader should take care of unloading
+    
+    }
+    
+    package
+       DynaLoader;
+    
+    my $file = $class->get_the_location_to_wx_xs_module;
+    
+    #------------------------------------------
+    # From XSLoader
+    #------------------------------------------
+    
+    my $module  = 'Wx';
+    
+    my $boots = "$module\::bootstrap";
+    my $bootname = "boot_$module";
+    $bootname =~ s/\W/_/g;
+    @DynaLoader::dl_require_symbols = ($bootname);
+    my $boot_symbol_ref;
+    
+    my $libref = dl_load_file($file, 0) or do {
+        require Carp;
+        Carp::croak("Can't load '$file' for module $module: " . dl_error());
+    };
+    push(@DynaLoader::dl_librefs,$libref);  # record loaded object
+
+    my @unresolved = dl_undef_symbols();
+    if (@unresolved) {
+        require Carp;
+        Carp::carp("Undefined symbols present after loading $file: 
@unresolved\n");
+    }
+
+    $boot_symbol_ref = dl_find_symbol($libref, $bootname) or do {
+        require Carp;
+        Carp::croak("Can't find '$bootname' symbol in $file\n");
+    };
+
+    push(@DynaLoader::dl_modules, $module); # record loaded module
+
+    my $xs = dl_install_xsub($boots, $boot_symbol_ref, $file);
+
+    push(@DynaLoader::dl_shared_objects, $file); # record files loaded
+        
+    #------------ IMPORTANT ----------
+    return 1; # we have loaded Wx
+    #---------------------------------
+}
+
+=cut

Reply via email to