Wednesday, July 22, 2020

How to build Ruby in Windows natively without WSL, MSYS2 or Cygwin

Every Ruby release tarball contains file win32/README.win32. If you decide to distribute Ruby alongside your Windows app, you can either struggle with the instructions from that file or use MSYS2 (== the modern RubyInstaller). In the past there was Ruby-mswin32 project with an uninspiring motto The forever war against Windows ;-( [sic], but it has died of neglect.

When you ask anyone knowledgeable about compiling Ruby under Windows, they oft (always?) say it's unbearably difficult to get right. On hearing that, I, of course, knew I was destined to repeat the endeavour.

If you're going to do that blindly by installing VS2019 (the 'Community' edition, which is supposedly free) & by following the steps in win32/README.win32, you most probably come through, but end up with a crippled Ruby variant that has no openssl support whatsoever & hence you cannot run the gem command. Smashing.

After wasting time on that I searched for a binary version of openssl suitable for the VS, recompiled Ruby to make sure rubygems was working & decided that the process was indeed getting mighty wearisome.

Turns out, it can be simplified.

At the time of writing, there's exactly 1 post on the interwebs about this topic by some Japanese guy on a Japanese knowledge community platform in Japanese. Instead of building/finding dependencies manually (we need at least 3 of them: openssl, readline & zlib) we can employ vcpkg for that job.

I.e.:

  1. Install VS2019.

  2. Clone the vcpkg repo (say, to D:\opt\s\vcpkg).

  3. Open x64 Native Tools Command Prompt for VS 2019.

  4. Run bootstrap-vcpkg.bat inside the cloned vcpkg repo directory.

  5. Download & compile the dependencies:

     > vcpkg --triplet x64-windows install libxml2 libxslt openssl readline zlib
  6. Set 3 env variables:

     > set PATH=%PATH%;D:\opt\s\vcpkg\installed\x64-windows\bin
    > set INCLUDE=%INCLUDE%;D:\opt\s\vcpkg\installed\x64-windows\include
    > set LIB=%LIB%;D:\opt\s\vcpkg\installed\x64-windows\lib
  7. cd to the unpacked Ruby src directory & type:

     > win32\configure.bat --prefix=d:\opt\s\ruby

    then nmake & nmake install.

If you did everything correctly, even irb should work:

> irb -rfiddle -rfiddle/import
irb(main):001:1* module User32
irb(main):002:1* extend Fiddle::Importer
irb(main):003:1* dlload 'user32'
irb(main):004:1* extern 'int MessageBoxA(int, char*, char*, int)'
irb(main):005:0> end
=> #<Fiddle::Function:0x000000000676fbc8 ...>
irb(main):006:0> User32::MessageBoxA 0, RUBY_DESCRIPTION, "", 0
=> 1

By today's standards, the resulting full Ruby installation is pleasantly small:

$ du -shc vcpkg/installed/x64-windows/bin ruby/{bin,lib} --exclude '*.pdb'
7.0M    vcpkg/installed/x64-windows/bin
2.5M    ruby/bin
35M     ruby/lib
45M     total