Build an env dict with the venv's Scripts dir prepended to PATH. Why this exists (#2853): fbuild internally shells out to helper tools like ``zccache`` via plain PATH resolution. If the user has another ``zccache.exe`` earlier on the system PATH (e.g. ``C:\\tools\\python13\\Scripts\
()
| 98 | |
| 99 | |
| 100 | def fbuild_subprocess_env() -> dict[str, str]: |
| 101 | """Build an env dict with the venv's Scripts dir prepended to PATH. |
| 102 | |
| 103 | Why this exists (#2853): fbuild internally shells out to helper tools |
| 104 | like ``zccache`` via plain PATH resolution. If the user has another |
| 105 | ``zccache.exe`` earlier on the system PATH (e.g. |
| 106 | ``C:\\tools\\python13\\Scripts\\zccache.exe`` from a globally |
| 107 | installed ``clang-tool-chain``), it can race with the venv-installed |
| 108 | one and produce cross-version daemon corruption — the symptom that |
| 109 | motivated #2853 (5 GB daemon balloons, locked named pipes, "daemon |
| 110 | started but not accepting connections after 10s"). |
| 111 | |
| 112 | Building the env here ensures any tool fbuild's Rust binary resolves |
| 113 | via PATH picks the venv copy first. This mirrors what |
| 114 | ``get_fbuild_executable()`` already does for ``fbuild`` itself — |
| 115 | extending the same hygiene to its dependencies. |
| 116 | """ |
| 117 | env = os.environ.copy() |
| 118 | venv_scripts = Path(sys.executable).resolve().parent |
| 119 | if venv_scripts.is_dir(): |
| 120 | path_sep = os.pathsep |
| 121 | existing_path = env.get("PATH", "") |
| 122 | # Only prepend if not already first — avoids unbounded growth on |
| 123 | # repeated invocations. |
| 124 | existing_head = existing_path.split(path_sep, 1)[0] if existing_path else "" |
| 125 | if existing_head.lower() != str(venv_scripts).lower(): |
| 126 | env["PATH"] = str(venv_scripts) + ( |
| 127 | path_sep + existing_path if existing_path else "" |
| 128 | ) |
| 129 | return env |
| 130 | |
| 131 | |
| 132 | def ensure_fbuild_daemon() -> None: |
no test coverage detected