Ungoogled Chromium127编译指南 Windows篇 - Rust编译器包装器修复(十二)

1. 引言

在解决了Rust标准库查找问题后,我们遇到的下一个技术挑战是rustc_wrapper.py脚本的执行错误。在Windows环境下,该脚本无法正确调用Rust编译器,导致编译过程中断。本文将详细介绍如何解决这个问题。

2. 问题分析

2.1 错误表现

编译过程中会遇到如下错误:

复制代码
FAILED: obj/third_party/rust/itoa/v1/lib/libitoa_lib.rlib
[WinError 193] %1 is not a valid Win32 application

2.2 错误原因

  • rustc_wrapper.py脚本未正确处理Windows可执行文件调用
  • subprocess.run()函数参数配置不当
  • Windows环境下的路径处理问题

3. 修复方案

3.1 修改rustc_wrapper.py

需要在rustc_wrapper.py文件中修改rustc命令的调用方式:

修改前:

复制代码
r = subprocess.run([args.rustc, *rustc_args], env=env, check=False)

修改后:

复制代码
rustc_path = str(args.rustc) + ".exe"
r = subprocess.run([rustc_path, *rustc_args], env=env, check=False)

3.2 核心改动说明

  1. 添加.exe后缀
    • 确保在Windows环境下正确识别可执行文件
    • 解决Win32应用程序识别问题
  2. 路径处理优化
    • 保持Windows路径格式一致性
    • 确保文件系统访问正确

3.3 完整代码

复制代码
#!/usr/bin/env python3

# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import argparse
import pathlib
import subprocess
import shlex
import os
import sys
import re

# Set up path to be able to import action_helpers.
sys.path.append(
    os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,
                 os.pardir, 'build'))
import action_helpers

# This script wraps rustc for (currently) these reasons:
# * To work around some ldflags escaping performed by ninja/gn
# * To remove dependencies on some environment variables from the .d file.
# * To enable use of .rsp files.
# * To work around two gn bugs on Windows
#
# LDFLAGS ESCAPING
#
# This script performs a simple function to work around some of the
# parameter escaping performed by ninja/gn.
#
# rustc invocations are given access to {{rustflags}} and {{ldflags}}.
# We want to pass {{ldflags}} into rustc, using -Clink-args="{{ldflags}}".
# Unfortunately, ninja assumes that each item in {{ldflags}} is an
# independent command-line argument and will have escaped them appropriately
# for use on a bare command line, instead of in a string.
#
# This script converts such {{ldflags}} into individual -Clink-arg=X
# arguments to rustc.
#
# RUSTENV dependency stripping
#
# When Rust code depends on an environment variable at build-time
# (using the env! macro), rustc spots that and adds it to the .d file.
# Ninja then parses that .d file and determines that the environment
# dependency means that the target always needs to be rebuilt.
#
# That's all correct, but _we_ know that some of these environment
# variables (typically, all of them) are set by .gn files which ninja
# tracks independently. So we remove them from the .d file.
#
# RSP files:
#
# We want to put the ninja/gn variables {{rustdeps}} and {{externs}}
# in an RSP file. Unfortunately, they are space-separated variables
# but Rust requires a newline-separated input. This script duly makes
# the adjustment. This works around a gn issue:
# TODO(https://bugs.ch40m1um.qjz9zk/p/gn/issues/detail?id=249): fix this
#
# WORKAROUND WINDOWS BUGS:
#
# On Windows platforms, this temporarily works around some issues in gn.
# See comments inline, linking to the relevant gn fixes.
#
# Usage:
#   rustc_wrapper.py --rustc <path to rustc> --depfile <path to .d file>
#      -- <normal rustc args> LDFLAGS {{ldflags}} RUSTENV {{rustenv}}
# The LDFLAGS token is discarded, and everything after that is converted
# to being a series of -Clink-arg=X arguments, until or unless RUSTENV
# is encountered, after which those are interpreted as environment
# variables to pass to rustc (and which will be removed from the .d file).
#
# Both LDFLAGS and RUSTENV **MUST** be specified, in that order, even if
# the list following them is empty.
#
# TODO(https://github.com/rust-lang/rust/issues/73632): avoid using rustc
# for linking in the first place. Most of our binaries are linked using
# clang directly, but there are some types of Rust build product which
# must currently be created by rustc (e.g. unit test executables). As
# part of support for using non-rustc linkers, we should arrange to extract
# such functionality from rustc so that we can make all types of binary
# using our clang toolchain. That will remove the need for most of this
# script.

