Khariton has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing crossplatform code(for Linux and Win32 platform).
Now I need using for Win32 platform module Win32::OLE.
How I must writing my code for using Win32::OLE only when I run this code on Win32? It's posible?
I needn't installing this module on Linux platform.

Replies are listed 'Best First'.
Re: Modules and crossplatform code
by marto (Cardinal) on Dec 06, 2010 at 15:54 UTC

    Query the OS name using $^O before requiring the module. See perlvar.

Re: Modules and crossplatform code
by JavaFan (Canon) on Dec 06, 2010 at 16:28 UTC
    use if $^O eq 'Win32', Win32::OLE;
    Or whatever $^O is on Window32 platforms.
      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.

        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". ;-)

      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.