Raku / App-Rakubrew

Raku environment manager
https://rakubrew.org/
Other
26 stars 13 forks source link

`rakubrew download` behaves different than `rakubrew build` on Windows #54

Closed hythm7 closed 1 year ago

hythm7 commented 2 years ago

The below program runs fine using rakudo installed with rakubrew build moar 2022.04 but dies if run with rakudo installed with rakubrew download moar 2022.04 , on windows only. On linux the program runs fine for both downloaded and built rakudo.

OS: Windows 10. Compiler: gcc Libcurl: curl-7.82.0-win64-mingw

#!/usr/bin/env raku

use NativeCall;

# curl-7.82.0-win64-mingw
constant LIB = 'curl';

constant CURLE_OK = 0;

class Handle is repr( 'CPointer' ) {

        sub curl_easy_init( --> Handle ) is native( LIB ) { * }
        sub curl_easy_cleanup( Handle )  is native( LIB ) { * }
        sub curl_easy_reset( Handle )    is native( LIB ) { * }

        sub curl_easy_escape(   Handle, Buf, int32              --> Pointer ) is native( LIB ) { * }
        sub curl_easy_unescape( Handle, Buf, int32, int32 is rw --> Pointer ) is native( LIB ) { * }

        sub curl_easy_setopt_str(  Handle, uint32, Str     --> uint32 ) is native( LIB ) is symbol( 'curl_easy_setopt' ) { * }
        sub curl_easy_setopt_ptr(  Handle, uint32, Pointer --> uint32 ) is native( LIB ) is symbol( 'curl_easy_setopt' ) { * }
        sub curl_easy_setopt_long( Handle, uint32, long    --> uint32 ) is native( LIB ) is symbol( 'curl_easy_setopt' ) { * }

        sub curl_easy_setopt_data-cb( Handle, uint32, &cb ( Pointer, uint32, uint32, Pointer --> uint32 ) --> uint32 )  is native( LIB ) is symbol('curl_easy_setopt') { * }

        sub curl_easy_perform( Handle --> uint32 ) is native( LIB ) { * }

        method new( --> Handle ) { curl_easy_init }

        method cleanup( ) { curl_easy_cleanup(self) }

        method reset( ) { curl_easy_reset(self) }
        method perform( ) { curl_easy_perform( self ) }

        multi method setopt( $option, Str $param ) {

                my $ret = curl_easy_setopt_str( self, $option, $param );

                die unless $ret == CURLE_OK;

        }

        multi method setopt( $option, Int $param ) {

                my $ret = curl_easy_setopt_long( self, $option, $param );

                die unless $ret == CURLE_OK;

        }

        multi method setopt( $option, &callback ) {

                my $ret = curl_easy_setopt_data-cb( self, $option, &callback );

                die unless $ret == CURLE_OK;

        }

        multi method setopt( $option, Pointer $ptr ) {

                my $ret = curl_easy_setopt_ptr( self, $option, $ptr );

                die unless $ret == CURLE_OK;

        }

        multi method setopt( $option, Handle $ptr ) {

                my $ret = curl_easy_setopt_ptr( self, $option, $ptr);

                die unless $ret == CURLE_OK;

        }

}

sub curl_global_init( long --> uint32 ) is native( LIB ) { * }

sub curl_global_cleanup( ) is native( LIB ) { * }

sub curl_free( Pointer $ptr ) is native( LIB ) { * }

my $curl;

class Curl {

        has Blob       $.buf;
        has IO::Handle $!download-fh;

        has Handle  $.handle handles <escape unescape>;

        method new( ) {

                constant CURLOPT_URL            = 10002;
                constant CURLOPT_WRITEDATA      = 10001;
                constant CURLOPT_WRITEFUNCTION  = 20011;
                constant CURLOPT_SSL_OPTIONS    = 216;
                constant CURLSSLOPT_NATIVE_CA   = 16;
                constant CURL_GLOBAL_DEFAULT    = 3;

                curl_global_init( CURL_GLOBAL_DEFAULT );

                my $handle = Handle.new;

                $handle.setopt( CURLOPT_WRITEDATA,     $handle              );
                $handle.setopt( CURLOPT_WRITEFUNCTION, &writefunction       );
                $handle.setopt( CURLOPT_SSL_OPTIONS,   CURLSSLOPT_NATIVE_CA );

                $curl = self.bless( :$handle );

                return $curl;
        }

