mrbgems 是一个便于使用 C 语言和 ruby 脚本来扩展 mruby 的库管理器,本章会专门介绍它。
首先要在构建配置文件中启用它。 你可以用如下的方式,启用一个特定的 GEM
conf.gem '/path/to/your/gem/dir'
也可以用相对路径指定一个 GEM
conf.gem 'examples/mrbgems/ruby_extension_example'
在这种情况下,
build_config
目录,那么指定 GEM 的路径会相对于 MRUBY_ROOT
(即 mruby 源代码的根目录)。mruby 同样支持托管于 GIT repo 的 GEM:
conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master'
conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master'
conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master'
你还可以用 :path
参数来指定 repo 的子目录:
conf.gem github: 'mruby/mruby', path: 'mrbgems/mruby-socket'
你还可以通过使用 :mgem
来启用 mgem-list 中的 GEM:
conf.gem :mgem => 'mruby-yaml'
conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted
可以指定 :checksum_hash
参数来选择一个特定的提交版本:
conf.gem mgem: 'mruby-redis', checksum_hash: '3446d19fc4a3f9697b5ddbf2a904f301c42f2f4e'
如果有缺失的依赖项,mruby 就会引用 core 或 mgem-list 当中的 GEM。
要在构建时从远程 GIT 仓库中提取所有 gem, 调用 rake -p
, 或 rake --pull-gems
。
注: :bitbucket
只在 git 方式指定 GEM 时有效。
在某些情况下,你可能会想要一次性地添加若干个 mrbgems 到 mruby 中;在这种场合下,你肯定希望用一个配置文件记录这些 mrbgems 并且复用它,而不是在每个构建配置文件中都写一次要添加的 mrbgems。 若干 mrbgems 的集合叫作 GemBox,它记录了要加载的 mrbgems 列表 (同样通过 conf.gem 的方式,但包装在一个 MRuby::GemBox
对象中)。 GemBox 通过 config.gembox 'boxname'
添加到 mruby 中。
举例,下列代码创建一个 GemBox 并将 mruby-time 和 mrbgems-example 包含其中:
MRuby::GemBox.new do |conf|
conf.gem "#{root}/mrbgems/mruby-time"
conf.gem :github => 'masuidrive/mrbgems-example'
end
如上所述,GemBox 和 MRuby::Build
的设定方式相同。GemBox 必须存储在以 .gembox 为扩展名的文件,并放在 mrbgems 目录下以便让 mruby 引用。
我们把上述例子的 GemBox 存储为 custom.gembox
并放在 mruby 的 * mrbgems * 目录下,以及在配置文件中添加如下代码:
conf.gembox 'custom'
这会使 custom GemBox 在构建过程中被加载,也就是 mruby-time 和 mrbgems-example 这两个 GEM 被添加到当前构建中。
如果你需要的话,也可以把 GemBox 放在 mruby 源代码目录之外,这种情况下用绝对路径就可以了:
conf.gembox "#{ENV ["HOME"]}/mygemboxes/custom"
有两个 GemBox 是 mruby 自带的: default 包含了几个 mruby 的核心组件; full-core 则包含了 mrbgems 目录下的所有 GEM
一个最完整的 GEM 目录结构如下:
+- GEM_NAME <- GEM 名称
|
+- include/ <- 用于扩展 mruby 的头文件 (这个目录会添加到相应的编译器搜索路径)
|
+- mrblib/ <- Ruby 扩展源代码
|
+- src/ <- C 扩展源代码
|
+- test/ <- 测试代码 (Ruby)
|
+- mrbgem.rake <- GEM 说明文件
|
+- README.md <- GEM 自述文件
mrblib 目录下只包含纯 Ruby 源代码,而 src 目录包含的是 C/C++ 源代码。include 目录则存放 C/C++ 头文件。test 将会存放用于 mrbtest
测试时使用的 C/C++ 和 Ruby 源代码。mrbgem.rake 会存放关于 C 和 Ruby 扩展的信息 (比如一些编译 flags). README.md 则是 GEM 的一些简述文本.
mrbgems 需要一个叫作 mrbgem.rake 的说明文件存放在 GEM 目录下,该文件的典型内容如下:
MRuby::Gem::Specification.new ('c_and_ruby_extension_example') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Example mrbgem using C and ruby'
end
构建过程将会根据该文件的设置来编译 C 和 Ruby 源代码,编译结果会被归档到 lib/libmruby.a 静态库中。因此 GEM 的功能会被添加到像 mruby
或是 mirb
这样的应用产品上。
可以在 MRuby::Gem::Specification
对象中添加的描述:
spec.license
或 spec.licenses
(一个或多个该 GEM 的授权协议 (的列表),如 "MIT")spec.author
或 spec.authors
(一个或多个开发者名称 (的列表))spec.version
(当前 GEM 的版本)spec.description
(细节描述)spec.summary
spec.homepage
(该 GEM 的主页)spec.requirements
(关于该 GEM 所需外部依赖项的提示说明)其中,license
与 author
是必填项。
如果该 GEM 需要依赖另外的 GEM,可以用: spec.add_dependency (gem, *requirements [, default_get_info])
MRuby::Gem::Specification.new ('c_and_ruby_extension_example') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
# 添加 mruby-parser 依赖
# 该依赖版本必须在 1.0.0 和 1.5.2 之间
spec.add_dependency ('mruby-parser', '>= 1.0.0', '<= 1.5.2')
# 添加托管于 github 上的 mruby-uv 依赖 (任意版本)
spec.add_dependency ('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv')
# 添加托管于 github 上最新版本的 mruby-onig-regexp
spec.add_dependency ('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp')
# 也可以添加一些只在测试状况下才依赖的 GEM
spec.add_test_dependency ('mruby-process', :github => 'iij/mruby-process')
end
版本号并不是必须的 如果要填写,请看如下列表:
当你传递了多个版本需求时,依赖关系必须满足所有的版本需求。
构建配置中没有添加默认 gem 时,你可以使用它作为依赖。 当 add_dependency
调用的最后一个参数是 Hash 类型时,它将被视为默认的 gem 信息。其格式与方法 MRuby::Build#gem
的参数相同,但不能作为 gem 的路径来处理。
当需要特殊版本的依赖项时,在构建配置中使用 MRuby::Build#gem
来覆盖默认的 gem。
和依赖项相反,如果有不兼容其它 GEM 的情况,可以写:
spec.add_conflict (gem, *requirements)
requirements
参数和 add_dependency
是一致的.示例:
MRuby::Gem::Specification.new'some-regexp-binding' do |spec|
spec.license = 'BSD'
spec.author = 'John Doe'
spec.add_conflict 'mruby-onig-regexp', '> 0.0.0'
spec.add_conflict 'mruby-hs-regexp'
spec.add_conflict 'mruby-pcre-regexp'
spec.add_conflict 'mruby-regexp-pcre'
end
如果该 GEM 有更复杂的构建要求,你可以使用下列选项。
spec.cc.flags
(C 编译器 flags)spec.cc.defines
(C 编译器 defines)spec.cc.include_paths
(C 编译器的搜索路径)spec.linker.flags
(链接器 flags)spec.linker.libraries
(链接时的附加依赖库)spec.linker.library_paths
(链接时的附加依赖库的搜索路径)spec.bins
(生成的可执行文件)spec.rbfiles
(要编译的 Ruby 文件)spec.objs
(要编译产出的目标文件)spec.test_rbfiles
(测试时要编译的 Ruby 文件)spec.test_objs
(测试时要编译产出的目标文件)spec.test_preload
(mrbtest 时要加载的初始文件)你也可以通过 spec.mruby.cc
和 spec.mruby.linker
直接向整个构建过程添加额外的编译 / 链接选项。
GEM 可以把 include 目录导出给其它依赖于该 GEM 的其它 GEM 来使用。 默认情况下,/...absolute path.../{GEM_NAME}/include
会被导出。 所以推荐把该 GEM 用到的头文件放入 include/ 目录下。
这些导出都是可传递的,比如: 当 B 依赖 C 而 A 又依赖 B, A 也就可以用到 C 所导出的 include 目录。
导出的 include 目录会被 rake 程序自动添加到 GEM 本地的 include_paths 选项。 你也可以通过操作 spec.export_include_paths
选项来手动控制更复杂的构建情况。
mruby 可以用 C 语言 编写扩展,也就是你可以通过 mruby 的 C API 来把 C 库的功能传入 mruby。
mrbgems 需要你编写一个名为 mrb_YOURGEMNAME_gem_init (mrb_state)
的 C 函数。YOURGEMNAME
是该 GEM 的名称。如果你的 GEM 名为 c_extension_example, 你的初始化函数应该写成这样:
void
mrb_c_extension_example_gem_init (mrb_state* mrb) {
struct RClass *class_cextension = mrb_define_module (mrb, "CExtension");
mrb_define_class_method (mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE ());
}
译者注:如果你使用 C++ 编写扩展,别忘了在该函数前添加 extern "C" 来确保该函数匹配 C ABI。
mrbgems 需要你编写一个名为 mrb_YOURGEMNAME_gem_final (mrb_state)
的 C 函数。YOURGEMNAME
是该 GEM 的名称。如果你的 GEM 名为 c_extension_example, 你的初始化函数应该写成这样:
void
mrb_c_extension_example_gem_final (mrb_state* mrb) {
free (someone);
}
+- c_extension_example/
|
+- src/
| |
| +- example.c <- C 扩展的源代码
|
+- test/
| |
| +- example.rb <- 测试用的 Ruby 代码
|
+- mrbgem.rake <- GEM 说明文件
|
+- README.md
mruby 也可以用纯 Ruby 代码进行扩展。这就便于你覆盖一些现存的类或者干脆创建一些新的类。这种扩展方式只要把所有 Ruby 源代码放入 GEM 的 mrblib 目录下即可。
+- ruby_extension_example/
|
+- mrblib/
| |
| +- example.rb <- Ruby 扩展源代码
|
+- test/
| |
| +- example.rb <- 测试用的 Ruby 代码
|
+- mrbgem.rake <- GEM 说明文件
|
+- README.md
你可以同时使用 C 和 Ruby 代码来扩展 mruby 的功能。
mrblib 目录下的代码会在 C 函数 gem_init
执行后调用。所以要确保 *mruby 脚本 * 依赖 *C 代码 * 而不是 *C 代码 * 依赖 *mruby 脚本 *。
+- c_and_ruby_extension_example/
|
+- mrblib/
| |
| +- example.rb <- Ruby 扩展源代码
|
+- src/
| |
| +- example.c <- C 扩展的源代码
|
+- test/
| |
| +- example.rb <- 测试用的 Ruby 代码
|
+- mrbgem.rake <- GEM 说明文件
|
+- README.md