Class: Rubycc::Rmake::Parser

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

Overview

Turns Makefile text into the raw model — variables plus explicit rules with their recipes — that Makefile then resolves into suffix rules, phony targets, the suffix list and a dependency graph. The parser accepts only the constructs the mkmf corpus (test/fixtures/mkmf) actually emits: variable assignments in all four flavours, backslash line continuation, # comments, explicit rules (single and double colon, with tab recipes) and the .c.o:-style two-suffix inference rules. Anything it cannot place is a ParseError rather than a silent skip, so an unsupported Makefile fails loudly instead of mis-building.

Constant Summary collapse

NAME =

A variable name: letters, digits and underscore, not starting with a digit. mkmf never uses dotted or otherwise exotic names, so keeping this narrow is what lets a leading-dot line (.PHONY:, .c.o:) fall through to rule handling instead of being mistaken for an assignment.

/[A-Za-z_][A-Za-z0-9_]*/
ASSIGN =

An assignment is a name, optional spaces, then an operator anchored right after the name. +=/?=/::=/:= are tried before bare =, and all of them before a rule's : can match, so all: dep (colon then space) is never read as :=.

/\A[ \t]*(#{NAME})[ \t]*(\+=|\?=|::=|:=|=)(.*)\z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(overrides: {}, defaults: {}) ⇒ Parser

overrides are command-line variable definitions (make's VAR=value operands): they are seeded before parsing and win over any assignment the Makefile makes to the same name, which is make's rule that a command-line variable overrides a makefile variable. They are stored as simple (already-expanded) variables so a := assignment referencing one during the parse sees the command-line value.

defaults are POSIX's built-in variables (currently just MAKE): unlike overrides they are ordinary variables once seeded, so a Makefile assignment to the same name replaces them exactly as it would replace any other pre-existing value. They are seeded first so an override (or a Makefile assignment) to the same name still wins.



43
44
45
46
47
48
49
50
51
52
# File 'lib/rubycc/rmake/parser.rb', line 43

def initialize(overrides: {}, defaults: {})
  @variables = {}
  @rules = []
  @order = 0
  @current_rule = nil
  @expander = Expander.new(@variables)
  @overrides = overrides || {}
  (defaults || {}).each { |name, value| @variables[name] = Variable.new(:simple, value.to_s) }
  @overrides.each { |name, value| @variables[name] = Variable.new(:simple, value.to_s) }
end

Instance Attribute Details

#rulesObject (readonly)

Returns the value of attribute rules.



54
55
56
# File 'lib/rubycc/rmake/parser.rb', line 54

def rules
  @rules
end

#variablesObject (readonly)

Returns the value of attribute variables.



54
55
56
# File 'lib/rubycc/rmake/parser.rb', line 54

def variables
  @variables
end

Class Method Details

.parse(text, overrides: {}, defaults: {}) ⇒ Object



56
57
58
# File 'lib/rubycc/rmake/parser.rb', line 56

def self.parse(text, overrides: {}, defaults: {})
  new(overrides: overrides, defaults: defaults).tap { |p| p.run(text) }
end

Instance Method Details

#run(text) ⇒ Object



60
61
62
63
64
65
66
67
68
69
# File 'lib/rubycc/rmake/parser.rb', line 60

def run(text)
  logical_lines(text).each do |content, kind, line_no|
    if kind == :recipe
      handle_recipe(content, line_no)
    else
      handle_normal(content, line_no)
    end
  end
  self
end