Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Re^3: Better way to force Inline to use compiled binary instead of C source?

by syphilis (Archbishop)
on Jun 28, 2022 at 01:42 UTC ( [id://11145123]=note: print w/replies, xml ) Need Help??


in reply to Re^2: Better way to force Inline to use compiled binary instead of C source?
in thread Better way to force Inline to use compiled binary instead of C source?

Has the same capability not been ported to Inline::C ?

I've had a look ... and the capability in question has NOT been ported to Inline::C, but it seems do-able.
Firstly, the patch to Inline/C.pm:
--- C.pm_orig 2021-12-20 22:11:48 +1100 +++ C.pm 2022-06-27 19:27:27 +1000 @@ -177,6 +177,10 @@ $o->{ILSM}{XS}{PREFIX} = $value; next; } + if($key eq 'OBJECT') { + $o->add_string($o->{ILSM}{MAKEFILE}, $key, $value); + next; + } if ($key eq 'FILTERS') { next if $value eq '1' or $value eq '0'; # ignore ENABLE, +DISABLE $value = [$value] unless ref($value) eq 'ARRAY';
And the Inline::C demo:
use warnings; use Inline C => Config => BUILD_NOISY => 1, OBJECT => '$(O_FILES)', ; use Inline C =><<'EOC'; #include "bar.h" #include "baz.h" void foo(int i) { printf("bar: %d\n", bar(i)); printf("baz: %d\n", baz(i)); } EOC foo(42);
bar.h prototypes the bar function:  int bar (int);
baz.h prototpyes the baz function:  int baz (int);
bar.c defines the bar() function:
#include "bar.h" int bar (int in) { return in; }
and baz.c defines the baz() function:
#include "baz.h" int baz (int in) { return in + 1; }
It's probably important that each of those 4 files terminate with at least one empty line.

But there's a snag I haven't quite got around yet: The first time I run my Inline::C script, it fails to compile because of undefined references to 'bar' and 'baz'. I then manually copy bar.h, baz.h, bar.c and baz.c to the Inline build directory that was created by that first run ... then I re-run the script, and all goes fine. And I end up with the expected output of:
bar: 42 baz: 43
So ... I wonder if there's some way that I can make bar.h, baz.h, bar.c and baz.c visible to the build process without having to move them to the build directory ?
Essentially, I think the problem is that, with the current OBJECT => '$(O_FILES)' arg, the .c snd .h files need to be in the same directory as the auto-generated Makefile.PL (ie the script's build directory).
I'm hoping there's some way to get the message across that we want to use the .c and.h files from a different location.
Otherwise, we face the task of getting Inline to move those files across to the build directory *before* the auto-generated Makefile.PL is run.

Any ideas ? (I'll keep fiddling with it and see if I can come up with the right solution.)
Having to manually copy files and re-run commands strikes me as being a little less than optimal ;-)

Cheers,
Rob

Replies are listed 'Best First'.
Re^4: Better way to force Inline to use compiled binary instead of C source?
by syphilis (Archbishop) on Jun 28, 2022 at 12:34 UTC
    So ... I wonder if there's some way that I can make bar.h, baz.h, bar.c and baz.c visible to the build process without having to move them to the build directory ?

    Not that I can see.
    So we're left with the option of having Inline automatically copy our '.h' and '.c' files to the required location.
    The required patch to C.pm changes to:
    --- C.pm_orig 2021-12-20 22:11:48 +1100 +++ C.pm 2022-06-28 22:08:10 +1000 @@ -177,6 +177,20 @@ $o->{ILSM}{XS}{PREFIX} = $value; next; } + if($key eq 'OBJECT') { + $o->mkpath($o->{API}{build_dir}) unless -d $o->{API}{buil +d_dir}; + require File::Copy; + $o->add_string($o->{ILSM}{MAKEFILE}, 'OBJECT', '$(O_FILES +)'); + die "'OBJECT' must specify an array reference" + unless ref($value) eq 'ARRAY'; + for my $fn(@$value) { + File::Copy::copy("${fn}.h", $o->{API}{build_dir} . "/${ +fn}.h") + if -e "${fn}.h"; + File::Copy::copy("${fn}.c", $o->{API}{build_dir} . "/${ +fn}.c") + if -e "${fn}.c"; + } + next; + } if ($key eq 'FILTERS') { next if $value eq '1' or $value eq '0'; # ignore ENABLE, +DISABLE $value = [$value] unless ref($value) eq 'ARRAY'; @@ -374,7 +388,7 @@ open $lockfh, '>', $file or die "lockfile $file: $!"; flock($lockfh, LOCK_EX) or die "flock: $!\n" if $^O !~ /^VMS| +riscos|VOS$/; } - $o->mkpath($o->{API}{build_dir}); + $o->mkpath($o->{API}{build_dir}) unless -d $o->{API}{build_dir}; $o->call('preprocess', 'Build Preprocess'); $o->call('parse', 'Build Parse'); $o->call('write_XS', 'Build Glue 1');
    And instead of providing the OBJECT config option as OBJECT => '$(O_FILES)', we now have to do something like:
    OBJECT => ['./bar', './baz'],
    That's assuming that the '.h' and '.c' files are in the cwd. Otherwise, modify accordingly. ... oops ... I'll modify the patch later to allow for files not being in the cwd. )
    The demo I provided earlier therefore becomes:
    use warnings; use Inline C => Config => BUILD_NOISY => 1, OBJECT => ['./bar', './baz'], ; use Inline C =><<'EOC'; #include "bar.h" #include "baz.h" void foo(int i) { printf("bar: %d\n", bar(i)); printf("baz: %d\n", baz(i)); } EOC foo(42);
    And that works fine straight away.
    Thoughts ?

    Cheers,
    Rob

      syphilis++, perhaps compiling the bar/baz sources to objects ends up too complex? E.g. more than 1 source files, complicated compilation flags, 3rdparty dependencies, lex+yacc preprocessing.

      How about user either provides objects already compiled (and you have coded that in above post). This is the case where author distributes objects-only. Or *author* provides a tarball/src-dir containing all the necessary files and workflows (e.g. a Makefile which a) provides a target such as 'objects-for-inline-C' and b) reads optional ENV-vars for CC and other flags that Inline::C wishes to inject into this sub-compilation) to compile the required objects. This case is for when preparing for the public distribution of objects-only version. It can be run via a subshell, before Inline::C starts its own compilations.

      bw, bliako

        I think that Inline::C already nicely handles the cases where additional C code has been compiled into object files or a library.
        This is aimed at accommodating additional C code without having to do any pre-compiling.
        This is pretty common in XS modules. I've done it myself in Math::Ryu, and you'll find it in ScalarList::Utils - to name just one of many other examples.

        One unnecessary thing I've done in my demo script, is to #include the 2 headers ('bar.h' and 'baz.h').
        In general, one might want to avoid doing that because those headers might define something that clobbers perl.
        Removing the inclusion of those headers from the script still allows the script to work just fine, though "implicit declaration of function" warnings are emitted at compile-time for both bar and baz. Those warnings can be silenced by declaring:
        extern int bar (int); extern int baz (int);
        in the script. In checking up on these things, I changed baz.h to:
        #define NVSIZE 100 int baz (int);
        and baz.c to:
        #include "baz.h" int baz (int in) { return in + NVSIZE; }
        (That was just to check that my perl's NVSIZE, which is defined to 8, did not get clobbered.) The demo became:
        use warnings; use Inline C => Config => BUILD_NOISY => 1, OBJECT => ['bar', 'baz'], ; use Inline C =><<'EOC'; extern int bar (int); extern int baz (int); void foo(int i) { printf("bar: %d\n", bar(i)); printf("baz: %d\n", baz(i)); printf("NVSIZE: %d\n", NVSIZE); } EOC foo(42);
        which, after compiling cleanly, output:
        bar: 42 baz: 142 NVSIZE: 8
        I might one day tidy the C.pm patch up a bit, create a test case, and file a PR with Inline::C.
        At the moment, I'm not sure if I'm overlooking something significant.
        (As previously mentioned, with the present patch, the '.h' and '.c' files need to be in the cwd. I've decided that I would prefer to keep it that way.)

        Thanks bliako - always good to get some feedback from you !

        Cheers,
        Rob

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11145123]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (3)
As of 2024-04-19 01:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found