in reply to Re: Shameless plug and QR japh
in thread Shameless plug and QR japh

An Alien module would be great, but I am totally alien to Alien. Also, OpenCV discourage you from using pkg-config (which is more or less standard with any *nix dev tools package), instead they suggest cmake (which is a hit+miss for both M$ and *nix). See this forum post. In fact the only failed test I have for Image::DecodeQR::WeChat is on a M$ machine with no cmake. Installing OpenCV binaries on M$ does not include cmake.

So, an Alien module would be a breaze on *nix but it will be a real headache for M$ windows. But I am prepared to give it a try in about a month's time.

In the meantime, I include some code, at the bottom, I use to detect OpenCV flags in above module's Makefile.PL.

Regarding compiling OpenCV code as part of XS. There's a trick suggested by Botje @ #perl: do not include any OpenCV code or headers in the XS file. Place them in separate C++ files, and add an entry function which does not depend on OpenCV data structures and headers at all. So that excludes at the moment passing or returning said data structures lest they are casted to anonymous pointers which is a fair solution. Then link that C++ code to XS careful with function name mangling. Including OpenCV headers in XS clashes with Perl's internals (seed and ... cv come to mind and also the linked posts mention it, plus Inline::CPP + OpenCV = problems. I have used that trick successfully, so at least there is a way. I explain the process and various subleties at the pod of Image::DecodeQR::WeChat. Having C++ code linking to XS is another tough to crack. But solvable.

use 5.006; use strict; use warnings; use ExtUtils::MakeMaker; use File::ShareDir::Install; use File::Spec; use File::Copy; use FindBin; use File::Temp qw/tempdir/; use Cwd qw/abs_path/; use Data::Roundtrip qw/perl2dump/; use IPC::Run; # FFI::CheckLib can check_lib with specified symbol which is handy, so + use that # it does not depend on FFI! use FFI::CheckLib; # but we also need this to compile a piece of C code (not really) #use Devel::CheckLib; my $VERBOSE = exists($ENV{VERBOSE}) && defined($ENV{VERBOSE}) ? $ENV{V +ERBOSE} : 1; # returns an arrayref of [$cflags, $ldflags] # NOTE: 'cflags' contains the includes (as -I... -I...) # 'ldflags' contains the libs (as -L.. -L.. -l.. -l..) # returns undef on failure # when it fails, your only chance to have this run is to specify # ENV vars: OPENCV_CFLAGS and OPENCV_LDFLAGS # to contain -I and -L -l # Unix/Linux usually contains pkg-config which outputs this params # given that OpenCV was properly installed. # On M$ all goes, it's just a mess and should not be even supported. sub find_opencv_settings { my ($out, $err, $ret); # 1. check if env vars OPENCV_CFLAGS and OPENCV_LDFLAGS were speci +fied if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : checkin +g ENV for 'OPENCV_LDFLAGS' and 'OPENCV_CFLAGS' ...\n" } my $ldflags = exists($ENV{OPENCV_LDFLAGS}) && defined($ENV{OPENCV_ +LDFLAGS}) ? $ENV{OPENCV_LDFLAGS} : undef; my $cflags = exists($ENV{OPENCV_CFLAGS}) && defined($ENV{OPENCV_CF +LAGS}) ? $ENV{OPENCV_CFLAGS} : undef; if( $ldflags and $cflags ){ if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : fou +nd ldflags='$ldflags', cflags='$cflags' (using supplied ENV vars).\n" + } return [$cflags, $ldflags] } # 2. check if there's a pkg-config if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : checkin +g with pkg-config ...\n" } for my $opv (qw/opencv4 opencv/){ my @cmd = ('pkg-config', $opv, '--libs'); eval { IPC::Run::run \@cmd, \undef, \$out, \$err, IPC::Run::timeo +ut(30) }; if( $@ ){ if( $VERBOSE > 0 ){ print STDERR "find_opencv_settings() : + failed to run command '@cmd': $@\n"; } next } chomp($out); $ldflags = $out; @cmd = ('pkg-config', $opv, '--cflags'); IPC::Run::run \@cmd, \undef, \$out, \$err, IPC::Run::timeout(3 +0) or next; chomp($out); $cflags = $out; last; } if( $ldflags and $cflags ){ if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : fou +nd ldflags='$ldflags', cflags='$cflags' (using pkg-config).\n" } return [$cflags, $ldflags] } # 3. check with cmake script, provided cmake is installed if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : checkin +g with cmake ...\n" } my $tmpdir = tempdir(CLEANUP => 1); my $curdir = Cwd::abs_path(Cwd::cwd); die "find_opencv_settings() : failed to chdir to tempdir '$tmpdir' +." unless chdir $tmpdir; if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : writing + CMakeLists.txt and test.cpp into tempdir '$tmpdir' ...\n"; } # write the CMakeLists.txt for cmake open my $fh, '>', 'CMakeLists.txt' or die "find_opencv_settings() +: failed to open file 'CMakeLists.txt' for writing: $!"; print $fh <<'EOCM'; cmake_minimum_required(VERSION 2.9) project(RUBBISH_CMAKE_PROJECT) find_package( OpenCV REQUIRED ) include_directories( ${OpenCV_INCLUDE_DIRS} ) add_executable( _cmake_opencv_testme test.cpp ) target_link_libraries( _cmake_opencv_testme ${OpenCV_LIBS} ) message(STATUS "RUBBISH_CMAKE_PROJECT::INCLUDE_DIRS: ${OpenCV_INCLUDE_ +DIRS}") message(STATUS "RUBBISH_CMAKE_PROJECT::LIB_PATH: ${OpenCV_LIB_PATH}") message(STATUS "RUBBISH_CMAKE_PROJECT::INSTALL_PREFIX: ${OpenCV_INSTAL +L_PREFIX}") message(STATUS "RUBBISH_CMAKE_PROJECT::LIBS: ${OpenCV_LIBS}") EOCM close $fh; # now write a basic test C++ program open $fh, '>', 'test.cpp' or die "find_opencv_settings() : failed +to open file 'test.cpp' for writing: $!"; print $fh <<'EOCM'; #include <opencv2/opencv.hpp> int main(void){ return(0); } EOCM close $fh; my @cmd = ('cmake', '.'); # does cmake even exist? if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : attempt +ing to run '@cmd' into tempdir '$tmpdir' ...\n"; } eval { IPC::Run::run \@cmd, \undef, \$out, \$err }; if( $@ ){ if( $VERBOSE > 0 ){ print STDERR "find_opencv_settings() : run +ning command has failed: '@cmd': $@\n"; } } else { my %buildparams; for my $l (split /\R/, $out){ if( $l =~ /^\-\- RUBBISH_CMAKE_PROJECT::([A-Z_]+): (.+?)$/ + ){ $buildparams{$1} = $2 } } print "Build Params: \n".perl2dump(\%buildparams); if( exists($buildparams{INCLUDE_DIRS}) ){ $cflags .= ' -I'.$_ +for split /\s*;\s*/, $buildparams{INCLUDE_DIRS} } if( exists($buildparams{LIB_PATH}) ){ $ldflags .= ' -L'.$_ for + split /\s*;\s*/, $buildparams{LIB_PATH} } if( exists($buildparams{LIBS}) ){ $ldflags .= ' -l'.$_ for spl +it /\s*;\s*/, $buildparams{LIBS} } } die "find_opencv_settings() : failed to chdir to previous current +dir '$curdir'." unless chdir $curdir; if( $ldflags and $cflags ){ if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : fou +nd ldflags='$ldflags', cflags='$cflags' (using cmake).\n" } return [$cflags, $ldflags] } # 4. check with FFI::CheckLib, but this only gives us libraries! if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : finding + ldflags (only!) with FFI::CheckLib, this is just for extra info but +it is useless because it does not find the INCLUDE dir ...\n" } $cflags = ' '; # still make cflags empty and hope for the best (it + must have 1 space there) $ldflags = undef; for my $wanted ('opencv_wechat_qrcode', 'opencv_world'){ # this will fail because symbol names are mangled, e.g. _ZN2cv +13wechat_qrcode12WeChatQRCode15detectAndDecodeB5cxx11ERKNS_11_InputAr +rayERKNS_12_OutputArrayE my ($lib) = FFI::CheckLib::find_lib( lib => $wanted, symbol => 'wechat_qrcode::WeChatQRCode' ); # example output: /usr/local/lib64/libopencv_world.so.4.5.5 # this needs fine-tuning perhaps. easily done for linux but wi +ndows? ouch can't be bothered if( $lib ){ $ldflags .= ' -l'.$lib; last } } if( $ldflags and $cflags ){ if( $VERBOSE > 0 ){ print STDOUT "find_opencv_settings() : fou +nd ldflags='$ldflags', cflags='$cflags' (using ".'FFI::CheckLib::find +_lib()'.").\n" } return [$cflags, $ldflags] } print STDERR "find_opencv_settings() : error, failed to find OpenCV compilation fl +ags (library location and include-dir) using several methods." ."\nThe best way to proceed from here is to find where OpenCV library +and include dir are located " ."and set ENVironment variables before re-running this script. " ."FOR EXAMPLE if library is located at '/usr/lib64/libopencv_world.so' + " ."and include dir at '/usr/local/include/opencv4/include/', " ."then set these ENV vars (FOR EXAMPLE):" ."\n OPENCV_LDFLAGS='-L/usr/lib64/ -lopencv_world'" ."\n OPENCV_CFLAGS='-I/usr/local/include/opencv4/include/'" ."and re-run this script." ."The only complication then is if you don't thave the libopencv_world +.so which includes all OpenCV symbols " ."but instead you have hundreds of smaller OpenCV component libraries. + In which case set this:" ."\n OPENCV_LDFLAGS='-L/usr/lib64/ " # add here all libs: ."-lopencv_core " ."-lopencv_dnn " ."-lopencv_imgproc " ."-lopencv_features2d " ."-lopencv_flann " ."-lopencv_imgcodecs " ."-lopencv_wechat_qrcode " ."-lopencv_highgui " ."'" # ending the libs here ."\n\nIf running this script was successful but 'make' fails, then Per +haps you may also " ."need to add '-lpng -ltiff -ljpeg' in the same way as described above +. Also -ltbb etc. In general add all libraries via ENV vars when all +else fails." ."\nGood luck unless you have Unix/Linux which is a solid dev env.\n" ."\n" ; return undef # failed }

bw, bliako

Replies are listed 'Best First'.
Re^3: Shameless plug and QR japh
by etj (Priest) on Mar 24, 2022 at 15:09 UTC
    Fair enough. There's an Alien::cmake3 you could use to get cmake available in more places.
Re^3: Shameless plug and QR japh
by etj (Priest) on Aug 06, 2024 at 15:26 UTC
    A note from the future: the mighty zmughal made an Alien::OpenCV, which you might want to change your module over to using.

      noted thanks