Skip to content

[bug] macOS 上 dep build.mcpp 收不到 G3 契约环境(MCPP_OUT_DIR 等):capture_exec 的 shell 前缀 env 绑定到 cd 而非 build.mcpp(cd && bin 跨 && 丢 env) #248

Description

@Sunrisepeak

现象

macOS 上,消费一个带内联 ["build.mcpp"] 的依赖(consumer-side 合成源码——如 compat.opencv 合成 jpeg12/16 tu-stub、字体、OpenCL kernel)时,dep 的 build.mcpp 一起步就 abort:

error: dependency 'compat.opencvmac': build.mcpp exited with 1 (build aborted):
compat.opencv build.mcpp: MCPP_OUT_DIR unset (mcpp >= 0.0.95 required)
  build.mcpp compiling
  build.mcpp running

同一份包在 linux 上 build.mcpp 正常(MCPP_OUT_DIR 有值)。即 dep build.mcpp 拿不到 G3 契约环境(MCPP_OUT_DIR / MCPP_MANIFEST_DIR / MCPP_FEATURE_* / MCPP_DEP_*_DIR)仅发生在 macOS

根因

src/platform/process.cppmcapture_exec 按平台分叉:

  • linux(L337 #if defined(__linux__)):posix_spawn + merged_environ(extraEnv) + addchdir_np(cwd) —— 子进程 envp 正确带上 extraEnv,cwd 也对。✅
  • macOS/其它(L378 #else):
    std::string cmd = command_from_argv(argv) + " 2>&1";
    if (!cwd.empty())
        cmd = "cd " + shell::quote(cwd) + " && " + cmd;   // ← "cd <cwd> && <bin>"
    return capture_with_env(cmd, extraEnv);
    capture_with_env(L218 #else)把 env 拼成命令最前缀:
    prefixed = "KEY1='v1' KEY2='v2' ... " + command;   // = "ENV... cd <cwd> && <bin>"

最终 shell 收到:

MCPP_OUT_DIR='...' MCPP_MANIFEST_DIR='...' ... cd <cwd> && <bin> 2>&1

POSIX shell 语义:VAR=val cmd1 && cmd2VAR=val 只对 cmd1(这里是 cd)生效,不跨 && 传给 cmd2(真正的 <bin>=build.mcpp)。于是 build.mcpp 一个 MCPP_* 都收不到 → getenv("MCPP_OUT_DIR")==nullptr → abort。

build.mcpp 是唯一同时传入非空 cwd 且非空 extraEnv 的调用点(L598 capture_exec({bin}, childEnv, root));编译探针那些调用 extraEnv 为空(L295),所以此 bug 只在 macOS 的 dep build.mcpp 上暴露。linux 走 posix_spawn 无此问题;windows 走 _putenv_s(改父进程 env,子进程继承)也没问题——只有 macOS 的 shell 前缀路径炸。openblas/spdlog 等 macOS build.mcpp 之所以"没事",是它们的 build.mcpp 不读 MCPP_OUT_DIR/契约变量,静默容忍了缺失。

影响

挡死 macOS 上一切依赖 consumer-side build.mcpp 合成的源码构建包 —— compat.opencv(jpeg12/16 tu-stub + 可选 unifont 字体 + OpenCL kernel 合成)首当其冲,是 mcpp 生态 macOS 全平台的最后一道系统性障碍(FFmpeg 无 build.mcpp 已在 macos 全绿,见 mcpp-index PR#89)。

建议修复

代码里 L301-303 已有 TODO(launcher-unify)预判此场景("if macOS/Windows ever need the same child-only env isolation … unify both onto posix_spawn")。macOS 完全支持 posix_spawnp + posix_spawn_file_actions_addchdir_np(10.15+)+ environ,把 #if defined(__linux__) 扩为 #if defined(__linux__) || defined(__APPLE__)(run_execcapture_exec 两处)即可让 macOS 走与 linux 相同的直接 exec + merged_environ 路径,契约 env 正确落到子进程,cwd 也对。

最小替代方案(不改 launch primitive):macOS 分支把 env 前缀放到 cd && 之后绑定到真正的 bin——cd <cwd> && KEY='v' ... <bin> 2>&1(即 capture_exec 自己拼前缀,不再交给 capture_with_env 拼到最前)。但 posix_spawn 统一更干净且消掉 shell quoting/注入面。

复现

任意带内联 ["build.mcpp"] 且 build.mcpp 读 MCPP_OUT_DIR 的包,在 macos-15 上 mcpp test -p <member>。手边:mcpp-index 临时 spike PR#90(compat.opencvmac,headless aarch64)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions