diff --git a/en/clice/design/command-resolve.md b/en/clice/design/command-resolve.md index da61535cd..c81358189 100644 --- a/en/clice/design/command-resolve.md +++ b/en/clice/design/command-resolve.md @@ -54,7 +54,7 @@ After classification, each command is parsed **once** into a structured command Two derived views matter: -- **Rendering**: the same structured command can be rendered on demand as a driver command line (for querying or for handing agents a runnable command) or combined with query results into frontend arguments. Rendering normalizes alias spellings, so identity is not disturbed by cosmetic variation in the CDB. +- **Rendering**: the same structured command can be rendered on demand as a driver command line (for querying or for handing agents a runnable command) or combined with query results into frontend arguments. A driver command line gives each option as the command writes it: GCC knows an option only by its own name, which may not be clang's (`-msmall-data-limit=8` is clang's `-G 8`, which RISC-V GCC rejects). - **Command hash**: the frontend-relevant view of the command, together with the input's slot position and working directory, is hashed into its **command hash** — the stable identity used for CDB diffing, index snapshot validation, and pinned context choices. The classification also buys toolchain querying cache efficiency. Querying requires actually invoking the compiler driver (`g++ -E -dM -v`), which takes from a few milliseconds to a hundred or more. The querying result depends only on the driver and query-relevant options -- user-content options (`-I`, `-D`) do not affect the system paths or target triple output by the driver. Therefore, regardless of what different `-I` paths files may have, files sharing the query-relevant view share one querying result. In real projects, tens of thousands of files typically collapse to a few dozen distinct query keys. @@ -148,7 +148,7 @@ Different compiler families use different querying strategies, each in its own f - The job the driver runs. A clang job is clice's cc1 command as it stands. A GCC job comes with GCC's target, its header search list, and the options GCC's driver added from its configuration and specs (target defaults such as `-march=armv7-a+fp -mthumb`, macros such as `-D_REENTRANT`, hardening such as `-fstack-protector-strong`). Clang's own driver then builds the cc1 command pinned to that target, with none of its own system headers (`-nostdinc`), those options and the command's flags; GCC's search list follows in GCC's order, with clang's builtin headers in place of GCC's own private ones (`lib/gcc///include`): those are GCC's versions of the same headers, which clang cannot parse and its own `stdatomic.h` would otherwise reach through `#include_next`. - The language standard, read from `__cplusplus` or `__STDC_VERSION__`, which fills in `-std` when the command names none: GCC 15 compiles C as C23, Apple clang C++ as C++14. -Asked with the command's own flags, the driver answers for the command, and a GCC answers only when that compile succeeds: a flag that switches one of its defaults off (`-fno-stack-protector`), a `--sysroot` or a `--specs` file shows in its answer. CPU and ABI names clang does not know (GCC's `-mtune=intel`, `-mabi=lp64` on Darwin) are dropped from the cc1 command, and GCC's two-core form `-mcpu=cortex-a72.cortex-a53` keeps its first core. A universal build (`-arch arm64 -arch x86_64`) is read for its first architecture. +Asked with the command's own flags, written as the command writes them, the driver answers for the command, and a GCC answers only when that compile succeeds: a flag that switches one of its defaults off (`-fno-stack-protector`), a `--sysroot` or a `--specs` file shows in its answer. When it fails, the log names its error and the command clice ran. CPU and ABI names clang does not know (GCC's `-mtune=intel`, `-mabi=lp64` on Darwin) are dropped from the cc1 command, and GCC's two-core form `-mcpu=cortex-a72.cortex-a53` keeps its first core. A RISC-V `-march` loses the extensions LLVM does not know (WCH's `_xw`) and the versions it does not know (an older GCC's `i2p0`): clang would drop the whole string, and every feature macro with it. A universal build (`-arch arm64 -arch x86_64`) is read for its first architecture. **Cygwin and MSYS2 compilers** on Windows (Cygwin's and MSYS2's own GCC, and toolchains built on Cygwin such as Andes' RISC-V GCC) answer as any GCC-style driver does, but in POSIX paths (`/usr/include`, `/cygdrive/c/...`), which clice's clang cannot open. The mount table of the runtime the driver loads maps them, read from the runtime's own `mount` for each query. Of the runtimes (`cygwin1.dll`, `msys-2.0.dll`) beside the driver and on `PATH`, in the order Windows looks for a program's DLLs, that is the first whose table maps the program of the driver's job to a file that exists: taking the first alone would pick Git for Windows' MSYS2 where it comes ahead on `PATH` of the Cygwin a toolchain outside it loads. An assumed mapping would get them wrong, since Cygwin mounts `/usr/lib` at `/lib`, MSYS2 its drives at `/c`, and a user's `/etc/fstab` adds mounts of its own. The driver itself is found as Cygwin finds it: by a name without `.exe`, and through a Cygwin symlink (CMake records `/usr/bin/cc`, a link to `gcc.exe`). Such a compiler starts only with its runtime's `bin` directory on `PATH`; without it, the query fails with an error that says so, and [Editor Setup](../guide/editors.md#compiler-environment) sets it per editor. @@ -156,7 +156,7 @@ Asked with the command's own flags, the driver answers for the command, and a GC **nvcc**: nvcc's dry run names the CUDA root, the host compiler and the macros nvcc defines. The host compiler is asked as a GCC-style driver is, in the input's language, with the command's `--sysroot` but none of its other flags: a GCC host pins the target and the search list as above, a clang host needs nothing. Clang's driver then builds the cc1 command with nvcc's macros, in CUDA mode for a CUDA input. -**A compiler clice does not run**: a rule's `query_toolchain = false` keeps clice from running the compiler of the files it matches — one that is not on this machine (a database written in a container or on another host), or one that must not run. Clang's own driver builds the cc1 command instead, as a clang installed under the compiler's name would: `g++` compiles C++, and a target prefix naming an architecture clang knows (`aarch64-linux-gnu-g++`, but not `riscv-none-elf-gcc`) is the target unless the command names one. Everything else — the standard library, the system headers, the default standard — is clang's default for that target. +**A compiler clice does not run**: a rule's `query_toolchain = false` keeps clice from running the compiler of the files it matches — one that is not on this machine (a database written in a container or on another host), or one that must not run. Clang's own driver builds the cc1 command instead, as a clang installed under the compiler's name would: `g++` compiles C++, and a target prefix naming an architecture clang knows (`aarch64-linux-gnu-g++`, but not `riscv-none-elf-gcc`) is the target unless the command names one. Everything else — the standard library, the system headers, the default standard — is clang's default for that target. A compiler whose query fails is read for the target its name sets too. > The external driver's version may be newer than clice's embedded LLVM version, and its cc1 output may contain options that clice does not recognize. During parsing, unknown options are silently dropped to ensure compatibility. diff --git a/zh/clice/design/command-resolve.md b/zh/clice/design/command-resolve.md index 6d8a15620..be671b37f 100644 --- a/zh/clice/design/command-resolve.md +++ b/zh/clice/design/command-resolve.md @@ -54,7 +54,7 @@ clice 实现了整个命令处理流程的自动化:从 CDB 读取命令后, 有两个重要的派生视图: -- **渲染**:同一条结构化命令可以按需渲染成编译器驱动程序命令行(用于查询,或向代理提供可运行的命令),也可以与查询结果组合成前端参数。渲染会规范化别名的拼写形式,因此 CDB 中仅形式上的差异不会影响标识。 +- **渲染**:同一条结构化命令可以按需渲染成编译器驱动程序命令行(用于查询,或向代理提供可运行的命令),也可以与查询结果组合成前端参数。编译器驱动程序命令行会按命令原本的写法给出每个选项:GCC 只认它自己的选项名,而这个名字不一定是 clang 的(`-msmall-data-limit=8` 在 clang 里是 `-G 8`,RISC-V GCC 不接受后者)。 - **命令哈希**:命令中与前端相关的视图,连同输入文件的槽位位置和工作目录,会共同哈希为它的**命令哈希**——这是用于 CDB 差异比较、索引快照验证和固定上下文选择的稳定标识。 这种分类还能提高工具链查询的缓存效率。查询需要实际调用编译器驱动程序(`g++ -E -dM -v`),耗时从几毫秒到一百多毫秒不等。查询结果只取决于驱动程序和与查询相关的选项——用户内容选项(`-I`、`-D`)不会影响驱动程序输出的系统路径或目标三元组。因此,即使各文件的 `-I` 路径不同,只要查询相关视图相同,就会共用同一份查询结果。在实际项目中,上万个文件通常会归并为几十个不同的查询键。 @@ -148,7 +148,7 @@ CDB 加载使用 simdjson 进行流式 JSON 解析,逐条处理记录: - 驱动程序运行的任务(job)。clang 的任务原样就是 clice 要用的 cc1 命令。GCC 的任务则带有 GCC 的目标、它的头文件搜索列表,以及 GCC 驱动根据自身配置和 specs 添加的选项(目标默认值如 `-march=armv7-a+fp -mthumb`,宏如 `-D_REENTRANT`,加固选项如 `-fstack-protector-strong`)。随后由 Clang 自己的驱动构建 cc1 命令:固定为该目标,不带 Clang 自己的任何系统头文件(`-nostdinc`),并加上这些选项和命令自身的标志;GCC 的搜索列表按 GCC 的顺序排在后面,只是 GCC 自己的私有头文件(`lib/gcc///include`)换成了 clang 的内置头文件:那些是同一批头文件的 GCC 版本,clang 无法解析,不替换的话 clang 自己的 `stdatomic.h` 还会经由 `#include_next` 进入它们。 - 语言标准,从 `__cplusplus` 或 `__STDC_VERSION__` 读出,命令没有指定时用它补上 `-std`:GCC 15 默认按 C23 编译 C,Apple clang 默认按 C++14 编译 C++。 -询问时带着命令自身的标志,所以驱动程序回答的就是这条命令,而 GCC 只在这次编译成功时才会给出回答:关闭某项默认设置的标志(`-fno-stack-protector`)、`--sysroot` 或 `--specs` 文件都会体现在回答中。clang 不认识的 CPU 和 ABI 名称(GCC 的 `-mtune=intel`、Darwin 上的 `-mabi=lp64`)会从 cc1 命令中去掉,GCC 的双核写法 `-mcpu=cortex-a72.cortex-a53` 只保留第一个核。通用构建(`-arch arm64 -arch x86_64`)按其中第一个架构读取。 +询问时带着命令自身的标志,并按命令原本的写法给出,所以驱动程序回答的就是这条命令,而 GCC 只在这次编译成功时才会给出回答:关闭某项默认设置的标志(`-fno-stack-protector`)、`--sysroot` 或 `--specs` 文件都会体现在回答中。询问失败时,日志会写出它的报错和 clice 运行的命令。clang 不认识的 CPU 和 ABI 名称(GCC 的 `-mtune=intel`、Darwin 上的 `-mabi=lp64`)会从 cc1 命令中去掉,GCC 的双核写法 `-mcpu=cortex-a72.cortex-a53` 只保留第一个核。RISC-V 的 `-march` 会去掉 LLVM 不认识的扩展(WCH 的 `_xw`)和它不认识的版本(较老 GCC 的 `i2p0`):否则 clang 会丢掉整个字符串,连同所有特性宏。通用构建(`-arch arm64 -arch x86_64`)按其中第一个架构读取。 **Cygwin 和 MSYS2 编译器**:Windows 上的这类编译器(Cygwin 和 MSYS2 自带的 GCC,以及基于 Cygwin 构建的工具链,例如 Andes 的 RISC-V GCC)像任何 GCC 风格的驱动程序一样作答,只是给出的是 POSIX 路径(`/usr/include`、`/cygdrive/c/...`),clice 的 clang 无法打开。这些路径由驱动程序加载的运行时用其挂载表映射,每次查询都从该运行时自己的 `mount` 读取挂载表。在驱动程序旁边和 `PATH` 上的运行时(`cygwin1.dll`、`msys-2.0.dll`)中,按 Windows 查找程序 DLL 的顺序,取第一个能把驱动程序所跑作业的程序映射到实际存在的文件的那个:只取第一个运行时的话,若 Git for Windows 的 MSYS2 在 `PATH` 上排在工具链所加载的、位于其外的 Cygwin 之前,就会选错。假定一套映射会出错:Cygwin 把 `/usr/lib` 挂载在 `/lib`,MSYS2 把驱动器挂载在 `/c` 这样的路径上,用户的 `/etc/fstab` 还会加上自己的挂载。驱动程序本身按 Cygwin 的方式查找:名称可以不带 `.exe`,也可以经由 Cygwin 的符号链接找到(CMake 记录的是 `/usr/bin/cc`,它是指向 `gcc.exe` 的链接)。这样的编译器只有在其运行时的 `bin` 目录位于 `PATH` 中时才能启动;否则查询会失败,错误信息会说明这一点,[编辑器设置](../guide/editors.md#compiler-environment)介绍了如何在各个编辑器中设置它。 @@ -156,7 +156,7 @@ CDB 加载使用 simdjson 进行流式 JSON 解析,逐条处理记录: **nvcc**:nvcc 的 dry run 会给出 CUDA 根目录、宿主编译器以及 nvcc 定义的宏。宿主编译器按 GCC 风格驱动程序的方式询问,使用输入文件的语言,带上命令的 `--sysroot`,但不带命令的其他标志:GCC 宿主编译器像上文那样固定目标和搜索列表,clang 宿主编译器则什么都不需要。随后由 Clang 驱动带上 nvcc 的宏构建 cc1 命令,输入为 CUDA 文件时使用 CUDA 模式。 -**clice 不运行的编译器**:规则写上 `query_toolchain = false`,clice 就不运行它所匹配文件的编译器——用于本机上没有的编译器(在容器里或另一台机器上写出的数据库),或是不该运行的编译器。此时改由 Clang 自己的驱动构建 cc1 命令,就像一个以该编译器名字安装的 clang 那样:`g++` 按 C++ 编译,前缀所指的架构若是 clang 认识的(`aarch64-linux-gnu-g++` 是,`riscv-none-elf-gcc` 不是),这个前缀就是目标,除非命令自己指定了目标。其余一切——标准库、系统头文件、默认标准——都取 clang 对该目标的默认值。 +**clice 不运行的编译器**:规则写上 `query_toolchain = false`,clice 就不运行它所匹配文件的编译器——用于本机上没有的编译器(在容器里或另一台机器上写出的数据库),或是不该运行的编译器。此时改由 Clang 自己的驱动构建 cc1 命令,就像一个以该编译器名字安装的 clang 那样:`g++` 按 C++ 编译,前缀所指的架构若是 clang 认识的(`aarch64-linux-gnu-g++` 是,`riscv-none-elf-gcc` 不是),这个前缀就是目标,除非命令自己指定了目标。其余一切——标准库、系统头文件、默认标准——都取 clang 对该目标的默认值。询问失败的编译器,同样按名字前缀所指的目标读取。 > 外部驱动的版本可能比 clice 内嵌的 LLVM 版本更新,其 cc1 输出中可能包含 clice 无法识别的选项。解析时会静默丢弃未知选项,以保证兼容性。