Class: Rubycc::Rmake::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/rubycc/rmake/cli.rb

Overview

The command-line front end to rmake (M3 / ROADMAP ยง6 B6). It exists so RubyGems can drive rubycc's builds by setting ENV["MAKE"] to this program: Gem::Ext::Builder invokes $(MAKE) as three separate processes shaped like

rmake DESTDIR= sitearchdir=<tmp> sitelibdir=<tmp> clean
rmake DESTDIR= sitearchdir=<tmp> sitelibdir=<tmp>          (default goal)
rmake DESTDIR= sitearchdir=<tmp> sitelibdir=<tmp> install

so the argument grammar it must accept is: command-line variable definitions (VAR=value, which override the Makefile's own assignments), zero or more target names (an absent target builds the default goal) and, though RubyGems does not pass them, the -j/-f options make users expect. Because rmake is rubycc's build-only make, tool substitution ($(CC)/$(LDSHARED) routed to rubycc's Driver) is always on โ€” a CC = gcc left in the Makefile is still built by rubycc.

It is a plain method object rather than an OptionParser front end: the mkmf invocation shape is fixed and narrow, so hand-parsing keeps the accepted grammar explicit (and the "everything routes to rubycc" contract obvious).

Constant Summary collapse

DEFAULT_MAKEFILE =
"Makefile"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir:, out:, err:) ⇒ CLI

Returns a new instance of CLI.



37
38
39
40
41
# File 'lib/rubycc/rmake/cli.rb', line 37

def initialize(dir:, out:, err:)
  @dir = dir
  @out = out
  @err = err
end

Class Method Details

.run(argv, dir: Dir.pwd, out: $stdout, err: $stderr) ⇒ Object

Run rmake with argv in dir, returning the process exit status (0 success, 2 for any make-level failure โ€” the code GNU make uses). out and err are injectable so the CLI can be exercised without spawning.



33
34
35
# File 'lib/rubycc/rmake/cli.rb', line 33

def self.run(argv, dir: Dir.pwd, out: $stdout, err: $stderr)
  new(dir: dir, out: out, err: err).run(argv)
end

Instance Method Details

#run(argv) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rubycc/rmake/cli.rb', line 43

def run(argv)
  options = parse_argv(argv)

  makefile_path = File.expand_path(options[:file] || DEFAULT_MAKEFILE, @dir)
  unless File.file?(makefile_path)
    @err.puts("rmake: #{options[:file] || DEFAULT_MAKEFILE}: No such file")
    return 2
  end

  mk = Makefile.parse(File.read(makefile_path), dir: @dir, overrides: options[:overrides],
                       defaults: { "MAKE" => make_default })
  goals = options[:targets].empty? ? [nil] : options[:targets]
  # Tool substitution is always on: rmake is rubycc's build CLI, so the
  # compiler/linker words in every recipe are handed to rubycc's Driver.
  goals.each { |goal| mk.run(goal, out: @out, err: @err, tools: :rubycc, jobs: options[:jobs]) }
  0
rescue RmakeError => e
  @err.puts("rmake: #{e.message}")
  2
end