百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

Clang如何处理MSVC的编译参数

ztj100 2025-01-03 20:47 11 浏览 0 评论

LLVM里面的Clang已经可以替换MSVC的cl.exe(MSVC的编译过程的组织程序-driver),作为Visual Studio的独立工具链,能生成PDB文件支持在Visual Studio里面的源代码调试。为了支持替换cl.exe,clang构建会生成可执行文件clang-cl.exe,接收cl.exe的大部分参数而在内部转换成LLVM的参数形式。

虽然看起来是生成了一个单独的clang-cl.exe,它实际上就是clang.exe的一个副本,如果程序名是clang.exe,还可以在命令行传递"--driver-mode=cl"参数启用cl.exe的参数解析模式。所以clang-cl.exe和clang.exe是一样的,都接受"--target=i686-pc-windows", 但是为什么clang-cl.exe却不能解析"-triple i686-pc-windows"而clang.exe却可以呢?

程序本身通过检查自身的文件名(argv[0])来检测是不是要运行在兼容MSVC cl.exe的模式,如果文件名是"clang-cl.exe",则把对应的DriverMode放到main函数开始处的变量TargetAndMode里面(ToolChain::getTargetAndModeFromProgramName)。下面的代码显示了对应关系,可以看到把文件名clang.exe改成cl.exe也会有一样的效果。

// llvm_root\tools\clang\tools\driver\driver.cpp
const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) {
  // A list of known driver suffixes. Suffixes are compared against the
  // program name in order. If there is a match, the frontend type is updated as
  // necessary by applying the ModeFlag.
  static const DriverSuffix DriverSuffixes[] = {
      {"clang", nullptr},
      {"clang++", "--driver-mode=g++"},
      {"clang-c++", "--driver-mode=g++"},
      {"clang-cc", nullptr},
      {"clang-cpp", "--driver-mode=cpp"},
      {"clang-g++", "--driver-mode=g++"},
      {"clang-gcc", nullptr},
      {"clang-cl", "--driver-mode=cl"},
      {"cc", nullptr},
      {"cpp", "--driver-mode=cpp"},
      {"cl", "--driver-mode=cl"},
      {"++", "--driver-mode=g++"},
  };

在上面从程序名解析出target和mode后,main函数里面紧接着的代码检查了返回的mode和命令行参数,只要以一个满足则进入ClangCLMode。不过这里解析出来的ClangCLMode只用来处理命令行参数的分隔和cl.exe特有的环境变量,包括"CL"和"_CL_"。

// llvm_root\tools\clang\tools\driver\driver.cpp
  auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]);

  bool ClangCLMode = false;
  if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") ||
      std::find_if(argv.begin(), argv.end(), [](const char *F) {
        return F && strcmp(F, "--driver-mode=cl") == 0;
      }) != argv.end()) {
    ClangCLMode = true;
  }

再从main函数进入Driver类的对象TheDriver的BuildCompilation方法后,会调用 ParseDriverMode方法,里面会根据程序名重新获得driver mode(ToolChain::getTargetAndModeFromProgramName),然后把driver mode字符串传给下面的setDriverModeFromOption方法。这个方法根据传入的driver mode选项设置成员变量Mode.

