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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: aa70e92444211a2d9e68b297a9338d4d517f4c4ad746b95c3a3842e3347cb47f
|
|
4
|
+
data.tar.gz: f7139ecd66b6a7e3808da0f67f63edaaa8115620d92757bf6992656c1668bc49
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a14a2b3a760995c8dbece07b44d6d5b6c779da09372c5a1a2aa7eb32e673a7bfa02f4c47559c66ee4485fc1a384c47179c307ed5df056b9479e87233d900a494
|
|
7
|
+
data.tar.gz: 5c5d8960ee72abf7cd8a64904b9063ae4a86d04d2a57a36b9a365ebf4078b0b53214818ab2b2826db8553aa7e7f4acc0d1a8ab7eaf9d7281a472f9ee0f6d2bff
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes per release. The per-step design record — why each decision was made,
|
|
4
|
+
and what was measured to justify it — is in [docs/development/STEPS.md](docs/development/STEPS.md); this file is
|
|
5
|
+
the summary a consumer of the gem needs.
|
|
6
|
+
|
|
7
|
+
Versioning follows semver with one project-specific rule: **a regression in the corpus
|
|
8
|
+
pass rate is a breaking change**, whatever the code change looked like. See the
|
|
9
|
+
Versioning section of the README.
|
|
10
|
+
|
|
11
|
+
## 1.0.0 (2026-08-12)
|
|
12
|
+
|
|
13
|
+
First release. rubycc builds Ruby C extensions with no gcc, no binutils, no make and no
|
|
14
|
+
shell — it is a C compiler, assembler-free ELF writer, linker, `ar`, `make`, `pkg-config`
|
|
15
|
+
shim and preprocessor, written in Ruby.
|
|
16
|
+
|
|
17
|
+
### What works
|
|
18
|
+
|
|
19
|
+
- **31 gems verified**: bigdecimal, bootsnap, date, digest, erb, etc, fiddle,
|
|
20
|
+
google-protobuf, http_parser.rb, io-console, io-nonblock, io-wait, json, msgpack,
|
|
21
|
+
mysql2, nio4r, nkf, pg, prism, psych, puma, racc, redcarpet, sqlite3, stackprof,
|
|
22
|
+
stringio, strscan, syslog, websocket-driver, yajl-ruby, zlib. "Verified" means the
|
|
23
|
+
gem's own test suite passed against the `.so` that a `RUBYCC=1 gem install` produced
|
|
24
|
+
— the record is `data/verified_gems.json`, written only by
|
|
25
|
+
`tools/verify_gem_tests.rb`, never by hand. That is 31 of the 34 gems in the corpus
|
|
26
|
+
denominator (**91.2%**), meeting the 90% the design sets as its acceptance criterion.
|
|
27
|
+
- **Two machines**: x86-64 and aarch64, each with its own backend and ABI.
|
|
28
|
+
- **Two C libraries**: glibc and musl. The bundled headers carry both where they differ,
|
|
29
|
+
and every difference was measured against that environment's own gcc rather than
|
|
30
|
+
copied from a libc's sources.
|
|
31
|
+
- **Bundled libc headers** so a distroless image with no libc development package still
|
|
32
|
+
compiles `ruby.h`.
|
|
33
|
+
- `rubycc-doctor` reports whether a project's gems are known to build.
|
|
34
|
+
|
|
35
|
+
### Known limitations
|
|
36
|
+
|
|
37
|
+
Listed in full, with measurements, in the README. The ones most likely to matter:
|
|
38
|
+
|
|
39
|
+
- Compile throughput is 69% of the 20,000 lines/sec target.
|
|
40
|
+
- Generated code is unoptimized; up to 7.65x slower than `gcc -O2` on tight loops.
|
|
41
|
+
- C11 atomics are partial: `_Atomic` compiles to the unqualified type's layout and ABI
|
|
42
|
+
for scalars of 1, 2, 4 and 8 bytes, and the bundled `<stdatomic.h>` carries the fences
|
|
43
|
+
and the generic macros; `atomic_fetch_or`/`_and`/`_xor`, `atomic_flag` and the implicit
|
|
44
|
+
sequential consistency of a plain access are missing.
|
|
45
|
+
- `long double` is compiled as `double` (8 bytes, not the ABI's 80-bit x87 in 16), so it
|
|
46
|
+
loses precision and a value passed to `printf("%Lg", …)` reads back wrong.
|
|
47
|
+
- Shared objects bind their own global symbols directly (`ld -Bsymbolic` semantics),
|
|
48
|
+
with no switch to turn it off.
|
|
49
|
+
- 128-bit integers: passing, returning and shifting work; division, remainder, bitwise
|
|
50
|
+
operators and variadic passing do not.
|
|
51
|
+
|
|
52
|
+
### Not in scope
|
|
53
|
+
|
|
54
|
+
C++ (grpc), gems that run `configure` through mini_portile (nokogiri's vendored build —
|
|
55
|
+
`--use-system-libraries` is fine), and gems that ship assembly (ffi). The full list with
|
|
56
|
+
reasons is [docs/reference/OUT-OF-SCOPE-GEMS.md](docs/reference/OUT-OF-SCOPE-GEMS.md).
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DATE Ken
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/NOTICE
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
rubycc
|
|
2
|
+
======
|
|
3
|
+
|
|
4
|
+
This product bundles a set of C library compatibility headers under
|
|
5
|
+
`include/libc/` so that hosted translation units can be compiled without the
|
|
6
|
+
target system's own libc development headers (the "distroless" use case).
|
|
7
|
+
|
|
8
|
+
Portions of the bundled libc headers are derived from musl libc
|
|
9
|
+
(https://musl.libc.org/), used as the starting point for the standard
|
|
10
|
+
declarations, and then modified so their type widths, structure layouts and
|
|
11
|
+
macro values match the glibc x86-64 ABI (measured from the reference platform,
|
|
12
|
+
not copied from glibc). Each derived header carries a one-line provenance note
|
|
13
|
+
in its opening comment; headers whose entire mechanism is glibc/arch specific
|
|
14
|
+
(for example the <ctype.h> classification table accessors) or whose values come
|
|
15
|
+
from the Linux kernel UAPI (<errno.h>, <sys/stat.h>) are written clean room
|
|
16
|
+
against the published ABI rather than derived from musl.
|
|
17
|
+
|
|
18
|
+
musl's COPYRIGHT grants permission for its public header files to omit the
|
|
19
|
+
copyright and permission notices the MIT license would otherwise require, so
|
|
20
|
+
retaining the musl notice below is not an obligation for the derived headers;
|
|
21
|
+
rubycc keeps it as an acknowledgement of provenance. The full per-file
|
|
22
|
+
provenance ledger and the licensing analysis are in docs/reference/HEADER-LICENSING.md.
|
|
23
|
+
|
|
24
|
+
No source was copied from glibc (which is LGPL) or from the Linux kernel UAPI
|
|
25
|
+
headers (which are GPL); only observable ABI facts (sizeof/_Alignof, offsetof,
|
|
26
|
+
struct layouts, and macro values, printed by the reference compiler) were
|
|
27
|
+
measured and reproduced. ABI facts are not copyrightable expression.
|
|
28
|
+
|
|
29
|
+
musl libc
|
|
30
|
+
---------
|
|
31
|
+
|
|
32
|
+
Copyright (C) 2005-2020 Rich Felker, et al.
|
|
33
|
+
|
|
34
|
+
musl is licensed under the MIT license:
|
|
35
|
+
|
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
37
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
38
|
+
the Software without restriction, including without limitation the rights to
|
|
39
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
40
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
41
|
+
so, subject to the following conditions:
|
|
42
|
+
|
|
43
|
+
The above copyright notice and this permission notice shall be included in all
|
|
44
|
+
copies or substantial portions of the Software.
|
|
45
|
+
|
|
46
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
47
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
48
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
49
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
50
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
51
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
52
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# rubycc
|
|
2
|
+
|
|
3
|
+
**Almost Pure Ruby C toolchain** — build Ruby native extensions without gcc, binutils, or a shell.
|
|
4
|
+
|
|
5
|
+
rubycc is a C compiler, linker, archiver and `make` written entirely in Ruby. It compiles
|
|
6
|
+
C to ELF64 machine code directly (no assembly text, no external assembler) and links
|
|
7
|
+
shared objects itself, so a `gem install` that needs a C extension can run on a machine
|
|
8
|
+
that has no toolchain installed.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
$ RUBYCC=1 gem install msgpack
|
|
12
|
+
Building native extensions. This could take a while...
|
|
13
|
+
Successfully installed msgpack-1.8.3
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Status
|
|
17
|
+
|
|
18
|
+
Working. The toolchain compiles and links real gems, and the gems' own test suites pass
|
|
19
|
+
against the resulting binaries. **31 gems are verified this way**, each by running the
|
|
20
|
+
gem's own suite against the `.so` a `RUBYCC=1 gem install` produced — never by inspection:
|
|
21
|
+
|
|
22
|
+
bigdecimal bootsnap date digest erb etc fiddle google-protobuf http_parser.rb
|
|
23
|
+
io-console io-nonblock io-wait json msgpack mysql2 nio4r nkf pg prism psych
|
|
24
|
+
puma racc redcarpet sqlite3 stackprof strscan stringio syslog websocket-driver
|
|
25
|
+
yajl-ruby zlib
|
|
26
|
+
|
|
27
|
+
The verified environments are:
|
|
28
|
+
|
|
29
|
+
| environment | verified gems |
|
|
30
|
+
|---|---|
|
|
31
|
+
| glibc x86-64 | 31 |
|
|
32
|
+
| musl x86-64 (Alpine) | 3 |
|
|
33
|
+
| glibc aarch64 | 6 |
|
|
34
|
+
|
|
35
|
+
The bundled headers match the ABI of the supported environments on both architectures
|
|
36
|
+
and both C libraries.
|
|
37
|
+
|
|
38
|
+
The current corpus census has 39 candidates, 34 gems in the R10 machine-gate denominator,
|
|
39
|
+
and 31 verified gems: **91.2%**, which meets the 90% the design requires. `pg` and
|
|
40
|
+
`sqlite3` are in that denominator through explicit DESIGN-compatible profiles (pg's
|
|
41
|
+
native-source path, sqlite3 with `--enable-system-libraries`), so the rate is measured
|
|
42
|
+
against a larger corpus than the 90.6% reported before those two were added. See
|
|
43
|
+
[`test/corpus/include-census.md`](test/corpus/include-census.md) for the generated report.
|
|
44
|
+
|
|
45
|
+
It also compiles the SQLite amalgamation — a single 261,463-line translation unit — in
|
|
46
|
+
8.1 s using 467 MB of memory.
|
|
47
|
+
|
|
48
|
+
## What's in the box
|
|
49
|
+
|
|
50
|
+
| command | stands in for |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `rubycc` | `cc` — preprocessor, compiler, linker driver (`-c`, `-o`, `-E`, `-shared`, `-fPIC`, `-I`, `-D`, `-L`, `-l`) |
|
|
53
|
+
| `rmake` | `make` — parses Makefiles, runs recipes without a shell, parallel by default |
|
|
54
|
+
| `rubycc-ar` | `ar` — deterministic archives |
|
|
55
|
+
| `rubycc-pkgconf` | `pkg-config` |
|
|
56
|
+
| `rubycc-doctor` | diagnostics: check an environment or a gem for compatibility |
|
|
57
|
+
|
|
58
|
+
Targets **x86-64** and **aarch64** Linux (ELF64). The repository currently carries 81
|
|
59
|
+
physical bundled header files, representing 64 normalized angle-bracket spellings in the
|
|
60
|
+
census. They cover the C11 freestanding set plus the POSIX surface real gems use, so no
|
|
61
|
+
libc development package is needed for the headers.
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- **Ruby 3.3 or newer.** The suite is run on 3.3, 3.4 and 4.0; see *Known limitations*.
|
|
66
|
+
- **Ruby's own headers** (`ruby.h` and friends) and `rbconfig`. Official ruby images have
|
|
67
|
+
them; on a distro Ruby you need the `-dev`/`-devel` package.
|
|
68
|
+
- **Shared libraries you link against** must be present as binaries (`libc.so`, `libz.so`,
|
|
69
|
+
…). Their *headers* are not needed — the linker reads `.dynsym` and emits `DT_NEEDED`.
|
|
70
|
+
- A Ruby built with `--enable-shared` is recommended, because mkmf's `try_link` conftests
|
|
71
|
+
link an executable.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
Set `RUBYCC=1` and install as usual:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
RUBYCC=1 gem install <gem>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For a complete build-stage/runtime-stage example aimed at shell-less images,
|
|
82
|
+
see [examples/distroless](examples/distroless/README.md).
|
|
83
|
+
|
|
84
|
+
`RUBYCC=0` forces it off. With neither set, rubycc activates itself only when `cc` and
|
|
85
|
+
`make` are absent from `PATH`, so it stays out of the way on a normal development machine.
|
|
86
|
+
|
|
87
|
+
Direct use works too:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
rubycc -c foo.c -o foo.o
|
|
91
|
+
rubycc -shared -fPIC -o foo.so foo.o
|
|
92
|
+
rubycc -E foo.c # preprocess only
|
|
93
|
+
rubycc --target=aarch64 -c foo.c -o foo.o
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Known limitations
|
|
97
|
+
|
|
98
|
+
- **Compile speed is 69% of the target.** 13,854 preprocessed lines/sec (Ruby 4.0 + YJIT,
|
|
99
|
+
median over real gem sources) against a 20,000 goal. A typical gem still builds in
|
|
100
|
+
seconds.
|
|
101
|
+
- **Generated code is unoptimized.** No register allocation: every value is spilled to the
|
|
102
|
+
stack. Against `gcc -O2` the slowdown reaches 7.65x on tight loops (1.2x–2.6x on
|
|
103
|
+
branch- and call-bound code); against `gcc -O0` it is 1.1x–2.9x.
|
|
104
|
+
- **C11 atomics are partial.** `_Atomic` is accepted in both spellings (`_Atomic int`,
|
|
105
|
+
`_Atomic(int)`) and compiles to the unqualified type's layout and ABI — measured
|
|
106
|
+
against gcc — for integer, floating and pointer types of 1, 2, 4 or 8 bytes; an
|
|
107
|
+
aggregate, a 16-byte scalar, an array or a function under `_Atomic` is a compile
|
|
108
|
+
error rather than a silently non-atomic object. The bundled `<stdatomic.h>` provides
|
|
109
|
+
the memory-order constants, `atomic_thread_fence`/`atomic_signal_fence`,
|
|
110
|
+
`atomic_init`, and the load/store/exchange/compare-exchange/fetch-add/fetch-sub
|
|
111
|
+
generic macros, all lowered to locked machine sequences at 4 and 8 bytes.
|
|
112
|
+
What is missing: `atomic_fetch_or`/`_and`/`_xor`, `atomic_flag`,
|
|
113
|
+
`atomic_is_lock_free`, and the implicit sequential consistency C11 gives a plain
|
|
114
|
+
read or write of an `_Atomic` object (such an access compiles to an ordinary,
|
|
115
|
+
still-indivisible, instruction — use the macros where the ordering matters).
|
|
116
|
+
See `docs/reference/C11-COVERAGE.md`.
|
|
117
|
+
- **C23 checked arithmetic is partial.** The bundled `<stdckdint.h>` maps `ckd_add`,
|
|
118
|
+
`ckd_sub`, and `ckd_mul` to rubycc's overflow builtins; the rest of C23 is not implemented.
|
|
119
|
+
- **`<regex.h>` is a minimal ABI header.** It provides the glibc-compatible `regex_t`/
|
|
120
|
+
`regmatch_t` layout needed by C extensions, but the full POSIX regex implementation is
|
|
121
|
+
not part of rubycc.
|
|
122
|
+
- **`__GNUC__` is deliberately not defined.** Headers take their non-GNU fallback path.
|
|
123
|
+
- **128-bit integers**: passing, returning and shifting work; division, remainder, bitwise
|
|
124
|
+
`& | ^` and variadic passing do not.
|
|
125
|
+
- **`long double` is 8 bytes** — it is compiled as `double`, where the x86-64 psABI gives
|
|
126
|
+
it 80-bit x87 extended precision in 16 bytes (and AArch64 gives it IEEE binary128).
|
|
127
|
+
Arithmetic therefore carries double's 53-bit significand, `sizeof(long double)` is 8,
|
|
128
|
+
and a `long double` handed to a variadic function does not match what the C library
|
|
129
|
+
reads back: `printf("%Lg", x)` prints a wrong number, because glibc's `printf` reads
|
|
130
|
+
16 bytes where rubycc pushed 8. This is measured, not theoretical — it is the one
|
|
131
|
+
difference between rubycc and the reference compiler in the `oj` gem's suite, whose
|
|
132
|
+
`usual.c` formats a `long double` that way. Code that only stores and computes with
|
|
133
|
+
`long double` gets double's range and precision; code that passes it across the libc
|
|
134
|
+
boundary gets wrong values. Full support needs x87 (and binary128) arithmetic and is
|
|
135
|
+
not in this release.
|
|
136
|
+
- **Out of scope**: C++ input is rejected with a diagnostic (the compiler accepts C only),
|
|
137
|
+
so gems needing a C++ compiler (grpc) are out of scope. Gems that run `configure` through
|
|
138
|
+
mini_portile (nokogiri's vendored build; `--use-system-libraries` is fine), or that ship
|
|
139
|
+
assembly (ffi), are also out of scope.
|
|
140
|
+
- **Shared objects bind their own global symbols directly.** A symbol a shared object
|
|
141
|
+
both defines and references resolves to that object's own definition, not to an
|
|
142
|
+
earlier one in the process — the behaviour `ld -Bsymbolic` gives, which many
|
|
143
|
+
distributions enable on purpose because it skips the PLT/GOT indirection. rubycc
|
|
144
|
+
always does it and offers no switch. Calls, struct passing, varargs and alignment
|
|
145
|
+
are unaffected; what changes is `LD_PRELOAD` interposition of such a symbol, and
|
|
146
|
+
the case where the same symbol is already defined elsewhere in the process (two
|
|
147
|
+
live copies instead of one).
|
|
148
|
+
|
|
149
|
+
## No gem-side changes required
|
|
150
|
+
|
|
151
|
+
Compatibility is rubycc's job, not the gem's. A gem must build **unmodified**: rubycc never
|
|
152
|
+
asks for source changes, `extconf.rb` or gemspec edits, install-time patches, or
|
|
153
|
+
rubycc-specific code such as `#ifdef __RUBYCC__`. If a gem does not build, that is a rubycc
|
|
154
|
+
bug.
|
|
155
|
+
|
|
156
|
+
Two things are *not* considered gem-side changes: turning rubycc on (`RUBYCC=1`), and
|
|
157
|
+
choosing an install option the gem itself offers — for example
|
|
158
|
+
`gem install nokogiri -- --use-system-libraries`, which is a supported flag of that gem,
|
|
159
|
+
not a rubycc workaround.
|
|
160
|
+
|
|
161
|
+
## Versioning
|
|
162
|
+
|
|
163
|
+
Semantic versioning, with one project-specific rule:
|
|
164
|
+
|
|
165
|
+
- **A regression in the corpus pass rate is a breaking change.** The corpus
|
|
166
|
+
(`test/corpus/gems.rb`, 39 candidates and currently 34 R10 machine-gate targets) is the
|
|
167
|
+
contract. The selected profile and exact extconf arguments are part of each target's
|
|
168
|
+
identity; they do not by themselves count as an upstream-suite verification.
|
|
169
|
+
If a release stops building a gem that the previous release built, that is major-version
|
|
170
|
+
territory, not a patch — regardless of how small the code change was.
|
|
171
|
+
- **Minor** releases add language or header coverage, new targets, or new gems that build.
|
|
172
|
+
- **Patch** releases fix bugs and improve performance without changing what builds.
|
|
173
|
+
- The generated code's *speed* is not part of the compatibility contract, but throughput
|
|
174
|
+
and runtime performance are tracked (`rake bench:throughput`, `benchmark/run.rb`) and
|
|
175
|
+
regressions are treated as bugs.
|
|
176
|
+
|
|
177
|
+
The full picture is in [docs/reference/C11-COVERAGE.md](docs/reference/C11-COVERAGE.md) (clause-by-clause C11
|
|
178
|
+
conformance) and [docs/development/ROADMAP.md](docs/development/ROADMAP.md) §3 (known limitations).
|
|
179
|
+
|
|
180
|
+
## How it works
|
|
181
|
+
|
|
182
|
+
Source → preprocessor (translation phases 1–4) → parser → typed AST → IR → machine code →
|
|
183
|
+
ELF writer. The linker resolves symbols, merges sections and emits `.so`/executables. No
|
|
184
|
+
stage shells out; no stage writes assembly text.
|
|
185
|
+
|
|
186
|
+
- [docs/development/DESIGN.md](docs/development/DESIGN.md) — requirements, architecture decisions, scope
|
|
187
|
+
- [docs/internals/IR.md](docs/internals/IR.md) — the intermediate representation
|
|
188
|
+
- [docs/development/RELEASE-CHECKLIST.md](docs/development/RELEASE-CHECKLIST.md) — non-functional requirement status
|
|
189
|
+
- [docs/reference/HEADER-LICENSING.md](docs/reference/HEADER-LICENSING.md) — provenance of every bundled header
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
rake test # full suite
|
|
195
|
+
rake bench:throughput # compile-speed benchmark (network: fetches gems)
|
|
196
|
+
rake corpus:census # which headers do real gems need? (network)
|
|
197
|
+
R10_CORPUS_CACHE=... rake corpus:r10_scan # provenance + variadic candidates (no network)
|
|
198
|
+
ruby benchmark/run.rb # generated-code speed vs gcc
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The suite compares rubycc against gcc on every layer it can: preprocessed token streams,
|
|
202
|
+
exit codes and stdout of compiled programs, cross-architecture ABI probes, and the
|
|
203
|
+
c-testsuite. gcc is a development-time dependency only — it is never required at runtime.
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT. See [LICENSE.txt](LICENSE.txt) and [NOTICE](NOTICE) — the bundled headers' provenance
|
|
208
|
+
is documented in [docs/reference/HEADER-LICENSING.md](docs/reference/HEADER-LICENSING.md).
|
data/data/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# data/ — repository-attached reference data
|
|
2
|
+
|
|
3
|
+
## verified_gems.json
|
|
4
|
+
|
|
5
|
+
The build-verified gem database `rubycc doctor` consults as its **primary
|
|
6
|
+
reference** before attempting any on-the-fly build. JSON carries no comments, so
|
|
7
|
+
the schema is documented here.
|
|
8
|
+
|
|
9
|
+
Top level は **gem 名**をキーとするオブジェクト。1 gem = 1 エントリで、値は次のオブジェクト:
|
|
10
|
+
|
|
11
|
+
| key | type | meaning |
|
|
12
|
+
|-----------------|----------------------------|---------|
|
|
13
|
+
| `verifications` | array of verification 記録 | その gem を確認した**環境ごとの記録**。挿入順(古い順)。空にはしない |
|
|
14
|
+
| `notes` | string | 既知の但し書き(必要だったフラグ、手で補った手順など)。無ければ空文字 |
|
|
15
|
+
|
|
16
|
+
`verifications` の各要素:
|
|
17
|
+
|
|
18
|
+
| key | type | meaning |
|
|
19
|
+
|---------------|---------------------|---------|
|
|
20
|
+
| `versions` | array of string | この環境で検証したバージョン文字列(例 `"2.21.1"`)またはバージョン範囲(例 `">= 1.8, < 2"`)。ここに合致する gem は**ビルドせずに verified** と報告される |
|
|
21
|
+
| `environment` | string | その検証が成り立った環境。例 `"glibc x86_64 / ruby 3.4.5"` |
|
|
22
|
+
| `verified_at` | string `YYYY-MM-DD` | その環境での検証を記録した日付 |
|
|
23
|
+
| `evidence` | string | どう確認したか — どの rubycc ステップ/テストが証明したか(その gem 自身のテストスイートの合格など) |
|
|
24
|
+
|
|
25
|
+
`versions` が**トップレベルではなく各記録の内側にある**のは、環境ごとに実際に検証した
|
|
26
|
+
バージョンが違いうるからである。トップレベルに 1 本置くと、ある環境でしか測っていない
|
|
27
|
+
バージョンまで全環境で検証済みだと主張することになる。
|
|
28
|
+
|
|
29
|
+
同じ理由で、**ある環境で未検証であることは「その環境の記録が無いこと」で表す**。
|
|
30
|
+
散文で書かない(下記「更新は…」節を参照)。
|
|
31
|
+
|
|
32
|
+
### Version matching
|
|
33
|
+
|
|
34
|
+
`versions` の各要素は、gem の解決済みバージョンに対して RubyGems の要求文法
|
|
35
|
+
(`Gem::Requirement`)で照合される。`"2.21.1"` のような完全一致はそのバージョンだけに、
|
|
36
|
+
`">= 1.8, < 2"` のような範囲はその内側の全バージョンに一致する。**どれか 1 つの記録の
|
|
37
|
+
どれか 1 つの要素**を満たせばその gem は verified、つまり**どれか 1 つの環境で検証
|
|
38
|
+
されていれば verified** である。`rubycc doctor` の note 欄には、実際に合致した記録の
|
|
39
|
+
環境名が並ぶ。
|
|
40
|
+
|
|
41
|
+
### What may be added here
|
|
42
|
+
|
|
43
|
+
Only versions that were **actually built and exercised in this repository** — the
|
|
44
|
+
initial data is `json 2.21.1` and `msgpack 1.8.3`, both of which built with
|
|
45
|
+
rubycc and passed their own upstream test suites (Step 54, re-confirmed via the
|
|
46
|
+
in-process rmake build in Step 61 and the hermetic gem install in Step 64). The
|
|
47
|
+
intended long-term flow is to generate/extend this file from the corpus CI
|
|
48
|
+
results (ROADMAP H3) rather than hand-editing it.
|
|
49
|
+
|
|
50
|
+
### 更新は `tools/verify_gem_tests.rb` 経由で行う(手編集ではなく)
|
|
51
|
+
|
|
52
|
+
上記の「手編集ではなくコーパス CI の結果から生成/拡張したい」という意図は
|
|
53
|
+
**`tools/verify_gem_tests.rb` として実現済み**である。このツールは
|
|
54
|
+
|
|
55
|
+
1. scratch GEM_HOME に本チェックアウトの rubycc を入れ、
|
|
56
|
+
2. `RUBYCC=1 gem install <gem>` でその gem の C 拡張を rubycc でビルドし、
|
|
57
|
+
3. 本当に rubycc が使われたことを RubyGems が残す痕跡
|
|
58
|
+
(`gem_make.out` の `$(MAKE)` = rubycc の `exe/rmake`、生成 Makefile の
|
|
59
|
+
`CC = <...>/exe/rubycc`)で確認し、
|
|
60
|
+
4. 上流タグの tarball を取得して、そこへビルド済み `.so` を差し込み、
|
|
61
|
+
5. **その gem 自身のテストスイート**を実走してサマリ行を実測パースする。
|
|
62
|
+
|
|
63
|
+
**書き込み経路はこのツールの `--update --step N` だけ**である。PASS した gem
|
|
64
|
+
だけが記録され、失敗した gem・サマリ行を読み取れなかった gem は決して書かれない。
|
|
65
|
+
`evidence` / `environment` / `verified_at` はすべて**その実行の実測値**から生成
|
|
66
|
+
される。書式(インデント 2、`versions` は 1 行のインライン配列)も既存に合わせて
|
|
67
|
+
出力されるため、意図した行以外に差分は出ない。
|
|
68
|
+
|
|
69
|
+
記録先の選び方は、その実行が**どの環境で走ったか**で決まる:
|
|
70
|
+
|
|
71
|
+
- そのエントリに `environment` が一致する記録がある → **その記録だけ**を更新する
|
|
72
|
+
(`versions` は和集合、`verified_at` は当日、`evidence` は追記)。他の環境の記録は触らない
|
|
73
|
+
- エントリはあるが、その環境の記録が無い → `verifications` の**末尾に新しい記録を足す**。
|
|
74
|
+
`evidence` はこのとき新規形から始める(他環境で測った証拠を引き継ぐと、
|
|
75
|
+
そこで測っていない事実をその環境で測ったことにしてしまう)
|
|
76
|
+
- エントリ自体が無い → 記録 1 本と `notes` を持つ新エントリを作る
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
tools/verify_gem_tests.rb --all # 実走して報告するだけ
|
|
80
|
+
tools/verify_gem_tests.rb --update --step 143 redcarpet # 合格した gem を記録する
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
このツールを使ううえでの決まりごと:
|
|
84
|
+
|
|
85
|
+
- **レシピには `sanity` 式が必須**。gem によっては C 拡張がロードされず純 Ruby の
|
|
86
|
+
フォールバック(racc)や処理系同梱の別コピー(date・bigdecimal・json)が使われても
|
|
87
|
+
テストスイートは合格しうる。実測例: racc の `cparse.so` を壊したままスイートを走らせると
|
|
88
|
+
**71 tests / 0 failures / 100% passed** になる(純 Ruby ランタイムに落ちている)。
|
|
89
|
+
sanity 式が無ければ、この状態が「rubycc で検証済み」として記録されてしまう。
|
|
90
|
+
そのためツールは `sanity` を持たないレシピの実行を拒否する。
|
|
91
|
+
- **`notes` は人間の責務**であり、**このファイルで唯一、手で書き換えてよい欄**。
|
|
92
|
+
エントリ階層にあり、環境をまたいだ但し書きを書く。新規エントリで `--notes` を省略すると
|
|
93
|
+
空文字が入り、警告が出る。
|
|
94
|
+
「機械が観測できない但し書き」(racc の `lib/racc/parser-text.rb` を手で補った、など)は
|
|
95
|
+
必ず手で書き加えること。既存エントリを更新するとき、ツールは `notes` を**保持**する
|
|
96
|
+
(実測できる skip / pending / omission の件数だけは事実として追記する)。
|
|
97
|
+
ツールが上書きしないのは「機械が人間の但し書きを消さない」ためであって、
|
|
98
|
+
**人間が古くなった但し書きを直すのは正しい操作**である
|
|
99
|
+
(実例: stackprof の「`dlclose` 後始末は保証されない」は Step 156 で解消したので
|
|
100
|
+
手で書き換えた)。`versions` / `environment` / `verified_at` / `evidence` は
|
|
101
|
+
verification 記録の側にあり、実測から生成される欄なので、**手では触らない**こと。
|
|
102
|
+
- **`notes` に「X ではまだ未検証」と書いてはならない**。ある環境で未検証であることは
|
|
103
|
+
**その環境の verification 記録が無いこと**で既に表現されている。散文にも書くと
|
|
104
|
+
同じ事実の管理箇所が 2 つになり、片方(必ず散文の側)が古くなる。実際、
|
|
105
|
+
全エントリが持っていた "musl and aarch64 not yet verified." がこれで、
|
|
106
|
+
スキーマを入れ子にしたときに削除した。
|
|
107
|
+
- **既存の verification 記録の `evidence` は上書きではなく追記**される。`evidence` は
|
|
108
|
+
**その環境で**その gem を確認した全ステップの履歴を溜める欄で(json は Step 54・61・64、
|
|
109
|
+
msgpack は Step 138 で H4 の 1 文が足された)、今日の実走は「今日測った事実」を
|
|
110
|
+
足すだけであり、過去の確認が無かったことにはならない。上書きすると再実行では
|
|
111
|
+
復元できない部分が黙って消える。ただし**その環境の記録を新たに作るときは新規形から
|
|
112
|
+
始める**(他環境の履歴を引き継ぐと、そこで測っていない事実を主張することになる)。
|
|
113
|
+
- **`test/test_doctor.rb` の許可リストは手で更新する**。
|
|
114
|
+
`test_verified_gems_json_holds_only_confirmed_gems` が持つ gem 名の許可リストと
|
|
115
|
+
DB のキー集合が食い違うと、ツールは貼り付け用の `assert_equal` 行を表示して警告する
|
|
116
|
+
だけで、テストファイルは決して自動編集しない(gem の追加を意識的な編集に留める
|
|
117
|
+
ための意図的なゲート)。
|