
在 Windows Subsystem for Linux 上运行 Compiler ExplorerWSL 互操作限制、MSVC 托管与调试实战【免费下载链接】compiler-explorerRun compilers interactively from your web browser and interact with the assembly项目地址: https://gitcode.com/gh_mirrors/co/compiler-explorer本文以 Compiler Explorer下称 CE官方文档 WindowsSubsystemForLinux.md 为核心骨架结合仓库内 WSL 适配源码与 MSVC 配置实例系统讲解如何在 WSL 中构建、启动并调试 CE重点剖析 WSL 与 Windows 进程互操作的五大限制以及托管 Microsoft Visual CMSVC编译器所需的路径转换、WSLENV环境传递与cl19配置方法。读完本文你将掌握一套可在本地 WSL 环境完整复现的 CE 部署与 MSVC 接入方案并理解 CE 为此在源码层面做了哪些针对性改动。为什么选择在 WSL 上运行 CEWSLWindows Subsystem for Linux为 CE 提供了一种鱼与熊掌兼得的运行形态CE 的核心服务运行在 Linux 环境中Linux 原生编译器GCC、Clang 等可以像在真实 Linux 服务器上一样以原生性能持续运行而 Windows 编译器尤其是cl.exe则通过 WSL 的进程互操作能力在真正的 Windows 环境中执行。正如文档所言这种架构使Linux 编译器继续原生运行Windows 编译器在真实 Windows 环境中运行成为可能。CE 在 WSL 下运行不需要任何特殊配置只有托管 MSVC 编译器时才需要额外配置。官方测试主要在 Ubuntu 发行版上进行但文档明确说明任何发行版理论上都应可用。WSL 与 Windows 进程互操作的五大限制WSL 提供了丰富的互操作能力——你可以从 bash shell 直接运行任何 Windows 可执行文件如cl.exe。但这种互操作存在以下已被 CE 开发者在实践中验证过的限制理解它们是在 WSL 上用好 CE 的前提1. Windows 卷Volume隔离虽然 Windows 可执行文件可以从 bash 启动但它们看不到 Linux 卷。需要读写文件的 Windows 可执行文件必须运行在 Windows 卷上。这意味着所有 MSVC 编译都必须在 Windows 的%TEMP%目录中进行而不是 bash 环境的临时目录。2. 路径差异PathWSL 的 bash 环境将 Windows 路径前置到 PATH 中。Linux 文件系统支持空格、括号等奇怪命名Windows 路径同样习以为常地使用它们例如c:\Program Files (x86)。此外Windows 路径分隔符是\而非/并使用带冒号分隔的盘符而非挂载点。3. 路径名转换Path NamesWindows 路径c:\tmp在 bash 中通常写作/mnt/c/tmp。但用户可能自定义drvfs挂载点因此新版本 Windows 提供了工具/bin/wslpath用于双向转换。CE 当前采用字符串操作在两种惯例之间转换见后文wsl-vc.ts源码解析这是一个值得注意的实现选择。4. 环境变量隔离Environment Variables尽管 Windows 路径在 bash 中可用但Windows 环境变量在 bash 中并不可见。CE 通过调用cmd.exe /c echo %TEMP%来确定 Windows 临时目录该逻辑如今已抽离到 lib/app/temp-dir.ts 中稍后详解。5. 执行环境无法注入Execution Environment在childprocess.spawn时无法设置执行环境这对高度依赖环境的 MSVC 编译器是个严重问题——MSVC 依赖%INCLUDE%、%LIBPATH%等环境变量来定位头文件和库文件。CE 对此的缓解方案是借助WSLENV机制见下文。在 WSL 中安装与启动 CE文档为初入 Linux 的 WSL 用户提供了详细的引导步骤。若打算调试 CE建议将 CE 仓库克隆到 Windows 卷如/mnt/c/...以确保调试工具链的兼容性。CE 构建在 Node.js 之上最便捷的安装方式是通过 NVMNode Version Manager。在 bash shell 中依次执行apt-get update # 确保 apt 索引是最新的 apt-get install build-essential libssl-dev # 构建工具链通常已安装 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash # 安装 NVM版本号请查阅 NVM 最新 release source ~/.profile # 重新加载 profile使 NVM 进入当前环境 nvm ls-remote --lts # 查看最新的 Node.js LTS 版本 nvm install 10.15.3 # 安装指定版本可替换为最新 LTS说明上文的 Node 版本号仅为文档编写时的示例请以nvm ls-remote --lts实际输出的最新 LTS 版本为准。随后进入 CE 克隆目录执行make。make会安装大量 node 依赖包最终输出类似以下信息info: info: Listening on http://localhost:10240/ info: serving static files from static info: git release bbf1407109d0439199f71bfdf4037fdeb0eb8393 info: 此时打开浏览器访问http://localhost:10240即可看到属于你自己的 CE 实例。源码级解析CE 为 WSL 做的适配改动文档Code changes一节罗列了 CE 为支持 WSL 所做的几处关键改动。结合当前仓库源码这些改动的实现细节如下它们共同构成了完整的 WSL 适配链路。WSL 环境检测uname -a中的 MicrosoftCE 通过检查uname -a的输出是否包含字符串 Microsoft 来判断自身是否运行在 WSL 中。这一判断如今封装在 lib/app/cli.ts 的detectWsl()函数中仅当process.platform linux时才执行检测执行uname -a并将输出转为小写后检查是否包含microsoft检测失败异常时仅记录警告并返回false。由于所有 WSL 发行版都运行在微软的基础 Linux 内核之上因此该检测对所有 WSL 发行版均有效。检测结果通过appArgs.isWsl传入应用最终在 app.ts 中设置process.env.wsl true供后续各模块读取。临时目录适配从 Windows%TEMP%到/mnt/盘符WSL 检测到之后CE 需要确定一个 Windows 可执行文件可用的临时目录。lib/app/temp-dir.ts 的setupTempDir(tmpDir, isWsl)实现了完整逻辑显式指定若命令行提供了-tmpDir即tmpDir参数则直接使用该值并同时设置TMPDIR、TMP、TEMP三个环境变量以确保所有被派生进程保持一致若设置后os.tmpdir()未生效则抛出错误。WSL 自动探测在 WSL 下且未显式指定时调用cmd.exe /c echo %TEMP%获取 Windows 临时目录将反斜杠替换为斜杠提取盘符与路径后转换为/mnt/盘符小写/路径格式例如/mnt/c/Users/.../AppData/Local/Temp并导出若调用cmd.exe失败仅记录警告后回退到 Linux 临时目录。警告文档特别提醒——如果-tmpDir被指定为非 Windows 卷Windows 可执行文件将无法正常运行。这是因为 Windows 进程看不到 Linux 卷上的目录。这一实现与文档中os.tmpdir() 被设置为 Windows %TEMP% 目录当 CE 能从 WSL 调用 cmd.exe 获取该路径时的描述完全对应。编译执行的工作目录lib/exec.tslib/exec.ts 是 CE 执行外部进程编译器、工具等的公共模块。其中针对 WSL 的关键逻辑是第 119 行const cwd options.customCwd || (command.startsWith(/mnt) process.env.wsl ? os.tmpdir() : undefined);含义为在 WSL 环境中当被执行的命令路径以/mnt开头即位于 Windows 卷上的可执行文件时将子进程的工作目录cwd强制设为 CE 的临时目录——即前文适配过的 Windows%TEMP%对应挂载路径。这正是文档所述在临时目录中执行编译器的实现确保 Windows 编译器在读写文件时处于 Windows 卷上而不是无法感知的 Linux 卷。在 WSL 上托管 MSVC 编译器wsl-vc.ts深度剖析托管 MSVC 是 WSL 场景下最复杂的部分。CE 为此提供了专属编译器类 lib/compilers/wsl-vc.tsWslVcCompilerkey 为wsl-vc它继承自 lib/compilers/win32-vc.ts 中的Win32VcCompiler并重写了三个关键方法。文档指出它与wine-vc.tsWine 版本相对二者为同一编译器提供了不同宿主下的定制行为。filename()Linux 目录 → Windows 目录override filename(fn: string, tmpDirForTest?: string) { const tmpdir tmpDirForTest || os.tmpdir(); const driveLetter unwrap(tmpdir).substring(5, 6); const directoryPath unwrap(tmpdir).substring(7); const windowsStyle driveLetter.concat(:/, directoryPath); return fn.replace(unwrap(tmpdir), windowsStyle); }该函数假定os.tmpdir()的格式为/mnt/X/dirX 为盘符取第 5 个字符作为盘符、第 7 个字符起作为目录路径拼出X:/dir形式的 Windows 路径并将文件路径中的 Linux 挂载前缀替换为 Windows 形式。这样CL.exe才能定位到输入文件。这是文档所述CompileCl 函数将 Linux 风格目录翻译为 Windows 风格目录/mnt/c/tmp→c:/tmp的当前实现形态。exec()通过WSLENV传递环境变量override exec(compiler: string, args: string[], options_: ExecutionOptions) { const options Object.assign({}, options_); options.env Object.assign({}, options.env); let old_env options.env[WSLENV]; if (old_env) { old_env : old_env; } else { old_env ; } options.env[WSLENV] INCLUDE:LIB old_env; return super.exec(compiler, args, options); }WSLENV是 WSL 提供的共享环境变量列表机制声明后即可在 Linux 与 Windows 进程之间传递环境变量。这里将INCLUDE和LIB追加进WSLENV保留原有条目使 bash 侧设置的INCLUDE/LIB能被cl.exe感知部分缓解了执行环境无法注入的限制。这也印证了文档对wsl-vc.ts的描述——MSVC 高度依赖%INCLUDE%、%LIBPATH%等环境变量。runCompiler()反向路径转换override async runCompiler(compiler, options, inputFilename, execOptions?) { if (!execOptions) execOptions this.getDefaultExecOptions(); // inputFilename 保证是 Windows 格式如 c:/但 Node.js 需要 Unix 语法 const inputDirectory path.dirname(inputFilename); const driveLetter inputDirectory.substring(0, 1).toLowerCase(); const directoryPath inputDirectory.substring(2).trim(); execOptions.customCwd path.join(/mnt, driveLetter, directoryPath); return await super.runCompiler(compiler, options, inputFilename, execOptions); }与filename()相反这里将 Windows 格式的输入目录如C:/...反向转换为/mnt/盘符/路径的 Unix 挂载形式供 Node.js 侧使用例如传递给 lib/exec.ts 的customCwd。配置层面cl19与 properties 文件文档提到在 etc/config/c.defaults.properties 中为 MSVC 编译器添加了cl19配置配置系统详见 Configuration.md并坦率地指出了当时该配置存在的两处问题路径被硬编码到特定安装位置详见下文MSVC setup%INCLUDE%通过/I开关传递由于 WSL 启动 Windows 进程时不传递环境当时只能以命令行参数/I形式注入 include 路径这非常笨拙一旦超出命令行长度限制就会失败。文档同时强调这两处问题不影响主 CE 实例因为它使用amazon系列 properties 文件也不影响本地运行者因为 CE 找不到编译器时只会静默失败。在实际的生产配置中MSVC 编译器配置位于 etc/config/c.amazonwin.properties 等 Windows 专用文件中。以cl19系列为例其中exwine后缀表示经 Wine 运行的 MSVCgroup.vcpp_x64.compilers...:cl19_64_exwine:cl_new_64_exwine:... compiler.cl19_64_exwine.exeZ:/compilers/msvc-legacy-from-wine/19.10.25017/lib/native/bin/amd64/cl.exe compiler.cl19_64_exwine.libPathZ:/compilers/msvc-legacy-from-wine/19.10.25017/lib/native/lib compiler.cl19_64_exwine.includePathZ:/compilers/msvc-legacy-from-wine/19.10.25017/lib/native/include;Z:/compilers/msvc-legacy-from-wine/10.0.10240.0/ucrt compiler.cl19_64_exwine.namex64 msvc v19.10 (ex-WINE) compiler.cl19_64_exwine.semver14.10.25017 compiler.cl19_64_exwine.aliascl19_64 compiler.cl19_64_exwine.supportsBinaryfalse compiler.cl19_64_exwine.supportsBinaryObjectfalse compiler.cl19_64_exwine.supportsExecutefalse从中可以看到 MSVC 编译器的典型声明方式通过exe指定cl.exe绝对路径通过includePath/libPath声明头文件与库目录分号分隔多个路径并辅以name、semver、alias以及supportsBinary/supportsExecute等能力开关。这些配置同样展示了文档所说硬编码到特定安装位置的实际形态——路径中直接内嵌了msvc-legacy-from-wine目录与版本号本地使用者需要自行替换为真实安装路径。在 WSL 下调试 CE文档指出WSL 下唯一可行的调试方案是使用 VS CodeVSCode 的 Auto Attach自动附加选项在 WSL 上可用是最简便的调试入口。操作流程为确保 Auto Attach 已开启默认即开启在 VSCode 终端中以任意方式启动 CEmake或npm start均可首次运行必须使用make用于安装依赖与完成初始化构建。启动后 VSCode 会自动附加调试器即可像调试普通 Node.js 应用一样打断点、查看变量。适用前提与注意事项小结发行版官方测试以 Ubuntu 为主其他发行版理论上可用WSL 检测基于共享的微软内核与发行版无关。克隆位置如需调试建议将仓库克隆到 Windows 卷。临时目录默认 WSL 模式会自动把临时目录切换到 Windows%TEMP%的挂载路径显式-tmpDir必须指向 Windows 卷否则 Windows 编译器无法运行。MSVC 配置cl19类配置路径硬编码且使用/I传递 include 路径仅适合在理解其局限的前提下本地试验找不到编译器时 CE 会静默跳过不影响 CE 本身启动。环境传递MSVC 所需的环境变量依赖WSLENVINCLUDE:LIB与cmd.exe探测两条通道这与 Linux 原生编译器的运行方式有本质差异。通过上述配置与源码机制CE 在 WSL 上实现了Linux 编译器原生运行 Windows 编译器真实运行的双环境融合形态。对希望在本机 Windows 上同时体验 GCC/Clang 与 MSVC 的开发者而言按本文路径即可搭建一套完整的本地 CE 环境。【免费下载链接】compiler-explorerRun compilers interactively from your web browser and interact with the assembly项目地址: https://gitcode.com/gh_mirrors/co/compiler-explorer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考