Skip to content

命令分析

cmd 模块提供编译器和归档工具命令行的结构化分析。它能识别编译阶段、产物类型、输入文件、输出文件以及依赖边。

CompilerAnalysis

分析编译器命令行(GCC、Clang),提取结构化信息。

js
import { cmd, io, service } from "catter";

service.onCommand((ctx) => {
  if (!ctx.capture.success) return;

  const argv = ctx.capture.data.argv;
  const analysis = cmd.CompilerAnalysis.analyze(argv);

  if (analysis) {
    io.println(`Phase: ${analysis.phase}`);
    io.println(`Artifact: ${analysis.artifact}`);
    io.println(`Compiler: ${analysis.compiler}`);
    io.println(`Sources: ${analysis.sourceInputs().join(", ")}`);
    io.println(`Outputs: ${analysis.outputs().join(", ")}`);
  }
});

静态方法

方法描述
CompilerAnalysis.supports(argv)检查命令是否看起来像编译器调用
CompilerAnalysis.analyze(argv)完整分析,非编译器命令返回 undefined
CompilerAnalysis.from(analysis)将通用 Analysis 收窄为 CompilerAnalysis

实例属性

属性类型描述
compiler"clang" | "gcc"识别到的编译器
phaseCompilerPhase编译阶段(见下文)
artifactCompilerArtifact输出产物类型(见下文)
style"gnu" | "cl"驱动选项的语法风格
typeCompilerType | undefined旧版兼容分类

实例方法

方法返回值描述
inputs()string[]所有输入文件路径
sourceInputs()string[]仅源文件路径
inputEntries()CompilerInput[]带元数据的输入:{path, kind, index}
outputs()string[]生成的输出文件路径
edges()Edge[]依赖边(输入到输出的映射)

每个 CompilerInput 包含:

  • path -- 文件路径
  • kind -- "source""link"
  • index -- 在原始 argv 中的位置

每个 Edge 包含:

  • output -- 输出文件路径
  • inputs -- 产生该输出的输入文件路径数组

对于编译命令,边会将每个目标文件与其对应的源文件配对。对于链接命令,所有输入都指向每个输出。

支持的阶段

阶段
CompilerPhase.Preprocess"preprocess"
CompilerPhase.SyntaxOnly"syntax-only"
CompilerPhase.Compile"compile"
CompilerPhase.Link"link"
CompilerPhase.Archive"archive"
CompilerPhase.RelocatableLink"relocatable-link"
CompilerPhase.DeviceLink"device-link"

支持的产物类型

产物
CompilerArtifact.None"none"
CompilerArtifact.Stdout"stdout"
CompilerArtifact.Object"object"
CompilerArtifact.Executable"exe"
CompilerArtifact.SharedLibrary"shared"
CompilerArtifact.StaticLibrary"static-lib"
CompilerArtifact.Assembly"asm"
CompilerArtifact.LlvmIR"llvm-ir"
CompilerArtifact.LlvmBitcode"llvm-bc"
CompilerArtifact.Pch"pch"
CompilerArtifact.Pcm"pcm"
CompilerArtifact.Ptx"ptx"
CompilerArtifact.Cubin"cubin"
CompilerArtifact.Fatbin"fatbin"

ArchiverAnalysis

分析归档命令(arllvm-argcc-ar)。

js
import { cmd } from "catter";

const argv = ["llvm-ar", "rcs", "libfoo.a", "foo.o", "bar.o"];
const analysis = cmd.ArchiverAnalysis.analyze(argv);

if (analysis) {
  io.println(`Operation: ${analysis.operation}`);  // "r"
  io.println(`Archive: ${analysis.archive}`);       // "libfoo.a"
  io.println(`Members: ${analysis.members}`);       // ["foo.o", "bar.o"]
  io.println(`Thin: ${analysis.thin}`);             // false
}

实例属性

属性类型描述
operationArchiverOperation归档操作("r""x""t" 等)
modifiersstring[]修饰符字母(如 ["c", "s"]
thinboolean是否请求了 thin 归档模式
archivestring | undefined归档文件路径
membersstring[]成员文件路径

支持的操作

分析器目前支持以下操作:

操作描述
ArchiverOperation.Print"p"打印成员
ArchiverOperation.QuickAppend"q"快速追加
ArchiverOperation.ReplaceOrInsert"r"替换或插入成员
ArchiverOperation.Table"t"列出内容

其他 ar 操作(dmsx)在枚举中已定义,但 ArchiverAnalysis.analyze() 对它们会返回 undefined

Registry

分析器被组织在 Registry 中。默认注册表包含 CompilerAnalysisArchiverAnalysis

使用默认注册表

js
import { cmd } from "catter";

// 使用所有内置分析器进行分析
const analysis = cmd.analyze(["clang", "-c", "main.c"]);

// 检查是否有分析器能处理该命令
const canHandle = cmd.canHandle(["clang", "-c", "main.c"]);

自定义注册表

js
import { cmd } from "catter";

const registry = new cmd.Registry();
registry.register(cmd.CompilerAnalysis);
registry.register(cmd.ArchiverAnalysis);

const analysis = registry.analyze(["gcc", "-c", "main.c"]);

添加自定义分析器

实现 Analyzer 接口,提供 keysupports()analyze()

js
import { cmd } from "catter";

class MyToolAnalysis extends cmd.Analysis {
  static key = "my-tool";

  static supports(argv) {
    return argv[0]?.endsWith("my-tool") ?? false;
  }

  static analyze(argv) {
    if (!MyToolAnalysis.supports(argv)) return undefined;
    return new MyToolAnalysis(argv);
  }

  constructor(argv) {
    const inputs = [argv[1]];
    const outputs = [argv[2]];
    super("my-tool", inputs, outputs);
  }
}

// 注册到全局注册表
cmd.register(MyToolAnalysis);

// 或使用独立的注册表
const registry = new cmd.Registry();
registry.register(MyToolAnalysis);

收窄分析结果

从注册表获取通用 Analysis 后,使用静态 from() 方法进行类型收窄:

js
const analysis = cmd.analyze(argv);
const compiler = cmd.CompilerAnalysis.from(analysis);
const archiver = cmd.ArchiverAnalysis.from(analysis);

if (compiler) {
  io.println(`Compiler phase: ${compiler.phase}`);
} else if (archiver) {
  io.println(`Archiver operation: ${archiver.operation}`);
}