(This is Ruby 1.8.6, for Ruby 1.9, see here)
I decided to see what it would take to compile Ruby as a Visual Studio project. Now you could just include it as a makefile project, but I figure that doing this would give me a better understanding of exactly what is involved in a build of Ruby. Essentially I would want something that would compile miniruby.exe, so I could then use that as a base for a stripped down version I can incorporate it into a full project.
Miniruby.exe is linked from
- main.obj (built from main.c)
- dmydln.obj (built from dmydln.c)
- msvcr80-ruby18-static.lib
And the following windows system libraries
- oldnames.lib
- user32.lib
- advapi32.lib
- wsock32.lib
So obviously the big one here is msvcr80-ruby18-static.lib, which is built from:
array.obj bignum.obj class.obj compar.obj dir.obj dln.obj enum.obj error.obj eval.obj file.obj gc.obj hash.obj inits.obj io.obj marshal.obj math.obj numeric.obj object.obj pack.obj parse.obj process.obj prec.obj random.obj range.obj re.obj regex.obj ruby.obj signal.obj sprintf.obj st.obj string.obj struct.obj time.obj util.obj variable.obj version.obj acosh.obj crypt.obj erf.obj win32.obj dmyext.obj
These are mostly the platform independent files that live in the root source folder, there are three files at the end (acosh.obj crypt.obj erf.obj) that live in the \missing folder. Then there is win32.obj and dmyext.obj. Win32.obj simply comes from compiling /win32/win32.c, and dmyext.obj is from dmyext.c, which again is in the root (not sure why it’s not in with the rest of them).
So, in theory I should simply be able to compile all the constituent files of msvcr80-ruby18-static.lib, and then link in main.obj, dmydln.obj and the four windows libs, and I’ll be set.
Using Ruby 1.8.6 source, in c:\ruby-src\ruby-1.8.6, I started out by:
Create a Win32 console project, c:\ruby-src\mickruby. Do not use precompiled header. Add oldnames.lib;user32.lib;advapi32.lib;wsock32.lib to the additional dependancies in the project
Drag into the project (or just add) all the .c files from c:\ruby-src\ruby-1.8.6, except for lex.c, and dmydln.c (dmydln.c, just sets a flag and includes dln.c, I’m unsure why the normal build seems to include both)
Drag in (or just add) the extra files acosh.c, crypt.c and erf.c from c:\ruby-src\ruby-1.8.6\missing
Drag in (or just add) win32.c from c:\ruby-src\ruby-1.8.6\win32
Add the include paths c:\ruby-src\ruby-1.8.6 and c:\ruby-src\ruby-1.8.6\missing
You now need an appropriate config.h, which I temporarily copied from a “configured” \build directory, into the root source folder. This kind of assumes you were building Ruby from the command line, which involves running the batch file c:\ruby-src\ruby-1.8.6\win32\configure.bat from the build folder. Or, more explictly: md c:\ruby-src\build then cd c:\ruby-src\build then c:\ruby-src\ruby-1.8.6\win32\configure.bat (you’ll need to type the full path). This will create the file config.h in c:\ruby-src\build, and you can just copy it into c:\ruby-src\mickruby\
Change configuration\general\character set to “not set”
Add the preprocessor definitions:
- RUBY_EXPORT
- _CRT_SECURE_NO_DEPRECATE
- _CRT_NONSTDC_NO_DEPRECATE
In the project entry point file (mickruby.cpp), just comment out all the source code for now. We want to use main.c as the project entry point for now. (You could also obvious copy over the code from main.c into mickruby.cpp, and exclude main.c from the project.
At this point, I can build the project, with 0 errors, and 4735 warnings, and IT WORKS, in release mode I get a 659K executable (mickruby.exe) that runs ruby code. The normal build gives me a 593K executable for miniruby.exe – probably a bunch of extra windows crap? Or maybe some options are different.
If I turn off link-time code generation, and twiddle a few options, the executable goes down to 602,112. Setting the optimizations as closely as possible to -O2b2xty-, gives us 598,016. Close enough.
Adding all this to my procedural tree sample takes the release build from 104K to 452K – that’s with nothing being called. If I add a call to ruby_init() in the initialization code, then it jumps up to 712K, which sounds exactly as would be expected from the above. That’s a chunk of code, half a meg, but in the grand scheme of things games typically need 512MB to run (XBox 360 and PS3 both have 512MB, PCs have that as a minimum). So while an extra 600K might have been crippling to a PS2 game, next-gen (which is really current-gen now) platforms are less impacted.
Having done a bit of Ruby work in the past, I must say that their C interface sucks. The fact that the language isn’t even remotely 64 bit compatible (although 1.9/2.0 are “going to be”, I’ve yet to see them make progress on that front).
I do like the language though, it’s quite fun much like Python.
Comment by Washu — October 1, 2007 @ 9:52 pm
There’s some surprisingly hair-raising code under the hood with Ruby. Like this in rb_call0 (called from method_call, so executed every time a Ruby object’s method is called)
“Better that nothing”. Cowboys!
Comment by Mick West — October 2, 2007 @ 7:42 am
The whole codebase is riddled with undefined behavior, and unfortunately that undefinedness does crop up when building it against platforms it wasn’t designed for (aka, GCC 3.x + 32 bit CPU).
Comment by Washu — October 4, 2007 @ 3:15 pm
Cool. I didn’t know you could do that with a Visual Studio
Comment by kotiteatteri — May 3, 2009 @ 11:42 pm
thankssssss
Comment by çelik kapı — November 19, 2009 @ 8:31 am