http://qs321.pair.com?node_id=1169301

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

I'm trying to use Inline::C to wrap around a C library that I've written, and am looking for assistance on the proper approach to include this C code inside of a Perl script. I'm not a C dev, so I may have terminology etc wrong while I'm trying to describe things. To build the library and the ensuing binary, I do this (note that the tempmon.c code is separate from the dht11.c code I'm including in the Perl script):

cc -c -o dhtll.o dht11.c cc -o tempmon tempmon.c dht11.o -l wiringPi

When I put the C code in my test Perl script with:

use strict; use warnings; use Inline 'C';

...I get:

perl: symbol lookup error: /home/pi/repos/scripts/c/rpi/dht11/library/ +_Inline/lib/auto/perl_pl_96d1/perl_pl_96d1.so: undefined symbol: pinM +ode

I'm sure this is because I'm not linking to wiringPi which I do on the command line. wiringPi contains the pinMode() amongst other functions.

When I try to change the ccflags per the documentation:

use Config; my $ccflags .= $Config{ccflags} . " -l wiringPi"; use Inline ('C' => 'END', ccflags => $ccflags);

I get a great big long error that I'm not experienced enough to understand, but it looks like it's missing prototypes, which leads me to believe that it's still not linking something properly. Here's a snip:

Running Mkbootstrap for perl_pl_b1a3 () chmod 644 "perl_pl_b1a3.bs" "/home/pi/perl5/perlbrew/perls/perl-5.24.0/bin/perl" "/home/pi/perl5/p +erlbrew/perls/perl-5.24.0/lib/5.24.0/ExtUtils/xsubpp" -typemap "/hom +e/pi/perl5/perlbrew/perls/perl-5.24.0/lib/5.24.0/ExtUtils/typemap" +perl_pl_b1a3.xs > perl_pl_b1a3.xsc && mv perl_pl_b1a3.xsc perl_pl_b1a +3.c cc -c -I"/home/pi/repos/scripts/c/rpi/dht11/library" -fwrapv -fno-str +ict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_L +ARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -O2 -DVE +RSION=\"0.00\" -DXS_VERSION=\"0.00\" -fPIC "-I/home/pi/perl5/perlbrew +/perls/perl-5.24.0/lib/5.24.0/armv7l-linux/CORE" perl_pl_b1a3.c In file included from perl_pl_b1a3.xs:3:0: /home/pi/perl5/perlbrew/perls/perl-5.24.0/lib/5.24.0/armv7l-linux/CORE +/XSUB.h:142:31: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute_ +_’ before ‘void’ # define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unu +sed__) ^ perl_pl_b1a3.c:163:1: note: in expansion of macro ‘XS_EXTERNAL’ XS_EXTERNAL(boot_perl_pl_b1a3); /* prototype to pass -Wmissing-protot +ypes */ ^ Makefile:331: recipe for target 'perl_pl_b1a3.o' failed

Here's the full code. Some of it was harvested from elsewhere, other parts are mine. I'm not a C coder, so the code is probably less than desirable. This is just a test:

use warnings; use strict; use Config; my $ccflags .= $Config{ccflags} . " -l wiringPi"; use Inline ('C' => 'END', ccflags => $ccflags); #use Inline 'C'; my $dht_pin = 4; my $err_pin = 1; my $temp_limit = 65.0; my $warn_pin = 6; my $ret = read_temp($dht_pin, $err_pin, $temp_limit, $warn_pin); print "$ret\n"; cleanup($dht_pin, $err_pin, $warn_pin); __END__ __C__ #include <wiringPi.h> #include "dht11.h" #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #define MAXTIMINGS 85 float read_temp(int dhtPin, int tempErrPin, float tempErrLimit, int te +mpWarnPin) { int dht11_dat[5] = { 0, 0, 0, 0, 0 }; uint8_t laststate = HIGH; uint8_t counter = 0; uint8_t j = 0, i; float f; /* fahrenheit */ dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_ +dat[4] = 0; /* pull pin down for 18 milliseconds */ pinMode(dhtPin, OUTPUT); digitalWrite(dhtPin, LOW ); delay( 18 ); /* then pull it up for 40 microseconds */ digitalWrite(dhtPin, HIGH ); delayMicroseconds( 40 ); /* prepare to read the pin */ pinMode(dhtPin, INPUT ); /* detect change and read data */ for ( i = 0; i < MAXTIMINGS; i++ ) { counter = 0; while ( digitalRead( dhtPin ) == laststate ) { counter++; delayMicroseconds( 1 ); if ( counter == 255 ) { break; } } laststate = digitalRead( dhtPin ); if ( counter == 255 ) break; /* ignore first 3 transitions */ if ( (i >= 4) && (i % 2 == 0) ) { /* shove each bit into the storage bytes */ dht11_dat[j / 8] <<= 1; if ( counter > 16 ) dht11_dat[j / 8] |= 1; j++; } } /* * check we read 40 bits (8bit x 5 ) + verify checksum in the last + byte * print it out if data is good */ if ( (j >= 40) && (dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2 +] + dht11_dat[3]) & 0xFF) ) ) { f = dht11_dat[2] * 9. / 5. + 32; // debugging // printf("f: %.1f: warn: %.1f\n", f, tempErrLimit); // printf("warnPin: %d\n", tempWarnPin); int warn = 0; if (tempErrPin > -1) { pinMode(tempErrPin, OUTPUT); if (f > tempErrLimit) { warn = 1; digitalWrite(tempErrPin, HIGH); if (tempWarnPin > -1) { pinMode(tempWarnPin, OUTPUT); digitalWrite(tempWarnPin, HIGH); } } else { digitalWrite(tempErrPin, LOW); } } /* * printf( "Humidity = %d.%d %% Temperature = %d.%d *C (%.1f * +F)\n", * dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3], f ) +; */ if (warn > 0) { return f; } else { return 0.0; } } } int cleanup(int dhtPin, int tempErrPin, int tempWarnPin) { digitalWrite(dhtPin, LOW); pinMode(dhtPin, INPUT); digitalWrite(tempErrPin, LOW); pinMode(tempErrPin, INPUT); digitalWrite(tempWarnPin, LOW); pinMode(tempWarnPin, INPUT); return(0); }

Replies are listed 'Best First'.
Re: With Inline::C, how to link to external library? (XS)
by tye (Sage) on Aug 07, 2016 at 19:44 UTC

    Worst case you can use Inline::C to generate the XS and then build the XS module in the normal manner where you can add options to your Makefile.PL to control how linking is done. I tend to go that route anyway as Inline::C often encourages better (less buggy) interface choices while not attempting to rebuild at run-time is often a better choice.

    - tye        

      Thanks tye.

      So I've made an attempt at this, but am still getting XS errors that I can't figure out, and am hoping I'm doing something blatantly wrong, or someone with understanding can spot something else. Here's my Makefile.PL:

      use ExtUtils::MakeMaker; my %options = %{ { 'INC' => '-I/home/pi/repos/scripts/c/rpi/dht11/library -I/usr/local/ +lib -I/home/pi/repos/wiringPi/wiringPi', 'NAME' => 'perl_pl_b1a3', 'TYPEMAPS' => [ '/home/pi/perl5/perlbrew/perls/perl-5.24.0/lib/5.24.0/ExtUtils/typ +emap' ], 'VERSION' => '0.00', 'CCFLAGS' => ' -fwrapv -fno-strict-aliasing -pipe -fstack-protector- +strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=6 +4 -D_FORTIFY_SOURCE=2 -lwiringPi' } }; WriteMakefile(%options); # Remove the Makefile dependency. Causes problems on a few systems. sub MY::makefile { '' }

      The library I'm trying to load is wiringPi. I've tried also libwiringPi. I've also tried adding -I/usr/local/lib to the CCFLAGS, as well as -L/usr/local/lib. Here's the /usr/local/lib dir that I've included in INC:

      libwiringPiDev.so -> libwiringPiDev.so.2.32 libwiringPiDev.so.2.32 libwiringPi.so -> libwiringPi.so.2.32 libwiringPi.so.2.32

      ...and the error:

      Running Mkbootstrap for perl_pl_b1a3 () chmod 644 "perl_pl_b1a3.bs" cc -c -I/home/pi/repos/scripts/c/rpi/dht11/library -I/usr/local/lib - +I/home/pi/repos/wiringPi/wiringPi -fwrapv -fno-strict-aliasing -pipe +-fstack-protector-strong -I/usr/local/include -I/usr/local/lib -D_LAR +GEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -lwiringPi +-O2 -DVERSION=\"0.00\" -DXS_VERSION=\"0.00\" -fPIC "-I/home/pi/perl +5/perlbrew/perls/perl-5.24.0/lib/5.24.0/armv7l-linux/CORE" perl_pl_ +b1a3.c In file included from perl_pl_b1a3.xs:3:0: /home/pi/perl5/perlbrew/perls/perl-5.24.0/lib/5.24.0/armv7l-linux/CORE +/XSUB.h:142:31: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute_ +_’ before ‘void’ # define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unu +sed__) ^ perl_pl_b1a3.c:163:1: note: in expansion of macro ‘XS_EXTERNAL’ XS_EXTERNAL(boot_perl_pl_b1a3); /* prototype to pass -Wmissing-protot +ypes */ ^ Makefile:331: recipe for target 'perl_pl_b1a3.o' failed make: *** [perl_pl_b1a3.o] Error 1

      It's like it's not finding the library or something, as I can reproduce this exact behaviour with this simple script, by trying to link to a non-existent library:

      use warnings; use strict; use Inline C => Config => ccflagsex => '-lblah'; use Inline C => 'END'; say(); __END__ __C__ void say() { int x = myget(); printf("myvar: %d\n", x); }

        See the "Dynamic Linking" section of "perl -V". My first thought was that you may need to specify changes/additions to one or more of ccdlflags, cccdlflags, and lddlflags, perhaps dropping parts of what you specified to be added to CCFLAGS.

        But, after several tries, I finally did find the actual error amid all of the context information:

        error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’ # define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unu +sed__) ^

        Which sounded like a C preprocessor directive was being interpreted as C code. But then I realized that this was just more (actually helpful) context information and the location of the error was actually shown as:

        perl_pl_b1a3.c:163:1: note: in expansion of macro ‘XS_EXTERNAL’ XS_EXTERNAL(boot_perl_pl_b1a3); /* prototype to pass -Wmissing-protot +ypes */ ^

        Which tells me that you need to look at the line(s) before that, line 162 of perl_pl_bla3.c, where you are likely missing some terminating punctuation, like a ';'. Or you have some unclosed construct (like a comment or quoted string or such). The error could be further up from line 162, of course, and perhaps even in some other file (if line 163 is almost immediately preceeded by an #include, for example).

        I didn't notice any mistakes that would explain this problem in the C code that you posted.

        But I was surprised at a very specific path to your typemap definitions:

        'TYPEMAPS' => [ '/home/pi/perl5/perlbrew/perls/perl-5.24.0/lib/5.24.0/ExtUtils/typ +emap' ],

        - tye        

        I can reproduce this exact behaviour with this simple script, by trying to link to a non-existent library

        I can reproduce the problem with an even simpler script:
        use Inline C => 'END'; say(); __END__ __C__ void say() { printf("6\n"); }
        The first line of that code is wrong. The corrected version of that script is:
        use Inline C; say(); __END__ __C__ void say() { printf("6\n"); }
        Update: An alternative way of writing out the script would be:
        say(); use Inline C => <<'END'; void say() { printf("6\n"); } END
        I think that might have led to your confusion.

        Cheers,
        Rob
Re: With Inline::C, how to link to external library?
by beech (Parson) on Aug 07, 2016 at 22:31 UTC

      Thanks beech,

      I forgot to mention that I tried all manner of those examples of using LIBS.