+2  A: 

In one of my modules I have the following piece of code:

my $C_support = Module::Build::ConfigData->feature("C_support")

my $builder = Module::Build->new(
    ...
    config_data => {
        C_support => $C_support
    }
);
$builder->xs_files({}) if not $C_support;

Then in the code I detect it by loading Module_name::ConfigData and calling the config method.

if (Module_name::ConfigData->config("C_support")) {
    XSLoader::load(__PACKAGE__, $VERSION);
}
if (not defined &somefunction) {
    #define it
}

For details, look at my Build.PL and Module.pm

Leon Timmermans
+3  A: 

Ideally, use Module::Build. At configure time (perl Build.PL), detect the platform and header location (but also let the user specify command-line options to override detection), set the relevant extra_compiler_flags and extra_linker_flags in the constructor and then copy the relevant files from e.g. contrib to lib (where they will be automatically picked up by ExtUtils::CBuilder). Now the distribution is customised to the platform - the next steps (./Build ; …) will work as normal.

daxim
Intriguing ... I'm going to bite the `Module::Build` bullet and try this out. Should the files in `contrib/` be C or XS? XS would be another bullet to bite.
mobrule
Needs to be XS.
daxim
+1  A: 

I've used techniques like this:

sub slow_function {
    # slow fallback perl code if possible
}
BEGIN {
    eval {
        require Inline;

        if ($condition) {
            Inline->import(C => q {
                int slow_function (...) {
                    // c function to conditionally compile
                }
            })
        } else {
            Inline->import(C => q {
                int slow_function (...) {
                    // c function with something different
                }
            })
        }   
        1;
    } or print STDERR "Inline::C error: $@ perl fallback used instead\n";
}
Eric Strom
This is similar to the approach I had in mind, but compiling at run-time (taking the hit the first time `slow_function` was used rather than the first time the script was called).
mobrule
That certainly works too. In my case I knew I was going to need the function ahead of time. During development, one of the nice things about using Inline::C to do this is that simply changing the C code will trigger a recompile the next time its run, so there is no need to remember to rerun the Build script.
Eric Strom