Skip to content

feat(ftxui): a modules feature for 7.0.3's named modules - #331

Merged
Sunrisepeak merged 1 commit into
mcpplibs:mainfrom
cloud-teahouse:feat/ftxui-modules
Sep 1, 2026
Merged

feat(ftxui): a modules feature for 7.0.3's named modules#331
Sunrisepeak merged 1 commit into
mcpplibs:mainfrom
cloud-teahouse:feat/ftxui-modules

Conversation

@yspbwx2010

Copy link
Copy Markdown
Contributor

What

FTXUI 7.0.0 added named modules beside the headers it has always shipped: an ftxui
umbrella that re-exports four sub-modules — ftxui.component, ftxui.dom,
ftxui.screen and ftxui.util. #292 brought 7.0.3 into the index but left those
units out of the build, after an attempt to compile them unconditionally went red on
the linux gcc leg. This adds them back behind an opt-in modules feature, with a
test member, and with a correction to what that red leg was actually telling us.

The descriptor change

The feature adds one glob, */src/ftxui/*.cppm, and nothing else. Those five files
are precisely the list in upstream's cmake/ftxui_modules.cmake, so the set tracks
the release rather than a transcription of it.

The units carry no definitions. Each sub-module textually includes the matching
public headers in its global module fragment and then re-exports the names with
export namespace ftxui { using ... }; the umbrella is four export import lines.
The module surface therefore layers on top of the same libftxui.a the default build
already produces rather than replacing it — both surfaces live in one archive and a
consumer picks either. Measured on the built package, that is 81 compiled units with
the feature and 76 without, a difference of exactly the five module units; the base
source set does not move.

That shape is also why the feature needs no include_dirs of its own: the
#include <ftxui/...> in those global module fragments resolve through the
package-level */include, which mcpp applies to the package's own translation units
as well as to consumers.

