Class: Rubycc::Doctor::Builder
- Inherits:
-
Object
- Object
- Rubycc::Doctor::Builder
- Defined in:
- lib/rubycc/doctor/builder.rb
Overview
The on-the-fly build a gem gets when it is not in the verified database: the
same path RubyGems takes for a gem install, driven by hand so each stage's
outcome can be reported separately.
Per gem: fetch the .gem, read its real extensions list; with none it needs no C toolchain (no_ext). For each extension, run its extconf.rb with the mkmf_shim loaded (so mkmf's conftests and the Makefile it writes route to rubycc), then build the generated Makefile with rmake (which always substitutes rubycc for the compiler/linker). On success the freshly built .so is required in a child process to prove its Init function dlopens.
A failure is pinned to a stage — extconf, compile, link, require or the per-gem timeout — with a short summary of the tool's stderr.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- REPO_LIB =
Repository lib/ (this file lives at lib/rubycc/doctor/) and the executables the build shells out to.
File.("../..", __dir__)
- REPO_ROOT =
File.("../../..", __dir__)
- RMAKE_EXE =
File.join(REPO_ROOT, "exe", "rmake")
- MKMF_SHIM_REQUIRE =
"-rrubycc/mkmf_shim"
Instance Method Summary collapse
-
#build(entry) ⇒ Object
Build
entry(a Gemfile::Entry). -
#initialize(cache_dir:, timeout: 300, out: nil) ⇒ Builder
constructor
timeoutis the per-gem ceiling in seconds.
Constructor Details
#initialize(cache_dir:, timeout: 300, out: nil) ⇒ Builder
timeout is the per-gem ceiling in seconds. out receives progress lines.
44 45 46 47 48 |
# File 'lib/rubycc/doctor/builder.rb', line 44 def initialize(cache_dir:, timeout: 300, out: nil) @cache_dir = cache_dir @timeout = timeout @out = out end |
Instance Method Details
#build(entry) ⇒ Object
Build entry (a Gemfile::Entry). Returns a Result. Never raises: every
failure mode is folded into a Result the CLI can print.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/rubycc/doctor/builder.rb', line 52 def build(entry) fetcher = Fetcher.new(@cache_dir) version = entry.version || fetcher.latest_version(entry.name) return Result.new(status: :unknown, reason: "could not resolve a version") unless version gem_path = begin fetcher.download(entry.name, version) rescue Fetcher::FetchError => e return Result.new(status: :unknown, reason: "fetch failed (offline?): #{e.}") end spec = fetcher.spec(gem_path) return Result.new(status: :no_ext) if spec.extensions.empty? Timeout.timeout(@timeout) { build_extensions(entry.name, version, gem_path, spec) } rescue Timeout::Error Result.new(status: :failed, stage: :timeout, reason: "exceeded #{@timeout}s") rescue StandardError => e Result.new(status: :failed, stage: :internal, reason: "#{e.class}: #{e.}") end |