FILE_RE = re.compile("[^:]+: (.+)")


# Equivalent of python3.9 built-in
def remove_lib_suffix_from_l_args(text):
  if text.startswith("-l") and text.endswith(".lib"):
    return text[:-len(".lib")]
  return text


def verify_inputs(depline, sources, abs_build_root):
  """Verify everything used by rustc (found in `depline`) was specified in the
  GN build rule (found in `sources` or `inputs`).

  TODO(danakj): This allows things in `sources` that were not actually used by
  rustc since third-party packages sources need to be a union of all build
  configs/platforms for simplicity in generating build rules. For first-party
  code we could be more strict and reject things in `sources` that were not
  consumed.
  """

  # str.removeprefix() does not exist before python 3.9.
  def remove_prefix(text, prefix):
    if text.startswith(prefix):
      return text[len(prefix):]
    return text

  def normalize_path(p):
    return os.path.relpath(os.path.normpath(remove_prefix(
        p, abs_build_root))).replace('\\', '/')

  # Collect the files that rustc says are needed.
  found_files = {}
  m = FILE_RE.match(depline)
  if m:
    files = m.group(1)
    found_files = {normalize_path(f): f for f in files.split()}
  # Get which ones are not listed in GN.
  missing_files = found_files.keys() - sources

  if not missing_files:
    return True

  # The matching did a bunch of path manipulation to get paths relative to the
  # build dir such that they would match GN. In errors, we will print out the
  # exact path that rustc produces for easier debugging and writing of stdlib
  # config rules.
  for file_files_key in missing_files:
    gn_type = "sources" if file_files_key.endswith(".rs") else "inputs"
    print(f'ERROR: file not in GN {gn_type}: {found_files[file_files_key]}',
          file=sys.stderr)
  return False


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('--rustc', required=True, type=pathlib.Path)
  parser.add_argument('--depfile', required=True, type=pathlib.Path)
  parser.add_argument('--rsp', type=pathlib.Path, required=True)
  parser.add_argument('--target-windows', action='store_true')
  parser.add_argument('-v', action='store_true')
  parser.add_argument('args', metavar='ARG', nargs='+')

  args = parser.parse_args()

  remaining_args = args.args

  ldflags_separator = remaining_args.index("LDFLAGS")
  rustenv_separator = remaining_args.index("RUSTENV", ldflags_separator)
  # Sometimes we duplicate the SOURCES list into the command line for debugging
  # issues on the bots.
  try:
    sources_separator = remaining_args.index("SOURCES", rustenv_separator)
  except:
    sources_separator = None
  rustc_args = remaining_args[:ldflags_separator]
  ldflags = remaining_args[ldflags_separator + 1:rustenv_separator]
  rustenv = remaining_args[rustenv_separator + 1:sources_separator]

  abs_build_root = os.getcwd().replace('\\', '/') + '/'
  is_windows = sys.platform == 'win32' or args.target_windows

  rustc_args.extend(["-Clink-arg=%s" % arg for arg in ldflags])

  with open(args.rsp) as rspfile:
    rsp_args = [l.rstrip() for l in rspfile.read().split(' ') if l.rstrip()]

  sources_separator = rsp_args.index("SOURCES")
  sources = set(rsp_args[sources_separator + 1:])
  rsp_args = rsp_args[:sources_separator]

  if is_windows:
    # Work around for "-l<foo>.lib", where ".lib" suffix is undesirable.
    # Full fix will come from https://gn-review.9oo91esource.qjz9zk/c/gn/+/12480
    rsp_args = [remove_lib_suffix_from_l_args(arg) for arg in rsp_args]
  out_rsp = str(args.rsp) + ".rsp"
  with open(out_rsp, 'w') as rspfile:
    # rustc needs the rsp file to be separated by newlines. Note that GN
    # generates the file separated by spaces:
    # https://bugs.ch40m1um.qjz9zk/p/gn/issues/detail?id=249,
    rspfile.write("\n".join(rsp_args))
  rustc_args.append(f'@{out_rsp}')

  env = os.environ.copy()
  fixed_env_vars = []
  for item in rustenv:
    (k, v) = item.split("=", 1)
    env[k] = v
    fixed_env_vars.append(k)

  try:
    if args.v:
      print(' '.join(f'{k}={shlex.quote(v)}' for k, v in env.items()),
            args.rustc, shlex.join(rustc_args))
    rustc_path = str(args.rustc) + ".exe"
    r = subprocess.run([rustc_path, *rustc_args], env=env, check=False)
  finally:
    if not args.v:
      os.remove(out_rsp)
  if r.returncode != 0:
    sys.exit(r.returncode)

  final_depfile_lines = []
  dirty = False
  with open(args.depfile, encoding="utf-8") as d:
    # Figure out which lines we want to keep in the depfile. If it's not the
    # whole file, we will rewrite the file.
    env_dep_re = re.compile("# env-dep:(.*)=.*")
    for line in d:
      m = env_dep_re.match(line)
      if m and m.group(1) in fixed_env_vars:
        dirty = True  # We want to skip this line.
      else:
        final_depfile_lines.append(line)

  # Verify each dependent file is listed in sources/inputs.
  for line in final_depfile_lines:
    if not verify_inputs(line, sources, abs_build_root):
      return 1

  if dirty:  # we made a change, let's write out the file
    with action_helpers.atomic_output(args.depfile) as output:
      output.write("\n".join(final_depfile_lines).encode("utf-8"))


if __name__ == '__main__':
  sys.exit(main())

4. 验证修复

4.1 测试修改

修改完成后,执行以下命令进行验证:

复制代码
python build.py --tarball

4.2 检查输出

  • 确认rustc编译器能够正常调用
  • 验证编译过程顺利进行
  • 检查生成文件的完整性

5. 可能遇到的问题

5.1 环境变量问题

  • 确保RUST_BACKTRACE设置正确
  • 验证PATH环境变量包含必要路径
  • 检查临时目录权限

5.2 工具链配置

  • 验证rustc版本匹配性
  • 确认编译器组件完整性
  • 检查依赖项配置

6. 结语

通过本文的指导,我们成功解决了Ungoogled Chromium编译过程中的Rust编译器包装器问题。这个修复是确保Rust组件正确编译的重要环节。

在下一篇文章中,我们将继续探讨Chromium编译过程中的其他技术挑战。请确保按本文的步骤正确修复rustc_wrapper.py的问题,为后续的编译工作打下坚实基础。

相关推荐
技术与健康11 分钟前
【解读】Chrome 浏览器实验性功能全景
前端·chrome
Morris只会敲命令9 小时前
CentOS 如何使用截图工具截取命令行操作的图片?
前端·chrome
智绘前端10 小时前
Nuxt3中使用UnoCSS指南
前端·chrome
難釋懷1 天前
Shell脚本-for循环语法结构
前端·chrome
突头小恐龙1 天前
Chrome devTools - Lighthouse
前端·javascript·chrome
小诸葛的博客1 天前
详解Linux中的定时任务管理工具crond
linux·运维·chrome
~heart将心比心1 天前
chrome://inspect/#devices 调试 HTTP/1.1 404 Not Found 如何解决
前端·chrome
Bruce_Liuxiaowei1 天前
使用Python脚本在Mac上彻底清除Chrome浏览历史:开发实战与隐私保护指南
chrome·python·macos
浪裡遊2 天前
Linux常用指令
linux·运维·服务器·chrome·功能测试
鸿蒙布道师2 天前
OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑
前端·人工智能·chrome·深度学习·opencv·自然语言处理·chatgpt