Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .agents/docs/2026-11-14-add-meshoptimizer-cgltf-mwrap-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Add mcpplibs.mwrap.meshoptimizer (meshoptimizer-m 1.3) and mcpplibs.mwrap.cgltf (cgltf-m 1.15)

## Form

- Third-party upstreams [zeux/meshoptimizer](https://github.com/zeux/meshoptimizer), release v1.3, MIT, and
[jkuhlmann/cgltf](https://github.com/jkuhlmann/cgltf), release v1.15, MIT (it embeds jsmn, MIT).
- Form A external mwrap module repositories [mcpplibs/meshoptimizer-m](https://github.com/mcpplibs/meshoptimizer-m) and
[mcpplibs/cgltf-m](https://github.com/mcpplibs/cgltf-m); packages `mcpplibs.mwrap.meshoptimizer` 1.3 and
`mcpplibs.mwrap.cgltf` 1.15 (first releases of both identities), tags `mwrap-1.3` and `mwrap-1.15`; release assets
`meshoptimizer-m-1.3.tar.gz` (`5172030f…4e7e`) and `cgltf-m-1.15.tar.gz` (`242d90c6…070b`), CN mirrors
`gitcode.com/mcpp-res/mwrap-meshoptimizer` and `gitcode.com/mcpp-res/mwrap-cgltf` with identical bytes.
- Implementation ownership: neither library has a compat package in this index, so each wrapper vendors its upstream
from the release archive (checksums in `upstream/documents.json`) and builds it itself: meshoptimizer's `src/*.cpp`
unchanged; cgltf's two headers unchanged, their implementation in one C translation unit of the package
(`port/cgltf.c`) with upstream's defaults. Both deploy upstream's README and license with their own LICENSE/NOTICE
(`runtime.deploy`).

## Modules

- `mcpplibs.mwrap.meshoptimizer`: every public name of `<meshoptimizer.h>` (9 types, 20 enumerators, 89 functions) by
`export using`, in the global namespace where upstream declares them; `mcpplibs.mwrap.meshoptimizer.macros`:
`MESHOPTIMIZER_VERSION` as a constant.
- `mcpplibs.mwrap.cgltf`: every public name of `<cgltf.h>` (72 types, 102 enumerators, 37 functions), re-exporting
`mcpplibs.mwrap.cgltf.write` (`<cgltf_write.h>`: `cgltf_write`, `cgltf_write_file`). Neither header's public part
has an object-like constant, so there is no `.macros` module; the configuration macros (`CGLTF_MALLOC` …) are fixed
at upstream's defaults by the package's one implementation.
- Both export lists are derived from the headers by each repository's `tools/surface.py` and audited in CI
(`tools/check_exports.py`).

## Verification

- Wrapper CI (Linux x86_64 GCC 16.1 and LLVM 23.1.3, macOS LLVM, Windows LLVM): the module tests, header mixing, the
export check, and the default template from an installed copy with a strict build.
- This index: consumers `tests/mwrap/meshoptimizer-simplify` (indexing, vertex cache order, simplification, meshlets,
both codecs) and `tests/mwrap/cgltf-inspect` (parse, load, validate, accessors, a node's transform, the writer, the
reasons broken documents are refused with); the installed-template check with `--deploy` for both
(`STATIC_NOTICES`).
- `tests/mwrap/check_installed_template.py` reads the generated manifest's dependency whether its key is quoted or
dotted: both templates write the dotted form (mcpp 2026.11.8.1 warns on a quoted key that needs none, and the check
builds with `--strict`).
10 changes: 10 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,16 @@ jobs:
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.xxhash@0.8.3 --deploy

- name: Installed meshoptimizer wrapper archive, actual default simplification template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' meshoptimizer-simplify ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.meshoptimizer@1.3 --deploy

- name: Installed cgltf wrapper archive, actual default inspection template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' cgltf-inspect ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.cgltf@1.15 --deploy

- name: Installed libarchive wrapper archive, actual default archive template and its relocated notices
if: contains(format(' {0} ', env.MEMBERS), ' libarchive-bundle ') && matrix.toolchain == 'default'
shell: bash
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@

### Added

- **mcpplibs.mwrap.meshoptimizer 1.3、mcpplibs.mwrap.cgltf 1.15(新包)。** 两个 mwrap 包:meshoptimizer-m 以 `export using`
导出 `<meshoptimizer.h>` 的全部公开名字(9 个类型、20 个枚举值、89 个函数),`MESHOPTIMIZER_VERSION` 在 `.macros` 中为同名常量;
cgltf-m 导出 `<cgltf.h>` 的全部公开名字(72 个类型、102 个枚举值、37 个函数),并再导出 `.write`(`<cgltf_write.h>`)。
本索引没有这两个库的 compat 包,封装仓库各自从上游发布包取源码(校验和在 `upstream/documents.json`)并自行构建;导出清单由
各仓库的 `tools/surface.py` 从头文件推出并在 CI 中审计。消费者测试 `tests/mwrap/meshoptimizer-simplify`、
`tests/mwrap/cgltf-inspect`,安装后模板检查带 `--deploy`(`STATIC_NOTICES`);该检查读取生成的清单时同时接受带引号与
点分两种依赖键。GLOBAL 为 GitHub 发布资产,CN 为 GitCode `mcpp-res/mwrap-<lib>` 镜像,sha256 相同。
- **mtest 0.8.3.** 四个包的 0.8.3(同一标签、同一归档;含未入索引的 0.8.1、0.8.2 的内容):`bench::each` 的一次调用可以说明自己的组成
(`part`、`time`、`value`),给出最慢的几次、超过上限的次数与分配计数;`bench::laps` 是同一测量的循环形式;
程序单独运行时打印测得的数字,并按标识、名字或带 `*` 的模式选择用例。只增不改。
Expand Down
3 changes: 3 additions & 0 deletions docs/mwrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ Templates are data, without execution hooks. With the pinned current mcpp,
```

Quote the selector as one key; do not place it under another namespace table.
From mcpp 2026.11.8.1 a quoted key that needs no quotes is reported (and `--strict` refuses it); a template
tested with that client writes the same selector as a dotted key, `{{self.name}} = "{{self.version}}"`, which
names the same package. `tests/mwrap/check_installed_template.py` reads either form.
Declare required features explicitly and keep the library version tied to
`{{self.version}}`. Test actual `mcpp new`, then build and run the generated
project. A source candidate may use an isolated preseeded installed-package
Expand Down
4 changes: 3 additions & 1 deletion docs/zh/mwrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ mcpp new --list-templates mcpplibs.mwrap.opencv@5.0.0
mcpp new viewer --template mcpplibs.mwrap.opencv@5.0.0:window
```

模板是纯数据,不执行 hook。当前 mcpp 的 self.name 为完整包选择器,应作为一个带引号的 key:
模板是纯数据,不执行 hook。当前 mcpp 的 self.name 为完整包选择器,应作为一个带引号的 key(mcpp 2026.11.8.1 起,不需要引号的
key 带引号会被提示,`--strict` 下被拒绝;以该版本测试的模板把同一选择器写成点分 key `{{self.name}} = "{{self.version}}"`,
指向同一个包,`tests/mwrap/check_installed_template.py` 两种写法都读):

```toml
[dependencies]
Expand Down
2 changes: 2 additions & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ members = [
"tests/mwrap/zstd-squeeze",
"tests/mwrap/xz-pack",
"tests/mwrap/xxhash-digest",
"tests/mwrap/meshoptimizer-simplify",
"tests/mwrap/cgltf-inspect",
"tests/mwrap/libarchive-bundle",
"tests/mwrap/libc-stdio",
"tests/mwrap/miniaudio-engine",
Expand Down
50 changes: 50 additions & 0 deletions pkgs/mwrap/cgltf.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- Immutable upstream-aligned mwrap source release (cgltf-m mwrap-1.15);
-- GLOBAL and CN bytes are identical.
--
-- The wrapper is Apache-2.0; the library it vendors and links statically,
-- cgltf 1.15 (it embeds jsmn), is MIT: the package deploys its README and
-- LICENSE beside programs (runtime.deploy).
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "cgltf",
description = "cgltf-m: cgltf 1.15 (glTF 2.0 loader and writer: parsing, buffers, validation, accessors, node transforms) as C++23 modules",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/cgltf-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/cgltf-m/releases/download/${tag}/cgltf-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["1.15"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/cgltf-m/releases/download/mwrap-1.15/cgltf-m-1.15.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-cgltf/releases/download/1.15/cgltf-m-1.15.tar.gz",
},
sha256 = "242d90c65178d0746a25a22b375984bf95af19d9820f7d91b0fb32a06d51070b",
},
},
macosx = {
["1.15"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/cgltf-m/releases/download/mwrap-1.15/cgltf-m-1.15.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-cgltf/releases/download/1.15/cgltf-m-1.15.tar.gz",
},
sha256 = "242d90c65178d0746a25a22b375984bf95af19d9820f7d91b0fb32a06d51070b",
},
},
windows = {
["1.15"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/cgltf-m/releases/download/mwrap-1.15/cgltf-m-1.15.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-cgltf/releases/download/1.15/cgltf-m-1.15.tar.gz",
},
sha256 = "242d90c65178d0746a25a22b375984bf95af19d9820f7d91b0fb32a06d51070b",
},
},
},
}
50 changes: 50 additions & 0 deletions pkgs/mwrap/meshoptimizer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- Immutable upstream-aligned mwrap source release (meshoptimizer-m mwrap-1.3);
-- GLOBAL and CN bytes are identical.
--
-- The wrapper is Apache-2.0; the library it vendors and links statically,
-- meshoptimizer 1.3, is MIT: the package deploys its README and LICENSE beside
-- programs (runtime.deploy).
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "meshoptimizer",
description = "meshoptimizer-m: meshoptimizer 1.3 (indexing, vertex cache and overdraw ordering, simplification, meshlets, vertex and index codecs) as C++23 modules",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/meshoptimizer-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/meshoptimizer-m/releases/download/${tag}/meshoptimizer-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["1.3"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/meshoptimizer-m/releases/download/mwrap-1.3/meshoptimizer-m-1.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-meshoptimizer/releases/download/1.3/meshoptimizer-m-1.3.tar.gz",
},
sha256 = "5172030f7b27b757cff2945d661e12969a191b08ed8b8ce19e70220fd3654e7e",
},
},
macosx = {
["1.3"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/meshoptimizer-m/releases/download/mwrap-1.3/meshoptimizer-m-1.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-meshoptimizer/releases/download/1.3/meshoptimizer-m-1.3.tar.gz",
},
sha256 = "5172030f7b27b757cff2945d661e12969a191b08ed8b8ce19e70220fd3654e7e",
},
},
windows = {
["1.3"] = {
url = {
GLOBAL = "https://github.com/mcpplibs/meshoptimizer-m/releases/download/mwrap-1.3/meshoptimizer-m-1.3.tar.gz",
CN = "https://gitcode.com/mcpp-res/mwrap-meshoptimizer/releases/download/1.3/meshoptimizer-m-1.3.tar.gz",
},
sha256 = "5172030f7b27b757cff2945d661e12969a191b08ed8b8ce19e70220fd3654e7e",
},
},
},
}
11 changes: 11 additions & 0 deletions tests/mwrap/cgltf-inspect/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "cgltf-inspect"
version = "1.15"
description = "Consume the immutable cgltf wrapper: parse a glTF document, load and validate its buffer, read its accessors, write it and parse it again"

[indices]
# Use the complete candidate built-in index prepared by tests/run_members.sh.
# An explicit empty table overrides the legacy workspace redirect.

[dependencies]
mcpplibs.mwrap.cgltf = "1.15"
64 changes: 64 additions & 0 deletions tests/mwrap/cgltf-inspect/tests/behavior.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// The installed cgltf wrapper: a triangle whose buffer is in the document,
// parsed, loaded, validated and read; a broken document refused with its
// reason; the writer's output parsed again.
import std;
import mcpplibs.mwrap.cgltf;

namespace {

// Three positions (36 bytes of floats) and three 16-bit indices (6 bytes, padded to 44), as base64.
constexpr std::string_view triangle = R"({
"asset": {"version": "2.0"},
"scene": 0,
"scenes": [{"nodes": [0]}],
"nodes": [{"name": "a triangle", "mesh": 0, "translation": [1, 2, 3]}],
"meshes": [{"name": "tri", "primitives": [{"attributes": {"POSITION": 0}, "indices": 1, "material": 0}]}],
"materials": [{"name": "red", "pbrMetallicRoughness": {"baseColorFactor": [1, 0, 0, 1], "metallicFactor": 0.25}}],
"buffers": [{"byteLength": 44, "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAABAAIAAAA="}],
"bufferViews": [{"buffer": 0, "byteLength": 36}, {"buffer": 0, "byteOffset": 36, "byteLength": 6}],
"accessors": [
{"bufferView": 0, "componentType": 5126, "count": 3, "type": "VEC3", "min": [0, 0, 0], "max": [1, 1, 0]},
{"bufferView": 1, "componentType": 5123, "count": 3, "type": "SCALAR"}
]
})";

} // namespace

int main() {
const cgltf_options options{};
cgltf_data* data = nullptr;
if (cgltf_parse(&options, triangle.data(), triangle.size(), &data) != cgltf_result_success) return 1;
if (cgltf_load_buffers(&options, data, nullptr) != cgltf_result_success || cgltf_validate(data) != cgltf_result_success) return 2;
if (data->meshes_count != 1 || data->nodes[0].mesh != &data->meshes[0] || std::string_view(data->nodes[0].name) != "a triangle") return 3;

const cgltf_primitive& primitive = data->meshes[0].primitives[0];
const cgltf_accessor* positions = cgltf_find_accessor(&primitive, cgltf_attribute_type_position, 0);
std::array<cgltf_float, 9> corners{};
if (!positions || cgltf_accessor_unpack_floats(positions, corners.data(), corners.size()) != 9) return 4;
if (corners != std::array<cgltf_float, 9>{0, 0, 0, 1, 0, 0, 0, 1, 0}) return 5;
if (!primitive.indices || cgltf_accessor_read_index(primitive.indices, 2) != 2) return 6;
if (!primitive.material || primitive.material->pbr_metallic_roughness.metallic_factor != 0.25f) return 7;
std::array<cgltf_float, 16> world{};
cgltf_node_transform_world(&data->nodes[0], world.data());
if (world[12] != 1 || world[13] != 2 || world[14] != 3) return 8;

// The writer (mcpplibs.mwrap.cgltf.write, re-exported) gives a document that parses the same.
std::string written(cgltf_write(&options, nullptr, 0, data), '\0');
if (written.empty() || cgltf_write(&options, written.data(), written.size(), data) != written.size()) return 9;
cgltf_data* again = nullptr;
if (cgltf_parse(&options, written.data(), written.size() - 1, &again) != cgltf_result_success) return 10;
const bool same = again->accessors_count == 2 && std::string_view(again->materials[0].name) == "red" && again->nodes[0].translation[2] == 3;
cgltf_free(again);
cgltf_free(data);
if (!same) return 11;

// What is not glTF is refused with its reason.
const std::string_view broken = R"({"asset": {"version": "2.0"}, "meshes": [)";
const std::string_view past = R"({"asset": {"version": "2.0"}, "nodes": [{"mesh": 3}]})";
cgltf_data* none = nullptr;
if (cgltf_parse(&options, broken.data(), broken.size(), &none) != cgltf_result_invalid_json) return 12;
if (cgltf_parse(&options, past.data(), past.size(), &none) != cgltf_result_invalid_gltf) return 13;

std::println("cgltf 1.15 through import mcpplibs.mwrap.cgltf: a triangle of {} positions read, written in {} bytes and parsed again", corners.size() / 3, written.size() - 1);
return 0;
}
9 changes: 8 additions & 1 deletion tests/mwrap/check_installed_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"mcpplibs.mwrap.xz": ("xz.cppm", "upstream/xz/COPYING.0BSD", b"Permission to use, copy, modify", b"XZ Utils authors"),
"mcpplibs.mwrap.xxhash": ("xxhash.cppm", "upstream/xxhash/LICENSE", b"BSD 2-Clause", b"Yann Collet"),
"mcpplibs.mwrap.libarchive": ("libarchive.cppm", "upstream/libarchive/COPYING", b"Tim Kientzle", b"Tim Kientzle"),
"mcpplibs.mwrap.meshoptimizer": ("meshoptimizer.cppm", "upstream/meshoptimizer/LICENSE.md", b"MIT License", b"Arseny Kapoulkine"),
"mcpplibs.mwrap.cgltf": ("cgltf.cppm", "upstream/cgltf/LICENSE", b"Permission is hereby granted", b"Kuhlmann"),
"mcpplibs.mwrap.libc": ("libc.cppm", None, b"", b"ISO/IEC 9899:2018"),
}

Expand Down Expand Up @@ -59,7 +61,12 @@ def main():
subprocess.run(command, cwd=temporary, env=env, check=True)
project = temporary / "installed-quickstart"
manifest = tomllib.loads((project / "mcpp.toml").read_text(encoding="utf-8"))
dependency = manifest["dependencies"][identity]
# The key as written: quoted (one key) or dotted (nested tables), the form mcpp 2026.11.8.1 asks for.
dependency = manifest["dependencies"].get(identity)
if dependency is None:
dependency = manifest["dependencies"]
for part in identity.split("."):
dependency = dependency[part]
actual_version = dependency if isinstance(dependency, str) else dependency["version"]
assert actual_version == version, dependency
if isinstance(dependency, dict):
Expand Down
11 changes: 11 additions & 0 deletions tests/mwrap/meshoptimizer-simplify/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "meshoptimizer-simplify"
version = "1.3"
description = "Consume the immutable meshoptimizer wrapper: index a grid, order it for the vertex cache, simplify it, cut meshlets, and round-trip both codecs"

[indices]
# Use the complete candidate built-in index prepared by tests/run_members.sh.
# An explicit empty table overrides the legacy workspace redirect.

[dependencies]
mcpplibs.mwrap.meshoptimizer = "1.3"
Loading
Loading