feat: add fmt 12.2.0 as C++23 module package (import fmt;)#63
Conversation
Module sibling of the header-based compat.fmt: the public `fmt` package (empty namespace) exposes the C++23 module `fmt` so users write `import fmt;`. fmt's release ships an official module unit at src/fmt.cc, but mcpp's M1 scanner rejects any `import` inside a conditional preprocessor block (even a dead one), and fmt.cc guards `import std;` behind `#ifdef FMT_IMPORT_STD`. So the descriptor reproduces fmt.cc via generated_files with exactly those three lines removed — everything else verbatim, including the tail `#include "format.cc"/"os.cc"` that attaches the implementation to module fmt. include_dirs exposes */include and */src so both the module unit and its impl includes resolve. Empty-namespace packages can't be workspace members (like the imgui module package), so a thin driver tests/smoke_fmt_module.sh reseeds the default index and consumes `import fmt;`, wired into validate.yml as the fmt-module job. Verified locally on mcpp 0.0.81 (== CI): the smoke builds module fmt and its behavioral assertions pass. No mcpp-res write access, so the download url is a plain upstream string (CN mirror to be added by a maintainer). Co-Authored-By: Claude <noreply@anthropic.com>
| modules = { "fmt" }, | ||
| include_dirs = { "*/include", "*/src" }, | ||
| generated_files = { | ||
| ["mcpp_generated/fmt_module.cc"] = "// Formatting library for C++ - C++20 module\n//\n// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors\n// All rights reserved.\n//\n// For the license information refer to format.h.\n\nmodule;\n\n#define FMT_MODULE\n\n#ifdef _MSVC_LANG\n# define FMT_CPLUSPLUS _MSVC_LANG\n#else\n# define FMT_CPLUSPLUS __cplusplus\n#endif\n\n// Put all implementation-provided headers into the global module fragment\n// to prevent attachment to this module.\n#ifndef FMT_IMPORT_STD\n# include <algorithm>\n# include <bitset>\n# include <chrono>\n# include <cmath>\n# include <complex>\n# include <cstddef>\n# include <cstdint>\n# include <cstdio>\n# include <cstdlib>\n# include <cstring>\n# include <ctime>\n# include <exception>\n# if FMT_CPLUSPLUS > 202002L\n# include <expected>\n# endif\n# include <filesystem>\n# include <fstream>\n# include <functional>\n# include <iterator>\n# include <limits>\n# include <locale>\n# include <memory>\n# include <optional>\n# include <ostream>\n# include <source_location>\n# include <stdexcept>\n# include <string>\n# include <string_view>\n# include <system_error>\n# include <thread>\n# include <type_traits>\n# include <typeinfo>\n# include <utility>\n# include <variant>\n# include <vector>\n#else\n# include <limits.h>\n# include <stdint.h>\n# include <stdio.h>\n# include <stdlib.h>\n# include <string.h>\n# include <time.h>\n#endif\n#include <cerrno>\n#include <climits>\n#include <version>\n\n#if __has_include(<cxxabi.h>)\n# include <cxxabi.h>\n#endif\n#if defined(_MSC_VER) || defined(__MINGW32__)\n# include <intrin.h>\n#endif\n#if defined __APPLE__ || defined(__FreeBSD__)\n# include <xlocale.h>\n#endif\n#if __has_include(<winapifamily.h>)\n# include <winapifamily.h>\n#endif\n#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \\\n defined(__linux__)) && \\\n (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))\n# include <fcntl.h>\n# include <sys/stat.h>\n# include <sys/types.h>\n# ifndef _WIN32\n# include <unistd.h>\n# else\n# include <io.h>\n# endif\n#endif\n#ifdef _WIN32\n# if defined(__GLIBCXX__)\n# include <ext/stdio_filebuf.h>\n# include <ext/stdio_sync_filebuf.h>\n# endif\n# define WIN32_LEAN_AND_MEAN\n# include <windows.h>\n#endif\n\nexport module fmt;\n\n\n#define FMT_EXPORT export\n#define FMT_BEGIN_EXPORT export {\n#define FMT_END_EXPORT }\n\n// If you define FMT_ATTACH_TO_GLOBAL_MODULE\n// - all declarations are detached from module 'fmt'\n// - the module behaves like a traditional static library, too\n// - all library symbols are mangled traditionally\n// - you can mix TUs with either importing or #including the {fmt} API\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\nextern \"C++\" {\n#endif\n\n#ifndef FMT_OS\n# define FMT_OS 1\n#endif\n\n// All library-provided declarations and definitions must be in the module\n// purview to be exported.\n#include \"fmt/args.h\"\n#include \"fmt/chrono.h\"\n#include \"fmt/color.h\"\n#include \"fmt/compile.h\"\n#include \"fmt/format.h\"\n#if FMT_OS\n# include \"fmt/os.h\"\n#endif\n#include \"fmt/ostream.h\"\n#include \"fmt/printf.h\"\n#include \"fmt/ranges.h\"\n#include \"fmt/std.h\"\n#include \"fmt/xchar.h\"\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\n}\n#endif\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\nextern \"C++\" {\n#endif\n\n#if FMT_HAS_INCLUDE(\"format.cc\")\n# include \"format.cc\"\n#endif\n#if FMT_OS && FMT_HAS_INCLUDE(\"os.cc\")\n# include \"os.cc\"\n#endif\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\n}\n#endif\n", |
There was a problem hiding this comment.
这个可以使用多行语法展开 不用使用 \n 放到单行。类似下面
generated_files = {
["mcpp_generated/nlohmann.json.cppm"] = [[
// L1
// L2
// ....
]],
},
There was a problem hiding this comment.
尝试用多行语法展开遇到如下报错:
Workspace building member 'tests/examples/fmtlib.fmt'
Resolving toolchain
Resolved gcc@16.1.0 → @mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
error: dependency 'fmtlib.fmt': xpkg-lua://fmtlib.fmt@12.2.0: error: malformed mcpp segment near key 'Formatting'
是否是因为mcpp不支持lua的多行展开语法
|
这个62PR 很规范, 建议 本PR直接 作为支持模块化的 fmt 库 然后 添加类似 62的 tests 最小测试工程。就可以省略 62 compat 模式了 |
|
最新版本好像默认就是支持 模块化的 不用额外 使用 generated_files 好像 |
|
#ifdef FMT_IMPORT_STD 包描述文件直接定义这个宏就可以了 |
Replace the shell smoke driver for the {fmt} module package with a native
workspace member under tests/examples/fmtlib.fmt, matching the compat.* /
nlohmann.json members. The empty-namespace `fmt` package resolved through the
builtin default index, which a member can't point at a local path — so the
package is renamed to the `fmtlib` namespace (fmtlib.fmt), letting the member
route the dependency to this repo via `[indices] fmtlib = { path }`. The
exported module is still plain `fmt` (import fmt;). Drops tests/
smoke_fmt_module.sh and its fmt-module CI job; `mcpp test --workspace` now
covers it.
Co-Authored-By: Claude <noreply@anthropic.com>
The generated fmt_module.cc now `#define FMT_IMPORT_STD` (right after FMT_MODULE) so fmt's own `#ifndef FMT_IMPORT_STD` block takes the import-std path — dropping the ~40 `#include <algorithm>`…`<vector>` lines — and the `#ifdef FMT_IMPORT_STD` guard around `import std;` is removed so the import is unconditional at module top level (which M1's text scanner accepts). Sets import_std = true so mcpp builds the std module first. Faster to compile than the header-include variant; behavior unchanged (format_test passes). Co-Authored-By: Claude <noreply@anthropic.com>
|
环境:mcpp v0.0.84 — modern C++23 build tool 遇到如下问题 agent分析定位 基于当前现状,采取的做法是 |
| modules = { "fmt" }, | ||
| include_dirs = { "*/include", "*/src" }, | ||
| generated_files = { | ||
| ["mcpp_generated/fmt_module.cc"] = "// Formatting library for C++ - C++20 module\n//\n// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors\n// All rights reserved.\n//\n// For the license information refer to format.h.\n\nmodule;\n\n#define FMT_MODULE\n\n#define FMT_IMPORT_STD\n\n#ifdef _MSVC_LANG\n# define FMT_CPLUSPLUS _MSVC_LANG\n#else\n# define FMT_CPLUSPLUS __cplusplus\n#endif\n\n// Put all implementation-provided headers into the global module fragment\n// to prevent attachment to this module.\n#ifndef FMT_IMPORT_STD\n# include <algorithm>\n# include <bitset>\n# include <chrono>\n# include <cmath>\n# include <complex>\n# include <cstddef>\n# include <cstdint>\n# include <cstdio>\n# include <cstdlib>\n# include <cstring>\n# include <ctime>\n# include <exception>\n# if FMT_CPLUSPLUS > 202002L\n# include <expected>\n# endif\n# include <filesystem>\n# include <fstream>\n# include <functional>\n# include <iterator>\n# include <limits>\n# include <locale>\n# include <memory>\n# include <optional>\n# include <ostream>\n# include <source_location>\n# include <stdexcept>\n# include <string>\n# include <string_view>\n# include <system_error>\n# include <thread>\n# include <type_traits>\n# include <typeinfo>\n# include <utility>\n# include <variant>\n# include <vector>\n#else\n# include <limits.h>\n# include <stdint.h>\n# include <stdio.h>\n# include <stdlib.h>\n# include <string.h>\n# include <time.h>\n#endif\n#include <cerrno>\n#include <climits>\n#include <version>\n\n#if __has_include(<cxxabi.h>)\n# include <cxxabi.h>\n#endif\n#if defined(_MSC_VER) || defined(__MINGW32__)\n# include <intrin.h>\n#endif\n#if defined __APPLE__ || defined(__FreeBSD__)\n# include <xlocale.h>\n#endif\n#if __has_include(<winapifamily.h>)\n# include <winapifamily.h>\n#endif\n#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \\\n defined(__linux__)) && \\\n (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))\n# include <fcntl.h>\n# include <sys/stat.h>\n# include <sys/types.h>\n# ifndef _WIN32\n# include <unistd.h>\n# else\n# include <io.h>\n# endif\n#endif\n#ifdef _WIN32\n# if defined(__GLIBCXX__)\n# include <ext/stdio_filebuf.h>\n# include <ext/stdio_sync_filebuf.h>\n# endif\n# define WIN32_LEAN_AND_MEAN\n# include <windows.h>\n#endif\n\nexport module fmt;\n\nimport std;\n\n#define FMT_EXPORT export\n#define FMT_BEGIN_EXPORT export {\n#define FMT_END_EXPORT }\n\n// If you define FMT_ATTACH_TO_GLOBAL_MODULE\n// - all declarations are detached from module 'fmt'\n// - the module behaves like a traditional static library, too\n// - all library symbols are mangled traditionally\n// - you can mix TUs with either importing or #including the {fmt} API\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\nextern \"C++\" {\n#endif\n\n#ifndef FMT_OS\n# define FMT_OS 1\n#endif\n\n// All library-provided declarations and definitions must be in the module\n// purview to be exported.\n#include \"fmt/args.h\"\n#include \"fmt/chrono.h\"\n#include \"fmt/color.h\"\n#include \"fmt/compile.h\"\n#include \"fmt/format.h\"\n#if FMT_OS\n# include \"fmt/os.h\"\n#endif\n#include \"fmt/ostream.h\"\n#include \"fmt/printf.h\"\n#include \"fmt/ranges.h\"\n#include \"fmt/std.h\"\n#include \"fmt/xchar.h\"\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\n}\n#endif\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\nextern \"C++\" {\n#endif\n\n#if FMT_HAS_INCLUDE(\"format.cc\")\n# include \"format.cc\"\n#endif\n#if FMT_OS && FMT_HAS_INCLUDE(\"os.cc\")\n# include \"os.cc\"\n#endif\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\n}\n#endif\n", |
There was a problem hiding this comment.
这个是不是可以省略, 源码里好像已经有对应模块化的代码了 src/fmt.cc 具体见下面
| generated_files = { | ||
| ["mcpp_generated/fmt_module.cc"] = "// Formatting library for C++ - C++20 module\n//\n// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors\n// All rights reserved.\n//\n// For the license information refer to format.h.\n\nmodule;\n\n#define FMT_MODULE\n\n#define FMT_IMPORT_STD\n\n#ifdef _MSVC_LANG\n# define FMT_CPLUSPLUS _MSVC_LANG\n#else\n# define FMT_CPLUSPLUS __cplusplus\n#endif\n\n// Put all implementation-provided headers into the global module fragment\n// to prevent attachment to this module.\n#ifndef FMT_IMPORT_STD\n# include <algorithm>\n# include <bitset>\n# include <chrono>\n# include <cmath>\n# include <complex>\n# include <cstddef>\n# include <cstdint>\n# include <cstdio>\n# include <cstdlib>\n# include <cstring>\n# include <ctime>\n# include <exception>\n# if FMT_CPLUSPLUS > 202002L\n# include <expected>\n# endif\n# include <filesystem>\n# include <fstream>\n# include <functional>\n# include <iterator>\n# include <limits>\n# include <locale>\n# include <memory>\n# include <optional>\n# include <ostream>\n# include <source_location>\n# include <stdexcept>\n# include <string>\n# include <string_view>\n# include <system_error>\n# include <thread>\n# include <type_traits>\n# include <typeinfo>\n# include <utility>\n# include <variant>\n# include <vector>\n#else\n# include <limits.h>\n# include <stdint.h>\n# include <stdio.h>\n# include <stdlib.h>\n# include <string.h>\n# include <time.h>\n#endif\n#include <cerrno>\n#include <climits>\n#include <version>\n\n#if __has_include(<cxxabi.h>)\n# include <cxxabi.h>\n#endif\n#if defined(_MSC_VER) || defined(__MINGW32__)\n# include <intrin.h>\n#endif\n#if defined __APPLE__ || defined(__FreeBSD__)\n# include <xlocale.h>\n#endif\n#if __has_include(<winapifamily.h>)\n# include <winapifamily.h>\n#endif\n#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \\\n defined(__linux__)) && \\\n (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))\n# include <fcntl.h>\n# include <sys/stat.h>\n# include <sys/types.h>\n# ifndef _WIN32\n# include <unistd.h>\n# else\n# include <io.h>\n# endif\n#endif\n#ifdef _WIN32\n# if defined(__GLIBCXX__)\n# include <ext/stdio_filebuf.h>\n# include <ext/stdio_sync_filebuf.h>\n# endif\n# define WIN32_LEAN_AND_MEAN\n# include <windows.h>\n#endif\n\nexport module fmt;\n\nimport std;\n\n#define FMT_EXPORT export\n#define FMT_BEGIN_EXPORT export {\n#define FMT_END_EXPORT }\n\n// If you define FMT_ATTACH_TO_GLOBAL_MODULE\n// - all declarations are detached from module 'fmt'\n// - the module behaves like a traditional static library, too\n// - all library symbols are mangled traditionally\n// - you can mix TUs with either importing or #including the {fmt} API\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\nextern \"C++\" {\n#endif\n\n#ifndef FMT_OS\n# define FMT_OS 1\n#endif\n\n// All library-provided declarations and definitions must be in the module\n// purview to be exported.\n#include \"fmt/args.h\"\n#include \"fmt/chrono.h\"\n#include \"fmt/color.h\"\n#include \"fmt/compile.h\"\n#include \"fmt/format.h\"\n#if FMT_OS\n# include \"fmt/os.h\"\n#endif\n#include \"fmt/ostream.h\"\n#include \"fmt/printf.h\"\n#include \"fmt/ranges.h\"\n#include \"fmt/std.h\"\n#include \"fmt/xchar.h\"\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\n}\n#endif\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\nextern \"C++\" {\n#endif\n\n#if FMT_HAS_INCLUDE(\"format.cc\")\n# include \"format.cc\"\n#endif\n#if FMT_OS && FMT_HAS_INCLUDE(\"os.cc\")\n# include \"os.cc\"\n#endif\n\n#ifdef FMT_ATTACH_TO_GLOBAL_MODULE\n}\n#endif\n", | ||
| }, | ||
| sources = { "mcpp_generated/fmt_module.cc" }, |
There was a problem hiding this comment.
使用库自带的模块化代码 src/fmt.cc 并且 使用 宏进行开启。应该可以避免 generated_files
mcpp = {
...
cxxflags = { "-DFMT_IMPORT_STD" },
}
There was a problem hiding this comment.
还是报同样的错,根因在于mcpp 的 M1 模块扫描器是预处理之前的纯文本扫描,它根本不求值 #ifdef。所以:
- -DFMT_IMPORT_STD 对扫描器毫无作用;
- 上游 fmt.cc:104 的 import std; 被包在 #ifdef FMT_IMPORT_STD 里,扫描器只看到「条件块里有 import」就直接报错。
Sunrisepeak
left a comment
There was a problem hiding this comment.
目前mcpp版本对宏支持待优化, 该版本暂先合入。后面mcpp更新最新版本后 再进行优化包描述
The generated module wrapper was `mcpp_generated/fmt_module.cc`. On macOS/Windows mcpp drives Clang in gnu mode (`--driver-mode=g++`), where Clang decides "is this a module interface unit?" purely from the file extension: `.cppm`/`.ixx` yes, `.cc` no. A `.cc` module unit still compiles (only the fopen deprecation warning), but Clang silently drops `-fmodule-output` as an unused argument, so no BMI (`fmt.pcm`) is ever written — the consumer's `import fmt;` then fails with `fatal error: module 'fmt' not found`. GCC (Linux) keys off `export module` rather than the extension, so `.cc` happened to work there and masked the bug; nlohmann.json already uses a `.cppm` wrapper, which is why it passed on all three platforms. Rename both the generated_files key and sources to `fmt_module.cppm` to match. Verified end-to-end on Windows (clang 20.1.7, MSVC STL): fmt.pcm is now produced and format_test passes. import std is unrelated and retained. Co-Authored-By: Claude <noreply@anthropic.com>
|
根据上一轮CI结果报告另一项缺陷 现象CI(以及本地 Windows)构建 Linux 通过,macOS 与 Windows(两者都是 Clang)失败。 根因mcpp 生成的模块封装文件原先命名为 在 macOS/Windows 上,mcpp 以 GNU 驱动模式( 因此,对 结果 BMI( 修复在 descriptor 中把封装文件从 - ["mcpp_generated/fmt_module.cc"] = "…"
+ ["mcpp_generated/fmt_module.cppm"] = "…"
- sources = { "mcpp_generated/fmt_module.cc" }
+ sources = { "mcpp_generated/fmt_module.cppm" }已在 Windows 上端到端验证: 建议的上游跟进(mcpp)这是 mcpp 自身的一个陷阱:当某个包通过 |

概述
新增公开的
fmt包(空命名空间),将 {fmt} 12.2.0 以 C++23 模块形态收录,用户可直接
import fmt;。它是 header 版compat.fmt的模块孪生包,两者消费模型不同、可共存。
形态:module wrapper(generated_files)
fmt 的 release 自带官方模块单元
src/fmt.cc(export module fmt;),但不能逐字喂给 mcpp:mcpp 的 M1 模块扫描器拒绝任何位于条件预处理块内的
import(即使是不启用的死分支),而 fmt.cc 把
import std;包在#ifdef FMT_IMPORT_STD里。该限制在 mcpp 0.0.81 / 0.0.82 / main 均存在,非版本问题。
因此描述符用
generated_files复刻 fmt.cc,仅删除那 3 行(#ifdef/import std;/#endif),其余逐字保留:#include(std 头挂到全局模块)export module fmt;(mcpp 据此注册 provider)export)#include "format.cc"/"os.cc"(把实现附着到模块 fmt;若作为独立 TU编译会留在全局模块,破坏模块链接)
include_dirs暴露*/include(模块单元的#include <fmt/*.h>可解析)与*/src(末尾 impl include 可解析)。import_std = false(默认走文本 std 头)。CI
空命名空间包无法作为 workspace 成员(同公开的
imgui包:空命名空间映射到mcpp 内建默认索引,
[indices]无法把默认索引重定向到本地 path)。故新增tests/smoke_fmt_module.sh—— reseed 默认索引后消费import fmt;,在validate.yml中作为fmt-modulejob 运行(与imgui-module同构)。镜像
无 mcpp-res 写权限,下载 url 采用纯上游字符串(lint 对纯字符串 url 放行),
CN 镜像待维护者后续补充。
验证
mcpp 0.0.81(与 CI
MCPP_VERSION一致)本地跑 smoke:构建模块fmt并通过行为断言。
本地复现 CI lint 全过,validate.yml YAML 校验通过。