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
5 changes: 5 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ MRuby.each_target do |build|
end
end
gems.setup(self) if enable_gems?

# The config has been read and every gem's mrbgem.rake body has run, so the
# defines a gem contributes are all in. `build.has_define?` answers from
# here on, and refuses before.
build.defines_final!
end

# load basic rules
Expand Down
29 changes: 29 additions & 0 deletions doc/guides/mrbgems.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,35 @@ after all setup blocks for GEMs including dependencies have been called.
**NOTE**: Using the `build_settings` method will cause GEM's all build command settings
directly written in the block passed to `MRuby::Gem::Specification.new` to be ignored.

### Asking what the build defines

A GEM announces a capability to the rest of the build by adding to
`spec.build.defines`, which becomes a `-D` on every translation unit:

```ruby
spec.build.defines << "HAVE_MRUBY_IO_GEM"
```

`MRuby::Build#has_define?` is how another GEM reads one back:

```ruby
spec.build_settings do
spec.cc.flags << "-any_flags" if build.has_define?("MRB_UTF8_STRING")
end
```

It reports a define whether the build configuration asked for it or a GEM
contributed it, and matches on the name alone, so a define added as `"FOO=1"`
answers `has_define?("FOO")`. The `-D` belongs to the compiler flag and not to
the define, so it is no part of the name to ask under.

It has to be asked from `build_settings` and not from the block passed to
`MRuby::Gem::Specification.new`. A GEM contributes its defines when its own
block runs, and the blocks run in the order the GEMs were added, so during
that phase the answer would depend on how far down the list the caller sits.
`has_define?` raises there rather than hand back an answer that is right for
some GEM orders and wrong for others.

## Platform Ports (ports/)

A gem may ship platform-specific C sources under `ports/<name>/`
Expand Down
32 changes: 32 additions & 0 deletions lib/mruby/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def initialize(name='host', build_dir=nil, internal: false, &block)
@install_prefix = nil
@install_excludes = []
@defines = []
@defines_final = false
@cc = Command::Compiler.new(self, %w(.c), label: "CC")
@cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp), label: "CXX")
@objc = Command::Compiler.new(self, %w(.m), label: "OBJC")
Expand Down Expand Up @@ -378,6 +379,37 @@ def compilers
end
end

# Declare that every gem in the build has had its mrbgem.rake run, so the
# defines gems contribute through `spec.build.defines` are all in. Called
# once from the Rakefile, between loading the build config and defining
# any rule.
def defines_final!
@defines_final = true
end

# True when this build compiles with -D<name>, whether the build config
# asked for it or a gem contributed it. A gem reads this to configure
# itself against a capability another gem provides.
#
# Until `defines_final!` the answer would depend on how far down the gem
# list the caller sits, since a gem contributes its defines when its own
# mrbgem.rake body runs. Rather than hand back an answer that is right
# for some gem orders and wrong for others, this refuses to answer at all
# before then. `spec.build_settings` is the hook that runs late enough.
def has_define?(name)
unless @defines_final
fail "build.has_define?(#{name.inspect}) cannot be answered while gems " \
"are still being set up, because a gem contributes its defines " \
"then. Ask from a `spec.build_settings` block instead."
end
name = name.to_s
# A define may carry a value, as `FOO=1` does, so compare the name and
# not the value. The `-D` is the compiler's, added when the flags are
# assembled, and is no part of the name.
[defines, *compilers.map(&:defines)].flatten
.any? {|d| d.to_s.split('=', 2).first == name}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end

def define_rules
[@cc, *(@cxx if cxx_exception_enabled?)].each do |compiler|
compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.object)
Expand Down
Loading