Skip to content

[Part A] build: XRGUI under mcpp, beside xmake (Windows / MSVC, Linux and macOS / clang, with CI) - #11

Open
Sunrisepeak wants to merge 16 commits into
base/upstream-masterfrom
pr/mcpp-build-support
Open

[Part A] build: XRGUI under mcpp, beside xmake (Windows / MSVC, Linux and macOS / clang, with CI)#11
Sunrisepeak wants to merge 16 commits into
base/upstream-masterfrom
pr/mcpp-build-support

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Part A / 3(Part B:#10 文档,Part C:#12 resize 崩溃修复)。第二条构建链,与 xmake 并存;基于上游 master(base/upstream-master = 6d63715),16 个 commit,按「上游能单独采纳哪一段」拆。

commit 与对 xmake 的影响

# commit 内容 对 xmake
1 fix(msvc) 14.52 不再泄漏的名字,显式写出 修好了它:未改动的 master 在自家 CI 装的 toolset 上编不过
2 refactor 删掉 XRGUI_FUCK_MSVC_INCLUDE_CPP_HEADER_IN_MODULE 无,删的是没走的分支
3 build 让消费者提供单头实现(两个宏) 无,xmake 不定义它们
4 ci 配置 xmake workflow 里构建敏感的东西 只动 workflow
5 port Linux / clang 22 / libc++ 和 MSVC 14.52 都接受的标准 C++;两个 shim 模块(mo_yanxi.views / mo_yanxi.functional)加进 xmake 的源码集 xmake.lua 一行 add_files;源码改动对 MSVC 等价
6 fix(vulkan) 不再强制要求 VK_KHR_maintenance9:树里没有任何使用,而它让 Linux 上所有设备都被拒 运行期行为:接受更多设备
7 fix(glfw) 窗口创建时隐藏,vk::context 在首帧 present 后显示(两条 present 路径都调);之前 ~650 ms 是一块没画过的矩形 运行期行为,两条链一样
8 build(mcpp) mcpp.tomlbuild.mcppmcpp/ 下三个 submodule 的 path 包和补丁、图标工具 mcpp/svg_outline、Windows 图标 纯新增;render_context.cpp / showcase main.cppXRGUI_SHADER_SURFACE#else 分支是原代码
9 ci(mcpp) mcpp-windows.yml(两条腿:机器上的 VS、mcpp 自装的 toolset)、mcpp-linux.yml 只加 workflow
10 port(macos) arm64 macOS / clang 22 拒绝的三件事,都只对 Apple 生效:rect_ortho.ixx 的 SSE 代码在补丁里得到标量回退;SDK assert() 里的 __builtin_expect 被 clang 22 在模块之间报成歧义,mcpp/darwin/assert.h 影子掉它(build.mcpp 只在 macOS 加这个 -I);cxx_runtime = "host-coupled" 从全局收窄到 MSVC 三元组(仍是 /MD),Linux / macOS 用默认值静态链接工具链的 libc++ 无:补丁只在 mcpp 构建时应用,其余两处是 mcpp 的文件
11 ci(mcpp) macOS 腿:Unix workflow 变成 Linux + macOS 两台机器的矩阵,manifest 为两者配置同一个 llvm@22.1.8 只动 workflow
12 test src.tests/ 改为 mcpp 约定的 tests/,每个文件一个程序,mcpp test 直接跑;gtest 走 [dev-dependencies],main feature 即 gtest_main,共用的 main.cpp 删掉,xrgui_tests target 和 tests feature 一并删掉 xmake.lua:add_files("tests/**.cpp"),gtest 加 main = true 拿同一个入口
13 fix(graphic) instruction_buffer 的四个访问器去掉 CONST_FN([[gnu::const]]):它们读 storage_,clang -O2 把 resize 前的 data() 复用到 resize 后,showcase 首个 draw push 写空指针。MSVC 上这个宏为空,所以那边从未暴露 无:MSVC 上是空宏
14 refactor(cfg) render_context::load_shader(name) 成为取 xrgui 自己着色器的唯一入口:XRGUI_SHADER_SURFACE 下读编入的数组,否则读 .spv;renderer bundle 和 showcase 都走它,showcase 的 main.cpp 不再碰宏和生成头,才能作为独立包(成员、任何消费者)构建 两条链共用;xmake 下语义不变(仍读 .spv)
15 build(mcpp) xrgui 是库;src.hellosrc.examples 就是 workspace 成员(各自目录里一份 mcpp.toml,入口还是原来的 main.cpp,没有文件搬家),path 依赖 xrgui:mcpp run -p src.hello / -p src.examplesmcpp build --workspace、成员目录下 mcpp pack。bin target、图标、Windows 子系统从根 manifest 移到成员;examples feature 保留,由 showcase 成员请求,仍排除成员的 main.cpp;成员各带 release 的 NDEBUG(profile 不继承)
16 build(mcpp) showcase 在 dev 构建下要的 validation layer 来自生态:根 manifest 的 [target.linux.xlings.workspace] 声明 xim:vulkan-validation-layers(when = "run",只在 mcpp run / mcpp test 时安装,不进产物,Linux 上有这个包);mcpp run -p src.examples 不再依赖机器上的 Vulkan SDK,也不加载宿主的 layer

本地验证(Linux,clang 22):mcpp build --workspacemcpp test(5 个程序 54 个用例)、成员目录下 mcpp pack(tar、AppImage)都通过;mcpp run -p src.hellomcpp run -p src.examples --release 都开窗渲染(showcase 截图确认,FPS 3500+)。[profile.release] 加了 -DNDEBUG,和 xmake 的 mode.release 一致,showcase 的 validation layer 开关靠它。

已知(不在本 PR 修):showcase 拖动窗口大小时约一半概率 VK_ERROR_DEVICE_LOST,根因是 context::get_extent() 用 GLFW 缓存的窗口尺寸而不是 swapchain 尺寸,修复在 Part C(#12)。另外 sync validation 报 renderer 上传路径 vkCmdCopyBuffer 之间的 WRITE_AFTER_WRITE,以及 compositor 里 vkCmdSetEvent2 用了需要 maintenance8 的 dependencyFlags(本机驱动不支持该扩展),都是上游可以单独看的。

Review 后的收紧(本轮):port 提交里两处运行期改动拆成自己的 commit(第 6、7 行);源码里的注释压缩到说明"哪个编译器、报什么、为什么是这个形状"的几句(-270 行);mcpp-windows.yml 从 448 行减到 190 行,去掉了历史叙述、PYTHONUTF8 和一步手动安装的 Vulkan SDK(compat.vulkan 已经提供);build.mcpp 的文件头和一处过期路径更正,补丁文件缺失时报错而不是静默;path 包 manifest 里 23 行的历史注释压到 5 行。

mcpp 这条链的形状:机器上只装 mcpp,编译器、Vulkan、Slang、每个第三方库由 mcpp.toml 声明并自动提供;着色器由 mcpp.rules.slang 逐个编成构建边并嵌入二进制;图标由仓库自带的 C++ 工具做几何展开(替代原来 python + node + potrace 描摹);运行期数据、Windows 图标、GUI 子系统、AppImage / MSI 打包都是 manifest 里的数据。build.mcpp 只做 mcpp 没有对应键的事(submodule 补丁、图标汇总头)。

平台:Windows / MSVC 14.52 是参考;Linux / clang 22 构建、54 个测试通过、tar 和 AppImage 产出;macOS(arm64,同一个 clang)构建、54 个测试通过;src/platform/__APPLE__ 分支(CoreText、线程名)是原代码,port 见第 11 行;打包暂不做(mcpp 内建的闭包遍历还不处理 Mach-O 程序)。

CI:四个 mcpp job(Windows 两腿、Linux、macOS)加上游的 xmake job 都要绿。

已知:Windows 上 mcpp 构建的 GUI 程序是 /SUBSYSTEM:WINDOWS,stdout 不再接终端。从仓库外把 xrgui 作为 path 依赖消费时,clang 下会碰到 mcpp 的一个引擎缺陷(依赖的 module_extensions 没有用于扫描边),已单独跟进,与本仓无关。

Unmodified master does not compile with the toolset its own workflow
installs. Measured, not inferred: a probe branch carrying one markdown file
and nothing else fails on 14.52.36629 with

    typesetting.rich_text.argument.ixx: error C2039:
        'vec2': is not a member of 'mo_yanxi::math'

plus the C4430 / C3646 / C2440 cascade behind it, and no other file in the
tree reports anything.

The file uses math::vec2 and nothing it already imports re-exports it:
graphic.color re-exports math.vector4, and vector4 only plain-imports
mo_yanxi.math. It built on 14.52.36510 because names leaked across the module
boundary there; 36629 does not leak them. So the import is written down.

Nothing to do with the rest of this branch — this is the one change
unmodified master needs today, and it is worth taking on its own.
…for it

xmake.lua asked for this: "msvc 新版好像没这问题了,哪天删了,,,".

The macro chose between `#include <gch/small_vector.hpp>` and
`import <gch/small_vector.hpp>;` in sixteen files, and add_xrgui_core_deps()
defined it — so xmake was taking the HEADER UNIT branch, and this commit
keeps the `#include` one. That is not a no-op, and the three source changes
here are its price rather than independent fixes:

A header unit's declarations are reachable to whoever imports the module; a
global module fragment's are discarded unless the purview names them. gch
spells the container's operator== and its iterator's operator-(a, b) as free
function templates rather than hidden friends, so once the header moves into
the GMF, an importer has neither.

  typesetting.rich_text.ixx     operator== written out, comparing features
                                through data()/size() rather than the
                                container. A defaulted one is synthesized at
                                each odr-use — in every module that compares a
                                layout_config — and fails there. Writing the
                                body is not enough on its own: gch::operator==
                                is itself a template and instantiates at the
                                call, dragging std::equal and its need for
                                operator-(a, b) along. `const hb_feature_t*`
                                has none of that.
  typesetting.ixx               assign(data(), data() + size()) for the same
                                reason: vector's range constructor sizes the
                                allocation with operator-(a, b).
  typesetting.segmented_layout  includes the header itself, rather than
                                hoping to inherit it from rich_text.

That these are consequences and not pre-existing breakage is measured: the
baseline probe (unmodified master, macro on) reports no C2243 and nothing out
of <xutility>, and its log shows small_vector.hpp.ifc — the header unit doing
exactly the job this commit gives up.

WHAT IT BUYS. Two things. The macro's live branch was the one nobody had
exercised in a long time — the CI on this branch is what says the surviving
one compiles. And header units are one of the two constructs mcpp's scanner
cannot follow, which a conditional does not help with: it refuses an `import`
inside one whether or not the branch is live.

The hardest part to place was not the fix but the report: cl blames the
template definition (ui.util.ixx:140) while the instantiation is in
text_edit.ixx, label.ixx, and one more for every future comparison — so
fixing it at the importers is whack-a-mole.
Two headers are compiled here by defining an implementation macro before
including them. Both become a problem when the library comes from a package
that already did it, so both get an opt-out — and NEITHER macro is defined by
xmake, so the xmake build is unchanged byte for byte.

  XRGUI_MINIAUDIO_IMPL_PROVIDED   MINIAUDIO_IMPLEMENTATION
      compat.miniaudio compiles upstream's own miniaudio.c. Instantiating it
      here as well defines every ma_* symbol twice — and only at LINK time,
      which is a long way from the cause.

  XRGUI_MSDFGEN_NO_CPP11          MSDFGEN_USE_CPP11
      compat.msdfgen is built with it OFF and says so through the
      msdfgen-config.h that every public header includes — which is precisely
      how that package and its consumers agree on which declarations exist.
      Defining it here anyway breaks that agreement one-sidedly:
      Contour::addEdge(EdgeHolder&&) gets declared for this TU and exists
      nowhere, surfacing as an unresolved external. xrepo's msdfgen IS built
      with it, so xmake keeps the define.

Consumer-supplied rather than auto-detected: whether the implementation is
already compiled is a property of the build that provides the library, and
only that build knows it.
Four changes to the xmake workflow, all about it answering the same question
twice in a row. None of them is about mcpp.

  runs-on: windows-2025      `windows-latest` currently maps to the VS 2026
      image — currently. This build is sensitive to the toolchain, so it says
      which one it wants.

  assert VCToolsVersion      `-vcvars_ver=14.52` is a request; nothing checked
      the answer. Dropping to the image's 14.51 is a real failure (C1001 ICE
      in template.cpp at vector2.ixx:67, "IFC import detected") and is
      presumably why this workflow reached for a preview at all — so a 14.51
      answer now fails the job. The exact build number moves whenever
      aka.ms/vs/18/insiders does, which is Microsoft's schedule and not a
      reason to fail a commit: that is a ::warning:: and the run continues,
      while staying the first thing to suspect when this job reddens on an
      unrelated change.

  concurrency + cancel       Each run installs VS 2026 Insider and the Vulkan
      SDK before compiling anything. A superseded run is a very long slot
      spent on an answer nobody wants.

  pull_request (any base)    It only ran for PRs targeting master, so a PR
      into a topic branch got no build at all.
@Sunrisepeak Sunrisepeak changed the title build: XRGUI under mcpp, beside xmake (Windows / MSVC and Linux / clang, with CI) [Part A] build: XRGUI under mcpp, beside xmake (Windows / MSVC, Linux and macOS / clang, with CI) Sep 12, 2026
@Sunrisepeak
Sunrisepeak force-pushed the pr/mcpp-build-support branch 3 times, most recently from aa3c2a5 to 561b2fb Compare September 12, 2026 20:19
Standard C++ where MSVC was lenient, in the shape the errors named:

  * explicit imports where a name arrived through another module's
    reachability; an int passed for a float non-type template parameter;
    a lambda odr-using an enclosing constexpr; operator== of another
    module's global module fragment; std::vector::erase required noexcept
    (MSVC's library adds it, libc++ does not); a deduced return type used
    before its definition during instantiation.
  * two facilities libc++ has not shipped -- views::stride / slide and
    move_only_function -- and range_const_reference_t, routed through two
    module interfaces added beside the mcpp manifest, mo_yanxi.views and
    mo_yanxi.functional, which resolve to std:: where the library has them.
    xmake compiles them too; MSVC's library has everything, so there they
    are using-declarations.
  * gtl's instantiation moved into the translation unit that includes the
    header, for clang only: MSVC 14.52 loses the class's member template
    at the use site when the specialisation is explicitly instantiated in
    the interface.
  * a coroutine owned by two objects, and a union initialised through a
    conversion MSVC 14.52 tries brace elision on first.

Examples and tests build on Linux (clang 22, libc++) and 54 tests pass.
The reference build stays Windows / MSVC 14.52.
Nothing in the tree uses it (no VkPhysicalDeviceMaintenance9Features, no
call gated on it), and requiring it rejected every device on a Linux
machine -- neither a current NVIDIA driver nor llvmpipe reports it -- so
the application exited before opening a window. Commented out beside its
neighbours; put it back with the code that needs it.
GLFW maps a window as soon as it is created, about 650 ms before this
project draws into it; under a compositor that is a transparent
rectangle that suddenly fills in. window::show() is idempotent and
vk::context calls it after each successful present, on both of its
present paths, so every application -- the hello, the showcase, a
consumer's -- gets it without a call of its own.
A second build system that changes nothing for the first: mcpp.toml is a
transcription of xmake.lua, and xmake.lua is untouched by this commit.

What mcpp gets told, and what it is left to do itself:

  * the source set, defines, include directories and system libraries as
    data in mcpp.toml; every third-party library from the index, pinned;
    the three submodules as path packages under mcpp/, each manifest a
    transcription of that repository's own xmake.lua;
  * the shaders as build-graph edges: mcpp.rules.slang compiles every
    .slang into the binary, and the two consumers reach them through the
    generated header under XRGUI_SHADER_SURFACE (the #else branches are the
    xmake code, verbatim);
  * the icons through mcpp/svg_outline, a one-file tool this repository
    builds: a stroked icon becomes a filled one geometrically (one capsule
    contour per segment; msdfgen's overlap support does the union) instead
    of being rasterised and traced back by a node package;
  * runtime data through [runtime] deploy, the Windows icon through
    [resources], the GUI subsystem through windows_subsystem, the
    distributables through mcpp:plugins' appimage and wix members.

build.mcpp keeps what mcpp has no key for: two submodule patches (standard
C++ for clang, applied before the module scan; their home is upstream) and
the icon summary header. No tool is looked up on PATH.

Known cost: on Windows the GUI programs no longer attach to a parent
console, so their stdout is not visible there.
Windows: the same VS 2026 Insider toolset (14.52) build_and_dispatch.yml
installs, so the two workflows differ in the build system and nothing
else. Two legs, because `msvc@system` and `msvc@<toolset>` are different
questions: the system leg proves an explicit VSINSTALLDIR outranks the
vswhere probe on a runner that has two Visual Studios; the managed leg has
no Visual Studio at all and builds with the content-pinned payload the
manifest names. Both pack a zip and an MSI.

Linux: the clang the manifest pins, the tests run, a vendored tarball and
an AppImage. Each job installs one thing, mcpp, and asserts its version.
@Sunrisepeak
Sunrisepeak force-pushed the pr/mcpp-build-support branch 2 times, most recently from 233bf85 to 7471ad4 Compare September 13, 2026 07:50
Three things, all scoped to Apple:

- rect_ortho.ixx includes <immintrin.h> and uses SSE unconditionally; the
  submodule patch gains a hunk that keys the intrinsics on SSE being
  available and gives the overlap test a scalar fallback, which is what
  an arm64 build gets.

- The SDK's assert() expands to __builtin_expect. Named in a module
  purview, clang 22 attaches the builtin to that module, and a unit that
  imports two such modules and asserts itself is told the call is
  ambiguous. mcpp/darwin/assert.h shadows the SDK header on this
  package's own -I (build.mcpp adds it on macOS only) and redefines the
  macro without the hint; same __assert_rtn, same NDEBUG behaviour.

- cxx_runtime = "host-coupled" was project-wide, for /MD on MSVC. On
  macOS it means the system libc++, which is older than the clang 22
  headers and lacks symbols they reference (__hash_memory, the atomic
  wait table). The key moves to the MSVC triple; Linux and macOS take
  mcpp's default, the toolchain's libc++ linked statically. Linux was
  already linked that way, so nothing changes there.
The Unix workflow becomes a two-row matrix (ubuntu-24.04, macos-15); the
manifest configures the same llvm@22.1.8 for macOS as for Linux. Packing
stays Linux-only: mcpp's closure walk does not handle Mach-O yet.
src.tests/ becomes tests/: `mcpp test` builds every file there into its
own program and runs it, with gtest as a [dev-dependencies] entry whose
`main` feature is gtest_main -- so the shared main.cpp goes, and with it
the xrgui_tests target, the tests feature and its feature-deps. xmake's
single xrgui.tests binary takes the same entry from its gtest package's
`main` config. CI runs `mcpp test` in place of build-then-find-binary.
CONST_FN on size()/begin()/data()/end() told clang the result depends on
`this` alone; they read storage_, which resize_and_overwrite replaces.
At -O2 the data() taken before a resize was reused after it, and the
showcase's first draw push memcpy'd into a null pointer (SIGSEGV in
contiguous_draw_list::push). MSVC defines the macro empty, so the
release build never showed it there. Found by running the release
showcase on Linux.
render_context::load_shader("post_process.bloom") -- and the free
load_builtin_shader behind it -- is now the only place that knows where
a shader comes from: the arrays mcpp.rules.slang compiled in under
XRGUI_SHADER_SURFACE, a .spv beside the program otherwise. The renderer
bundle and the showcase both go through it, so the showcase's main.cpp
carries no macro and no generated header, which is what lets it be
built as a separate package (a workspace member, or any consumer):
the macro and the header are private to xrgui.
xrgui is the library; src.hello and src.examples, the directories the
two programs already live in, become packages of their own that depend
on it by path, so the commands are mcpp's: `mcpp run -p src.hello`,
`mcpp run -p src.examples`, `mcpp build --workspace`, and `mcpp pack`
from a member's directory. The bin targets, the exe icon and the Windows
subsystem leave the root manifest for the members; the `examples`
feature stays, requested by the showcase member, and still excludes the
member's main.cpp. Each member carries the release profile's NDEBUG,
which is not inherited. Nothing moves: xmake's targets read the same
files they always did.
…er's runs

A debug build asks the loader for VK_LAYER_KHRONOS_validation, and the
host's copy is not reachable from the glibc an mcpp-built program runs on
(found by manifest, lost at dlopen). The root manifest now names
xim:vulkan-validation-layers at the `run` tier: installed for `mcpp run`
and `mcpp test`, never part of an artifact, inherited by the two
application members and by template projects through their dependency
on xrgui. `mcpp run -p showcase` in the dev profile runs with validation
on a machine that has no Vulkan SDK, and touches nothing on the host.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant