1、引言
在Windows上编译Ruby一直是一件要命的事情,虽然官方提供了Ruby Installer,但是从Ruby 1.9开始,Ruby Installer就是使用MinGW进行编译了,一般使用的话问题不大,但是如果你想要把Ruby解释器集成到自己的C/C++应用程序里面做深度开发的话,那么就意味着你必须要使用gcc作为后端(Ruby Installer只提供.a格式的lib库文件),那么也就意味着你可以和Visual Studio这个宇宙第一IDE说再见了……
我自己就有如上需求,主要是想要把Ruby嵌入游戏引擎中做脚本层(类似于其他引擎使用Lua、Python这种应用场景),所以折腾了很久。虽说Ruby一直都提供了win32的编译configure,但是在早期版本的VC的编译器下简直形同虚设,要么缺这个依赖要么缺那个依赖,总之就是完全编译不起来,令人窒息。
Visual Studio 2017之后情况开始好转,于是我尝试使用Visual Studio 2017进行Ruby的编译,虽然过程中也踩了些坑,但还是相对顺利地完成了编译,并且成功集成到了C++之中,下面就对整个过程做个总结。
2、准备
安装Visual Studio 2017,我用的是社区版。
-
编译Ruby最基本的自然是它的源码,可以从Ruby官网上下载,截至目前(2018年3月27日),latest stable release的Ruby版本号为2.5.0,因此我下载的就是2.5.0版本的Ruby。
下载下来文件名为
ruby-2.5.0.tar.gz
,解压出来,这里假设源代码的目录为:D:\ruby,我们要安装到D:\ruby-bin中。 -
除了Ruby源代码之外,Ruby的编译还依赖于一些第三方扩展,比如
dbm
、gdbm
、readline
、openssl
、zlib
之类的,这些扩展你可用可不用,如果要用到的话就需要去编译,如果不用的话就不管它(如果不编译不用的扩展的话,在编译Ruby的时候就会提示你某某扩展将不会被安装,这个时候不要惊慌不要害怕,统统无视掉就行了 _(:з」∠)_)。这里我选择了openssl
和zlib
作为依赖进行编译。. 首先去openssl官网上下载源码,我下载的是
openssl-1.0.2n.tar.gz,
,下载下来后本地解压,这里假设目录为:D:\openssl,为了编译openss,我们还需要安装Perl环境,这里简单地安装ActivePerl就行了(体积有点大);此外,还需要去nasm官网上下载nasm汇编器,我这里下载的是http://www.nasm.us/pub/nasm/releasebuilds/2.13.03/win64/nasm-2.13.03-installer-x64.exe
,简单安装完即可,这里假设安装目录为D:\nasm。 然后去zlib官网上下载源码,我下载的是
zlib-1.2.11.tar.gz
,同样地,下载下来后本地解压,这里假设目录为:D:\zlib。继续新建一个文件夹如D:\Dependency,然后在里面新建三个文件夹:D:\Dependency\bin、D:\Dependency\include、D:\Dependency\lib,之后我们编译好的依赖相关文件会分别放进这3个文件夹里面。
最后,在控制面板中打开
系统和安全 -> 高级设置 -> 环境变量
,然后在Path中添加D:\Dependency\bin以及D:\nasm,再新建一个INCLUDE,设定为D:\Dependency\include,最后再新建一个LIB然后设定为D:\Dependency\lib,这样一来,-上面2个依赖编译好的相关文件,在Ruby编译的时候就能够被找到了。
以上就是编译Ruby前所有准备工作。
3、编译过程
在开始菜单中打开
Visual Studio 2017 -> 适用于 VS 2017 的 x64 本机命令提示
,需要注意的是,这里我要编译的是64位的Ruby,所以选择的是64位的本机命令提示,如果你要编译32位的,则选择适用于 VS 2017 的 x86 本机命令提示
,这个时候打开了一个命令行。-
先编译openssl,
D: cd openssl perl Configure VC-WIN64A --prefix="D:/Dependency" nmake nmake test nmake install
完成后,所需的文件就已经在D:\Dependency里面了。
-
然后编译zlib:
cd .. cd zlib nmake -f win32/Makefile.msc AS=ml64 LOC="-DASMV -DASMINF -I." OBJA="inffasx64.obj gvmat64.obj inffas8664.obj"
编译完成后,还需要进一步把源代码目录下生成的以下文件分别拷贝到对应的文件夹里面:
-
zdll.exp/zdll.lib/zlib.lib/zlib.pdb/zlib1.pdb
->D:\Dependency\lib
-
zlib1.dll
->D:\Dependency\bin
-
zconf.h/zlib.h
->D:\Dependency\include
-
-
最后编译Ruby
cd .. cd ruby win32\configure.bat --prefix=/ruby-bin --target=x64-mswin64 nmake nmake test nmake install
然后你就可以在D:\ruby-bin下面找到编译好的Ruby啦~ (〃'▽'〃)
4、集成
接下来说说怎么在VC++中调用Ruby解释器。
想要在VC++中调用Ruby解释器需要这么几个文件:
所有的头文件:
D:\ruby-bin\include
动态运行库文件:
D:\ruby-bin\x64-vcruntime140-ruby250.dll
静态链接库文件:
D:\ruby-bin\lib\x64-vcruntime140-ruby250.lib
如果你不想要动态运行库的话,那么可以使用静态运行库:D:\ruby-bin\x64-vcruntime140-ruby250-static.lib.dll
文件。
首先,新建一个Visual Studio的C++工程,这个我就不细讲了(这个如果还要细讲的话,估计你上面那一坨都没搞定),然后把上面的文件复制到工程目录下。
接下来是配置Visual Studio工程的头文件搜索路径,把下面3个路径添加到属性 -> VC++ 目录 -> 包含目录
中就行了:
- include\ruby-2.5.0\x64-mswin64_140
- include\ruby-2.5.0
- include
新建一个main.cpp
,编写如下代码,代码的功能在注释里面已经写好了。
#include <string>
// 把Ruby的头文件包含进来,里面包含了所有集成Ruby所需的函数声明
#include "include\ruby-2.5.0\ruby.h"
// 把静态链接库link进来
#pragma comment(lib, "x64-vcruntime140-ruby250.lib")
int main(int argc, char* argv[]) {
// 初始化Ruby环境和堆栈。注意,从某个Ruby版本之后,这两行就绝对不能少
ruby_sysinit(&argc, &argv);
RUBY_INIT_STACK
// 初始化Ruby解释器
ruby_init();
// 初始化Ruby加载路径
ruby_init_loadpath();
// 为$0命名
ruby_script("embeedded");
// 在Ruby中创建一个字符串
auto str = std::string("Hello! I'm Ruby from Visual Studio!");
auto rbStr = rb_str_new(str.c_str(), str.length());
// 调用p方法显示字符串
rb_p(rbStr);
// 关闭Ruby解释器
ruby_finalize();
return 0;
}
需要注意的是,生成目标一定要选择x64,不然会编译不过,原因是ruby.h
的第108行是这么写的:
typedef char ruby_check_sizeof_voidp[SIZEOF_VOIDP == sizeof(void*) ? 1 : -1];
而对于SIZEOF_VOIDP
的定义为:
#define SIZEOF_VOIDP 8
也就是说如果你编译的是64位版本的Ruby的话,那么头文件中会把VOIDP默认为64位长度,如果你还是32位的编译目标的话,sizeof(void*)
就是4,这里就编译不通过了(数组的索引为负数,使得编译器强行报错,算是一种trick)。
如果你编译的Ruby是32位的话,同理。
运行结果:
5、总结
关于Ruby编译集成的相关内容就这么多了,更多相关的可以关注Ruby的github项目:https://github.com/ruby/ruby
Ruby相关的C API在这里:https://github.com/ruby/ruby/blob/trunk/doc/extension.rdoc
希望本文能够帮到你,不用像多年以前的我那样无头苍蝇一般地满世界乱搜 (*╹▽╹*)
顺便吐槽一下,真希望Windows下也能有apt-get这样的神物啊 _(:з」∠)_