in reply to Modules and crossplatform code

use if $^O eq 'Win32', Win32::OLE;
Or whatever $^O is on Window32 platforms.

Replies are listed 'Best First'.
Re^2: Modules and crossplatform code
by elef (Friar) on Dec 06, 2010 at 16:51 UTC
    I needed this for a crossplatform project. I found no documentation of the OS strings, so I did a bit of testing and ended up with this:
    if ($^O =~ /mswin/i) {# Windows code} elsif ($^O =~ /linux/i) {# linux code} elsif ($^O =~ /darwin/i){# mac code}

    This is designed to introduce some tolerance just in case some old or weird OS or perl versions produce slightly different OS strings. Seems to work fine so far.

    In fact, I even added a fallback in case the OS string doesn't match any of these (let the user decide):
    my $OS; if ($^O =~ /mswin/i) {$OS = "Windows";print "OS detected: Windows\n"} elsif ($^O =~ /linux/i) {$OS = "Linux";print "OS detected: Linux\n"} elsif ($^O =~ /darwin/i) {$OS = "Mac";print "OS detected: Mac OS X\n"} + else {print "\nUnable to detect OS type, choose your OS:\n\nWindows + Any version of Microsoft Windows\nMac Any flavour of Mac OS X\nLi +nux Linux of some sort\n\n"; do { chomp ($OS = <STDIN>); print "\nIncorrect OS type. Try again.\n\n" unless $OS eq "Windows" or + $OS eq "Mac" or $OS eq "Linux";} until ($OS eq "Windows" or $OS eq " +Mac" or $OS eq "Linux"); }
    Your "use if" construct is intriguing... My script also uses different modules on different platforms. So far, I've been resorting to changing the code for each platform (I need to produce separate releases for other reasons anyway) becuse if ($^O =~ /mswin/i) {use module::name;}
    didn't work. Maybe I'll try use if $OS eq 'Windows', module::name;

      If you look at perlvar under the section for $^O, there's a link to a section of the perlport documentation that has a detailed listing of the OS strings returned by $^O.

      Also, the perlvar documentation for the variable points out that $^O is not too useful in distinguishing exactly which version of Windows the code is running on (XP, Vista, etc.) since it always returns MSWin32. There's a note about using some functions from the Win32 module to get more detailed information about the version of Windows.

      Just wanted to toss out that information in case others might find it useful.

        thanks.
        For detecting OS I use $Tk::platform (I use Tk and can use this variable).
        This method may be usefull if needn't using Tk...

      Perhaps a trick stolen from File::Spec could help you? Have a class defining an OS-independant interface, make that class inherit from OS-specific classes. Make the OS-specific classes inherit from an OS-independant base class, if needed.

      Something like this:

      Update: Use one file per package (<code> block), don't stuff everything into a single file.

      package Foo::Bar; use strict; use warnings; use parent "Foo::Bar::$^O"; # will die here for any OS not yet support +ed 1; =pod Documentation here =cut
      package Foo::Bar::MSWin32; use strict; use warnings; use parent 'Foo::Bar::base'; use Win32::OLE; sub frob { Win32::OLE->... } # ... 1;
      package Foo::Bar::unixoid; use strict; use warnings; use parent 'Foo::Bar::base'; sub frob { # do it without Win32::OLE } # ... 1;
      package Foo::Bar::linux; use parent 'Foo::Bar::unixoid'; 1;
      package Foo::Bar::solaris; use parent 'Foo::Bar::unixoid'; 1;
      package Foo::Bar::cygwin; use parent 'Foo::Bar::MSWin32'; # assuming you can use OLE with cygwin + - never tried it 1;
      package Foo::Bar::base; use strict; use warnings; sub new { my $proto=shift; my $class=ref($proto)||$proto; my $self=bless {},$class; $self->init(@_) or die "init() failed"; return $self; } sub init { # ... } sub some_generic_function { # ... } 1;

      And elsewhere in your code:

      use Foo::Bar; my $f=Foo::Bar->new(); $f->frob('bla','bla'); $f->some_generic_function();

      If you don't like or don't need OOP, add something similar to File::Spec::Functions.

      Note that you don't need to inherit from Exporter with any sufficiently modern Perl, even if File::Spec::Functions does. File::Spec is old code, with its own set of problems. It's good, but not perfect. Using File::Spec::Unix as default is ok, but making all other classes inherit from File::Spec::Unix looks strange. And File::Spec also has a design problem.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        Keeping Foo::Bar::Win32 in the same file as Foo::Bar::unixoid and having use statements in Foo::Bar::Win32 will not work the way you would like. Loading the appropriate module from a separate file at runtime will only (try to) load a module on Win32 when run on Win32.

        If you really want to keep all things in one file, you will need to change the use statements to require statements together with a call to ->import.

Re^2: Modules and crossplatform code
by thargas (Deacon) on Dec 07, 2010 at 12:52 UTC

    Updated to include correction (see below).

    Or, if you have to use earlier versions of perl which don't have the "if" module in core, you could do the equivalent:

    BEGIN { # need this to happen at compile time if ( $^O =~ /whatever/ ) { require Win32::OLE; eval { Win32::OLE->import(); }; } }

    @JavaFan: Thanks for the correction. I learned something today. I was aware of the difference between require and use, but I was under the mistaken impression that it wouldn't be important inside a BEGIN block. I chose use so that I wouldn't have to invoke import() explicitly.

      That's not equivalent.
        please explain your assertion.