Skip to content

增设关闭src自动扫描字段 - mcpp.toml 使用问题 #386

Description

@tz12323

workspace中子模块
cmake2mcpp_generated/spdlog/mcpp.toml

[build]
include_dirs = ['/home/tz/cmake2mcpp/spdlog-1.17.0/include', 'include']
sources = [
    'src/async.cpp',
    'src/bundled_fmtlib_format.cpp',
    'src/cfg.cpp',
    'src/color_sinks.cpp',
    'src/file_sinks.cpp',
    'src/spdlog.cpp',
    'src/stdout_sinks.cpp',
]
defines = ['SPDLOG_COMPILED_LIB']
[package]
name = 'spdlog'
version = '0.1.0'

[targets.spdlog]
cxxflags = ['-O3 -DNDEBUG']

kind = 'lib'

中包含各种宏定义
而主模块mcpp.toml

[package]
name = 'spdlog'
version = '0.1.0'

[workspace]
members = [ 'cmake2mcpp_generated/spdlog' ]

则会因为自动扫描src目录下代码,缺少宏定义导致报错。

Activity

  1. speak-agent commented on Aug 8, 2026

    @speak-agent
    Member

    感谢报告,已复现并定位。结论先说:这里缺的不是一个"关扫描"的字段,而是根包的角色没定下来——你的问题现在就有解;不过你指出的表达力缺口是真的,我们会修,只是修法和你提的不同。下面分四段:现象、参考方案、你清单里另外两处坑、以及我们打算怎么改。

    (以下结论都在本机 mcpp 2026.8.6.2 / main 上实测过,不是纸面推导。)

    1. 现在发生了什么

    根清单同时写了 [package] 和 [workspace],这在 mcpp 里是 rooted workspace(带根包的工作区),它的语义是:

    • 裸 mcpp build 只构建根包——[workspace] members 里的成员一个都不会建;
    • 而你的根目录正好压在 spdlog 上游源码树上,根包按约定扫描 src/** → 缺 SPDLOG_COMPILED_LIB → 报错。

    也就是说,现在这份配置不只是"多编了不该编的东西",而是你想建的那个成员压根没被建。最小复现:

    写法 实际结果
    根 [package] + [workspace],裸 mcpp build 编根目录的 src/** → 报错;成员未构建
    根加 sources = [] 被静默忽略,照样打印 Inferred sources […] 并失败(见第 4 段)
    根删掉 [package] ✅ Workspace building member …,正常
    mcpp build -p spdlog / --workspace ✅ 只建成员,根包不参与

    2. 参考方案

    方案 A(推荐;改一行,现在就能用)

    根清单去掉 [package],只留 [workspace]:

    [workspace]
    members = ['cmake2mcpp_generated/spdlog']

    理由:根本身没有源码、也不产出任何东西,它就是个管理节点——这正是 mcpp 的虚拟工作区(见 docs/06-workspace.md §2.2)。这样:

    • mcpp build 构建全部成员,mcpp test --workspace 测全部成员;
    • 根目录下的 src/ 不再被任何包认领,自动扫描无从触发——问题从根上消失,而不是被一个开关压住。

    对 cmake2mcpp 来说这也是更稳的生成策略:生成器不知道自己会被写进什么样的上游目录,不声明 [package] 就不会去认领脚下的任何文件。

    方案 B(仅当你需要"一条命令建出一张完整链接图"时)

    根保留 [package],但必须补 [dependencies] 把成员挂进依赖图,并且根清单不能落在别人的源码树上:

    [package]
    name    = 'spdlog-workspace'   # 注意:别和成员同名
    version = '0.1.0'
    
    [dependencies]
    spdlog = { path = 'cmake2mcpp_generated/spdlog' }
    
    [workspace]
    members = ['cmake2mcpp_generated/spdlog']

    这是 mbun 的形状(99 个成员,根包用 path 依赖聚合整张图,[workspace] members 只服务 mcpp test --workspace 的扇出)。实测:只要根目录下没有 src/,这样一个"无源码聚合根"今天就能正常工作。

    但在你的场景里根目录就是 spdlog 源码树,所以 B 在第 4 段的修复落地之前走不通——这正是我们认为你的需求成立的原因。

    ⚠️ 顺带:你现在根包名和成员包名都是 spdlog。-p spdlog 命中成员、裸 mcpp build 命中根,同名两套语义,建议错开。

    3. 你贴的成员清单里另外两处(都实测过,建议 cmake2mcpp 一并改)

    (1) [targets.spdlog] cxxflags = ['-O3 -DNDEBUG'] 现在是空转,一个字都没落地。

    per-target 的 cxxflags / cflags / defines 只作用于该 target 的 entry main(docs/05-mcpp-toml.md §2.2 有标注),而 kind = 'lib' 没有 entry。我核对了生成的 build.ninja,编译行里没有 -O3。应该写在 [build]:

    [build]
    cxxflags = ['-O3']
    defines  = ['SPDLOG_COMPILED_LIB', 'NDEBUG']

    另外 '-O3 -DNDEBUG' 是一个字符串而不是两个数组元素——Linux 上靠 shell 分词侥幸能过,但它不是一个 argv token,别依赖。

    (2) 成员的 sources 是相对「成员目录」解析的,不是相对工作区根。

    你的成员在 cmake2mcpp_generated/spdlog/,所以 sources = ['src/async.cpp'] 找的是 cmake2mcpp_generated/spdlog/src/async.cpp。如果真实源码在上游树里,现在的行为是:匹配不到任何文件 → 产出一个空的 .a → 打印 Finished,零告警。请确认这一点,否则你拿到的是一个"绿色的空包"。

    两种写法都实测可用:

    sources = ['../../src/async.cpp']                                # 相对成员目录,可以往上走
    sources = ['/home/tz/cmake2mcpp/spdlog-1.17.0/src/async.cpp']    # 绝对路径,原样采纳

    对生成器建议用绝对路径,和你已经在 include_dirs 里用绝对路径的做法一致。

    ("声明了却匹配不到任何文件"应该至少给个告警,而不是静默出空包——这条我们会补。)

    4. 关于"增设关闭 src 自动扫描字段"

    需求成立,但我们不打算新增字段,而是把已有写法修对。 具体三条:

    1. sources = [] 现在被静默忽略。 引擎只判 .empty(),分不清"写了空列表"和"根本没写",于是照样注入默认 glob,还反过来打印 Inferred sources […]——用户明确说了话,引擎当没听见。这是 bug。修完之后 sources = [] 就是字面意思:零源文件,不做任何推导。
    2. "自动扫描"其实有两个,得一起关。 除了源 glob,还有目标推导(src/main.cpp → bin、src/**/*.cppm → lib)。目前 sources = ['!src/**'] 能关掉前者(未文档化的偏方),但关不掉后者——实测根目录有 src/main.cpp 时,它仍会被推导成二进制入口并编译。所以"显式空源集"会同时关掉目标推导,否则这个开关只关了一半,反而更难排查。
    3. 不加 auto_scan = false 的理由:它和 sources = [] 是同一句话的两种说法,多一个词汇就要在解析、缓存键、文档、序列化等多处同步维护;而 sources = [] 已经是这句话最自然的写法。另外参考 mbun 的 99 份清单——0 份声明显式 sources,0 份用排除模式:布局正确时约定扫描完全够用。这个能力只在"清单被生成进别人的源码树"这一类场景(也就是 cmake2mcpp 这类转换工具)才需要,做成"显式空值生效"比做成一个常驻开关更贴切。

    已按这个方向排进修复。在它落地之前,请先用方案 A,那条路径今天就是通的。再次感谢反馈 🙏

  2. Sunrisepeak commented on Aug 8, 2026

    @Sunrisepeak
    Member
  3. Sunrisepeak commented on Aug 8, 2026

    @Sunrisepeak
    Member

    插件最近在做 mcpp.toml 里字段的语法的代码提示 后面应该能缓解很多这个问题

    https://github.com/mcpp-community/mcpp-vscode

  4. tz12323 commented on Aug 8, 2026

    @tz12323
    Author

    根据你的推荐,我修改了根mcpp.toml

    [dependencies.spdlog]
    path = 'cmake2mcpp_generated/spdlog'
    
    [package]
    name = 'spdlog-workspace'
    version = '0.1.0'
    
    [workspace]
    members = [ 'cmake2mcpp_generated/spdlog' ]
    

    子toml保持不变,但是此时报错,找不到宏定义

    #error Please define SPDLOG_COMPILED_LIB to compile this file.
    

    但是,如果将根mcpp.toml改为您提及的A做法时,则可以正常构建.但是无法作为包被其他项目依赖

    [workspace]
    members = [ 'cmake2mcpp_generated/spdlog' ]
    
    mcpp run
       Resolving toolchain
        Resolved llvm@22.1.8 → @mcpp/registry/data/xpkgs/xim-x-llvm/22.1.8/bin/clang++
    error: path dependency 'spdlog' (at '/home/tz/test/spdlog') has no mcpp.toml
    
    
  5. Sunrisepeak commented on Aug 8, 2026

    @Sunrisepeak
    Member

    根据你的推荐,我修改了根mcpp.toml

    [dependencies.spdlog]
    path = 'cmake2mcpp_generated/spdlog'
    
    [package]
    name = 'spdlog-workspace'
    version = '0.1.0'
    
    [workspace]
    members = [ 'cmake2mcpp_generated/spdlog' ]
    

    子toml保持不变,但是此时报错,找不到宏定义

    #error Please define SPDLOG_COMPILED_LIB to compile this file.
    

    但是,如果将根mcpp.toml改为您提及的A做法时,则可以正常构建.

    从贴的两个 toml 结构推测 是一个项目 spdlog

    • 方法一: 只要保留一个mcpp.toml
    [package]
    name = 'spdlog'
    version = '0.1.0'
    
    [build]
    include_dirs = ['/home/tz/cmake2mcpp/spdlog-1.17.0/include', 'include']
    defines = ['SPDLOG_COMPILED_LIB']
    sources = [
        'src/async.cpp',
        'src/bundled_fmtlib_format.cpp',
        'src/cfg.cpp',
        'src/color_sinks.cpp',
        'src/file_sinks.cpp',
        'src/spdlog.cpp',
        'src/stdout_sinks.cpp',
    ]
    
    [targets.spdlog]
    cxxflags = ['-O3 -DNDEBUG']
    
    kind = 'lib'
    
    • 方法二: 还是使用 workspace 机制 顶层只保留
    [workspace]
    members = [ 'cmake2mcpp_generated/spdlog' ]
    

    感觉最核心的还是你的意图是什么 / 以及可以上传一个临时仓库 贴一下链接可能才知道具体项目结构是什么样 (以上都是我根据你描述推论的不准确)

  6. Sunrisepeak commented on Aug 8, 2026

    @Sunrisepeak
    Member

    结合

    #388

    以及

    如果将根mcpp.toml改为您提及的A做法时,则可以正常构建.但是无法作为包被其他项目依赖

    保留单个 toml 应该就可以了

    
    [package]
    name = 'spdlog'
    version = '0.1.0'
    
    [build]
    include_dirs = ['/home/tz/cmake2mcpp/spdlog-1.17.0/include', 'include']
    defines = ['SPDLOG_COMPILED_LIB']
    sources = [
        'src/async.cpp',
        'src/bundled_fmtlib_format.cpp',
        'src/cfg.cpp',
        'src/color_sinks.cpp',
        'src/file_sinks.cpp',
        'src/spdlog.cpp',
        'src/stdout_sinks.cpp',
        'cmake2mcpp_generated/cmake2mcpp_generated.cpp' 
    ]
    
    [targets.spdlog]
    cxxflags = ['-O3 -DNDEBUG']
    
    kind = 'lib'
    
    
  7. tz12323 commented on Aug 8, 2026

    @tz12323
    Author

    我的想法是这样的,为了避免cmake项目中的源文件重叠,我希望能够利用workspace机制,将不同的bin, lib, shared, 拆分为不同的toml, 避免宏定义, 源文件的混杂.
    对cmakefileapi文件分析后的期望项目结构,如下图所示
    Image
    其目的就是把编译项目分开,并且能够正常通过dependence被其他项目依赖

  8. Sunrisepeak commented on Aug 8, 2026

    @Sunrisepeak
    Member

    如果是一个库, 使用 单 mcpp.toml 加 feature 机制。具体 把mcpp文档链接 给 Agent 然后描述你的需求 就可以

  9. changed the title [-]增设关闭src自动扫描字段[/-] [+]增设关闭src自动扫描字段 - mcpp.toml 使用问题[/+] on Aug 8, 2026
  10. speak-agent commented on Aug 31, 2026

    @speak-agent
    Member

    这条 issue 要的能力已经有了:sources = [] 现在生效,写下它就等于「什么都不要编」。可以关闭。

    为什么这不是新增一个「关闭扫描」的键

    当时的答复是「缺的不是一个关扫描的字段,而是根包的角色没定下来」—— 那半对半错。根包角色的部分是对的(rooted workspace 的语义,以及 Sunrisepeak 后来给的单 toml 方案)。错的是把表达力缺口推给了配置形态:解析器在向量为空时一律填默认 glob,所以作者没有任何写法能表达「这个包不编译任何东西」。你在第 4 段实测到的「根加 sources = [] 被静默忽略,照样打印 Inferred sources […]」正是这个。

    修法是记键有没有出现,而不是向量空不空 —— BuildConfig::sourcesDeclared,与 XlingsConfig::subosDeclared 同一个模式。modules/manifest/src/toml.cppm:

    // `!sourcesDeclared` and not `sources.empty()`: an author who wrote
    // `sources = []` asked for NOTHING, and filling the default glob over that
    // answers a question they already answered.
    if (!m.buildConfig.sourcesDeclared) {
        m.buildConfig.sources = mcpp::default_source_globs(extTable);
        ...
    }

    真正把它逼出来的是二进制分发:一个纯头文件的包不编译任何东西,而 src/ 下任何遗留文件都会被扫进消费者的构建,并可能与预编译库里的符号重复定义。你的 cmake2mcpp 场景和它是同一个需求的两个面。

    顺带修掉的第二条

    同一轮核验发现默认 glob 有两份且已经漂移(一处 4 项、一处 7 项),于是走多版本 staging 的包当时会漏掉汇编源。现在两处都从唯一的 default_source_globs(extTable) 派生,并且这份表由 module_extensions 决定 —— 否则声明 .ixx 会改变它被如何对待却不改变它是否被找到,那个键就等于什么都没做。

    你原来那个工程

    单 mcpp.toml 的形态(Sunrisepeak 在上面给的)仍然是首选。你图里那种「按 bin / lib / shared 拆成多个 toml」的结构现在也更好写了:[workspace.package] 与 [workspace.build] 的继承在 2026.8.30.2 落地,成员可以从工作区根继承 standard / defines / include_dirs 而不必每份清单重写一遍,并且继承会到达作为兄弟成员的 path 依赖被编译的那个成员(#527 / #539,docs/06-workspace.md 中英双份)。

    如果按这条路走下来还有表达不出来的形状,另开一条 issue 贴出目录结构,比在这条下面接着长更容易被排期。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions