rubycc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +56 -0
- data/LICENSE.txt +21 -0
- data/NOTICE +52 -0
- data/README.md +208 -0
- data/data/README.md +117 -0
- data/data/r10_corpus_scan.json +4082 -0
- data/data/r10_manual_classification.json +5342 -0
- data/data/r10_verification_m4a.json +1531 -0
- data/data/r10_verification_m4b.json +1818 -0
- data/data/r10_verification_m4c.json +1489 -0
- data/data/r10_verification_m4d.json +318 -0
- data/data/r10_verification_rbs.json +192 -0
- data/data/verified_gems.json +397 -0
- data/exe/rmake +16 -0
- data/exe/rubycc +9 -0
- data/exe/rubycc-ar +114 -0
- data/exe/rubycc-doctor +14 -0
- data/exe/rubycc-pkgconf +8 -0
- data/include/float.h +87 -0
- data/include/iso646.h +18 -0
- data/include/libc/alloca.h +18 -0
- data/include/libc/arpa/inet.h +61 -0
- data/include/libc/assert.h +43 -0
- data/include/libc/dirent.h +72 -0
- data/include/libc/dlfcn.h +58 -0
- data/include/libc/features.h +205 -0
- data/include/libc/glibc/aarch64/ctype.h +108 -0
- data/include/libc/glibc/aarch64/endian.h +54 -0
- data/include/libc/glibc/aarch64/errno.h +154 -0
- data/include/libc/glibc/aarch64/fcntl.h +144 -0
- data/include/libc/glibc/aarch64/inttypes.h +182 -0
- data/include/libc/glibc/aarch64/limits.h +63 -0
- data/include/libc/glibc/aarch64/pthread.h +131 -0
- data/include/libc/glibc/aarch64/setjmp.h +84 -0
- data/include/libc/glibc/aarch64/stdint.h +174 -0
- data/include/libc/glibc/aarch64/sys/epoll.h +88 -0
- data/include/libc/glibc/aarch64/sys/fcntl.h +19 -0
- data/include/libc/glibc/aarch64/sys/select.h +75 -0
- data/include/libc/glibc/aarch64/sys/stat.h +136 -0
- data/include/libc/glibc/aarch64/sys/syscall.h +167 -0
- data/include/libc/glibc/aarch64/sys/time.h +74 -0
- data/include/libc/glibc/aarch64/sys/types.h +127 -0
- data/include/libc/glibc/aarch64/time.h +113 -0
- data/include/libc/glibc/x86_64/ctype.h +108 -0
- data/include/libc/glibc/x86_64/endian.h +54 -0
- data/include/libc/glibc/x86_64/errno.h +154 -0
- data/include/libc/glibc/x86_64/fcntl.h +134 -0
- data/include/libc/glibc/x86_64/inttypes.h +182 -0
- data/include/libc/glibc/x86_64/limits.h +61 -0
- data/include/libc/glibc/x86_64/pthread.h +111 -0
- data/include/libc/glibc/x86_64/setjmp.h +82 -0
- data/include/libc/glibc/x86_64/stdint.h +163 -0
- data/include/libc/glibc/x86_64/sys/epoll.h +91 -0
- data/include/libc/glibc/x86_64/sys/fcntl.h +19 -0
- data/include/libc/glibc/x86_64/sys/select.h +75 -0
- data/include/libc/glibc/x86_64/sys/stat.h +130 -0
- data/include/libc/glibc/x86_64/sys/syscall.h +188 -0
- data/include/libc/glibc/x86_64/sys/time.h +74 -0
- data/include/libc/glibc/x86_64/sys/types.h +123 -0
- data/include/libc/glibc/x86_64/time.h +113 -0
- data/include/libc/grp.h +50 -0
- data/include/libc/langinfo.h +126 -0
- data/include/libc/link.h +18 -0
- data/include/libc/locale.h +83 -0
- data/include/libc/math.h +191 -0
- data/include/libc/netinet/in.h +126 -0
- data/include/libc/netinet/tcp.h +44 -0
- data/include/libc/poll.h +46 -0
- data/include/libc/pwd.h +58 -0
- data/include/libc/regex.h +53 -0
- data/include/libc/sched.h +35 -0
- data/include/libc/signal.h +204 -0
- data/include/libc/stdio.h +157 -0
- data/include/libc/stdlib.h +92 -0
- data/include/libc/string.h +74 -0
- data/include/libc/strings.h +25 -0
- data/include/libc/sys/cdefs.h +126 -0
- data/include/libc/sys/inotify.h +111 -0
- data/include/libc/sys/ioctl.h +36 -0
- data/include/libc/sys/mman.h +65 -0
- data/include/libc/sys/param.h +41 -0
- data/include/libc/sys/resource.h +109 -0
- data/include/libc/sys/socket.h +167 -0
- data/include/libc/sys/statfs.h +82 -0
- data/include/libc/sys/timerfd.h +55 -0
- data/include/libc/sys/uio.h +40 -0
- data/include/libc/sys/un.h +25 -0
- data/include/libc/sys/utsname.h +35 -0
- data/include/libc/sys/wait.h +135 -0
- data/include/libc/termios.h +179 -0
- data/include/libc/unistd.h +194 -0
- data/include/stdalign.h +16 -0
- data/include/stdarg.h +31 -0
- data/include/stdatomic.h +158 -0
- data/include/stdbool.h +15 -0
- data/include/stdckdint.h +28 -0
- data/include/stddef.h +60 -0
- data/include/stdnoreturn.h +18 -0
- data/include/x86intrin.h +16 -0
- data/lib/rubycc/backend/aarch64.rb +1724 -0
- data/lib/rubycc/backend/x86_64.rb +1369 -0
- data/lib/rubycc/compile_error.rb +36 -0
- data/lib/rubycc/compiler.rb +305 -0
- data/lib/rubycc/doctor/builder.rb +151 -0
- data/lib/rubycc/doctor/cli.rb +190 -0
- data/lib/rubycc/doctor/fetcher.rb +84 -0
- data/lib/rubycc/doctor/gemfile.rb +115 -0
- data/lib/rubycc/doctor/verified_gems.rb +114 -0
- data/lib/rubycc/doctor.rb +16 -0
- data/lib/rubycc/driver.rb +463 -0
- data/lib/rubycc/front/ast.rb +528 -0
- data/lib/rubycc/front/constant_evaluator.rb +631 -0
- data/lib/rubycc/front/initializer_resolver.rb +592 -0
- data/lib/rubycc/front/lexeme_reader.rb +460 -0
- data/lib/rubycc/front/lexer.rb +232 -0
- data/lib/rubycc/front/parser.rb +4122 -0
- data/lib/rubycc/front/token.rb +49 -0
- data/lib/rubycc/ir/call_convention.rb +486 -0
- data/lib/rubycc/ir/generator.rb +6036 -0
- data/lib/rubycc/ir/ir.rb +417 -0
- data/lib/rubycc/link/compat_runtime.rb +89 -0
- data/lib/rubycc/link/errors.rb +13 -0
- data/lib/rubycc/link/executable_linker.rb +405 -0
- data/lib/rubycc/link/library_resolver.rb +437 -0
- data/lib/rubycc/link/partial_linker.rb +546 -0
- data/lib/rubycc/link/shared_linker.rb +1732 -0
- data/lib/rubycc/mkmf_shim.rb +83 -0
- data/lib/rubycc/objfile/ar_archive.rb +437 -0
- data/lib/rubycc/objfile/elf_reader.rb +646 -0
- data/lib/rubycc/objfile/elf_writer.rb +891 -0
- data/lib/rubycc/objfile/relocatable_writer.rb +376 -0
- data/lib/rubycc/pkgconf/cli.rb +139 -0
- data/lib/rubycc/pkgconf/errors.rb +39 -0
- data/lib/rubycc/pkgconf/model.rb +28 -0
- data/lib/rubycc/pkgconf/parser.rb +109 -0
- data/lib/rubycc/pkgconf/pkgconf.rb +15 -0
- data/lib/rubycc/pkgconf/resolver.rb +70 -0
- data/lib/rubycc/pkgconf/search_path.rb +38 -0
- data/lib/rubycc/pkgconf/system_path_filter.rb +131 -0
- data/lib/rubycc/preprocess/constant_expression.rb +219 -0
- data/lib/rubycc/preprocess/glibc_version.rb +124 -0
- data/lib/rubycc/preprocess/pp_token.rb +62 -0
- data/lib/rubycc/preprocess/preprocessor.rb +2020 -0
- data/lib/rubycc/preprocess/scanner.rb +290 -0
- data/lib/rubycc/preprocess/token_converter.rb +157 -0
- data/lib/rubycc/rmake/cli.rb +143 -0
- data/lib/rubycc/rmake/errors.rb +71 -0
- data/lib/rubycc/rmake/executor.rb +818 -0
- data/lib/rubycc/rmake/expander.rb +251 -0
- data/lib/rubycc/rmake/makefile.rb +352 -0
- data/lib/rubycc/rmake/model.rb +137 -0
- data/lib/rubycc/rmake/parser.rb +226 -0
- data/lib/rubycc/rmake/rmake.rb +15 -0
- data/lib/rubycc/type.rb +1236 -0
- data/lib/rubycc/version.rb +5 -0
- data/lib/rubycc.rb +36 -0
- data/lib/rubygems_plugin.rb +102 -0
- metadata +219 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubycc
|
|
4
|
+
module Rmake
|
|
5
|
+
# A make variable. The flavor decides *when* the right-hand side is expanded:
|
|
6
|
+
# a recursive (`=`) variable keeps its text verbatim and is expanded afresh
|
|
7
|
+
# on every reference, so it can see values defined later; a simple (`:=`)
|
|
8
|
+
# variable is expanded once at definition and thereafter holds a plain
|
|
9
|
+
# string. `?=` produces a recursive variable (only when the name is unset)
|
|
10
|
+
# and `+=` appends in whichever flavor the variable already has.
|
|
11
|
+
class Variable
|
|
12
|
+
attr_reader :flavor, :value
|
|
13
|
+
|
|
14
|
+
def initialize(flavor, value)
|
|
15
|
+
@flavor = flavor
|
|
16
|
+
@value = value
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def simple?
|
|
20
|
+
@flavor == :simple
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def recursive?
|
|
24
|
+
@flavor == :recursive
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# An explicit rule as written: its target and prerequisite words and recipe
|
|
29
|
+
# lines are stored unexpanded, because make expands them against the final
|
|
30
|
+
# variable table (all mkmf assignments precede the rules that use them). A
|
|
31
|
+
# `::` rule sets +double_colon+; +order+ is the read order, used only to pick
|
|
32
|
+
# the default goal (the first non-special target seen).
|
|
33
|
+
class ExplicitRule
|
|
34
|
+
attr_reader :targets, :prerequisites, :recipe, :order
|
|
35
|
+
|
|
36
|
+
def initialize(targets:, prerequisites:, recipe:, double_colon:, order:)
|
|
37
|
+
@targets = targets
|
|
38
|
+
@prerequisites = prerequisites
|
|
39
|
+
@recipe = recipe
|
|
40
|
+
@double_colon = double_colon
|
|
41
|
+
@order = order
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def double_colon?
|
|
45
|
+
@double_colon
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# A double-suffix inference rule such as `.c.o:` — build a `to_suffix` file
|
|
50
|
+
# from a same-stem `from_suffix` file using +recipe+.
|
|
51
|
+
class SuffixRule
|
|
52
|
+
attr_reader :from_suffix, :to_suffix, :recipe
|
|
53
|
+
|
|
54
|
+
def initialize(from_suffix:, to_suffix:, recipe:)
|
|
55
|
+
@from_suffix = from_suffix
|
|
56
|
+
@to_suffix = to_suffix
|
|
57
|
+
@recipe = recipe
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# One shell command in an execution step: its expanded text with the
|
|
62
|
+
# recipe-line prefixes already interpreted. +silent+ (`@`), +ignore_error+
|
|
63
|
+
# (`-`) and +force+ (`+`) are retained as attributes so a dumper can show
|
|
64
|
+
# them; B1 only plans, it does not run anything.
|
|
65
|
+
class Command
|
|
66
|
+
attr_reader :text, :silent, :ignore_error, :force
|
|
67
|
+
|
|
68
|
+
def initialize(text:, silent:, ignore_error:, force:)
|
|
69
|
+
@text = text
|
|
70
|
+
@silent = silent
|
|
71
|
+
@ignore_error = ignore_error
|
|
72
|
+
@force = force
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def silent?
|
|
76
|
+
@silent
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def ignore_error?
|
|
80
|
+
@ignore_error
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def force?
|
|
84
|
+
@force
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# A single node of the execution plan: the target that would be (re)built and
|
|
89
|
+
# the ordered, fully-expanded commands that would build it. +prereqs+ names
|
|
90
|
+
# the other steps that must complete before this one may start — the edges a
|
|
91
|
+
# parallel scheduler (B3 `-j`) needs. It is empty for a step with no stale
|
|
92
|
+
# step among its prerequisites, and the sequential runner ignores it entirely
|
|
93
|
+
# (the plan is already emitted in a valid dependency order).
|
|
94
|
+
class Step
|
|
95
|
+
attr_reader :target, :commands
|
|
96
|
+
attr_accessor :prereqs
|
|
97
|
+
|
|
98
|
+
def initialize(target:, commands:, prereqs: [])
|
|
99
|
+
@target = target
|
|
100
|
+
@commands = commands
|
|
101
|
+
@prereqs = prereqs
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# The result of Makefile#plan: the steps in the order make would run them
|
|
106
|
+
# (prerequisites before dependents). It carries no execution logic — the
|
|
107
|
+
# recipe runner is B2.
|
|
108
|
+
class Plan
|
|
109
|
+
attr_reader :steps
|
|
110
|
+
|
|
111
|
+
def initialize(steps)
|
|
112
|
+
@steps = steps
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The command texts in execution order, one per line — the shape a
|
|
116
|
+
# `make -n` transcript takes, used by the golden tests.
|
|
117
|
+
def command_lines
|
|
118
|
+
@steps.flat_map { |step| step.commands.map(&:text) }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# A human-readable dump that, unlike #command_lines, keeps the target
|
|
122
|
+
# boundaries and the +@+/+-+/+++ prefix attributes visible.
|
|
123
|
+
def dump
|
|
124
|
+
@steps.map do |step|
|
|
125
|
+
lines = step.commands.map do |cmd|
|
|
126
|
+
flags = +""
|
|
127
|
+
flags << "@" if cmd.silent?
|
|
128
|
+
flags << "-" if cmd.ignore_error?
|
|
129
|
+
flags << "+" if cmd.force?
|
|
130
|
+
" #{flags.empty? ? '' : "#{flags} "}#{cmd.text}"
|
|
131
|
+
end
|
|
132
|
+
(["#{step.target}:"] + lines).join("\n")
|
|
133
|
+
end.join("\n")
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
require_relative "model"
|
|
5
|
+
require_relative "expander"
|
|
6
|
+
|
|
7
|
+
module Rubycc
|
|
8
|
+
module Rmake
|
|
9
|
+
# Turns Makefile text into the raw model — variables plus explicit rules with
|
|
10
|
+
# their recipes — that Makefile then resolves into suffix rules, phony
|
|
11
|
+
# targets, the suffix list and a dependency graph. The parser accepts only
|
|
12
|
+
# the constructs the mkmf corpus (test/fixtures/mkmf) actually emits:
|
|
13
|
+
# variable assignments in all four flavours, backslash line continuation,
|
|
14
|
+
# `#` comments, explicit rules (single and double colon, with tab recipes)
|
|
15
|
+
# and the `.c.o:`-style two-suffix inference rules. Anything it cannot place
|
|
16
|
+
# is a ParseError rather than a silent skip, so an unsupported Makefile fails
|
|
17
|
+
# loudly instead of mis-building.
|
|
18
|
+
class Parser
|
|
19
|
+
# A variable name: letters, digits and underscore, not starting with a
|
|
20
|
+
# digit. mkmf never uses dotted or otherwise exotic names, so keeping this
|
|
21
|
+
# narrow is what lets a leading-dot line (`.PHONY:`, `.c.o:`) fall through
|
|
22
|
+
# to rule handling instead of being mistaken for an assignment.
|
|
23
|
+
NAME = /[A-Za-z_][A-Za-z0-9_]*/
|
|
24
|
+
|
|
25
|
+
# An assignment is a name, optional spaces, then an operator anchored right
|
|
26
|
+
# after the name. `+=`/`?=`/`::=`/`:=` are tried before bare `=`, and all
|
|
27
|
+
# of them before a rule's `:` can match, so `all: dep` (colon then space)
|
|
28
|
+
# is never read as `:=`.
|
|
29
|
+
ASSIGN = /\A[ \t]*(#{NAME})[ \t]*(\+=|\?=|::=|:=|=)(.*)\z/
|
|
30
|
+
|
|
31
|
+
# +overrides+ are command-line variable definitions (make's `VAR=value`
|
|
32
|
+
# operands): they are seeded before parsing and win over any assignment the
|
|
33
|
+
# Makefile makes to the same name, which is make's rule that a command-line
|
|
34
|
+
# variable overrides a makefile variable. They are stored as simple
|
|
35
|
+
# (already-expanded) variables so a `:=` assignment referencing one during
|
|
36
|
+
# the parse sees the command-line value.
|
|
37
|
+
#
|
|
38
|
+
# +defaults+ are POSIX's built-in variables (currently just `MAKE`): unlike
|
|
39
|
+
# +overrides+ they are ordinary variables once seeded, so a Makefile
|
|
40
|
+
# assignment to the same name replaces them exactly as it would replace any
|
|
41
|
+
# other pre-existing value. They are seeded first so an override (or a
|
|
42
|
+
# Makefile assignment) to the same name still wins.
|
|
43
|
+
def initialize(overrides: {}, defaults: {})
|
|
44
|
+
@variables = {}
|
|
45
|
+
@rules = []
|
|
46
|
+
@order = 0
|
|
47
|
+
@current_rule = nil
|
|
48
|
+
@expander = Expander.new(@variables)
|
|
49
|
+
@overrides = overrides || {}
|
|
50
|
+
(defaults || {}).each { |name, value| @variables[name] = Variable.new(:simple, value.to_s) }
|
|
51
|
+
@overrides.each { |name, value| @variables[name] = Variable.new(:simple, value.to_s) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attr_reader :variables, :rules
|
|
55
|
+
|
|
56
|
+
def self.parse(text, overrides: {}, defaults: {})
|
|
57
|
+
new(overrides: overrides, defaults: defaults).tap { |p| p.run(text) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def run(text)
|
|
61
|
+
logical_lines(text).each do |content, kind, line_no|
|
|
62
|
+
if kind == :recipe
|
|
63
|
+
handle_recipe(content, line_no)
|
|
64
|
+
else
|
|
65
|
+
handle_normal(content, line_no)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
self
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# --- physical -> logical lines (continuation handling) ---------------
|
|
74
|
+
|
|
75
|
+
# Fold backslash-continued physical lines into logical lines, tagging each
|
|
76
|
+
# as a recipe (started with a tab) or a normal line. A continuation and the
|
|
77
|
+
# following line's leading whitespace collapse to a single space, matching
|
|
78
|
+
# make; this is exercised by the unit tests since the corpus itself has no
|
|
79
|
+
# continuations.
|
|
80
|
+
def logical_lines(text)
|
|
81
|
+
physical = text.split("\n", -1)
|
|
82
|
+
result = []
|
|
83
|
+
i = 0
|
|
84
|
+
while i < physical.length
|
|
85
|
+
raw = physical[i]
|
|
86
|
+
start_line = i + 1
|
|
87
|
+
recipe = raw.start_with?("\t")
|
|
88
|
+
buf = raw
|
|
89
|
+
while continuation?(buf)
|
|
90
|
+
buf = drop_trailing_backslash(buf)
|
|
91
|
+
i += 1
|
|
92
|
+
nxt = (physical[i] || "")
|
|
93
|
+
nxt = nxt.sub(/\A\t/, "") if recipe
|
|
94
|
+
buf = "#{buf.rstrip} #{nxt.lstrip}"
|
|
95
|
+
end
|
|
96
|
+
result << [buf, recipe ? :recipe : :normal, start_line]
|
|
97
|
+
i += 1
|
|
98
|
+
end
|
|
99
|
+
result
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A line continues when it ends with an odd number of backslashes (an even
|
|
103
|
+
# number is that many escaped literal backslashes with no continuation).
|
|
104
|
+
def continuation?(line)
|
|
105
|
+
m = line.match(/(\\+)\z/)
|
|
106
|
+
m && m[1].length.odd?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def drop_trailing_backslash(line)
|
|
110
|
+
line.sub(/\\\z/, "")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# --- normal (non-recipe) lines ---------------------------------------
|
|
114
|
+
|
|
115
|
+
def handle_normal(content, line_no)
|
|
116
|
+
stripped = strip_comment(content)
|
|
117
|
+
return if stripped.strip.empty?
|
|
118
|
+
|
|
119
|
+
if (m = ASSIGN.match(stripped))
|
|
120
|
+
@current_rule = nil
|
|
121
|
+
apply_assignment(m[1], m[2], m[3])
|
|
122
|
+
elsif stripped.include?(":")
|
|
123
|
+
parse_rule(stripped, line_no)
|
|
124
|
+
else
|
|
125
|
+
raise ParseError.new("cannot parse line: #{content.strip.inspect}", line_number: line_no)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Strip a `#` comment. `\#` is an escaped literal hash. Recipe lines are
|
|
130
|
+
# never passed here, so `#` inside a recipe stays intact for the shell.
|
|
131
|
+
def strip_comment(line)
|
|
132
|
+
out = +""
|
|
133
|
+
i = 0
|
|
134
|
+
while i < line.length
|
|
135
|
+
c = line[i]
|
|
136
|
+
if c == "\\" && line[i + 1] == "#"
|
|
137
|
+
out << "#"
|
|
138
|
+
i += 2
|
|
139
|
+
elsif c == "#"
|
|
140
|
+
break
|
|
141
|
+
else
|
|
142
|
+
out << c
|
|
143
|
+
i += 1
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
out
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def apply_assignment(name, op, rhs)
|
|
150
|
+
# A command-line override wins over every makefile assignment to the same
|
|
151
|
+
# name (make's precedence rule), so ignore the assignment entirely.
|
|
152
|
+
return if @overrides.key?(name)
|
|
153
|
+
|
|
154
|
+
# make strips leading whitespace after the operator but keeps trailing
|
|
155
|
+
# whitespace in the value (a documented make behaviour the golden tests
|
|
156
|
+
# depend on, e.g. `dldflags = ... zlib ` contributes its trailing space).
|
|
157
|
+
value = rhs.sub(/\A[ \t]+/, "")
|
|
158
|
+
case op
|
|
159
|
+
when "="
|
|
160
|
+
@variables[name] = Variable.new(:recursive, value)
|
|
161
|
+
when ":=", "::="
|
|
162
|
+
@variables[name] = Variable.new(:simple, @expander.expand(value))
|
|
163
|
+
when "?="
|
|
164
|
+
@variables[name] ||= Variable.new(:recursive, value)
|
|
165
|
+
when "+="
|
|
166
|
+
append_assignment(name, value)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def append_assignment(name, value)
|
|
171
|
+
existing = @variables[name]
|
|
172
|
+
if existing.nil?
|
|
173
|
+
@variables[name] = Variable.new(:recursive, value)
|
|
174
|
+
elsif existing.simple?
|
|
175
|
+
joined = existing.value.empty? ? @expander.expand(value) : "#{existing.value} #{@expander.expand(value)}"
|
|
176
|
+
@variables[name] = Variable.new(:simple, joined)
|
|
177
|
+
else
|
|
178
|
+
joined = existing.value.empty? ? value : "#{existing.value} #{value}"
|
|
179
|
+
@variables[name] = Variable.new(:recursive, joined)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# --- rules -----------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
def parse_rule(line, _line_no)
|
|
186
|
+
idx = line.index(":")
|
|
187
|
+
target_part = line[0...idx]
|
|
188
|
+
rest = line[(idx + 1)..]
|
|
189
|
+
double_colon = rest.start_with?(":")
|
|
190
|
+
rest = rest[1..] if double_colon
|
|
191
|
+
|
|
192
|
+
# An inline recipe after `;` (target: dep ; cmd). Absent from the corpus
|
|
193
|
+
# but cheap and part of the grammar.
|
|
194
|
+
inline = nil
|
|
195
|
+
if (semi = rest.index(";"))
|
|
196
|
+
inline = rest[(semi + 1)..].sub(/\A[ \t]+/, "")
|
|
197
|
+
rest = rest[0...semi]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
@order += 1
|
|
201
|
+
rule = ExplicitRule.new(
|
|
202
|
+
targets: split_words(target_part),
|
|
203
|
+
prerequisites: split_words(rest),
|
|
204
|
+
recipe: [],
|
|
205
|
+
double_colon: double_colon,
|
|
206
|
+
order: @order
|
|
207
|
+
)
|
|
208
|
+
rule.recipe << inline if inline
|
|
209
|
+
@rules << rule
|
|
210
|
+
@current_rule = rule
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def handle_recipe(content, line_no)
|
|
214
|
+
raise ParseError.new("recipe line has no preceding rule", line_number: line_no) if @current_rule.nil?
|
|
215
|
+
|
|
216
|
+
# Drop only the leading recipe-marker tab; any further indentation is
|
|
217
|
+
# part of the command text (the prefix scanner ignores leading blanks).
|
|
218
|
+
@current_rule.recipe << content.sub(/\A\t/, "")
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def split_words(text)
|
|
222
|
+
text.split(/[ \t]+/).reject(&:empty?)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# rmake — the mkmf-Makefile subset (M3 / DESIGN R5). It ships the parser, the
|
|
4
|
+
# variable expander, the dependency-graph planner, the shell-less runner and the
|
|
5
|
+
# CLI (exe/rmake, B6) that RubyGems drives as `$(MAKE)`. This aggregate require is
|
|
6
|
+
# the entry point; it stays self-contained — depending on nothing else in
|
|
7
|
+
# lib/rubycc until a tool-substituting run reaches for the Driver — so it can be
|
|
8
|
+
# loaded on its own from the tests.
|
|
9
|
+
require_relative "errors"
|
|
10
|
+
require_relative "model"
|
|
11
|
+
require_relative "expander"
|
|
12
|
+
require_relative "parser"
|
|
13
|
+
require_relative "makefile"
|
|
14
|
+
require_relative "executor"
|
|
15
|
+
require_relative "cli"
|