chef-boneyard / win32-sound

A Ruby library for playing and controlling sounds on MS Windows.
13 stars 3 forks source link

Does not work on Cygwin with Ruby 2.5.1 #3

Open casper opened 6 years ago

casper commented 6 years ago

Any way to get this to work? I get the following error when requiring the library:

require 'win32/sound'
Traceback (most recent call last):
       11: from /usr/local/bin/irb:18:in `<main>'
       10: from (irb):2
        9: from /usr/local/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
        8: from /usr/local/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
        7: from /usr/local/lib/ruby/gems/2.5.0/gems/win32-sound-0.6.2/lib/win32/sound.rb:1:in `<top (required)>'
        6: from /usr/local/lib/ruby/gems/2.5.0/gems/win32-sound-0.6.2/lib/win32/sound.rb:1:in `require_relative'
        5: from /usr/local/lib/ruby/gems/2.5.0/gems/win32-sound-0.6.2/lib/win32/windows/functions.rb:3:in `<top (required)>'
        4: from /usr/local/lib/ruby/gems/2.5.0/gems/win32-sound-0.6.2/lib/win32/windows/functions.rb:4:in `<module:Windows>'
        3: from /usr/local/lib/ruby/gems/2.5.0/gems/win32-sound-0.6.2/lib/win32/windows/functions.rb:8:in `<module:SoundFunctions>'
        2: from /usr/local/lib/ruby/gems/2.5.0/gems/ffi-1.9.25/lib/ffi/library.rb:444:in `typedef'
        1: from /usr/local/lib/ruby/gems/2.5.0/gems/ffi-1.9.25/lib/ffi/types.rb:69:in `find_type'
TypeError (unable to resolve type 'uintptr_t')
casper commented 6 years ago

I get the strange suspicion something if not right with FFI here. This is on a 32-bit installation btw. That might also be relevant.

Full setup for clarification: Clean installation of Cygwin 32-bit + build of Ruby 2.5.1 under Cygwin + gem install of win32-sound.

domgetter commented 6 years ago

Okay I figured out the problem, and it is two-fold. Both ffi and win32-sound make incorrect assumptions about cygwin.

1) ffi doesn't recognize uintptr_t on cygwin

The first problem is that types.conf in ffi doesn't have a mapping for uintptr_t. This can be fixed by adding

rbx.platform.typedef.uintptr_t = uint64

as seen here

An alternative fix is to simply use :uint64 in place of :uintptr_t in these sorts of places.

Fortunately, this means this can be fixed within this project.

2) ffi incorrectly generates lib filenames on cygwin

Even if we fix the first problem, we get a new one. We start getting errors like this:

LoadError (Could not open library 'kernel32': No such file or directory.)
Could not open library 'cygkernel32.dll': No such file or directory

The problem here is that when we tell ffi to load a library like here, ffi decides to generate library file names here. Unfortunately, it has no special case for cygwin, and prepends the string 'cyg' to the library name, along with adding '.dll' to the end. Of course, there is no file named 'cygkernel32.dll', as this is nonsense.

What it needs to do is generate the string 'kernel32.dll'.

Of course, this would have to be a fix to ffi.

3) the ushort and ulong types on cygwin are not the same as on normal Windows

So even if you fix the uintptr_t problem, and you get ffi to correctly load kernel32 and winmm, you may run into the problem where the structs are the wrong size. This is caused by the use of ushort and ulong in this code. Cygwin isn't aware that we're trying to use a 32-bit library, and happily creates a modern struct with bigger members than we were expecting.

This is fixed by replacing ushort with uint16 and ulong with uint32. (And it would be a good idea to comb through any other repos that use ffi which may be used within a cygwin64 environment which hit old windows libraries).

Conclusion

So both ffi and win32-sound were making incorrect assumptions about cygwin, which resulted in incorrect file lookups and memory layouts, and fixes to code would be needed in both projects.

domgetter commented 6 years ago

@casper Ah, I didn't notice you were on a 32-bit Cygwin by the time I did my analysis, and I only checked on a 64-bit version. Luckily, these fixes should cover both.

casper commented 6 years ago

Thanks domgetter. That's an excellent summary (sorry for the late reply). But I gather that there really isn't any immediate solution for this then, as it would require upstream fixes on FFI first?

domgetter commented 6 years ago

There are a couple workarounds for you. You could modify your installation of the FFI gem so that it loads the library correctly, and modify your installation of win32-sound so that the types are picked up correctly.

You could also install ruby natively to Windows, and then call that installation within Cygwin. FFI can load native libraries just fine. This is what confused me at first with your bug. When I loaded up Cygwin, everything worked just fine because I had Ruby and the gems installed on Windows. But I noticed from the filepaths in your error that you installed everything from within Cygwin.

I think those are your only two options for now. If you want to do the first option (modify inplace), let me know and I can help you dig through and change the code. Personally, I think the second option is much easier :P, but I don't know your exact use case here.

casper commented 6 years ago

Ok. Thanks. I will think about these. I tried option 2 earlier, but the machine I'm using is too old to run the latest DevKit bundle for Ruby (MSYS2 support has been dropped for older Windows versions). That was actually the original reason for installing Ruby with Cygwin. Even the Ruby 2.5 Cygwin installation had problems on 32-bit Windows , because I think it's using the same broken FFI for its build process. I might give option 1 a shot at some point. Will comment here if I make some progress on it.