// llvm_root\tools\clang\lib\driver\driver.cpp
void Driver::setDriverModeFromOption(StringRef Opt) {
  const std::string OptName =
      getOpts().getOption(options::OPT_driver_mode).getPrefixedName();
  if (!Opt.startswith(OptName))
    return;
  StringRef Value = Opt.drop_front(OptName.size());

  const unsigned M = llvm::StringSwitch<unsigned>(Value)
                         .Case("gcc", GCCMode)
                         .Case("g++", GXXMode)
                         .Case("cpp", CPPMode)
                         .Case("cl", CLMode)
                         .Default(~0U);

  if (M != ~0U)
    Mode = static_cast<DriverMode>(M);

上面的Driver类的对象已经知道当前Mode,比如CLMode,下面会组织整个编译过程,包括调用编译器(clang.exe -cc1)和链接器(MSVC的link或者lld-link)。

BuildCompilation紧接着会调用ParseArgStrings。ParseArgStrings调用下面的getIncludeExcludeOptionFlagMask,根据Driver的当前Mode得到include mask和exclude mask两个掩码,用于后面(在调用链ParseArgString->ParseArg->ParseOneArg的最后的方法ParseOneArg里面)决定是否接受命令行参数。比如在CLMode下就只会接受ClOption和CoreOption。

// llvm_root\tools\clang\lib\driver\driver.cpp
std::pair<unsigned, unsigned> Driver::getIncludeExcludeOptionFlagMasks() const {
  unsigned IncludedFlagsBitmask = 0;
  unsigned ExcludedFlagsBitmask = options::NoDriverOption;

  if (Mode == CLMode) {
    // Include CL and Core options.
    IncludedFlagsBitmask |= options::CLOption;
    IncludedFlagsBitmask |= options::CoreOption;
  } else {
    ExcludedFlagsBitmask |= options::CLOption;
  }

  return std::make_pair(IncludedFlagsBitmask, ExcludedFlagsBitmask);
}

那么CLOption和CoreOption都有哪些具体参数呢?Clang的所有命令行参数选项都定义在llvm_root\tools\clang\include\clang\driver\Options.td里面,由tablegen转成C/C++头文件而被代码引用。下面是从里面截取的target的定义,看到"--target="选项是同时属于DriverOption和CoreOption,而CoreOption在CLMode和非CLMode下均能使用。

def target : Joined<["--"], "target=">, Flags<[DriverOption, CoreOption]>,
  HelpText<"Generate code for the given target">;

以下是"-target"的定义,没有定义Flags,所以在CLMode下也就不能解析,这也就解释了最开始"clang-cl.exe"不接受"-triple i686-pc-windows"参数。

def target_legacy_spelling : Separate<["-"], "target">, Alias<target>;

相关推荐

从IDEA开始,迈进GO语言之门(idea got)

前言笔者在学习GO语言编程的时候,GO语言在国内还没有像JAVA/Php/Python那样普及,绕了不少的弯路,要开始入门学习一门编程语言,最好就先从选择一个好的编程语言的开发环境开始,有了这个开发环...

基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于SpringBoot...

基于springboot的个人服装管理系统java网上商城jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

基于springboot的美食网站Java食品销售jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

贸易管理进销存springboot云管货管账分析java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目描述贸易管理进销存spring...

SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍SpringBoot+V...

目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗

帮粉丝找了一个基于SpringBoot的天猫商城项目,快速部署运行,所用技术:MySQL,Druid,Log4j2,Maven,Echarts,Bootstrap...免费给大家分享出来前台演示...

SpringBoot+Mysql实现的手机商城附带源码演示导入视频

今天为大家带来的是基于SpringBoot+JPA+Thymeleaf框架的手机商城管理系统,商城系统分为前台和后台、前台用的是Bootstrap框架后台用的是SpringBoot+JPA都是现在主...

全网首发!马士兵内部共享—1658页《Java面试突击核心讲》

又是一年一度的“金九银十”秋招大热门,为助力广大程序员朋友“面试造火箭”,小编今天给大家分享的便是这份马士兵内部的面试神技——1658页《Java面试突击核心讲》!...

SpringBoot数据库操作的应用(springboot与数据库交互)

1.JDBC+HikariDataSource...

SpringBoot 整合 Flink 实时同步 MySQL

1、需求在Flink发布SpringBoot打包的jar包能够实时同步MySQL表,做到原表进行新增、修改、删除的时候目标表都能对应同步。...

SpringBoot + Mybatis + Shiro + mysql + redis智能平台源码分享

后端技术栈基于SpringBoot+Mybatis+Shiro+mysql+redis构建的智慧云智能教育平台基于数据驱动视图的理念封装element-ui,即使没有vue的使...

Springboot+Mysql舞蹈课程在线预约系统源码附带视频运行教程

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的Springboot+Mysql舞蹈课程在线预约系统,系统项目源代码在【猿来入此】获取!https://www.yuan...

SpringBoot+Mysql在线众筹系统源码+讲解视频+开发文档(参考论文

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的在线众筹管理系统,主要实现了普通用户在线参与众筹基本操作流程的全部功能,系统分普通用户、超级管理员等角色,除基础脚手架外...

Docker一键部署 SpringBoot 应用的方法,贼快贼好用

这两天发现个Gradle插件,支持一键打包、推送Docker镜像。今天我们来讲讲这个插件,希望对大家有所帮助!GradleDockerPlugin简介...

取消回复欢迎 发表评论: