For gem developers
RubyInstaller2 for Windows porting guide for gem developers
- DLL loading
- Debug DLL loading
- MSYS2 library dependency
- Path separator
- Shell escaping
- How to use Ruby (-head) on Appveyor for CI?
RubyInstaller2 (aka RubyInstaller-2.4 and newer) distinguishs between search paths for executables and for libraries (DLLs).
PATH environment variable defines the paths to be searched for executables, it is ignored for all DLL searches within the Ruby process.
Dependent DLLs can instead be loaded by setting the Windows DLL search path using the environment variable
RUBY_DLL_PATH or by the function
This environment variable can be used to add DLL paths for Ruby sub-processes.
It can contain multiple semicolon separated unquoted absolute paths.
The variable is interpreted at the ruby startup - later changes doesn't affect the running process.
Please use the Ruby function
add_dll_directory to do runtime changes to DLL search paths.
This function immediately activates additional DLL paths. It can be used with a block, so that the path is deactivated after the block operation, like in the pg gem . Note, that sub-processes are not affected by this setting. They use the standard Windows DLL search order or whatever the executable requests to use.
Windows DLL loading is very complicated and depends on so many settings, that it's often not obviously, why it fails and what exactly fails. Windows is also often not very verbose about failures in DLL loading.
Fortunately it's possible to enable additional debugging information which helps to track down such issues.
This registry setting file enables DLL loader debug flags.
ruby.exe can be started per
gdb, which (after typing
r and Enter) prints useful debug information to the console window like so:
regedit debug-loader.reg gdb --args ruby -S rails s
RubyInstaller2 allows to define dependent MSYS2 packages in the gemspec which are required for installation of the gem. The gem can then link to libraries of this package or make use of commands provided by the package. Both MINGW and MSYS2 packages can be specified, although only MINGW packages are usable as library to link to.
MSYS2 packages can be specified per
MINGW packages can be specified per
gemspec.metadata['msys2_mingw_dependencies']. The MINGW architecture is set at install time according to the architecture of the running ruby process.
Therefore only the architecture independent part of the package name is expected - the prefix
mingw-w64-x86_64- must be omitted.
gemspec.metadata['msys2_mingw_dependencies'] = 'libusb sqlite'
Optionally the name can be followed by a version restriction as described in the pacman man page:
gemspec.metadata['msys2_mingw_dependencies'] = 'libusb>=1.0.21'
However keep in mind that MSYS2 usually provides only one version at the same time for each package.
MSYS2 package dependencies are installed per
pacman command, when a gem is about to be installed. The
extconf.rb within a gem can access the newly installed MSYS2-MINGW libraries.
If the package installation fails, the output of
pacman is printed to the console, but the gem installation continues nevertheless.
Insofar setting a MSYS2 dependency can make the installation easier, but will not brake the gem installation in case of somehow changed MSYS2 packages.
Installation of MSYS2/MINGW packages can be disabled per
gem install gemname.gem --ignore-dependencies .
Simple rule: Use backslashs for escaping and forward slashs as filesystem path separator.
Although the "official" path separator on Windows is the backslash, almost all Windows APIs calls accept forward slashs as well, regardless of the Windows version.
Since Ruby on Windows returns forward slashs for
Dir.glob and others, it is best to completely avoid backslashs in paths written in ruby code.
This makes the code portable and more readable.
Note: In contrast - registry access (per stdlib
win32/registry) requires the use of backslashs and doesn't work with forward slashs.
Shell escaping is a very difficult thing on Windows, since there are so many different shells (cmd, powershell, bash, msvcrt) with very different and partly obscure escaping rules.
The Ruby stdlib
shellwords supports bash escaping only (although there is an implementation on github for Windows shells).
So using shellwords often makes things even worse.
It is therefore best to avoid shell escaping at all by using Array argument methods where possible:
system('program', 'with arguments') # instead of system("program 'with arguments'") out = IO.popen(['program', 'with arguments'], &:read) # instead of out = `program 'with arguments'`
Appveyor is a popular cloud based CI provider for the Windows OS. They provide pre-installed RubyInstaller versions from 1.9 to 2.5 as both 32-bit and 64-bit flavours installed into these directories. They can be selected through the test matrix by setting the PATH environment variable like in this example.
In addition it's possible to run tests on the latest ruby-head version. It is not pre-installed by Appveyor, but can be downloaded and installed easily. See the above example or this commit for one way to do this.