Skip to content

Commit 98650e4

Browse files
author
Sunrisepeak
committed
mcpplibs.mwrap.sheenbidi 3.0.0: sheenbidi-m (SheenBidi as C++23 modules)
Descriptor pkgs/mwrap/sheenbidi.lua (form A, the immutable mwrap-3.0.0 release asset; CN is the gitcode mcpp-res/mwrap-sheenbidi mirror with the same bytes), over compat.sheenbidi 3.0.0. Consumer tests/mwrap/sheenbidi-bidi: UAX #9's N0 bracket-pair example in an RTL paragraph, a line's visual order with mirrored brackets, the mirror and script locators, Unicode 17.0's Sidetic, the mapped macros and enumerators, and the side header beside the import. The installed-template check in validate.yml with its deployment contract (the wrapper's LICENSE/NOTICE, SheenBidi's README and LICENSE and the Unicode License v3, byte for byte in a relocated bundle), and the plan.
1 parent f392bc9 commit 98650e4

9 files changed

Lines changed: 222 additions & 0 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Add mcpplibs.mwrap.sheenbidi (sheenbidi-m 3.0.0)
2+
3+
## Form
4+
5+
- Third-party upstream [Tehreer/SheenBidi](https://github.com/Tehreer/SheenBidi), release v3.0.0 (Unicode 17.0.0),
6+
Apache-2.0. The tag archive (`86c56014…f786`) is the one compat.sheenbidi 3.0.0 pins (the parent change).
7+
- Form A external mwrap module repository [mcpplibs/sheenbidi-m](https://github.com/mcpplibs/sheenbidi-m), package
8+
`mcpplibs.mwrap.sheenbidi`, version 3.0.0 (first release of the identity, no revision suffix), tag series
9+
`mwrap-${version}`; release asset `sheenbidi-m-3.0.0.tar.gz` (`76a0b0fe…3c41`), CN mirror
10+
`gitcode.com/mcpp-res/mwrap-sheenbidi` with identical bytes.
11+
- Implementation ownership: the library is compat.sheenbidi's (a static library from upstream's unity source; its
12+
configuration and ABI are sufficient). The wrapper vendors no upstream code. It retains upstream's README.md and
13+
LICENSE under `upstream/sheenbidi/` and the Unicode License v3 under `upstream/unicode/`, with checksums in
14+
`upstream/documents.json`, and deploys them with its own LICENSE/NOTICE (`runtime.deploy`).
15+
16+
## Modules
17+
18+
- `mcpplibs.mwrap.sheenbidi`: every type (32), struct tag (11) and `SB_PUBLIC` function (51) of the headers upstream
19+
installs, by `export using`, generated from the pinned headers. The experimental text API is compiled out
20+
(compat.sheenbidi does not define `SB_CONFIG_EXPERIMENTAL_TEXT_API`, as upstream's default builds do not).
21+
- `mcpplibs.mwrap.sheenbidi.macros`: 12 object-like macros as `inline constexpr` of the type of their expansion, 16
22+
function-like macros as constexpr function templates whose body is the macro, and the 236 enumerators of the
23+
headers' unnamed enums (TU-local, so not exportable as they are) as constants of the typedef the header gives their
24+
values (`SBBidiType`, `SBGeneralCategory`, `SBScript`, `SBStringEncoding`, `SBBoolean`).
25+
- Stay macros, in `<mwrap/sheenbidi/macros.hpp>` (= `<SheenBidi/SheenBidi.h>`): `SB_PUBLIC`, `SB_DEPRECATED`,
26+
`SB_EXTERN_C_BEGIN` / `SB_EXTERN_C_END`. Not mapped: the 22 include guards.
27+
28+
`tools/check_exports.py` fails when the generated sources are stale, a declaration is not exported, an enumerator is
29+
not mapped, or a macro is neither mapped nor listed; every mapping is checked against the raw header in a
30+
header-mixing test.
31+
32+
## Verification
33+
34+
Wrapper CI (Linux x86_64 GCC 16.1 and LLVM 23.1.3, Linux arm64 GCC 16.1, macOS LLVM, Windows LLVM, all required):
35+
build, the four test programs, the export audit, and Unicode 17.0.0's conformance tests on `examples/sheenbidi`
36+
(programs written against the module), the files taken from the release and checked byte for byte against
37+
unicode.org's: BidiTest.txt (770,241 cases) and BidiCharacterTest.txt (91,707 cases, with the mirrored characters
38+
against BidiMirroring.txt), 0 errors, nothing excluded. The templates job scaffolds the default `bidi` template as an
39+
installed package and runs it. Until compat.sheenbidi is on this index's main, the wrapper's CI resolves the compat
40+
namespace from the parent change's branch (`tools/candidate_index.py`, a no-op once published).
41+
42+
Here, with the CI-pinned mcpp 2026.10.26.1:
43+
44+
- `bash tests/run_members.sh sheenbidi-bidi`: the real release archive through the candidate index; UAX #9's N0
45+
bracket-pair example, a line's visual order with mirrored brackets, the mirror and script locators, Unicode 17.0's
46+
Sidetic, the macros, and the side header beside the import: `test result ok`.
47+
- `python3 tests/mwrap/check_installed_template.py --package mcpplibs.mwrap.sheenbidi@3.0.0 --deploy`: the unmodified
48+
template from the real archive, then a relocated self-contained bundle whose `licenses/sheenbidi/` holds the
49+
wrapper's LICENSE/NOTICE, SheenBidi's README and LICENSE and the Unicode license byte for byte.
50+
51+
## Motivation
52+
53+
openmui's text engine computes bidi levels; SheenBidi through this module replaces FriBidi there, whose
54+
LGPL-2.1 does not fit a statically linked, permissively licensed framework.

‎.github/workflows/validate.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,11 @@ jobs:
16331633
shell: bash
16341634
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.fribidi@1.0.16 --deploy
16351635

1636+
- name: Installed SheenBidi wrapper archive, actual default bidi template and its relocated notices
1637+
if: contains(format(' {0} ', env.MEMBERS), ' sheenbidi-bidi ') && matrix.toolchain == 'default'
1638+
shell: bash
1639+
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.sheenbidi@3.0.0 --deploy
1640+
16361641
- name: Installed FreeType wrapper archive, actual default banner template and its relocated FTL notices
16371642
if: contains(format(' {0} ', env.MEMBERS), ' freetype-render ') && matrix.toolchain == 'default'
16381643
shell: bash

‎CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99

1010
### Added
1111

12+
- **mcpplibs.mwrap.sheenbidi 3.0.0(新包)。** sheenbidi-m:以 C++23 模块(`import mcpplibs.mwrap.sheenbidi;`)
13+
使用 compat.sheenbidi 3.0.0。导出已安装头文件的全部类型、结构体标签与 51 个 `SB_PUBLIC` 函数(上游全局名不变);
14+
`mcpplibs.mwrap.sheenbidi.macros` 把宏映射为常量与 constexpr 函数,并把无名枚举的 236 个枚举值映射为其 typedef
15+
类型的常量;`SB_PUBLIC`、`SB_DEPRECATED` 与 `extern "C"` 括号留在 `<mwrap/sheenbidi/macros.hpp>`。包装仓库的 CI
16+
(Linux x86_64 GCC/LLVM、Linux arm64、macOS、Windows)运行 Unicode 17.0.0 的 BidiTest.txt(770,241 例)与
17+
BidiCharacterTest.txt(91,707 例),0 错误。GLOBAL 为 `mwrap-3.0.0` 发布资产,CN 为 GitCode `mcpp-res/mwrap-sheenbidi`
18+
镜像,sha256 一致。新增测试成员 `tests/mwrap/sheenbidi-bidi` 与已安装模板及其许可文件的部署检查。
1219
- **compat.sheenbidi 3.0.0(新包)。** SheenBidi:Unicode 双向算法(UAX #9)的 C 实现,Apache-2.0,无依赖;
1320
3.0.0 实现 Unicode 17.0.0,通过该版本的 BidiTest.txt 与 BidiCharacterTest.txt 全部用例。以上游的单一编译单元
1421
`Source/SheenBidi.c`(`SB_CONFIG_UNITY`)从源码构建为静态库,只导出公共头文件的 51 个 `SB*` 入口;使用者的包含路径

‎mcpp.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ members = [
2222
"tests/mwrap/libpng-image",
2323
"tests/mwrap/concurrentqueue-mpmc",
2424
"tests/mwrap/fribidi-bidi",
25+
"tests/mwrap/sheenbidi-bidi",
2526
"tests/mwrap/freetype-render",
2627
"tests/mwrap/harfbuzz-shape",
2728
"tests/mwrap/sdl3-headless",

‎pkgs/mwrap/sheenbidi.lua‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-- Immutable upstream-aligned mwrap source release (sheenbidi-m mwrap-3.0.0);
2+
-- GLOBAL and CN bytes are identical.
3+
--
4+
-- The wrapper is Apache-2.0, and so is the library it links, compat.sheenbidi
5+
-- (SheenBidi 3.0.0, Unicode 17.0.0), linked statically: the package deploys
6+
-- SheenBidi's README and LICENSE and the Unicode License v3 of the data its
7+
-- tables come from beside programs (runtime.deploy).
8+
package = {
9+
spec = "1",
10+
namespace = "mcpplibs.mwrap",
11+
name = "sheenbidi",
12+
description = "sheenbidi-m: SheenBidi (the Unicode Bidirectional Algorithm, Unicode 17.0.0) as C++23 modules",
13+
licenses = { "Apache-2.0" },
14+
repo = "https://github.com/mcpplibs/sheenbidi-m",
15+
type = "package",
16+
extensions = {
17+
autoupdate = {
18+
tag = "mwrap-${version}",
19+
url = "https://github.com/mcpplibs/sheenbidi-m/releases/download/${tag}/sheenbidi-m-${version}.tar.gz",
20+
},
21+
},
22+
xpm = {
23+
linux = {
24+
["3.0.0"] = {
25+
url = {
26+
GLOBAL = "https://github.com/mcpplibs/sheenbidi-m/releases/download/mwrap-3.0.0/sheenbidi-m-3.0.0.tar.gz",
27+
CN = "https://gitcode.com/mcpp-res/mwrap-sheenbidi/releases/download/3.0.0/sheenbidi-m-3.0.0.tar.gz",
28+
},
29+
sha256 = "76a0b0fe0000ae43483b1b6e606908c09381f2d1cd8c736bff8b685c2fec3c41",
30+
},
31+
},
32+
macosx = {
33+
["3.0.0"] = {
34+
url = {
35+
GLOBAL = "https://github.com/mcpplibs/sheenbidi-m/releases/download/mwrap-3.0.0/sheenbidi-m-3.0.0.tar.gz",
36+
CN = "https://gitcode.com/mcpp-res/mwrap-sheenbidi/releases/download/3.0.0/sheenbidi-m-3.0.0.tar.gz",
37+
},
38+
sha256 = "76a0b0fe0000ae43483b1b6e606908c09381f2d1cd8c736bff8b685c2fec3c41",
39+
},
40+
},
41+
windows = {
42+
["3.0.0"] = {
43+
url = {
44+
GLOBAL = "https://github.com/mcpplibs/sheenbidi-m/releases/download/mwrap-3.0.0/sheenbidi-m-3.0.0.tar.gz",
45+
CN = "https://gitcode.com/mcpp-res/mwrap-sheenbidi/releases/download/3.0.0/sheenbidi-m-3.0.0.tar.gz",
46+
},
47+
sha256 = "76a0b0fe0000ae43483b1b6e606908c09381f2d1cd8c736bff8b685c2fec3c41",
48+
},
49+
},
50+
},
51+
}

‎tests/mwrap/check_installed_template.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,27 @@ def main():
162162
assert b"Apache License" in (roots[0] / "LICENSE").read_bytes()
163163
assert b"mcpp-community" in (roots[0] / "NOTICE").read_bytes()
164164
print(f"Verified {len(declared)} byte-identical wrapper/FriBidi license files", flush=True)
165+
elif identity == "mcpplibs.mwrap.sheenbidi":
166+
# SheenBidi is Apache-2.0 and linked statically: its LICENSE and
167+
# README, and the Unicode License v3 of the data its tables are
168+
# generated from, travel with the program beside the wrapper's.
169+
sources = [path for store in (home / "xpkgs", home / "registry/data/xpkgs")
170+
for path in store.glob(f"mcpplibs.mwrap-x-sheenbidi/{version}/*/src/sheenbidi.cppm")]
171+
assert len(sources) == 1, sources
172+
source = sources[0].parent.parent
173+
declared = tomllib.loads((source / "mcpp.toml").read_text(encoding="utf-8"))["runtime"]["deploy"]
174+
roots = list(relocated.rglob("licenses/sheenbidi"))
175+
assert len(roots) == 1, f"Missing SheenBidi runtime license tree: {roots}"
176+
for entry in declared:
177+
notice = source / entry["from"]
178+
copied = roots[0].parent.parent / entry["to"] / notice.name
179+
assert copied.is_file() and copied.read_bytes() == notice.read_bytes(), copied
180+
assert b"Apache License" in (roots[0] / "upstream/sheenbidi/LICENSE").read_bytes()
181+
assert b"UNICODE LICENSE V3" in (roots[0] / "upstream/unicode/license.txt").read_bytes()
182+
assert b"Apache License" in (roots[0] / "LICENSE").read_bytes()
183+
notice = (roots[0] / "NOTICE").read_bytes()
184+
assert b"mcpp-community" in notice and b"Muhammad Tayyab Akram" in notice
185+
print(f"Verified {len(declared)} byte-identical wrapper/SheenBidi license files", flush=True)
165186
elif identity == "mcpplibs.mwrap.freetype":
166187
# FreeType is linked statically under the FreeType License (FTL),
167188
# whose credit the documentation carries: the license selector, the
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "sheenbidi-bidi"
3+
version = "3.0.0"
4+
description = "Consume the immutable SheenBidi wrapper: UAX #9's bracket-pair example in an RTL paragraph, a line's runs in visual order, the mirror and script locators, the mapped macros and enumerators, the side header beside the import"
5+
6+
[indices]
7+
# Use the complete candidate built-in index prepared by tests/run_members.sh.
8+
# An explicit empty table overrides the legacy workspace redirect.
9+
10+
[dependencies]
11+
"mcpplibs.mwrap.sheenbidi" = "3.0.0"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import std;
2+
import mcpplibs.mwrap.sheenbidi;
3+
4+
using namespace mcpplibs::mwrap::sheenbidi::macros;
5+
6+
int main() {
7+
if (std::string_view(SBVersionGetString()) != "3.0.0") return 1;
8+
9+
// UAX #9 N0's example in an RTL paragraph: the bracket pairs resolve together.
10+
const std::u32string s = U"אב(גד[&ef]!)gh";
11+
const SBCodepointSequence seq{SBStringEncodingUTF32, s.data(), s.size()};
12+
SBAlgorithmRef algorithm = SBAlgorithmCreate(&seq);
13+
SBParagraphRef paragraph = SBAlgorithmCreateParagraph(algorithm, 0, s.size(), 1);
14+
const SBLevel* levels = SBParagraphGetLevelsPtr(paragraph);
15+
if (std::vector<int>(levels, levels + s.size()) != std::vector<int>{1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2}) return 2;
16+
17+
// The line's runs in visual order, and its mirrored brackets.
18+
SBLineRef line = SBParagraphCreateLine(paragraph, 0, s.size());
19+
std::u32string visual;
20+
const SBRun* runs = SBLineGetRunsPtr(line);
21+
for (SBUInteger r = 0; r < SBLineGetRunCount(line); ++r)
22+
for (SBUInteger k = 0; k < runs[r].length; ++k) {
23+
const SBUInteger i = runs[r].level & 1 ? runs[r].offset + runs[r].length - 1 - k : runs[r].offset + k;
24+
const SBCodepoint mirror = runs[r].level & 1 ? SBCodepointGetMirror(s[i]) : 0;
25+
visual += static_cast<char32_t>(mirror != 0 ? mirror : s[i]);
26+
}
27+
if (visual != U"gh(![ef&]דג)בא") return 3;
28+
SBMirrorLocatorRef mirrors = SBMirrorLocatorCreate();
29+
SBMirrorLocatorLoadLine(mirrors, line, s.data());
30+
int mirrored = 0;
31+
while (SBMirrorLocatorMoveNext(mirrors)) ++mirrored;
32+
SBMirrorLocatorRelease(mirrors);
33+
if (mirrored != 4) return 4;
34+
SBLineRelease(line);
35+
SBParagraphRelease(paragraph);
36+
SBAlgorithmRelease(algorithm);
37+
38+
// Scripts over UTF-8, and Unicode 17.0's Sidetic.
39+
const std::string_view utf8 = "abc \xD8\xB9\xD8\xB1\xD8\xA8\xD9\x8A";
40+
const SBCodepointSequence u8seq{SBStringEncodingUTF8, utf8.data(), utf8.size()};
41+
SBScriptLocatorRef scripts = SBScriptLocatorCreate();
42+
SBScriptLocatorLoadCodepoints(scripts, &u8seq);
43+
std::vector<SBScript> found;
44+
while (SBScriptLocatorMoveNext(scripts)) found.push_back(SBScriptLocatorGetAgent(scripts)->script);
45+
SBScriptLocatorRelease(scripts);
46+
if (found != std::vector<SBScript>{SBScriptLATN, SBScriptARAB}) return 5;
47+
if (SBCodepointGetBidiType(0x10940) != SBBidiTypeR || SBCodepointGetScript(0x10940) != SBScriptSIDT) return 6;
48+
49+
// The mapped macros, at compile time.
50+
static_assert(SBBidiTypeIsStrong(SBBidiTypeAL) && !SBCodepointIsValid(0xD800u) && SBLevelMax == 125);
51+
return 0;
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// The side header from the installed package, beside the import: the raw
2+
// header's enumerators and macros with the module's entities.
3+
#include <mwrap/sheenbidi/macros.hpp>
4+
5+
import std;
6+
import mcpplibs.mwrap.sheenbidi;
7+
8+
SB_EXTERN_C_BEGIN
9+
SB_PUBLIC int mwrap_sheenbidi_consumer(int value);
10+
SB_EXTERN_C_END
11+
int mwrap_sheenbidi_consumer(int value) { return value * 2; }
12+
13+
int main() {
14+
namespace m = mcpplibs::mwrap::sheenbidi::macros;
15+
static_assert(m::SBBidiTypeR == ::SBBidiTypeR && std::is_same_v<decltype(m::SBBidiTypeR), const SBBidiType>);
16+
if (::SBCodepointGetBidiType(0x05D0) != SBBidiTypeR) return 1;
17+
if (SBBidiTypeIsStrong(SBBidiTypeR) != (m::SBBidiTypeIsStrong)(SBBidiTypeR)) return 2;
18+
if (&::SBAlgorithmCreate != &SBAlgorithmCreate) return 3;
19+
return mwrap_sheenbidi_consumer(21) == 42 ? 0 : 4;
20+
}

0 commit comments

Comments
 (0)