Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Splitting an extension based on OS

by syphilis (Archbishop)
on Nov 06, 2007 at 10:51 UTC ( #649176=perlquestion: print w/replies, xml ) Need Help??

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


(Sorry ... couldn't think of a decent title.)

Suppose I have a module (an extension) called The source distro contains a very basic 'Makefile.PL', a '' and a 'G.xs' (along with the usual supplementary files). The functions work differently, according to whether the operating system is Win32 or not - so there's a lot of checking to see whether $^O is 'MSWin32' or not. Similarly, in the XS file there's a need to #ifdef WIN32. It works fine .... but it might be cleaner if it was set up so that simply determined whether $^O was 'MSWin32', and then called either G/ or G/ (as appropriate).

There's an example of splitting things up along these lines in (an old version of) the File::Spec distro - but it's pure perl ... and how to incorporate the XS element into such a split has me baffled.

Questions that come to mind:
Do I split the XS file into 2 separate files (one for Win32, one for Unix) as well ?
If so, what do I call them and where do I place them ?
Is there a CPAN distro that provides a good demo of how to achieve this when XS is involved ?

The actual module in question is FileHandle::Fmode and I think it was suggested, quite some time ago, by dragonchild and/or BrowserUK that it be split along the lines that I've indicated here. (I've only just now got round to giving that serious consideration ... and sort of wish I hadn't :-)

I had a bit of a speculative play around ... to the point where it became apparent that I should stop speculating, and start asking for a pointer or two.


Replies are listed 'Best First'.
Re: Splitting an extension based on OS
by cdarke (Prior) on Nov 06, 2007 at 13:56 UTC
    Personally I would go for two files - then you can add a third when you support Mac, and a forth when you support Symbian, and so on. It is just easier to maintain. I don't know of another module on CPAN that does things like that, I use a condition use. Here are some examples:
    use if ($^O eq "MSWin32"), 'Win32::Process'; use if ($^O eq "MSWin32"), 'Win32::SearchPath'; use if ($^O ne "MSWin32"), 'POSIX'; use if ($^O ne "MSWin32"), 'POSIX' => ':sys_wait_h'; # Hack to allow compilation under Unix # (NORMAL_PRIORITY_CLASS and INFINITE are Win32 only) use if ($^O ne "MSWin32"), 'constant' => 'NORMAL_PRIORITY_CLASS'; use if ($^O ne "MSWin32"), 'constant' => 'INFINITE';

    Your idea of sub-modules under a "master" namespace seems reasonable to me.
Re: Splitting an extension based on OS
by BrowserUk (Patriarch) on Nov 06, 2007 at 14:24 UTC

    I think the ideal way would be to have the bootstrap statement in your .pm file simply load the appropriate .dll/.so. That is, the bootstrap statement already knows where to look and what to look for depending upon which platform it is invoked on.

    So, if you can make the build process only attempt to build the platform specific .xs file and put it into the right place, and if all platforms present the same interface, then the whole thing get taken care of at extension build times and there is nothing more to do.

    It would certainly move a heap of messy platform-conditional stuff out of the sources. Of course, it can also create a maintanence problem with code duplication, but on the basis that only the performance critical or "only possible in C" code should be done in XS with the rest done in the .PM file, the duplication would be minimal.

    Whether there is anything in the existing makefile generation processors to cater for this I'm not sure, but at the simplest of levels it could be something along the lines of having a module.xs.w32 and module.xs.nix and just copying or renaming the appropriate one to module.xs when setting up the blib.

    Alternatively, since compilers already know how to do platform specific selection at compile time, it might be better to have a single module.xs and platform specific includes. It also eliminates the need for common code duplication:

    // module.xs ... #if defined(WIN32) # include "module.win32"; #elseif define(MAC) # include "module.mac" #else # include "module.unix" #endif ...

    Same make file; same .xs file; same .pm file; same bootstrap. Just the contents of the .dll/.so changes?

    Somebody will point out that #include is for header files, and include files should be named .h, and having executable code in include files is frowned upon; but XS is a different langauge to C and it can set what rules it decides upon.

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Splitting an extension based on OS
by dk (Chaplain) on Nov 07, 2007 at 06:43 UTC
    I'd say that splitting XS in two parts really depends on the amount of the ifdef'd code. If it is small, do not split, otherwise do. You might even want to create win32.c and unix.c with common API that is called from the single XS.

    There's one thing more: cygwin and mingw environments. These two are curious mixes of pure win32 and unix APIs, and before attempting the separation I'd recommend to run your code on these first.

      Thanks for the thoughts, guys. The idea of having the XS file simply load the relevant file looks like a particularly good one to me - mainly because it *does* lend itself so readily to the inclusion of additional files for other operating systems (should the need arise).

Re: Splitting an extension based on OS
by DrHyde (Prior) on Nov 07, 2007 at 11:31 UTC
    Please don't assume, as you seem to be doing, that if $^O isn't MSWin32 then you're running on Unix. You might be running on Cygwin, or VMS, or RISCOS, or DOS, or OS400. If you have Unix-specific code, then check that you're running on Unix, by using Devel::CheckOS.

      <voice style='McEnroe'><c>You ... cannot ... be serious!<c></voice>

      A total of 42 modules to compare $^O against 40 strings totalling around 200 chars.

      What an exercise in pointless, useless over-engineering.

        So don't use it if you don't like it. And expect bug reports when you get the answer to something as simple as "is this a Unix system" wrong.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://649176]
Approved by moritz
Front-paged by Corion
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (3)
As of 2023-12-09 12:37 GMT
Find Nodes?
    Voting Booth?
    What's your preferred 'use VERSION' for new CPAN modules in 2023?

    Results (38 votes). Check out past polls.