        method content( :$URL!, :$encoding = 'utf-8' --> Str ) {

                $!buf = Buf.new;

                $!handle.setopt( CURLOPT_URL, $URL );

                my $ret = $!handle.perform;
                # $ret is 23 should be 0. google says that could happen if the 
                # return value of the below `writefunction` sub is not correct
                die $ret unless $ret == CURLE_OK;

                $!buf.decode: $encoding

        }
        sub writefunction( Pointer $ptr, uint32 $size, uint32 $nmemb, Pointer $handleptr --> uint32 ) {

                $curl.buf ~= Blob.new( nativecast( CArray[ uint8 ], $ptr )[ ^ $size * $nmemb ] );

                return $size * $nmemb;
        }

        method reset( ) { $!handle.reset; $!buf = Nil, $!download-fh = Nil }

        submethod DESTROY { self.cleanup; curl_global_cleanup()}

}

$curl = Curl.new;

my $URL = 'http://google.com';
# method content calls .perfrom, which calls `writefunction`
say $curl.content: :$URL;
patrickbkr commented 2 years ago

OS: Windows 10. Compiler: gcc Libcurl: curl-7.82.0-win64-mingw

In general calling C dlls compiled with a different compiler toolchain should work, even more so when using an abstraction like dyncall (which MoarVM uses). Still I think that you might be observing some incompatibility of an MSVC compiled Rakudo and a MinGW compiled libcurl. (The Rakudo that rakubrew download uses is built with MSVC.)

Do you have a MSVC compiler toolchain available? Could you try building a Rakudo with an MSVC compiler and see if that fails similar to the downloaded Rakudo?

hythm7 commented 2 years ago

Do you have a MSVC compiler toolchain available? Could you try building a Rakudo with an MSVC compiler and see if that fails similar to the downloaded Rakudo?

( unfortunately my windows experience is limited) I have downloaded and installed MSVC, Is there a way to tell rakubrew to use the newly installed build tool chain when building? Or how can i build rakudo using msvc from cli? I have looked into Rakudo build instructions in README.md and INSTALL.md and could not find something specific to windows or msvc.

patrickbkr commented 2 years ago

I think the following command should do the trick:

rakubrew build moar-2022.04 --configure-opts="--moar-option='--toolchain=msvc'"

This command is to be called in a Developer Console (either Powershell or CMD).

hythm7 commented 2 years ago
rakubrew build moar-2022.04 --configure-opts="--moar-option=--toolchain=msvc"

was able to build rakudo with msvc successfully, but got different error when run with rakudo built with above command, now it seems NativeCall is not able to load curl.dll

Cannot locate native library 'curl.dll': error 0x7e
  in method setup at C:\rakubrew\versions\moar-2022.04\install\share\perl6\core\sources\947BDAB9F96E0E5FCCB383124F923A6BF6F8D76B (NativeCall) line 319
  in method setup at C:\rakubrew\versions\moar-2022.04\install\share\perl6\core\sources\947BDAB9F96E0E5FCCB383124F923A6BF6F8D76B (NativeCall) line 366
  in sub raku-nativecall at C:\rakubrew\versions\moar-2022.04\install\share\perl6\core\sources\07D7A4E6581D1C9421412E7D2A0586F9FED3D9B5 (NativeCall::Dispatcher) line 46

curl.dll exists in System32 folder, also the program still works if switched to another rakudo built using rakubrew build moar-blead

sumanstats commented 2 years ago

You have curl.dll or libcurl.dll? If its libcurl.dll, then need to do

constant LIB = 'libcurl';
hythm7 commented 2 years ago

I have curl.dll, which exists in System32. folder. I use curl rather than libcurl because Raku will change the given name in constant LIB = 'curl'; based on platform, (e.g. on windows it will append .dll, on Linux it will prepend lib and append .so) so if its libcurl on Linux it will become liblibcurl.so

patrickbkr commented 1 year ago

Closing as this issue turned out to be an issue with Rakudo and not Rakubrew. @hythm7 Can you create a new ticket (referencing this) in rakudo/rakudo should you still be bothered by this issue?