Skip to content

Commit d5ec182

Browse files
2026.10.24.1: the target base and the execution host; trimmed release paths; spaced homes build (#834)
The target base: [target.<triple>].sysroot takes a package, a locator (glibc@system, musl@system, an Apple SDK, msvc@…), { path, provides } or "", from four tiers (CLI/env, the project, config.toml, the target row). It is resolved once into tc.base and measured with the driver. The artifact binds its base: PT_INTERP and RUNPATH, rule B, the fingerprint and the pack floor. A stated gnu-sysroot base hands ld --sysroot; mcpp no longer writes into a vendor's sysroot. x86_64-ios-sim is verified. The execution host: [target.<triple>].run_under composes <run_under…> <runner…> <artifact> <args>, and runners receive MCPP_RUN_*. An Apple artifact on a non-macOS host without run_under is refused. #832: release and dist define NDEBUG and trim the paths an artifact carries (trim_paths, ndebug). A pasted command quotes its path. A home whose path holds a space builds: build.ninja quotes its programs and toolchain paths for the host, and glibc's linker scripts quote their names (openxlings/xim-pkgindex#1044). A home outside ASCII names gcc's module mapper. xlings 2026.10.24.1 is bundled and is the vendored floor. Docs from #836: package-owned project templates and the mcpplibs.mwrap convention. Closes #833, #835. Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 4c80028 commit d5ec182

117 files changed

Lines changed: 5951 additions & 668 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/docs/2026-10-23-target-base-and-execution-host-design.md‎

Lines changed: 834 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env bash
2+
# Repro for 2026-10-23-target-base-and-execution-host-design.md §1.
3+
#
4+
# Builds, from the payloads an mcpp home already holds:
5+
# toolchain/ gcc 16.1.0 + binutils 2.42.1 in one prefix, pristine specs
6+
# (a "vendor" gcc: not a path inside mcpp's registry)
7+
# xgcc/ aarch64-linux-musl-gcc 16.1.0 (a cross toolchain carrying a sysroot)
8+
# sysroot-glibc244/ glibc 2.44.3 + linux-headers, crosstool-ng layout
9+
# sysroot-glibc231/ Debian 11 glibc 2.31 (only with FETCH_DEBIAN=1; needs network)
10+
# and runs the scenario matrix A-K against $MCPP (J and K drive the compilers directly:
11+
# mcpp has no spelling for a host sysroot yet).
12+
#
13+
# Usage: MCPP=/path/to/mcpp [FETCH_DEBIAN=1] [WORK=/tmp/sysroot-demo] bash this.sh
14+
set -uo pipefail
15+
MCPP=${MCPP:?set MCPP to the mcpp binary under test}
16+
WORK=${WORK:-/tmp/sysroot-demo}
17+
R=${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs
18+
GCC=$R/xim-x-gcc/16.1.0
19+
BINUTILS=$(ls -d $R/xim-x-binutils/* | tail -1)
20+
GLIBC=$R/xim-x-glibc/2.44.3
21+
HEADERS=$(ls -d $R/xim-x-linux-headers/* | tail -1)
22+
XGCC=$(ls -d $R/xim-x-aarch64-linux-musl-gcc/* | tail -1)
23+
export MCPP_NO_AUTO_INSTALL=1 MCPP_JOBS=${MCPP_JOBS:-8}
24+
25+
mkdir -p "$WORK"
26+
T=$WORK/toolchain S=$WORK/sysroot-glibc244 X=$WORK/xgcc
27+
28+
# ── toolchains ─────────────────────────────────────────────────────────────
29+
rm -rf "$T" "$X"
30+
cp -a "$GCC" "$T"; chmod -R u+w "$T"
31+
rm -f "$T"/lib/gcc/x86_64-linux-gnu/*/specs # the registry copy carries baked specs
32+
mkdir -p "$T/x86_64-linux-gnu/bin"
33+
for t in as ld ld.bfd ar nm ranlib objcopy objdump strip readelf; do
34+
cp -a "$BINUTILS/bin/$t" "$T/bin/$t"
35+
ln -sf "../../bin/$t" "$T/x86_64-linux-gnu/bin/$t"
36+
done
37+
cp -a "$XGCC" "$X"; chmod -R u+w "$X"
38+
39+
# ── sysroots (crosstool-ng layout: usr/include, usr/lib64, lib64 -> usr/lib64) ─
40+
layout() { mkdir -p "$1/usr/include" "$1/usr/lib64" "$1/lib" "$1/usr/lib"; ln -sfn usr/lib64 "$1/lib64"; }
41+
relink_scripts() { # $1 sysroot, $2 sed expression; text-only files (-I)
42+
for f in $(grep -lI "GROUP\|INPUT" "$1"/usr/lib64/*.so 2>/dev/null); do sed -i "$2" "$f"; done
43+
}
44+
rm -rf "$S"; layout "$S"
45+
cp -a "$GLIBC/include/." "$HEADERS/include/." "$S/usr/include/"
46+
cp -a "$GLIBC/lib/." "$S/usr/lib64/"; chmod -R u+w "$S"
47+
relink_scripts "$S" "s#$GLIBC/lib/#/lib64/#g"
48+
49+
if [ "${FETCH_DEBIAN:-0}" = 1 ]; then
50+
O=$WORK/sysroot-glibc231; rm -rf "$O"; mkdir -p "$O/x" "$WORK/dl"
51+
for f in g/glibc/libc6_2.31-13+deb11u11_amd64.deb g/glibc/libc6-dev_2.31-13+deb11u11_amd64.deb \
52+
l/linux/linux-libc-dev_5.10.223-1_amd64.deb; do
53+
[ -f "$WORK/dl/${f##*/}" ] || curl -sSfL -o "$WORK/dl/${f##*/}" "https://deb.debian.org/debian/pool/main/$f"
54+
dpkg-deb -x "$WORK/dl/${f##*/}" "$O/x"
55+
done
56+
layout "$O"
57+
cp -a "$O/x/usr/include/." "$O/x/usr/include/x86_64-linux-gnu/." "$O/usr/include/"
58+
cp -a "$O/x/lib/x86_64-linux-gnu/." "$O/x/usr/lib/x86_64-linux-gnu/." "$O/usr/lib64/"
59+
rm -rf "$O/x"
60+
relink_scripts "$O" "s#/usr/lib/x86_64-linux-gnu/#/lib64/#g; s#/lib/x86_64-linux-gnu/#/lib64/#g"
61+
for l in $(find "$O/usr/lib64" -maxdepth 1 -type l); do
62+
t=$(readlink "$l"); case $t in /lib/x86_64-linux-gnu/*) ln -sfn "$(basename "$t")" "$l";; esac
63+
done
64+
fi
65+
66+
# ── sources ────────────────────────────────────────────────────────────────
67+
GLIBC_MAIN='#include <gnu/libc-version.h>
68+
#include <pthread.h>
69+
import std;
70+
int main() {
71+
std::println("compiled against glibc {}.{}", __GLIBC__, __GLIBC_MINOR__);
72+
std::println("running on glibc {}", gnu_get_libc_version());
73+
#if __GLIBC_PREREQ(2, 42)
74+
std::println("pthread_gettid_np = {}", pthread_gettid_np(pthread_self())); // GLIBC_2.42
75+
#endif
76+
}'
77+
PLAIN_MAIN='import std;
78+
int main() { std::println("hello from a {}-bit target", sizeof(void*) * 8); }'
79+
80+
scenario() { # name, main, manifest-tail, [build args]
81+
local d=$WORK/m/$1; rm -rf "$d"; mkdir -p "$d/src"
82+
printf '%s\n' "$2" > "$d/src/main.cpp"
83+
printf '[package]\nname = "m%s"\nversion = "0.1.0"\n\n%s\n' "$1" "$3" > "$d/mcpp.toml"
84+
echo "================ $1 ${4:-}"; sed -n '5,$p' "$d/mcpp.toml"
85+
( cd "$d" && nice -n 10 "$MCPP" build ${4:-} 2>&1 | grep -v '^\s*\(Index\|Inferred\)' | tail -6 )
86+
local b; b=$(ls "$d"/target/*/*/bin/m$1 2>/dev/null | head -1)
87+
[ -n "$b" ] || return 0
88+
file "$b" | cut -d, -f1-2
89+
readelf -lW "$b" 2>/dev/null | grep -o 'interpreter: [^]]*'
90+
readelf -dW "$b" 2>/dev/null | grep -i 'rpath\|runpath'
91+
file "$b" | grep -q x86-64 && { echo "-- run:"; "$b" 2>&1 | head -3; }
92+
}
93+
94+
scenario A "$GLIBC_MAIN" "[toolchain.linux]
95+
path = \"$T\""
96+
scenario B "$GLIBC_MAIN" "[toolchain.linux]
97+
path = \"$T\"
98+
prefix = \"x86_64-linux-gnu-\""
99+
scenario C "$GLIBC_MAIN" "[toolchain.linux]
100+
path = \"$T\"
101+
sysroot = \"$S\""
102+
scenario D "$GLIBC_MAIN" "[toolchain.linux]
103+
path = \"$T\"
104+
sysroot = \"$S\"
105+
106+
[build]
107+
ldflags = [\"-Wl,--sysroot=$S\"]"
108+
scenario E "$GLIBC_MAIN" "[toolchain.linux]
109+
path = \"$T\"
110+
sysroot = \"$S\"
111+
112+
[build]
113+
ldflags = [\"-Wl,--sysroot=$S\", \"-Wl,--dynamic-linker=$S/lib64/ld-linux-x86-64.so.2\", \"-Wl,-rpath,$S/lib64\"]"
114+
scenario F "$GLIBC_MAIN" "[toolchain]
115+
linux = \"gcc@16.1.0\"
116+
117+
[target.x86_64-linux-gnu]
118+
sysroot = \"$S\""
119+
scenario G "$PLAIN_MAIN" "[toolchain.linux]
120+
path = \"$X\"
121+
prefix = \"aarch64-linux-musl-\""
122+
scenario H "$PLAIN_MAIN" "[toolchain.linux]
123+
path = \"$X\"
124+
prefix = \"aarch64-linux-musl-\"
125+
sysroot = \"$X/aarch64-linux-musl\""
126+
if [ -d "$WORK/sysroot-glibc231" ]; then
127+
scenario I "$PLAIN_MAIN" "[toolchain.linux]
128+
path = \"$T\"
129+
sysroot = \"$WORK/sysroot-glibc231\""
130+
fi
131+
132+
# ── J / K: the host's own root as the sysroot (Debian/Ubuntu multiarch) ─────
133+
# Not through mcpp: `glibc@system` is what the design adds. The ecosystem gcc is
134+
# built without multiarch (-print-multiarch is empty), so J fails until the
135+
# multiarch directories are wired; clang (K) resolves them itself.
136+
LLVM=$(ls -d $R/xim-x-llvm/* 2>/dev/null | tail -1)
137+
TUPLE=$(/usr/bin/gcc -print-multiarch 2>/dev/null); [ -n "$TUPLE" ] || TUPLE=$(uname -m)-linux-gnu
138+
mkdir -p "$WORK/m/J"; printf '%s\n' '#include <gnu/libc-version.h>' '#include <stdio.h>' \
139+
'int main(){printf("compiled against %d.%d, running on %s\n",__GLIBC__,__GLIBC_MINOR__,gnu_get_libc_version());}' > "$WORK/m/J/c.c"
140+
echo "================ J (gcc --sysroot=/)"
141+
"$T/bin/gcc" --sysroot=/ "$WORK/m/J/c.c" -o "$WORK/m/J/plain" 2>&1 | tail -2
142+
echo "-- with the multiarch directories wired:"
143+
"$T/bin/gcc" --sysroot=/ -B/usr/lib/$TUPLE -L/usr/lib/$TUPLE -L/lib/$TUPLE -isystem /usr/include/$TUPLE \
144+
-Wl,-rpath-link,/lib/$TUPLE "$WORK/m/J/c.c" -o "$WORK/m/J/wired" 2>&1 | tail -2 && "$WORK/m/J/wired"
145+
if [ -n "$LLVM" ]; then
146+
echo "================ K (clang --sysroot=/)"
147+
"$LLVM/bin/clang" --no-default-config --sysroot=/ "$WORK/m/J/c.c" -o "$WORK/m/J/clang" 2>&1 | tail -2 && "$WORK/m/J/clang"
148+
fi

‎.agents/docs/README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
1818
---
1919
```
2020

21-
335 records.
21+
336 records.
2222

2323
## By subject
2424

@@ -108,6 +108,7 @@ Records that declare one. Everything else is listed by date below.
108108

109109
### toolchain
110110

111+
- [目标基座与执行宿主:sysroot 的通用模型、运行器契约、自备输入,以及同批的发布产物路径(#832)](2026-10-23-target-base-and-execution-host-design.md) — landed
111112
- [LLVM 23.1.3 Part 2:Linux aarch64 默认工具链与 glibc 生态闭环](2026-10-08-llvm-2313-linux-aarch64-ecosystem-part2-design.md) — active
112113
- [LLVM 23.1.3 全平台统一默认:跨仓库联动方案(mcpp × xim-pkgindex)](2026-10-07-llvm-2313-unified-default-cross-repo-design.md) — active
113114

@@ -138,6 +139,7 @@ Records that declare one. Everything else is listed by date below.
138139

139140
### 2026-10
140141

142+
- [目标基座与执行宿主:sysroot 的通用模型、运行器契约、自备输入,以及同批的发布产物路径(#832)](2026-10-23-target-base-and-execution-host-design.md) — landed
141143
- [依赖 image 的资源归属、模块扫描边界与锁文件稳定写入](2026-10-21-resource-ownership-module-scan-and-lock-write-design.md) — active
142144
- [首次初始化、home 重定向与网络诊断优化设计](2026-10-19-first-run-home-and-network-design.md) — landed
143145
- [升级的连续性:打包修订不在使用路径上替换载荷、替换失败不拆掉可用状态、home 中的悬空记录自愈、真实的升级 lane](2026-10-17-upgrade-continuity-design.md) — active

‎.agents/skills/mcpp-usage/SKILL.md‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ license = "Apache-2.0"
7272

7373
## mcpp.toml 配置
7474

75+
### 传统头文件库的模块化封装
76+
77+
`mcpplibs.mwrap.<库名>` 为传统 C/C++ 库提供模块入口;API namespace 保留上游,例如
78+
`import mcpplibs.mwrap.opencv.core;` 后仍使用 `cv::Mat`。包版本对齐上游,封装修订最多追加一个数字尾号。
79+
具体已发布的版本/feature 先查索引,不把文档中的候选库当已发布包。
80+
81+
每个新建或重构的 -m 库提供包自带快捷模板:
82+
83+
```bash
84+
mcpp new vision --template mcpplibs.mwrap.opencv@5.0.0.1
85+
mcpp new --list-templates mcpplibs.mwrap.opencv@5.0.0.1
86+
```
87+
88+
模板语法为 `[namespace.]name[@version][:template]`,只有一个默认模板时可以省略模板名。
89+
包模板中的 `{{self.name}}` 是完整包选择器,应用 `"{{self.name}}"` 作为单个依赖 key,
90+
不要放到另一层 namespace 表下。模板必须实际创建、构建、运行,而不只做字符串检查。
91+
92+
feature 是加法并集;消费者可在依赖声明中使用 `default-features = false` 关闭该边的默认集合,
93+
显式 features 仍会启用,其他依赖边的请求不会被取消。根项目没有 `--no-default-features` 命令行选项。
94+
可选大依赖由包作者显式门控。模块可导入不等于实际后端已启用,窗口/推理/设备能力需分别验证。
95+
宏不能经 import 传递;`.macros` 只提供可保持语义的常量/函数映射,原始宏仍需兼容头。
96+
97+
维护规范与包装流程见 [mcpp-index mwrap 规范](https://github.com/mcpplibs/mcpp-index/blob/main/docs/mwrap.md)。
98+
修改普通消费者项目时按现有包契约使用,不自动替用户迁移其他依赖。
99+
75100
```toml
76101
[package]
77102
name = "myapp"
@@ -88,7 +113,7 @@ gtest = "1.15.2" # 仅测试使用;SemVer: ^, ~, 范围, 精确
88113
default = "gcc@16.1.0"
89114
```
90115

91-
更多配置:https://github.com/mcpp-community/mcpp/blob/main/docs/05-mcpp-toml.md
116+
更多配置:https://github.com/mcpp-community/mcpp/blob/main/docs/04-mcpp-toml.md
92117

93118
## 工具链
94119

‎.github/actions/bootstrap-mcpp/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ inputs:
3333
# `package.name`, so one of the two was simply unreachable — and which one
3434
# depended on the machine, which is why CI failed on `compat:lua` on
3535
# Windows and `mcpplibs.capi:lua` on Linux. Never pin below that.
36-
default: '2026.10.19.1'
36+
default: '2026.10.24.1'
3737

3838
outputs:
3939
sandbox-key:

‎.github/actions/setup-macos-llvm/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ inputs:
1515
# Floor imposed by the index, not a routine bump — see
1616
# .github/actions/bootstrap-mcpp/action.yml for why 0.4.69 is required
1717
# (two packages named `lua` in one repo need openxlings/xlings#381).
18-
default: '2026.10.19.1'
18+
default: '2026.10.24.1'
1919
image:
2020
description: >
2121
The runner label the job runs on (macos-15, xcode-27). It is part of the
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
# check_release_paths.sh — a released mcpp names no directory of the machine
3+
# that built it (mcpp#832; design 2026-10-23 §15.3, R4).
4+
#
5+
# mcpp's release profile trims the source paths an artifact carries
6+
# (`trim_paths`), so `__FILE__` in an `assert` of a vendored header reads
7+
# `libs@0.1.0/src/json/json.hpp` rather than
8+
# `/home/runner/work/mcpp/mcpp/modules/libs/src/json/json.hpp`. This gate reads
9+
# the shipped binary and fails on the build machine's checkout -- the source
10+
# paths #832 found. It names exactly that directory: the binary legitimately
11+
# carries `/tmp/` and `/home/` as string literals of its own code, and a
12+
# dynamically linked build's loader and run path name the toolchain store,
13+
# which is what `mcpp pack` rewrites, not a source path.
14+
#
15+
# Usage: bash .github/tools/check_release_paths.sh <binary>
16+
set -uo pipefail
17+
bin="${1:?usage: check_release_paths.sh <binary>}"
18+
[[ -f "$bin" ]] || { echo "FAIL: no binary at $bin"; exit 1; }
19+
roots=()
20+
[[ -n "${GITHUB_WORKSPACE:-}" ]] && roots+=("$GITHUB_WORKSPACE")
21+
[[ -n "${CHECKOUT:-}" ]] && roots+=("$CHECKOUT")
22+
(( ${#roots[@]} > 0 )) || { echo "FAIL: neither GITHUB_WORKSPACE nor CHECKOUT names the checkout"; exit 1; }
23+
fail=0
24+
for r in "${roots[@]}"; do
25+
hits=$(strings -a "$bin" | grep -F -- "$r" | head -5)
26+
if [[ -n "$hits" ]]; then
27+
echo "FAIL: $bin carries the build machine's path $r:"
28+
sed 's/^/ /' <<<"$hits"
29+
fail=1
30+
fi
31+
done
32+
[[ $fail -eq 0 ]] && echo "OK: $bin carries no path of the machine that built it (${roots[*]})"
33+
exit $fail

‎.github/workflows/bootstrap-macos.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
# Dormant (workflow_dispatch only), but kept in step with the rest —
1818
# check_version_pins.sh holds it there. Floor: 0.4.69, below which the
1919
# index cannot resolve two packages that share a short name.
20-
XLINGS_VERSION: '2026.10.19.1'
20+
XLINGS_VERSION: '2026.10.24.1'
2121
steps:
2222
- uses: actions/checkout@v4
2323

‎.github/workflows/ci-bootstrap.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
os: [ubuntu-24.04, macos-15, windows-2025]
4242
env:
4343
XLINGS_NON_INTERACTIVE: '1'
44-
XLINGS_VERSION: '2026.10.19.1'
44+
XLINGS_VERSION: '2026.10.24.1'
4545
steps:
4646
- uses: actions/checkout@v4
4747

@@ -50,7 +50,7 @@ jobs:
5050
shell: bash
5151
run: |
5252
set -euo pipefail
53-
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.19.1
53+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.24.1
5454
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
5555
echo "$HOME/.xlings/bin" >> "$GITHUB_PATH"
5656
@@ -124,7 +124,7 @@ jobs:
124124
shell: bash
125125
run: |
126126
set -euo pipefail
127-
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.19.1
127+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.24.1
128128
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
129129
130130
- name: Install the newest release and build the tree with it (mcpp build --strict)

‎.github/workflows/ci-fresh-install.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ jobs:
161161
env:
162162
XLINGS_NON_INTERACTIVE: '1'
163163
run: |
164-
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.19.1
164+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.24.1
165165
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
166166
167167
- name: Install mcpp and config mirror
@@ -324,7 +324,7 @@ jobs:
324324

325325
- name: Install xlings + mcpp
326326
run: |
327-
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.19.1
327+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.24.1
328328
# Deliberately NOT writing to $GITHUB_PATH here. On container
329329
# images that declare no PATH in their config (opensuse/
330330
# tumbleweed), appending a single dir to GITHUB_PATH makes the
@@ -473,7 +473,7 @@ jobs:
473473
# (older ones carry minos=15 and refuse to start).
474474
# v0.4.51+: in-process sha256 — this image has no sha256sum
475475
# binary, so pinned fetches failed before it.
476-
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.19.1
476+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.24.1
477477
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
478478
479479
- name: Install mcpp and config mirror

0 commit comments

Comments
 (0)