Class: Beni::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/beni/builder.rb,
sig/beni/builder.rbs

Overview

Drives the vendored mruby tree's build to produce the per-target libmruby.a archives, by running mruby's own Rakefile — the documented build entry point (doc/guides/compile.md). With no build_config, mruby falls back to its own build_config/default.rb; an explicit config is wired in via the MRUBY_CONFIG env var, which mruby resolves as an absolute path.

Constant Summary collapse

DEFAULT_TARGETS =

mruby's anonymous MRuby::Build.new names its target "host" (lib/mruby/build.rb), so the upstream default config produces build/host/lib/libmruby.a. A custom build config with different target names supplies its own list via Beni::Tasks.

Returns:

  • (Array[String])
%w[host].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(vendor_dir:, build_config: nil, targets: DEFAULT_TARGETS) ⇒ Builder

Returns a new instance of Builder.

Parameters:

  • vendor_dir: (String)
  • build_config: (String, nil) (defaults to: nil)
  • targets: (Array[String]) (defaults to: DEFAULT_TARGETS)


22
23
24
25
26
# File 'lib/beni/builder.rb', line 22

def initialize(vendor_dir:, build_config: nil, targets: DEFAULT_TARGETS)
  @vendor_dir = vendor_dir
  @build_config = build_config
  @targets = targets
end

Instance Attribute Details

#build_configString? (readonly)

Returns the value of attribute build_config.

Returns:

  • (String, nil)


20
21
22
# File 'lib/beni/builder.rb', line 20

def build_config
  @build_config
end

#targetsArray[String] (readonly)

Returns the value of attribute targets.

Returns:

  • (Array[String])


20
21
22
# File 'lib/beni/builder.rb', line 20

def targets
  @targets
end

#vendor_dirString (readonly)

Returns the value of attribute vendor_dir.

Returns:

  • (String)


20
21
22
# File 'lib/beni/builder.rb', line 20

def vendor_dir
  @vendor_dir
end

Instance Method Details

#artifact_pathsArray[String]

Every artifact the build must leave behind: the per-target archive and its flags.mak sidecar.

Returns:

  • (Array[String])


94
95
96
# File 'lib/beni/builder.rb', line 94

def artifact_paths
  libmruby_paths + flags_mak_paths
end

#buildvoid

This method returns an undefined value.

Run mruby's rake and raise unless every target's libmruby.a exists afterwards. Alongside the default task, each target's libmruby.flags.mak file task is requested explicitly — mruby's embedder interface recording the exact compile flags, which +beni-sys+'s build script parses to keep bindgen's view of the ABI aligned with the archive. (The file task is defined per target but not part of mruby's default products.) The underlying build is make-style incremental, so re-running on a partially built tree only compiles what is missing.



73
74
75
76
77
78
79
# File 'lib/beni/builder.rb', line 73

def build
  check_build_config!
  cmd = [RbConfig.ruby, "-S", "rake", "default", *flags_mak_paths]
  puts "[beni] cd #{mruby_dir} && #{env.map { |k, v| "#{k}=#{v}" }.join(" ")} #{cmd.join(" ")}"
  run_mruby_rake(env, cmd)
  verify_artifacts!
end

#built?Boolean

True when every target's artifacts — libmruby.a plus the libmruby.flags.mak sidecar beni-sys parses for ABI alignment — are already present, letting callers skip the build without spawning a subprocess. An archive without its sidecar triggers a rebuild, which is incremental and only emits the missing file.

Returns:

  • (Boolean)


45
46
47
# File 'lib/beni/builder.rb', line 45

def built?
  artifact_paths.all? { |path| File.exist?(path) }
end

#check_build_config!void

This method returns an undefined value.

The declared config belongs to the consumer; a path that does not exist is a configuration error named before anything spawns.

Raises:



125
126
127
128
129
# File 'lib/beni/builder.rb', line 125

def check_build_config!
  return if build_config.nil? || File.exist?(build_config)

  raise Error, "[beni] build config #{build_config} does not exist"
end

#cleanvoid

This method returns an undefined value.

Remove each target's build tree (keeps the vendored mruby source).



82
83
84
85
86
87
88
# File 'lib/beni/builder.rb', line 82

def clean
  targets.each do |target|
    dir = File.join(mruby_dir, "build", target)
    FileUtils.rm_rf(dir)
    puts "[beni] removed #{dir}"
  end
end

#ensure_builtvoid

This method returns an undefined value.

Idempotent build entry point for rake beni:build: skip with a note when every artifact is already present, otherwise build and report readiness. A declared config that does not exist aborts before the skip check — stale artifacts must not mask it.



53
54
55
56
57
58
59
60
61
62
# File 'lib/beni/builder.rb', line 53

def ensure_built
  check_build_config!
  if built?
    puts "[beni] libmruby.a already present for #{targets.join(" + ")} — skipping"
    return
  end

  build
  puts "[beni] libmruby.a ready for #{targets.join(" + ")}"
end

#envHash[String, String]

Environment for the mruby build subprocess. BENI_VENDOR_DIR lets build configs resolve the vendor tree without knowing where the consuming project lives on disk. MRUBY_CONFIG is only set for an explicit config — absent, mruby falls back to its own build_config/default.rb (lib/mruby/build.rb#mruby_config_path).

Returns:

  • (Hash[String, String])


110
111
112
113
114
# File 'lib/beni/builder.rb', line 110

def env
  env = { "BENI_VENDOR_DIR" => vendor_dir }
  env["MRUBY_CONFIG"] = build_config if build_config
  env
end

#flags_mak_pathsArray[String]

Per-target libmruby.flags.mak file-task paths, matching the task names mruby defines in tasks/libmruby.rake (absolute, anchored on the default build/<target> layout).

Returns:

  • (Array[String])


119
120
121
# File 'lib/beni/builder.rb', line 119

def flags_mak_paths
  targets.map { |target| File.join(mruby_dir, "build", target, "lib", "libmruby.flags.mak") }
end

#libmruby_path(target) ⇒ String

Parameters:

  • target (String)

Returns:

  • (String)


32
33
34
# File 'lib/beni/builder.rb', line 32

def libmruby_path(target)
  File.join(mruby_dir, "build", target, "lib", "libmruby.a")
end

#libmruby_pathsArray[String]

Returns:

  • (Array[String])


36
37
38
# File 'lib/beni/builder.rb', line 36

def libmruby_paths
  targets.map { |target| libmruby_path(target) }
end

#mruby_dirString

Returns:

  • (String)


28
29
30
# File 'lib/beni/builder.rb', line 28

def mruby_dir
  File.join(vendor_dir, "mruby")
end

#run_mruby_rake(env, cmd) ⇒ void

This method returns an undefined value.

Spawn mruby's rake with the parent environment plus the env overlay. Extracted as a seam so tests can fake the subprocess while observing the full env + cmd contract.

Parameters:

  • env (Hash[String, String])
  • cmd (Array[String])


101
102
103
# File 'lib/beni/builder.rb', line 101

def run_mruby_rake(env, cmd)
  system(env, *cmd, chdir: mruby_dir, exception: true)
end

#verify_artifacts!void

This method returns an undefined value.

Report every missing artifact at once, so a multi-target build failure shows the whole gap instead of one path per run.

Raises:



133
134
135
136
137
138
# File 'lib/beni/builder.rb', line 133

def verify_artifacts!
  missing = artifact_paths.reject { |path| File.exist?(path) }
  return if missing.empty?

  raise Error, "[beni] build completed but artifacts are missing:\n  #{missing.join("\n  ")}"
end