Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions build_config/ci/gcc-clang.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,20 @@
conf.enable_cxx_abi
end

MRuby::Build.new('default') do |conf|
MRuby::Build.new('byte-string') do |conf|
conf.toolchain

# The one build here on the default gembox. It leaves out mruby-encoding,
# which is what defines MRB_UTF8_STRING, so its strings index by byte. The
# tests written as the byte-indexed mirror of the UTF-8 ones (String#scrub
# The one build here whose strings index by byte. mruby-encoding is what
# defines MRB_UTF8_STRING, so dropping it is what makes "あ".length 3, and
# the tests written as the byte-indexed mirror of the UTF-8 ones (String#scrub
# degrading to a no-op, the byte-counting halves of mruby-regexp and
# mruby-string-ext) run nowhere else: every other build in CI, here and in
# ci/msvc, takes full-core. Tests only, since the binaries this gembox adds
# are the same ones the bintest above already covers.
conf.gembox 'default'
# ci/msvc, keeps the gem. Taking it out of full-core rather than reaching for
# a smaller gembox keeps the rest of the box on the byte-indexed side too.
# Tests only, since the binaries full-core adds are the same ones the bintest
# above already covers.
conf.gembox 'full-core'
conf.gems.delete 'mruby-encoding'

conf.enable_test
end
19 changes: 19 additions & 0 deletions doc/guides/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,30 @@ conf.gem :core => 'mruby-bin-mirb'

# Integrate GemBox (set of Gems)
conf.gembox "default"

# ... and take one back out of it
conf.gems.delete "mruby-socket"
```

A GemBox is a set of Gems defined in `mrbgems/default.gembox` for example.
It's just a set of `mrbgem` configurations.

`conf.gems.delete` removes a Gem the configuration has already added, so a
build can say "this GemBox, minus one" without restating the box. It has to
come after the `gembox` line that brought the Gem in, and naming a Gem that
is not in the build fails, so a misspelled name does not pass for a build
that quietly keeps the Gem. `conf.gems.reject!` takes a block instead and
removes every Gem it matches, returning `nil` when it matches none.

A Gem that another Gem in the build declares as a dependency cannot be
removed this way: dependency resolution loads it again, and reports

```
gem 'mruby-string-ext' can't be removed; mruby-regexp depends on it
```

Removing the Gem that depends on it as well is what makes it go.

There is a `RubyGem` (gem for CRuby) named `mgem` that help you to
manage `mrbgems`. Try `gem install mgem`. `mgem` can show you the list
of registered `mrbgems`.
Expand Down
35 changes: 33 additions & 2 deletions lib/mruby/gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ class List

def initialize
@ary = []
@removed = []
end

def each(&b)
Expand All @@ -442,6 +443,30 @@ def <<(gem)
self
end

# Remove the gem named +name+ and return it. Naming a gem that
# is not in this build is a typo, not a no-op, so this fails;
# use +reject!+ when a predicate matching nothing is acceptable.
#
# A gem another gem declares as a dependency comes back during
# dependency resolution; setup_dependencies says so when it does.
def delete(name)
gem = self[name]
fail "Can't remove gem '#{name}'; it is not in this build" unless gem
@ary.delete(gem)
@removed << name
gem
end

# Remove every gem for which the block returns true. Returns
# nil when nothing was removed, like Array#reject!.
def reject!(&block)
gone = @ary.select(&block)
return nil if gone.empty?
@ary -= gone
@removed.concat(gone.map(&:name))
self
end

def empty?
@ary.empty?
end
Expand Down Expand Up @@ -492,20 +517,26 @@ def setup_dependencies(build)
default_gems = {}
each do |g|
g.dependencies.each do |dep|
default_gems[dep[:gem]] ||= default_gem_params(dep)
default_gems[dep[:gem]] ||= default_gem_params(dep).merge(:required_by => g.name)
end
end

until default_gems.empty?
def_name, def_gem = default_gems.shift
next if gem_table[def_name]

# The build config asked for this one to go, but a gem that
# stayed cannot be built without it. Say so rather than
# letting the removal look like it took.
warn "gem '#{def_name}' can't be removed; #{def_gem[:required_by]} depends on it" if
@removed.include?(def_name)

spec = gem_table[def_name] = build.gem(def_gem[:default])
fail "Invalid gem name: #{spec.name} (Expected: #{def_name})" if spec.name != def_name
spec.setup

spec.dependencies.each do |dep|
default_gems[dep[:gem]] ||= default_gem_params(dep)
default_gems[dep[:gem]] ||= default_gem_params(dep).merge(:required_by => spec.name)
end
end

Expand Down
Loading