Two mechanism edges are worth naming, because the feature was shaped to avoid them.
A ! exclusion in mcpp is global and out-ranks a feature entry naming the same file,
so "exclude in the base, add back in the feature" is not expressible; here it never
arises, because *.cppm cannot be matched by the base **/*.cpp globs and the
feature is a pure addition. And a feature cannot carry include_dirs, which as above
is not needed.

modules in the descriptor is the declared export set, in the same spelling every
other module package in this index uses. It is worth being clear about what it does
not buy: mcpp validates [modules].exports against the scanner only for the primary
manifest of a build, never for a dependency's, so nothing in that list is enforced at
a consumer's build. Measured, by deleting ftxui.util from it and rebuilding with the
package cache bypassed — which built and passed. It is documentation, not a guard.

What #292's gcc leg was actually reporting

#292's summary reads that GCC 16 cannot consume the module units. Reproducing it says
something narrower, and the difference matters enough to correct here.

The failure is in the CONSUMER's translation unit, not in the package. Each sub-module
puts the public headers — and transitively libstdc++ — into a global module fragment.
A consumer TU that writes import ftxui; and then also textually #includes a
standard header hands gcc two copies of the standard library's declarations, and gcc
16 rejects that at volume:

c++config.h:355:15: error: redefinition of 'void std::__terminate()'
memoryfwd.h:68:11:  error: conflicting declaration of template 'template<class> struct std::allocator'
stringfwd.h:55:12:  error: conflicting declaration of template 'template<class _CharT> struct std::char_traits'

That is what the reverted commit's smoke TU did — import ftxui; above
#include <string> and #include <gtest/gtest.h> — and the errors above are the same
three, at the same file and line, that its workspace (linux default) job printed
before the ~16k that followed. clang accepts the mixed TU, which is exactly why llvm,
macOS and windows stayed green while only the gcc leg failed.

Kept off the textual surface, the module units are fine on gcc. A consumer that pairs
import std; with import ftxui; and includes nothing textually builds and runs on
gcc 16.1.0 and llvm 22.1.8 alike, on both the mcpp version CI pins and the current
release. So the feature is not llvm-only; it carries a consumer-side rule, and that
rule is the same discipline tests/examples/asio-module already documents for its own
module consumers.

Upstream's own module CI is llvm-only — test_modules in .github/workflows/build.yaml
is a one-entry ubuntu plus llvm matrix carrying # TODO add gcc / msvc, and
ftxui_modules.cmake still forces -fmodules-ts under CMAKE_COMPILER_IS_GNUCXX
above a bare # TODO: Explain why this is needed. So gcc is untested upstream, which
is worth knowing before leaning on the combination hard. It is not, as far as this
index can measure, broken.

Why it is still off by default

Not because it fails anywhere, but because it is an addition nobody should pay for
unasked: five extra translation units and their BMIs for every consumer of 7.0.3, most
of whom want the headers they already use. Upstream defaults FTXUI_BUILD_MODULES to
OFF for the same reason, and the consumer-side rule above is something only the
consumer can honour.

On 6.1.9 the feature's glob matches nothing, since there are no .cppm files before
7.0.0. That is a warning rather than an error — the same union-of-layouts tolerance
compat.catch2 and compat.redis-plus-plus already rely on.

The test member

tests/examples/ftxui-module consumes the feature and asserts it: import ftxui;
with a real render, plus each of the four sub-modules imported alone in a translation
unit of its own, so nothing leans on the umbrella or on a header. Every TU stays on
the module surface, which is the property the member exists to guard as much as the
imports themselves; the file says so, at length, next to the reason.

Because the units build under both compilers, the member needs no toolchain pin and
runs on both linux legs as an ordinary member. tests/examples/core keeps covering
the default header surface at the same version with no feature requested.

Verification

Local, against this checkout, on mcpp 2026.8.27.2 — the version CI pins — with
2026.9.1.1 agreeing throughout.

The member, on both of CI's linux toolchains:

ftxui-module   llvm@22.1.8   5 passed; 0 failed
ftxui-module   gcc@16.1.0    5 passed; 0 failed

The umbrella, rendering an hbox of two text elements around a separator laid out
with Dimension::Fit, and each sub-module alone:

umbrella      ... ok   rendered: [compat│ftxui]
sub_dom       ... ok   dom rendered: [dom-only/────────/row2]
sub_screen    ... ok   screen: [X   /    Y]        (Screen/Pixel/Color/_rgb/string_width)
sub_component ... ok   clicked=1                   (Component/Event/Button, event routed)
sub_util      ... ok                               (Ref/ConstRef/StringRef/AutoReset)

The negative direction, which is the one that decides whether the feature is a feature
at all — the same import ftxui; source with the feature not requested:

umbrella ... FAIL (compile)
tests/umbrella.cpp:5:8: fatal error: module 'ftxui' not found
    5 | import ftxui;
      | ~~~~~~~^~~~~

The mixed-TU rule, in both directions, on the shape the reverted commit used:

import ftxui; + #include <string>   gcc@16.1.0   FAILED  (16681 errors, signature above)
import ftxui; + #include <string>   llvm@22.1.8  ok

The existing header path, unchanged, with no feature requested, and with the feature
on since the two surfaces share one archive:

7.0.3  no feature    Cached compat.ftxui v7.0.3 (76 units)   rendered: [compat│ftxui]   ok
7.0.3  modules on    Cached compat.ftxui v7.0.3 (81 units)   rendered: [compat│ftxui]   ok
6.1.9  no feature    Compiling compat.ftxui v6.1.9           rendered: [compat│ftxui]   ok

And mcpp test -p core stays green.

Lints

check_mirror_urls, check_package_name, check_platform_version_parity,
check_duplicate_versions and check_cross_package_refs all pass, and
mcpp xpkg parse is clean over all 175 descriptors with 2026.8.27.2, so no index
floor bump is needed.

FTXUI 7 ships upstream's own module units — an `ftxui` umbrella re-exporting
ftxui.component/.dom/.screen/.util — and mcpplibs#292 left them out after an
unconditional attempt went red on the linux gcc leg. They come back here
behind an opt-in `modules` feature whose source list is exactly upstream's
cmake/ftxui_modules.cmake: five .cppm, a pure addition that cannot collide
with the base **/*.cpp globs, so the default build does not move (76 compiled
units without the feature, 81 with).

What that gcc leg was reporting is narrower than "GCC 16 cannot consume these
modules". The sub-modules put the public headers, and transitively libstdc++,
into a global module fragment, so a consumer TU that writes `import ftxui;`
AND textually #includes a standard header hands gcc two copies of the standard
library and it refuses — redefinition of std::__terminate, conflicting
declaration of std::allocator / std::char_traits. mcpplibs#292's smoke TU did exactly
that, and the only object that failed there was that TU's own (obj/module.o),
after the package's five module units had already compiled. clang accepts the
mixed TU, which is why only gcc went red.

Kept off the textual surface, the units build and run on gcc 16.1.0 and
llvm 22.1.8 alike, so tests/examples/ftxui-module needs no toolchain pin and
runs on both linux legs: `import ftxui;` with a real render, plus each
sub-module imported alone, every TU on the module surface and not one textual
include.

The `modules` export list is documentation, not a guard: mcpp validates
[modules].exports only for a build's PRIMARY manifest, never for a
dependency's — measured by deleting one name and rebuilding with the package
cache bypassed, which passed.

Verified (mcpp 2026.8.27.2, the version CI pins): ftxui-module 5 passed on
both toolchains; `mcpp test -p core` green; the same import with the feature
off fails with `module 'ftxui' not found`; 6.1.9 and the 7.0.3 header path
unaffected; check_mirror_urls, check_package_name,
check_platform_version_parity, check_duplicate_versions,
check_cross_package_refs and `mcpp xpkg parse` over all 175 descriptors green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Sunrisepeak
Sunrisepeak merged commit fb4b324 into mcpplibs:main Sep 1, 2026
11 checks passed
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.

2 participants