summaryrefslogtreecommitdiff
path: root/defs
diff options
context:
space:
mode:
Diffstat (limited to 'defs')
-rw-r--r--defs/gmake.mk626
-rw-r--r--defs/id.def230
-rw-r--r--defs/jit.mk107
-rw-r--r--defs/keywords53
-rw-r--r--defs/known_errors.def157
-rw-r--r--defs/lex.c.src53
-rw-r--r--defs/opt_insn_unif.def27
-rw-r--r--defs/opt_operand.def22
-rw-r--r--defs/separated_version.mk38
-rw-r--r--defs/tags.mk18
10 files changed, 1331 insertions, 0 deletions
diff --git a/defs/gmake.mk b/defs/gmake.mk
new file mode 100644
index 0000000000..0320f9b7d5
--- /dev/null
+++ b/defs/gmake.mk
@@ -0,0 +1,626 @@
+# -*- mode: makefile-gmake; indent-tabs-mode: t -*-
+
+reconfig config.status: export MAKE:=$(MAKE)
+export BASERUBY:=$(BASERUBY)
+export GIT
+override gnumake_recursive := $(if $(findstring n,$(firstword $(MFLAGS))),,+)
+override mflags := $(filter-out -j%,$(MFLAGS))
+MSPECOPT += $(if $(filter -j%,$(MFLAGS)),-j)
+nproc = $(subst -j,,$(filter -j%,$(MFLAGS)))
+
+ifeq ($(GITHUB_ACTIONS),true)
+# 93(bright yellow) is copied from .github/workflows/mingw.yml
+override ACTIONS_GROUP = @echo "::group::$(@:yes-%=%)"
+override ACTIONS_ENDGROUP = @echo "::endgroup::"
+endif
+
+ifneq ($(filter darwin%,$(target_os)),)
+INSTRUBY_ENV += SDKROOT=
+endif
+INSTRUBY_ARGS += --gnumake
+
+ifeq ($(DOT_WAIT),)
+CHECK_TARGETS := great exam love check test check% test% btest%
+# expand test targets, and those dependents
+TEST_TARGETS := $(filter $(CHECK_TARGETS),$(MAKECMDGOALS))
+TEST_DEPENDS := $(filter-out commit $(TEST_TARGETS),$(MAKECMDGOALS))
+TEST_TARGETS := $(patsubst great,exam,$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out great $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst exam,test-bundled-gems test-bundler-parallel check,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst check,test-syntax-suggest test-spec test-all test-tool test-short,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-rubyspec,test-spec,$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out exam check test-spec $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst love,check,$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out love $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst test-almost,test-all,$(patsubst check-%,test test-%,$(TEST_TARGETS)))
+TEST_DEPENDS := $(filter-out test-all $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst test,test-short,$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out test $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_TARGETS := $(patsubst test-short,btest-ruby test-knownbug test-basic,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-basic,test-basic test-leaked-globals,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-bundled-gems,test-bundled-gems-spec test-bundled-gems-run,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-bundled-gems-run,test-bundled-gems-run $(PREPARE_BUNDLED_GEMS),$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-bundled-gems-prepare,test-bundled-gems-prepare $(PRECHECK_BUNDLED_GEMS) test-bundled-gems-fetch,$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-bundler-parallel,test-bundler-parallel $(PREPARE_BUNDLER),$(TEST_TARGETS))
+TEST_TARGETS := $(patsubst test-syntax-suggest,test-syntax-suggest $(PREPARE_SYNTAX_SUGGEST),$(TEST_TARGETS))
+TEST_DEPENDS := $(filter-out test-short $(TEST_TARGETS),$(TEST_DEPENDS))
+TEST_DEPENDS += $(if $(filter great exam love check,$(MAKECMDGOALS)),all exts)
+TEST_TARGETS := $(patsubst yes-%,%,$(filter-out no-%,$(TEST_TARGETS)))
+endif
+
+in-srcdir := $(if $(filter-out .,$(srcdir)),$(CHDIR) $(srcdir) &&)
+
+ifeq ($(if $(filter all main exts enc trans libencs libenc libtrans \
+ prog program ruby ruby$(EXEEXT) \
+ wprogram rubyw rubyw$(EXEEXT) \
+ miniruby$(EXEEXT) mini,\
+ $(MAKECMDGOALS)),,$(MAKECMDGOALS)),)
+-include $(SHOWFLAGS)
+endif
+
+ifeq ($(HAVE_BASERUBY):$(HAVE_GIT),yes:yes)
+override modified := $(shell $(BASERUBY) -C $(srcdir) tool/file2lastrev.rb --modified='%Y %m %d')
+override RUBY_RELEASE_YEAR := $(word 1,$(modified))
+override RUBY_RELEASE_MONTH := $(word 2,$(modified))
+override RUBY_RELEASE_DAY := $(word 3,$(modified))
+endif
+
+ifneq ($(filter universal-%,$(arch)),)
+define archcmd
+%.$(1).S: %.c
+ @$$(ECHO) translating $$< with $(2)
+ $$(Q) $$(CC) $$(CFLAGS_NO_ARCH) $(2) $$(XCFLAGS) $$(CPPFLAGS) $$(COUTFLAG)$$@ -S $$<
+
+%.S: %.$(1).S
+
+%.$(1).i: %.c
+ @$$(ECHO) preprocessing $$< with $(2)
+ $$(Q) $$(CPP) $$(warnflags) $(2) $$(XCFLAGS) $$(CPPFLAGS) $$(COUTFLAG)$$@ -E $$< > $$@
+
+%.i: %.$(1).i
+endef
+
+$(foreach arch,$(filter -arch=%,$(subst -arch ,-arch=,$(ARCH_FLAG))),\
+ $(eval $(call archcmd,$(patsubst -arch=%,%,$(value arch)),$(patsubst -arch=%,-arch %,$(value arch)))))
+endif
+
+ifeq ($(DOT_WAIT),)
+.PHONY: $(addprefix yes-,$(TEST_TARGETS))
+
+ifneq ($(filter-out btest%,$(TEST_TARGETS)),)
+$(addprefix yes-,$(TEST_TARGETS)): $(TEST_DEPENDS)
+endif
+
+ORDERED_TEST_TARGETS := $(filter $(TEST_TARGETS), \
+ btest-ruby test-knownbug test-leaked-globals test-basic \
+ test-testframework test-tool test-ruby test-all \
+ test-spec test-syntax-suggest-prepare test-syntax-suggest \
+ test-bundler-prepare test-bundler test-bundler-parallel \
+ test-bundled-gems-precheck test-bundled-gems-fetch \
+ test-bundled-gems-prepare test-bundled-gems-run \
+ test-bundled-gems-spec \
+ )
+
+# grep ^yes-test-.*-precheck: template/Makefile.in defs/gmake.mk common.mk
+test_prechecks := $(filter $(ORDERED_TEST_TARGETS),\
+ test-leaked-globals \
+ test-all \
+ test-spec \
+ test-syntax-suggest \
+ test-bundler \
+ test-bundler-parallel \
+ test-bundled-gems\
+ )
+prev_test := $(subst test-bundler-parallel,test-bundler,$(test_prechecks))
+prev_test := $(addsuffix -precheck,$(prev_test))
+first_test_prechecks := $(prev_test)
+
+$(foreach test,$(ORDERED_TEST_TARGETS), \
+ $(eval yes-$(value test): $(addprefix yes-,$(value prev_test))); \
+ $(eval no-$(value test): $(addprefix no-,$(value prev_test))); \
+ $(eval prev_test := $(value test)))
+endif
+
+ifneq ($(if $(filter install,$(MAKECMDGOALS)),$(filter uninstall,$(MAKECMDGOALS))),)
+install-targets := $(filter install uninstall,$(MAKECMDGOALS))
+$(word 1,$(install-targets)): $(word 0,$(install-targets))
+endif
+
+ifneq ($(filter reinstall,$(MAKECMDGOALS)),)
+install-prereq: uninstall
+uninstall sudo-precheck: all $(if $(filter all,$(INSTALLDOC)),docs)
+endif
+
+ifneq ($(filter love,$(MAKECMDGOALS)),)
+showflags: up
+sudo-precheck: test yes-test-testframework no-test-testframework
+install-prereq: sudo-precheck
+yes-test-all no-test-all: install
+endif
+yes-test-bundler-parallel: PARALLELRSPECOPTS += $(if $(nproc),-n$(shell expr $(nproc) + $(nproc) / 2))
+
+# Cross reference needs to parse all files at once
+love install reinstall: RDOCFLAGS = --force-update
+
+ifneq ($(if $(filter -flto%,$(CFLAGS)),$(subst darwin,,$(arch)),$(arch)),$(arch))
+override EXE_LDFLAGS = $(filter-out -g%,$(LDFLAGS))
+endif
+
+$(srcdir)/missing/des_tables.c: $(srcdir)/missing/crypt.c
+ifeq ($(if $(filter yes,$(CROSS_COMPILING)),,$(CC)),)
+ touch $@
+else
+ @$(ECHO) building make_des_table
+ $(CC) $(INCFLAGS) $(CPPFLAGS) -DDUMP $(EXE_LDFLAGS) $(XLDFLAGS) $(LIBS) -omake_des_table $(srcdir)/missing/crypt.c
+ @[ -x ./make_des_table ]
+ @$(ECHO) generating $@
+ $(Q) $(MAKEDIRS) $(@D)
+ $(Q) ./make_des_table > $@.new
+ $(Q) mv $@.new $@
+ $(Q) $(RMALL) make_des_table*
+endif
+
+config.status: $(wildcard config.cache)
+
+ifneq (ruby,$(PROGRAM))
+ruby: $(PROGRAM);
+endif
+
+STUBPROGRAM = rubystub$(EXEEXT)
+IGNOREDPATTERNS = %~ .% %.orig %.rej \#%\#
+SCRIPTBINDIR := $(if $(EXEEXT),,exec/)
+SCRIPTPROGRAMS = $(addprefix $(SCRIPTBINDIR),$(addsuffix $(EXEEXT),$(filter-out $(IGNOREDPATTERNS),$(notdir $(wildcard $(srcdir)/bin/*)))))
+
+stub: $(STUBPROGRAM)
+scriptbin: $(SCRIPTPROGRAMS)
+ifneq ($(STUBPROGRAM),rubystub)
+rubystub: $(STUBPROGRAM)
+endif
+
+$(SCRIPTPROGRAMS): $(STUBPROGRAM)
+
+$(STUBPROGRAM): rubystub.$(OBJEXT) $(LIBRUBY) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(SETUP) $(PREP)
+
+rubystub$(EXEEXT):
+ @rm -f $@
+ $(ECHO) linking $@
+ $(Q) $(PURIFY) $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) rubystub.$(OBJEXT) $(EXTOBJS) $(LIBRUBYARG) $(MAINLIBS) $(LIBS) $(EXTLIBS) $(OUTFLAG)$@
+ $(Q) $(POSTLINK)
+ $(if $(STRIP),$(Q) $(STRIP) $@)
+
+$(SCRIPTBINDIR)%$(EXEEXT): bin/% $(STUBPROGRAM) \
+ $(if $(SCRIPTBINDIR),$(TIMESTAMPDIR)/.exec.time)
+ $(ECHO) generating $@
+ $(Q) { cat $(STUBPROGRAM); echo; sed -e '1{' -e '/^#!.*ruby/!i\' -e '#!/bin/ruby' -e '}' $<; } > $@
+ $(Q) chmod +x $@
+ $(Q) $(POSTLINK)
+
+$(SCRIPTBINDIR):
+ $(Q) mkdir $@
+
+.PHONY: commit
+COMMIT_PREPARE := $(subst :,\:,$(filter-out commit do-commit,$(MAKECMDGOALS))) up
+
+commit: pre-commit $(DOT_WAIT) do-commit $(DOT_WAIT) post_commit
+pre-commit: $(COMMIT_PREPARE)
+do-commit: $(if $(DOT_WAIT),,pre-commit)
+ @$(BASERUBY) -C "$(srcdir)" -I./tool/lib -rvcs -e 'VCS.detect(".").commit'
+post-commit: $(if $(DOT_WAIT),,do-commit)
+ +$(Q) \
+ { \
+ $(in-srcdir) \
+ exec sed -f tool/prereq.status defs/gmake.mk template/Makefile.in common.mk depend; \
+ } | \
+ $(MAKE) $(mflags) Q=$(Q) ECHO=$(ECHO) \
+ top_srcdir="$(top_srcdir)" srcdir="$(srcdir)" srcs_vpath="" CHDIR="$(CHDIR)" \
+ BOOTSTRAPRUBY="$(BOOTSTRAPRUBY)" BOOTSTRAPRUBY_OPT="$(BOOTSTRAPRUBY_OPT)" \
+ MINIRUBY="$(BASERUBY)" BASERUBY="$(BASERUBY)" HAVE_BASERUBY="$(HAVE_BASERUBY)" \
+ VCSUP="" ENC_MK=.top-enc.mk REVISION_FORCE=PHONY CONFIGURE="$(CONFIGURE)" -f - \
+ update-src srcs all-incs
+
+GITHUB_RUBY_URL = https://github.com/ruby/ruby
+PR =
+
+COMMIT_GPG_SIGN = $(shell $(GIT_IN_SRC) config commit.gpgsign)
+REMOTE_GITHUB_URL = $(shell $(GIT_IN_SRC) config remote.github.url)
+COMMITS_NOTES = commits
+
+.PHONY: fetch-github
+fetch-github:
+ $(call fetch-github,$(PR))
+
+define fetch-github
+ $(if $(1),,\
+ echo "usage:"; echo " make $@ PR=1234"; \
+ exit 1; \
+ )
+ $(eval REMOTE_GITHUB_URL := $(REMOTE_GITHUB_URL))
+ $(if $(REMOTE_GITHUB_URL),,
+ echo adding $(GITHUB_RUBY_URL) as remote github
+ $(GIT_IN_SRC) remote add github $(GITHUB_RUBY_URL)
+ $(GIT_IN_SRC) config --add remote.github.fetch +refs/notes/$(COMMITS_NOTES):refs/notes/$(COMMITS_NOTES)
+ $(eval REMOTE_GITHUB_URL := $(GITHUB_RUBY_URL))
+ )
+ $(if $(shell $(GIT_IN_SRC) rev-parse "github/pull/$(1)/head" -- 2> /dev/null),
+ $(GIT_IN_SRC) branch -f "gh-$(1)" "github/pull/$(1)/head",
+ $(GIT_IN_SRC) fetch -f github "pull/$(1)/head:gh-$(1)"
+ )
+endef
+
+.PHONY: checkout-github
+checkout-github: fetch-github
+ $(GIT_IN_SRC) checkout "gh-$(PR)"
+
+.PHONY: update-github
+update-github: fetch-github
+ $(eval PULL_REQUEST_API := https://api.github.com/repos/ruby/ruby/pulls/$(PR))
+ $(eval PULL_REQUEST_FORK_BRANCH := $(shell \
+ curl -s $(if $(GITHUB_TOKEN),-H "Authorization: bearer $(GITHUB_TOKEN)") $(PULL_REQUEST_API) | \
+ $(BASERUBY) -rjson -e 'JSON.parse(STDIN.read)["head"].tap { |h| print "#{h["repo"]["full_name"]} #{h["ref"]}" }' \
+ ))
+ $(eval FORK_REPO := $(word 1,$(PULL_REQUEST_FORK_BRANCH)))
+ $(eval PR_BRANCH := $(word 2,$(PULL_REQUEST_FORK_BRANCH)))
+
+ $(eval GITHUB_UPDATE_WORKTREE := $(shell mktemp -d "$(srcdir)/gh-$(PR)-XXXXXX"))
+ $(GIT_IN_SRC) worktree add $(notdir $(GITHUB_UPDATE_WORKTREE)) "gh-$(PR)"
+ $(GIT) -C "$(GITHUB_UPDATE_WORKTREE)" merge master --no-edit
+ @$(BASERUBY) -e 'print "Are you sure to push this to PR=$(PR)? [Y/n]: "; exit(gets.chomp != "n")'
+ $(GIT_IN_SRC) remote add fork-$(PR) git@github.com:$(FORK_REPO).git
+ $(GIT) -C "$(GITHUB_UPDATE_WORKTREE)" push fork-$(PR) gh-$(PR):$(PR_BRANCH)
+ $(GIT_IN_SRC) remote rm fork-$(PR)
+ $(GIT_IN_SRC) worktree remove $(notdir $(GITHUB_UPDATE_WORKTREE))
+ $(GIT_IN_SRC) branch -D gh-$(PR)
+
+.PHONY: pull-github
+pull-github: fetch-github
+ $(call pull-github,$(PR))
+
+define pull-github
+ $(eval GITHUB_MERGE_BASE := $(shell $(GIT_IN_SRC) rev-parse HEAD)
+ $(eval GITHUB_MERGE_BRANCH := $(shell $(GIT_IN_SRC) symbolic-ref --short HEAD))
+ $(eval GITHUB_MERGE_WORKTREE := $(shell mktemp -d "$(srcdir)/gh-$(1)-XXXXXX"))
+ $(GIT_IN_SRC) worktree prune
+ $(GIT_IN_SRC) worktree add $(notdir $(GITHUB_MERGE_WORKTREE)) "gh-$(1)"
+ $(GIT) -C "$(GITHUB_MERGE_WORKTREE)" rebase $(GITHUB_MERGE_BRANCH)
+ $(eval COMMIT_GPG_SIGN := $(COMMIT_GPG_SIGN))
+ $(if $(filter true,$(COMMIT_GPG_SIGN)), \
+ $(GIT) -C "$(GITHUB_MERGE_WORKTREE)" rebase --exec "$(GIT) commit --amend --no-edit -S" "$(GITHUB_MERGE_BASE)"; \
+ )
+ $(GIT) -C "$(GITHUB_MERGE_WORKTREE)" rebase --exec "$(GIT) notes add --message 'Merged: $(GITHUB_RUBY_URL)/pull/$(1)'" "$(GITHUB_MERGE_BASE)"
+endef
+
+.PHONY: fetch-github-%
+fetch-github-%:
+ $(call fetch-github,$*)
+
+.PHONY: checkout-github-%
+checkout-github-%: fetch-github-%
+ $(GIT_IN_SRC) checkout "gh-$*"
+
+.PHONY: pr-% pull-github-%
+pr-% pull-github-%: fetch-github-%
+ $(call pull-github,$*)
+
+HELP_EXTRA_TASKS = \
+ " checkout-github: checkout GitHub Pull Request [PR=1234]" \
+ " pull-github: rebase GitHub Pull Request to new worktree [PR=1234]" \
+ " update-github: merge master branch and push it to Pull Request [PR=1234]" \
+ " tags: generate TAGS file" \
+ ""
+
+# 1. squeeze spaces
+# 2. strip and skip comment/empty lines
+# 3. "gem x.y.z URL xxxxxx" -> "gem|x.y.z|xxxxxx|URL"
+# 4. "gem x.y.z URL" -> "gem-x.y.z"
+bundled-gems := $(shell sed \
+ -e 's/[ ][ ]*/ /g' \
+ -e 's/^ //;s/\#.*//;s/ *$$//;/^$$/d' \
+ $(if $(filter yes,$(HAVE_GIT)), \
+ -e 's/^\(.*\) \(.*\) \(.*\) \(.*\)/\1|\2|\4|\3/' \
+ ) \
+ -e 's/ /-/;s/ .*//' \
+ $(srcdir)/gems/bundled_gems)
+
+bundled-gems-rev := $(filter-out $(subst |,,$(bundled-gems)),$(bundled-gems))
+bundled-gems := $(filter-out $(bundled-gems-rev),$(bundled-gems))
+
+# calls $(1) with name, version, revision, URL
+foreach-bundled-gems-rev = \
+ $(foreach g,$(bundled-gems-rev),$(call foreach-bundled-gems-rev-0,$(1),$(subst |, ,$(value g))))
+foreach-bundled-gems-rev-0 = \
+ $(call $(1),$(word 1,$(2)),$(word 2,$(2)),$(word 3,$(2)),$(word 4,$(2)))
+bundled-gem-gemfile = $(srcdir)/gems/$(1)-$(2).gem
+bundled-gem-gemspec = $(srcdir)/gems/src/$(1)/$(1).gemspec
+bundled-gem-extracted = $(srcdir)/.bundle/gems/$(1)-$(2)
+bundled-gem-revision = $(srcdir)/.bundle/.timestamp/$(1).revision
+
+update-gems: | $(patsubst %,$(srcdir)/gems/%.gem,$(bundled-gems))
+update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-gemfile)
+update-gems: | $(call foreach-bundled-gems-rev,bundled-gem-gemspec)
+
+test-bundler-precheck: | $(srcdir)/.bundle/cache
+
+$(srcdir)/.bundle/cache:
+ $(MAKEDIRS) $(@D) $(CACHE_DIR)
+ $(LN_S) ../.downloaded-cache $@
+
+$(srcdir)/gems/%.gem:
+ $(ECHO) Downloading bundled gem $*...
+ $(Q) $(BASERUBY) -C "$(srcdir)" \
+ -I./tool -rdownloader \
+ -e 'gem = "$(@F)"' \
+ -e 'old = Dir.glob("gems/"+gem.sub(/-[^-]*$$/, "-*.gem"))' \
+ -e 'Downloader::RubyGems.download(gem, "gems", nil) and' \
+ -e '(old.delete("gems/#{gem}"); !old.empty?) and' \
+ -e 'File.unlink(*old) and' \
+ -e 'FileUtils.rm_rf(old.map{'"|n|"'n.chomp(".gem")})'
+
+extract-gems: | $(patsubst %,$(srcdir)/.bundle/gems/%,$(bundled-gems))
+extract-gems: | $(call foreach-bundled-gems-rev,bundled-gem-extracted)
+
+$(srcdir)/.bundle/gems/%: $(srcdir)/gems/%.gem | .bundle/gems
+ $(ECHO) Extracting bundle gem $*...
+ $(Q) $(BASERUBY) -C "$(srcdir)" \
+ -Itool/lib -rbundled_gem \
+ -e 'BundledGem.unpack("gems/$(@F).gem", ".bundle")'
+
+$(srcdir)/.bundle/.timestamp:
+ $(MAKEDIRS) $@
+
+define build-gem
+$(srcdir)/gems/src/$(1)/.git: | $(srcdir)/gems/src
+ $(ECHO) Cloning $(4)
+ $(Q) $(GIT) clone --depth=1 --no-tags $(4) $$(@D)
+
+$(bundled-gem-revision): \
+ $(if $(if $(wildcard $$(@)),$(filter $(3),$(shell cat $$(@)))),,PHONY) \
+ | $(srcdir)/.bundle/.timestamp $(srcdir)/gems/src/$(1)/.git
+ $(ECHO) Update $(1) to $(3)
+ $(Q) $(CHDIR) "$(srcdir)/gems/src/$(1)" && \
+ if [ `$(GIT) rev-parse HEAD` != $(3) ]; then \
+ $(GIT) fetch origin $(3) && \
+ $(GIT) checkout --detach $(3) && \
+ :; \
+ fi
+ echo $(3) | $(IFCHANGE) $$(@) -
+
+# The repository of minitest does not include minitest.gemspec because it uses hoe.
+# This creates a dummy gemspec.
+$(bundled-gem-gemspec): $(bundled-gem-revision) \
+ | $(srcdir)/gems/src/$(1)/.git
+ $(Q) $(BASERUBY) -I$(tooldir)/lib -rbundled_gem -e 'BundledGem.dummy_gemspec(*ARGV)' $$(@)
+
+$(bundled-gem-gemfile): $(bundled-gem-gemspec) $(bundled-gem-revision)
+ $(ECHO) Building $(1)@$(3) to $$(@)
+ $(Q) $(BASERUBY) -C "$(srcdir)" \
+ -Itool/lib -rbundled_gem \
+ -e 'BundledGem.build("gems/src/$(1)/$(1).gemspec", "$(2)", "gems", validation: false)'
+
+endef
+define build-gem-0
+$(eval $(call build-gem,$(1),$(2),$(3),$(4)))
+endef
+
+$(call foreach-bundled-gems-rev,build-gem-0)
+
+$(srcdir)/gems/src:
+ $(MAKEDIRS) $@
+
+$(srcdir)/.bundle/gems:
+ $(MAKEDIRS) $@
+
+ifneq ($(DOT_WAIT),)
+up:: $(DOT_WAIT) after-update
+after-update:: MINIRUBY = $(BASERUBY)
+endif
+
+ifneq ($(filter update-bundled_gems refresh-gems,$(MAKECMDGOALS)),)
+update-gems: update-bundled_gems
+endif
+
+.SECONDARY: update-unicode-files
+.SECONDARY: update-unicode-auxiliary-files
+.SECONDARY: update-unicode-ucd-emoji-files
+.SECONDARY: update-unicode-emoji-files
+
+ifneq ($(DOT_WAIT),)
+.NOTPARALLEL: update-unicode
+.NOTPARALLEL: update-unicode-files
+.NOTPARALLEL: update-unicode-auxiliary-files
+.NOTPARALLEL: update-unicode-ucd-emoji-files
+.NOTPARALLEL: update-unicode-emoji-files
+.NOTPARALLEL: $(UNICODE_FILES) $(UNICODE_PROPERTY_FILES)
+.NOTPARALLEL: $(UNICODE_AUXILIARY_FILES)
+.NOTPARALLEL: $(UNICODE_UCD_EMOJI_FILES) $(UNICODE_EMOJI_FILES)
+endif
+
+ifeq ($(HAVE_GIT),yes)
+REVISION_LATEST := $(shell $(GIT_IN_SRC) rev-parse HEAD 2>/dev/null)
+else
+REVISION_LATEST := update
+endif
+REVISION_IN_HEADER := $(shell sed '/^\#define RUBY_FULL_REVISION "\(.*\)"/!d;s//\1/;q' $(wildcard $(srcdir)/revision.h revision.h) /dev/null 2>/dev/null)
+ifeq ($(REVISION_IN_HEADER),)
+REVISION_IN_HEADER := none
+endif
+ifneq ($(REVISION_IN_HEADER),$(REVISION_LATEST))
+$(REVISION_H): PHONY
+endif
+
+include $(top_srcdir)/yjit/yjit.mk
+include $(top_srcdir)/zjit/zjit.mk
+include $(top_srcdir)/defs/jit.mk
+
+# Query on the generated rdoc
+#
+# $ make rdoc:Integer#+
+rdoc\:%: PHONY programs $(RDOCOUT) update-default-gemspecs
+ $(Q)$(RUNRUBY) $(RUNOPT0) -I$(tooldir)/lib -rbundled_gem \
+ -e "load BundledGem.command('rdoc', 'ri')" -- \
+ --no-standard-docs --doc-dir=$(RDOCOUT) $(patsubst rdoc:%,%,$@)
+
+test_%.rb test/%: programs PHONY
+ $(Q)$(exec) $(RUNRUBY) "$(TESTSDIR)/runner.rb" --ruby="$(RUNRUBY)" $(TEST_EXCLUDES) $(TESTOPTS) -- $(patsubst test/%,%,$@)
+
+spec/bundler/%: PHONY
+ $(Q)$(exec) $(XRUBY) -C $(srcdir) -Ispec/bundler .bundle/bin/rspec --require spec_helper $(RSPECOPTS) $@
+
+spec/bundler: test-bundler-parallel
+ $(Q)$(NULLCMD)
+
+# workaround to avoid matching non ruby files with "spec/%/" under GNU make 3.81
+spec/%_spec.c:
+ $(empty)
+$(srcdir)/$(RUBYSPEC_CAPIEXT)/rubyspec.h:
+ $(empty)
+
+benchmark/%: miniruby$(EXEEXT) update-benchmark-driver PHONY
+ $(Q)$(BASERUBY) -rrubygems -I$(srcdir)/benchmark/lib $(srcdir)/benchmark/benchmark-driver/exe/benchmark-driver \
+ --executables="compare-ruby::$(COMPARE_RUBY) -I$(EXTOUT)/common --disable-gem" \
+ --executables="built-ruby::$(BENCH_RUBY) --disable-gem" \
+ $(srcdir)/$@ $(BENCH_OPTS) $(OPTS)
+
+clean-local:: TARGET_SO = $(PROGRAM) $(WPROGRAM) $(LIBRUBY_SO) $(STATIC_RUBY) miniruby goruby
+clean-local::
+ -$(Q)$(RMALL) $(cleanlibs)
+
+clean-srcs-ext::
+ $(Q)$(RM) $(patsubst $(srcdir)/%,%,$(EXT_SRCS))
+
+clean-srcs-extra::
+ $(Q)$(RM) $(patsubst $(srcdir)/%,%,$(EXTRA_SRCS))
+
+ifneq ($(filter $(VCS),git),)
+update-src::
+ @$(BASERUBY) $(tooldir)/lib/colorize.rb pass "Latest commit hash = $(shell $(filter-out svn,$(VCS)) -C $(srcdir) rev-parse --short=10 HEAD)"
+endif
+
+# Update dependencies and commit the updates to the current branch.
+update-deps:
+ $(eval update_deps := $(shell date +update-deps-%Y%m%d))
+ $(eval deps_dir := $(shell mktemp -d)/$(update_deps))
+ $(eval GIT_DIR := $(shell $(GIT_IN_SRC) rev-parse --absolute-git-dir))
+ $(GIT) --git-dir=$(GIT_DIR) worktree add $(deps_dir)
+ cp $(tooldir)/config.guess $(tooldir)/config.sub $(deps_dir)/tool
+ [ -f config.status ] && cp config.status $(deps_dir)
+ cd $(deps_dir) && autoconf && \
+ exec ./configure -q -C --enable-load-relative --disable-install-doc --disable-rubygems 'optflags=-O0' 'debugflags=-save-temps=obj -g'
+ $(RUNRUBY) -C $(deps_dir) tool/update-deps --fix
+ $(GIT) -C $(deps_dir) diff --no-ext-diff --ignore-submodules --exit-code || \
+ $(GIT) -C $(deps_dir) commit --all --message='Update dependencies'
+ $(GIT) --git-dir=$(GIT_DIR) worktree remove $(deps_dir)
+ $(RMDIR) $(dir $(deps_dir))
+ $(GIT) --git-dir=$(GIT_DIR) merge --no-edit --ff-only $(update_deps)
+ $(GIT) --git-dir=$(GIT_DIR) branch --delete $(update_deps)
+
+fix-depends check-depends: all hello
+ $(BASERUBY) -C $(srcdir) tool/update-deps $(if $(filter fix-%,$@),--fix)
+
+# order-only-prerequisites doesn't work for $(RUBYSPEC_CAPIEXT)
+# because the same named directory exists in the source tree.
+$(RUBYSPEC_CAPIEXT)/%.$(DLEXT): $(srcdir)/$(RUBYSPEC_CAPIEXT)/%.c $(RUBYSPEC_CAPIEXT_DEPS) \
+ | build-ext yes-rubyspec-capiext
+ $(no_silence:no=$(ECHO) building $@)
+ $(Q) $(MAKEDIRS) $(@D)
+ $(Q) $(DLDSHARED) -L. $(XDLDFLAGS) $(XLDFLAGS) $(LDFLAGS) $(INCFLAGS) $(CPPFLAGS) $(OUTFLAG)$@ $< $(LIBRUBYARG)
+ifneq ($(POSTLINK),)
+ $(Q) $(POSTLINK)
+endif
+ $(Q) $(RMALL) $@.*
+
+RUBYSPEC_CAPIEXT_EXTS := $(patsubst %.c,$(RUBYSPEC_CAPIEXT)/%.$(DLEXT),$(notdir $(wildcard $(srcdir)/$(RUBYSPEC_CAPIEXT)/*.c)))
+rubyspec-capiext: $(RUBYSPEC_CAPIEXT_EXTS)
+
+spec/%/ spec/%_spec.rb: programs exts $(RUBYSPEC_CAPIEXT_BUILD) PHONY
+ +$(RUNRUBY) -r./$(arch)-fake $(srcdir)/spec/mspec/bin/mspec-run -B $(srcdir)/spec/default.mspec $(SPECOPTS) $(patsubst %,$(srcdir)/%,$@)
+
+ruby.pc: $(filter-out ruby.pc,$(ruby_pc))
+
+# `make matz`: bump up the MINOR;
+# Copying NEWS.md to doc/NEWS/, and empty the details in NEWS.md.
+#
+# `make matz NEW=x.y`: bump up to x.y.0;
+# Just update the version in the title of NEWS.md.
+
+matz: up
+matz-commit: OLD := $(MAJOR).$(MINOR).0
+ifdef NEW
+matz-commit: MAJOR := $(word 1,$(subst ., ,$(NEW)))
+matz-commit: MINOR := $(word 2,$(subst ., ,$(NEW)))
+matz-commit: $(DOT_WAIT) bump_news
+bump_news$(DOT_WAIT): up
+bump_headers$(DOT_WAIT): bump_news
+else
+matz-commit: MINOR := $(shell expr $(MINOR) + 1)
+matz-commit: $(DOT_WAIT) reset_news
+flush_news$(DOT_WAIT): up
+bump_headers$(DOT_WAIT): reset_news
+endif
+
+matz: $(DOT_WAIT) matz-commit
+matz-commit: bump_headers
+matz-commit: override NEW := $(MAJOR).$(MINOR).0
+matz-commit: files := include/ruby/version.h include/ruby/internal/abi.h
+matz-commit: message := Development of $(NEW) started.
+
+flush_news:
+ $(GIT_IN_SRC) mv -f NEWS.md doc/NEWS/NEWS-$(OLD).md
+ $(GIT_IN_SRC) commit -m "[DOC] Flush NEWS.md"
+
+.PHONY: flush_news reset_news bump_news bump_headers
+
+bump_headers:
+ sed -i~ \
+ -e "s/^\(#define RUBY_API_VERSION_MAJOR\) .*/\1 $(MAJOR)/" \
+ -e "s/^\(#define RUBY_API_VERSION_MINOR\) .*/\1 $(MINOR)/" \
+ -e "s/^\(#define RUBY_ABI_VERSION\) .*/\1 0/" \
+ $(files:%=$(srcdir)/%)
+
+reset_news: flush_news
+ $(BASERUBY) -C $(srcdir) -p -00 \
+ -e 'BEGIN {old, new = ARGV.shift(2); STDOUT.reopen(ARGV.shift)}' \
+ -e 'case $$.' \
+ -e 'when 1; $$_.sub!(/Ruby \K[0-9.]+/, new)' \
+ -e 'when 2; $$_.sub!(/\*\*\K[0-9.]+(?=\*\*)/, old)' \
+ -e 'end' \
+ -e 'next if /^[\[ *]/ =~ $$_' \
+ -e '$$_.sub!(/\n{2,}\z/, "\n\n")' \
+ $(OLD) $(NEW) NEWS.md doc/NEWS/NEWS-$(OLD).md
+
+bump_news:
+ $(BASERUBY) -C $(srcdir) -p -i \
+ -e 'BEGIN {new = ARGV.shift; print gets("").sub(/Ruby \K[0-9.]+/, new)}' \
+ $(NEW) NEWS.md
+
+matz: matz-commit matz-push
+
+matz-commit:
+ $(GIT_IN_SRC) add NEWS.md $(files)
+ $(GIT_IN_SRC) commit -m "$(message)"
+
+GIT_REMOTE_ORIGIN = origin
+
+matz-push: matz-commit
+ $(eval origin_url := $(shell $(GIT_IN_SRC) remote get-url $(GIT_REMOTE_ORIGIN)))
+ $(if $(origin_url),,@false)
+ $(eval last_commit := $(shell $(GIT_IN_SRC) log -n1 --format=%H --author=matz HEAD~..HEAD))
+ $(if $(last_commit),,$(ECHO) No matz commits 1>&2; false)
+ $(if $(filter 12-25 12-26,$(shell date +%m-%d)),,$(ECHO) Not the release date 1>&2; false)
+ $(ECHO) $$'\e[31m'Pushing to $$'\e[7m'$(GIT_REMOTE_ORIGIN)$$'\e[27m'" ($(origin_url))"$$'\e[m'
+ $(GIT_IN_SRC) push $(GIT_REMOTE_ORIGIN)
+
+tags:
+ $(MAKE) GIT="$(GIT)" -C "$(srcdir)" -f defs/tags.mk
+
+
+# ripper_srcs makes all sources at once. invoking this target multiple
+# times in parallel means all sources will be built for the number of
+# sources times respectively.
+ifneq ($(DOT_WAIT),)
+.NOTPARALLEL: ripper_srcs
+else
+ripper_src =
+$(foreach r,$(RIPPER_SRCS),$(eval $(value r): | $(value ripper_src))\
+ $(eval ripper_src := $(value r)))
+ripper_srcs: $(ripper_src)
+endif
diff --git a/defs/id.def b/defs/id.def
new file mode 100644
index 0000000000..344b072e76
--- /dev/null
+++ b/defs/id.def
@@ -0,0 +1,230 @@
+# -*- mode: ruby; coding: us-ascii -*-
+firstline, predefined = __LINE__+1, %[\
+ max
+ min
+ hash
+ freeze
+ nil?
+ inspect
+ intern
+ object_id
+ __id__
+ const_added
+ const_missing
+ method_missing MethodMissing
+ method_added
+ singleton_method_added
+ method_removed
+ singleton_method_removed
+ method_undefined
+ singleton_method_undefined
+ length
+ size
+ gets
+ succ
+ each
+ proc
+ lambda
+ send
+ __send__
+ __recursive_key__
+ clone
+ initialize
+ initialize_copy
+ initialize_clone
+ initialize_dup
+ to_int
+ to_ary
+ to_str
+ to_sym
+ to_hash
+ to_proc
+ to_io
+ to_a
+ to_s
+ to_i
+ to_f
+ to_r
+ bt
+ bt_locations
+ call
+ mesg
+ exception
+ locals
+ not NOT
+ and AND
+ or OR
+ div
+ divmod
+ fdiv
+ quo
+ name
+ nil
+ path
+ pack
+ buffer
+ include?
+ aborted
+ exited
+
+ _ UScore
+
+ # MUST be successive
+ _1 NUMPARAM_1
+ _2 NUMPARAM_2
+ _3 NUMPARAM_3
+ _4 NUMPARAM_4
+ _5 NUMPARAM_5
+ _6 NUMPARAM_6
+ _7 NUMPARAM_7
+ _8 NUMPARAM_8
+ _9 NUMPARAM_9
+ <it> ItImplicit
+ it It
+
+ "/*NULL*/" NULL
+ empty?
+ eql?
+ default
+ respond_to? Respond_to
+ respond_to_missing? Respond_to_missing
+ <IFUNC>
+ <CFUNC>
+ core#set_method_alias
+ core#set_variable_alias
+ core#undef_method
+ core#define_method
+ core#define_singleton_method
+ core#set_postexe
+ core#hash_merge_ptr
+ core#hash_merge_kwd
+ core#raise
+ core#sprintf
+
+ - debug#created_info
+
+ $_ LASTLINE
+ $~ BACKREF
+ $! ERROR_INFO
+
+ Ruby
+]
+
+# VM ID OP Parser Token
+token_ops = %[\
+ Dot2 .. DOT2
+ Dot3 ... DOT3
+ BDot2 .. BDOT2
+ BDot3 ... BDOT3
+ UPlus +@ UPLUS
+ UMinus -@ UMINUS
+ Pow ** POW
+ Cmp <=> CMP
+ PLUS +
+ MINUS -
+ MULT *
+ DIV /
+ MOD %
+ LTLT << LSHFT
+ GTGT >> RSHFT
+ LT <
+ LE <= LEQ
+ GT >
+ GE >= GEQ
+ Eq == EQ
+ Eqq === EQQ
+ Neq != NEQ
+ Not !
+ And &
+ Or |
+ Backquote `
+ EqTilde =~ MATCH
+ NeqTilde !~ NMATCH
+ AREF []
+ ASET []=
+ COLON2 ::
+ ANDOP &&
+ OROP ||
+ ANDDOT &.
+]
+
+class KeywordError < RuntimeError
+ def self.raise(mesg, line)
+ super(self, mesg, ["#{__FILE__}:#{line}", *caller])
+ end
+end
+
+def id2varname(token, prefix = nil)
+ if /#/ =~ token
+ token = "_#{token.gsub(/\W+/, '_')}"
+ else
+ token = token.sub(/\?/, 'P')
+ token = prefix + token if prefix
+ token.sub!(/\A[a-z]/) {$&.upcase}
+ token.sub!(/\A\$/, "_G_")
+ token.sub!(/\A@@/, "_C_")
+ token.sub!(/\A@/, "_I_")
+ token.gsub!(/\W+/, "")
+ end
+ token
+end
+
+predefined_ids = {}
+preserved_ids = []
+local_ids = []
+instance_ids = []
+global_ids = []
+const_ids = []
+class_ids = []
+attrset_ids = []
+token_op_ids = []
+names = {}
+predefined.split(/^/).each_with_index do |line, num|
+ next if /^#/ =~ line
+ line.sub!(/\s+#.*/, '')
+ name, token = line.split
+ next unless name
+ token = id2varname(token || name)
+ if name == '-'
+ preserved_ids << token
+ next
+ end
+ if prev = names[name]
+ KeywordError.raise("#{name} is already registered at line #{prev+firstline}", firstline+num)
+ end
+ if prev = predefined_ids[token]
+ KeywordError.raise("#{token} is already used for #{prev} at line #{names[prev]+firstline}", firstline+num)
+ end
+ names[name] = num
+ case name
+ when /\A[A-Z]\w*\z/; const_ids
+ when /\A(?!\d)\w+\z/; local_ids
+ when /\A\$(?:\d+|(?!\d)\w+|\W)\z/; global_ids
+ when /\A@@(?!\d)\w+\z/; class_ids
+ when /\A@(?!\d)\w+\z/; instance_ids
+ when /\A((?!\d)\w+)=\z/; attrset_ids
+ else preserved_ids
+ end << token
+ predefined_ids[token] = name
+end
+index = 127
+token_ops.split(/^/).each do |line|
+ next if /^#/ =~ line
+ line.sub!(/\s+#.*/, '')
+ id, op, token = line.split
+ next unless id and op
+ token ||= (id unless /\A\W\z/ =~ op)
+ token_op_ids << [id, op, token, (index += 1 if token)]
+end
+{
+ "LOCAL" => local_ids,
+ "INSTANCE" => instance_ids,
+ "GLOBAL" => global_ids,
+ "CONST" => const_ids,
+ "CLASS" => class_ids,
+ "ATTRSET" => attrset_ids,
+ :preserved => preserved_ids,
+ :predefined => predefined_ids,
+ :token_op => token_op_ids,
+ :last_token => index,
+}
diff --git a/defs/jit.mk b/defs/jit.mk
new file mode 100644
index 0000000000..2c1e819684
--- /dev/null
+++ b/defs/jit.mk
@@ -0,0 +1,107 @@
+# Make recipes that deal with the rust code of YJIT and ZJIT.
+#
+# $(gnumake_recursive) adds the '+' prefix to pass down GNU make's
+# jobserver resources to cargo/rustc as rust-lang.org recommends.
+# Without it, certain make version trigger a warning. It does not
+# add the prefix when `make --dry-run` so dry runs are indeed dry.
+
+ifneq ($(JIT_CARGO_SUPPORT),no)
+
+# Show Cargo progress when doing `make V=1`
+CARGO_VERBOSE_0 = -q
+CARGO_VERBOSE_1 =
+CARGO_VERBOSE = $(CARGO_VERBOSE_$(V))
+
+# Because of Cargo cache, if the actual binary is not changed from the
+# previous build, the mtime is preserved as the cached file.
+# This means the target is not updated actually, and it will need to
+# rebuild at the next build.
+RUST_LIB_TOUCH = touch $@
+
+# NOTE: MACOSX_DEPLOYMENT_TARGET to match `rustc --print deployment-target` to avoid the warning below.
+# ld: warning: object file (target/debug/libjit.a(<libcapstone object>)) was built for
+# newer macOS version (15.2) than being linked (15.0)
+# This limits us to an older set of macOS API in the rust code, but we don't use any.
+$(RUST_LIB): $(srcdir)/ruby.rs target/.rustc-version
+ $(Q)if [ '$(ZJIT_SUPPORT)' != no -a '$(YJIT_SUPPORT)' != no ]; then \
+ echo 'building YJIT and ZJIT ($(JIT_CARGO_SUPPORT:yes=release) mode)'; \
+ elif [ '$(ZJIT_SUPPORT)' != no ]; then \
+ echo 'building ZJIT ($(JIT_CARGO_SUPPORT) mode)'; \
+ elif [ '$(YJIT_SUPPORT)' != no ]; then \
+ echo 'building YJIT ($(JIT_CARGO_SUPPORT) mode)'; \
+ fi
+ $(gnumake_recursive)$(Q)CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
+ CARGO_TERM_PROGRESS_WHEN='never' \
+ MACOSX_DEPLOYMENT_TARGET=11.0 \
+ $(CARGO) $(CARGO_VERBOSE) build --manifest-path '$(top_srcdir)/Cargo.toml' $(CARGO_BUILD_ARGS)
+ $(RUST_LIB_TOUCH)
+else ifneq ($(strip $(RLIB_DIR)),) # combo build
+
+$(RUST_LIB): $(srcdir)/ruby.rs target/.rustc-version
+ $(ECHO) 'building $(@F)'
+ $(gnumake_recursive)$(Q) $(RUSTC) --edition=2024 \
+ $(RUSTC_FLAGS) \
+ '-L$(@D)' \
+ --extern=yjit \
+ --extern=zjit \
+ --crate-type=staticlib \
+ --cfg 'feature="yjit"' \
+ --cfg 'feature="zjit"' \
+ '--out-dir=$(@D)' \
+ '$(top_srcdir)/ruby.rs'
+
+# Absolute path to avoid VPATH ambiguity
+JIT_RLIB = $(TOP_BUILD_DIR)/$(RLIB_DIR)/libjit.rlib
+$(YJIT_RLIB): $(JIT_RLIB)
+$(ZJIT_RLIB): $(JIT_RLIB)
+$(JIT_RLIB): target/.rustc-version
+ $(ECHO) 'building $(@F)'
+ $(gnumake_recursive)$(Q) $(RUSTC) --crate-name=jit \
+ --edition=2024 \
+ $(JIT_RUST_FLAGS) \
+ $(RUSTC_FLAGS) \
+ '--out-dir=$(@D)' \
+ '$(top_srcdir)/jit/src/lib.rs'
+endif # ifneq ($(JIT_CARGO_SUPPORT),no)
+
+RUST_LIB_SYMBOLS = $(RUST_LIB:.a=).symbols
+$(RUST_LIBOBJ): $(RUST_LIB)
+ $(ECHO) 'partial linking $(RUST_LIB) into $@'
+ifneq ($(findstring darwin,$(target_os)),)
+ $(Q) $(CC) -nodefaultlibs -r -o $@ -exported_symbols_list $(RUST_LIB_SYMBOLS) $(RUST_LIB)
+else
+ $(Q) $(LD) -r -o $@ --whole-archive $(RUST_LIB)
+ -$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
+endif
+
+rust-libobj: $(RUST_LIBOBJ)
+rust-lib: $(RUST_LIB)
+
+rustc-version-check: target/.rustc-version
+
+target/.rustc-version: PHONY
+ $(eval prev_version := $(if $(wildcard $@),$(shell cat $@)))
+ $(eval curr_version := $(shell $(RUSTC) -V | cut -d' ' -f2))
+ $(eval clean := $(filter-out $(prev_version),$(curr_version)))
+ $(if $(clean),$(ECHO) Cleaning $(@D) for rustc $(curr_version))
+ $(if $(clean),$(Q)$(RMALL) $(@D))
+ $(if $(clean),$(Q)$(MAKEDIRS) $(@D))
+ $(if $(clean),$(Q)echo $(curr_version) > $@)
+
+# For Darwin only: a list of symbols that we want the glommed Rust static lib to export.
+# Unfortunately, using wildcard like '_rb_*' with -exported-symbol does not work, at least
+# not on version 820.1. Assume llvm-nm, so XCode 8.0 (from 2016) or newer.
+#
+# The -exported_symbols_list pulls out the right archive members. Symbols not listed
+# in the list are made private extern, which are in turn made local as we're using `ld -r`.
+# Note, section about -keep_private_externs in ld's man page hints at this behavior on which
+# we rely.
+ifneq ($(findstring darwin,$(target_os)),)
+$(RUST_LIB_SYMBOLS): $(RUST_LIB)
+ $(Q) $(tooldir)/darwin-ar $(NM) --defined-only --extern-only $(RUST_LIB) | \
+ sed -n -e 's/.* //' -e '/^$(SYMBOL_PREFIX)rb_/p' \
+ -e '/^$(SYMBOL_PREFIX)rust_eh_personality/p' \
+ > $@
+
+$(RUST_LIBOBJ): $(RUST_LIB_SYMBOLS)
+endif
diff --git a/defs/keywords b/defs/keywords
new file mode 100644
index 0000000000..a1b1f4f60f
--- /dev/null
+++ b/defs/keywords
@@ -0,0 +1,53 @@
+%{
+struct kwtable {short name, id[2], state;};
+const struct kwtable *rb_reserved_word(const char *, unsigned int);
+#ifndef RIPPER
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
+#define rb_reserved_word(str, len) reserved_word(str, len)
+%}
+
+struct kwtable;
+%%
+__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END
+__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END
+__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END
+BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END
+END, {keyword_END, keyword_END}, EXPR_END
+alias, {keyword_alias, keyword_alias}, EXPR_FNAME|EXPR_FITEM
+and, {keyword_and, keyword_and}, EXPR_VALUE
+begin, {keyword_begin, keyword_begin}, EXPR_BEG
+break, {keyword_break, keyword_break}, EXPR_MID
+case, {keyword_case, keyword_case}, EXPR_VALUE
+class, {keyword_class, keyword_class}, EXPR_CLASS
+def, {keyword_def, keyword_def}, EXPR_FNAME
+defined?, {keyword_defined, keyword_defined}, EXPR_ARG
+do, {keyword_do, keyword_do}, EXPR_BEG
+else, {keyword_else, keyword_else}, EXPR_BEG
+elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE
+end, {keyword_end, keyword_end}, EXPR_END
+ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG
+false, {keyword_false, keyword_false}, EXPR_END
+for, {keyword_for, keyword_for}, EXPR_VALUE
+if, {keyword_if, modifier_if}, EXPR_VALUE
+in, {keyword_in, keyword_in}, EXPR_VALUE
+module, {keyword_module, keyword_module}, EXPR_VALUE
+next, {keyword_next, keyword_next}, EXPR_MID
+nil, {keyword_nil, keyword_nil}, EXPR_END
+not, {keyword_not, keyword_not}, EXPR_ARG
+or, {keyword_or, keyword_or}, EXPR_VALUE
+redo, {keyword_redo, keyword_redo}, EXPR_END
+rescue, {keyword_rescue, modifier_rescue}, EXPR_MID
+retry, {keyword_retry, keyword_retry}, EXPR_END
+return, {keyword_return, keyword_return}, EXPR_MID
+self, {keyword_self, keyword_self}, EXPR_END
+super, {keyword_super, keyword_super}, EXPR_ARG
+then, {keyword_then, keyword_then}, EXPR_BEG
+true, {keyword_true, keyword_true}, EXPR_END
+undef, {keyword_undef, keyword_undef}, EXPR_FNAME|EXPR_FITEM
+unless, {keyword_unless, modifier_unless}, EXPR_VALUE
+until, {keyword_until, modifier_until}, EXPR_VALUE
+when, {keyword_when, keyword_when}, EXPR_VALUE
+while, {keyword_while, modifier_while}, EXPR_VALUE
+yield, {keyword_yield, keyword_yield}, EXPR_ARG
+%%
+#endif
diff --git a/defs/known_errors.def b/defs/known_errors.def
new file mode 100644
index 0000000000..23e9e53507
--- /dev/null
+++ b/defs/known_errors.def
@@ -0,0 +1,157 @@
+E2BIG Argument list too long
+EACCES Permission denied
+EADDRINUSE Address already in use
+EADDRNOTAVAIL Address not available
+EADV Advertise error
+EAFNOSUPPORT Address family not supported
+EAGAIN Resource temporarily unavailable, try again (may be the same value as EWOULDBLOCK)
+EALREADY Connection already in progress
+EAUTH Authentication error
+EBADARCH Bad CPU type in executable
+EBADE Bad exchange
+EBADEXEC Bad executable
+EBADF Bad file descriptor
+EBADFD File descriptor in bad state
+EBADMACHO Malformed Macho file
+EBADMSG Bad message
+EBADR Invalid request descriptor
+EBADRPC RPC struct is bad
+EBADRQC Invalid request code
+EBADSLT Invalid slot
+EBFONT Bad font file format
+EBUSY Device or resource busy
+ECANCELED Operation canceled
+ECAPMODE Not permitted in capability mode
+ECHILD No child processes
+ECHRNG Channel number out of range
+ECOMM Communication error on send
+ECONNABORTED Connection aborted
+ECONNREFUSED Connection refused
+ECONNRESET Connection reset
+EDEADLK Resource deadlock avoided
+EDEADLOCK File locking deadlock error
+EDESTADDRREQ Destination address required
+EDEVERR Device error; e.g., printer paper out
+EDOM Mathematics argument out of domain of function
+EDOOFUS Improper function use
+EDOTDOT RFS specific error
+EDQUOT Disk quota exceeded
+EEXIST File exists
+EFAULT Bad address
+EFBIG File too large
+EFTYPE Invalid file type or format
+EHOSTDOWN Host is down
+EHOSTUNREACH Host is unreachable
+EHWPOISON Memory page has hardware error
+EIDRM Identifier removed
+EILSEQ Invalid or incomplete multibyte or wide character
+EINPROGRESS Operation in progress
+EINTR Interrupted function call
+EINVAL Invalid argument
+EIO Input/output error
+EIPSEC IPsec processing failure
+EISCONN Socket is connected
+EISDIR Is a directory
+EISNAM Is a named file type
+EKEYEXPIRED Key has expired
+EKEYREJECTED Key was rejected by service
+EKEYREVOKED Key has been revoked
+EL2HLT Level 2 halted
+EL2NSYNC Level 2 not synchronized
+EL3HLT Level 3 halted
+EL3RST Level 3 reset
+ELIBACC Cannot access a needed shared library
+ELIBBAD Accessing a corrupted shared library
+ELIBEXEC Cannot exec a shared library directly
+ELIBMAX Attempting to link in too many shared libraries
+ELIBSCN .lib section in a.out corrupted
+ELNRNG Link number out of range
+ELOOP Too many levels of symbolic links
+EMEDIUMTYPE Wrong medium type
+EMFILE Too many open files
+EMLINK Too many links
+EMSGSIZE Message too long
+EMULTIHOP Multihop attempted
+ENAMETOOLONG Filename too long
+ENAVAIL No XENIX semaphores available
+ENEEDAUTH Need authenticator
+ENETDOWN Network is down
+ENETRESET Connection aborted by network
+ENETUNREACH Network unreachable
+ENFILE Too many open files in system
+ENOANO No anode
+ENOATTR Attribute not found
+ENOBUFS No buffer space available
+ENOCSI No CSI structure available
+ENODATA No data available
+ENODEV No such device
+ENOENT No such file or directory
+ENOEXEC Exec format error
+ENOKEY Required key not available
+ENOLCK No locks available
+ENOLINK Link has been severed
+ENOMEDIUM No medium found
+ENOMEM Not enough space/cannot allocate memory
+ENOMSG No message of the desired type
+ENONET Machine is not on the network
+ENOPKG Package not installed
+ENOPOLICY No such policy
+ENOPROTOOPT Protocol not available
+ENOSPC No space left on device
+ENOSR No STREAM resources
+ENOSTR Not a STREAM
+ENOSYS Functionality not implemented
+ENOTBLK Block device required
+ENOTCAPABLE Capabilities insufficient
+ENOTCONN The socket is not connected
+ENOTDIR Not a directory
+ENOTEMPTY Directory not empty
+ENOTNAM Not a XENIX named type file
+ENOTRECOVERABLE State not recoverable
+ENOTSOCK Not a socket
+ENOTSUP Operation not supported
+ENOTTY Inappropriate I/O control operation
+ENOTUNIQ Name not unique on network
+ENXIO No such device or address
+EOPNOTSUPP Operation not supported on socket
+EOVERFLOW Value too large to be stored in data type
+EOWNERDEAD Owner died
+EPERM Operation not permitted
+EPFNOSUPPORT Protocol family not supported
+EPIPE Broken pipe
+EPROCLIM Too many processes
+EPROCUNAVAIL Bad procedure for program
+EPROGMISMATCH Program version wrong
+EPROGUNAVAIL RPC program isn't available
+EPROTO Protocol error
+EPROTONOSUPPORT Protocol not supported
+EPROTOTYPE Protocol wrong type for socket
+EPWROFF Device power is off
+EQFULL Interface output queue is full
+ERANGE Result too large
+EREMCHG Remote address changed
+EREMOTE Object is remote
+EREMOTEIO Remote I/O error
+ERESTART Interrupted system call should be restarted
+ERFKILL Operation not possible due to RF-kill
+EROFS Read-only file system
+ERPCMISMATCH RPC version wrong
+ESHLIBVERS Shared library version mismatch
+ESHUTDOWN Cannot send after transport endpoint shutdown
+ESOCKTNOSUPPORT Socket type not supported
+ESPIPE Illegal seek
+ESRCH No such process
+ESRMNT Server mount error
+ESTALE Stale file handle
+ESTRPIPE Streams pipe error
+ETIME Timer expired
+ETIMEDOUT Connection timed out
+ETOOMANYREFS Too many references: cannot splice
+ETXTBSY Text file busy
+EUCLEAN Structure needs cleaning
+EUNATCH Protocol driver not attached
+EUSERS Too many users
+EWOULDBLOCK Operation would block
+EXDEV Invalid cross-device link
+EXFULL Exchange full
+ELAST Largest errno value
diff --git a/defs/lex.c.src b/defs/lex.c.src
new file mode 100644
index 0000000000..a1b1f4f60f
--- /dev/null
+++ b/defs/lex.c.src
@@ -0,0 +1,53 @@
+%{
+struct kwtable {short name, id[2], state;};
+const struct kwtable *rb_reserved_word(const char *, unsigned int);
+#ifndef RIPPER
+static const struct kwtable *reserved_word(register const char *str, register size_t len);
+#define rb_reserved_word(str, len) reserved_word(str, len)
+%}
+
+struct kwtable;
+%%
+__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END
+__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END
+__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END
+BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END
+END, {keyword_END, keyword_END}, EXPR_END
+alias, {keyword_alias, keyword_alias}, EXPR_FNAME|EXPR_FITEM
+and, {keyword_and, keyword_and}, EXPR_VALUE
+begin, {keyword_begin, keyword_begin}, EXPR_BEG
+break, {keyword_break, keyword_break}, EXPR_MID
+case, {keyword_case, keyword_case}, EXPR_VALUE
+class, {keyword_class, keyword_class}, EXPR_CLASS
+def, {keyword_def, keyword_def}, EXPR_FNAME
+defined?, {keyword_defined, keyword_defined}, EXPR_ARG
+do, {keyword_do, keyword_do}, EXPR_BEG
+else, {keyword_else, keyword_else}, EXPR_BEG
+elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE
+end, {keyword_end, keyword_end}, EXPR_END
+ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG
+false, {keyword_false, keyword_false}, EXPR_END
+for, {keyword_for, keyword_for}, EXPR_VALUE
+if, {keyword_if, modifier_if}, EXPR_VALUE
+in, {keyword_in, keyword_in}, EXPR_VALUE
+module, {keyword_module, keyword_module}, EXPR_VALUE
+next, {keyword_next, keyword_next}, EXPR_MID
+nil, {keyword_nil, keyword_nil}, EXPR_END
+not, {keyword_not, keyword_not}, EXPR_ARG
+or, {keyword_or, keyword_or}, EXPR_VALUE
+redo, {keyword_redo, keyword_redo}, EXPR_END
+rescue, {keyword_rescue, modifier_rescue}, EXPR_MID
+retry, {keyword_retry, keyword_retry}, EXPR_END
+return, {keyword_return, keyword_return}, EXPR_MID
+self, {keyword_self, keyword_self}, EXPR_END
+super, {keyword_super, keyword_super}, EXPR_ARG
+then, {keyword_then, keyword_then}, EXPR_BEG
+true, {keyword_true, keyword_true}, EXPR_END
+undef, {keyword_undef, keyword_undef}, EXPR_FNAME|EXPR_FITEM
+unless, {keyword_unless, modifier_unless}, EXPR_VALUE
+until, {keyword_until, modifier_until}, EXPR_VALUE
+when, {keyword_when, keyword_when}, EXPR_VALUE
+while, {keyword_while, modifier_while}, EXPR_VALUE
+yield, {keyword_yield, keyword_yield}, EXPR_ARG
+%%
+#endif
diff --git a/defs/opt_insn_unif.def b/defs/opt_insn_unif.def
new file mode 100644
index 0000000000..5ce67538f1
--- /dev/null
+++ b/defs/opt_insn_unif.def
@@ -0,0 +1,27 @@
+#
+# a definition of instruction unification
+#
+#
+
+__END__
+
+putobject putobject
+putobject dupstring
+putobject setlocal
+
+dupstring dupstring
+dupstring putobject
+dupstring setlocal
+
+# putnil end
+
+dup setlocal
+
+# from tarai
+getlocal getlocal
+# getlocal send
+
+# from tak, ackermann
+getlocal putobject
+
+
diff --git a/defs/opt_operand.def b/defs/opt_operand.def
new file mode 100644
index 0000000000..887e3da49a
--- /dev/null
+++ b/defs/opt_operand.def
@@ -0,0 +1,22 @@
+#
+# configuration file for operand union optimization
+#
+# format:
+# [insn name] op1, op2 ...
+#
+# wildcard: *
+#
+
+getlocal *, 0
+getlocal *, 1
+setlocal *, 0
+setlocal *, 1
+
+putobject INT2FIX(0)
+putobject INT2FIX(1)
+
+__END__
+
+putobject Qtrue
+putobject Qfalse
+
diff --git a/defs/separated_version.mk b/defs/separated_version.mk
new file mode 100644
index 0000000000..72ee093da7
--- /dev/null
+++ b/defs/separated_version.mk
@@ -0,0 +1,38 @@
+# ******** FOR DEVELEPERS ONLY ********
+# Separate version.o into a shared library which varies every
+# revisions, in order to make the rest shareable.
+
+include $(firstword $(wildcard GNUmakefile Makefile))
+
+ifneq ($(filter @executable_path/%,$(DLDFLAGS)),)
+RUBY_VERSION_SO = $(subst ruby,ruby_version,$(LIBRUBY_SO))
+RUBY_VERSION_DLDFLAGS := $(patsubst @executable_path/%/$(LIBRUBY_SO),@loader_path/$(RUBY_VERSION_SO),$(DLDFLAGS)) -exported_symbol=Init_version
+else ifneq ($(findstring -soname,$(DLDFLAGS)),)
+RUBY_VERSION_SO = $(subst ruby,ruby_version,$(LIBRUBY_SO))
+RUBY_VERSION_DLDFLAGS := $(subst ruby,ruby_version,$(DLDFLAGS)) -Wl,-rpath-link,'$${ORIGIN}'
+else
+ERROR
+endif
+
+ifneq ($(RUBY_VERSION_SO),)
+
+version.$(OBJEXT): XCFLAGS := $(subst -fvisibility=hidden,,$(XCFLAGS))
+
+MINIOBJS += version.$(OBJEXT)
+DLDOBJS += $(RUBY_VERSION_SO)
+LIBRUBYARG_SHARED := -lruby_version $(LIBRUBYARG_SHARED)
+$(LIBRUBY_SO): COMMONOBJS := $(filter-out version.$(OBJEXT),$(COMMONOBJS))
+$(LIBRUBY_A): COMMONOBJS := $(sort version.$(OBJEXT) $(COMMONOBJS))
+
+$(LIBRUBY_SO): $(RUBY_VERSION_SO)
+
+$(RUBY_VERSION_SO): version.$(OBJEXT)
+ $(ECHO) linking shared-library $@
+ $(LDSHARED) $(RUBY_VERSION_DLDFLAGS) version.$(OBJEXT) $(OUTFLAG)$@
+ -$(Q) $(MINIRUBY) -e 'ARGV.each{|link|' \
+ -e 'File.delete link rescue nil' \
+ -e 'File.symlink "$(RUBY_VERSION_SO)", link' \
+ -e '}' \
+ $(subst ruby,ruby_version,$(LIBRUBY_ALIASES)) || true
+
+endif
diff --git a/defs/tags.mk b/defs/tags.mk
new file mode 100644
index 0000000000..d29260c294
--- /dev/null
+++ b/defs/tags.mk
@@ -0,0 +1,18 @@
+# -*- mode: makefile-gmake; indent-tabs-mode: t -*-
+
+SRCS := $(shell $(GIT) ls-files \
+ *.[chy] *.def *.inc *.rb \
+ ccan/ coroutine/ include/ internal/ missing/ \
+ 'enc/**/*.[ch]' 'win32/**/*.[ch]' \
+ )
+
+TAGS: $(SRCS)
+ @echo updating $@
+ @tmp=$$(mktemp); \
+ trap 'rm -f "$$tmp"' 0; \
+ { \
+ $(GIT) grep -h --no-line-number -o '^ *# *define *RBIMPL_ATTR_[A-Z_]*(*' -- include | \
+ sed 's/^ *# *define *//;/_H$$/d;y/(/+/' | sort -u && \
+ echo 'NORETURN+'; \
+ } > "$$tmp" && \
+ ctags -e -I@"$$tmp" -h .def.inc --langmap=c:+.y.def.inc $(^)