Download LLVM to use clang-cl for Window (#1193)

As a preparation to start using clang-cl for Windows (#1179), with this
commit

  python build_tools/update_deps.py

will start deploying LLVM for windows into

  src/third_party/llvm/

by downloading the archive from LLVM's GitHub releases page.

The downloaded clang-cl is not yet used.

PiperOrigin-RevId: 730872577
This commit is contained in:
Yohei Yukawa
2025-02-26 15:33:56 +09:00
committed by GitHub
parent 87145233d2
commit a924c05f2e
3 changed files with 127 additions and 0 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ MODULE.bazel.lock
.DS_Store .DS_Store
# third_party dirs and cache dir checked out by update_deps.py # third_party dirs and cache dir checked out by update_deps.py
/src/third_party/llvm/
/src/third_party/ndk/ /src/third_party/ndk/
/src/third_party/ninja/ /src/third_party/ninja/
/src/third_party/qt/ /src/third_party/qt/

View File

@ -68,6 +68,7 @@ python build_tools/update_deps.py
In this step, additional build dependencies will be downloaded. In this step, additional build dependencies will be downloaded.
* [LLVM 19.1.7](https://github.com/llvm/llvm-project/releases/tag/llvmorg-19.1.7)
* [Ninja 1.11.0](https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-win.zip) * [Ninja 1.11.0](https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-win.zip)
* [Qt 6.8.0](https://download.qt.io/archive/qt/6.8/6.8.0/submodules/qtbase-everywhere-src-6.8.0.tar.xz) * [Qt 6.8.0](https://download.qt.io/archive/qt/6.8/6.8.0/submodules/qtbase-everywhere-src-6.8.0.tar.xz)
* [.NET tools](../dotnet-tools.json) * [.NET tools](../dotnet-tools.json)

View File

@ -35,6 +35,7 @@ build for OSS Mozc.
""" """
import argparse import argparse
from collections.abc import Iterator
import dataclasses import dataclasses
import hashlib import hashlib
import os import os
@ -42,6 +43,10 @@ import pathlib
import shutil import shutil
import stat import stat
import subprocess import subprocess
import sys
import tarfile
import time
from typing import Union
import zipfile import zipfile
import requests import requests
@ -109,6 +114,11 @@ NINJA_WIN = ArchiveInfo(
sha256='d0ee3da143211aa447e750085876c9b9d7bcdd637ab5b2c5b41349c617f22f3b', sha256='d0ee3da143211aa447e750085876c9b9d7bcdd637ab5b2c5b41349c617f22f3b',
) )
LLVM_WIN = ArchiveInfo(
url='https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.7/clang+llvm-19.1.7-x86_64-pc-windows-msvc.tar.xz',
size=845236708,
sha256='b4557b4f012161f56a2f5d9e877ab9635cafd7a08f7affe14829bd60c9d357f0',
)
def get_sha256(path: pathlib.Path) -> str: def get_sha256(path: pathlib.Path) -> str:
"""Returns SHA-256 hash digest of the specified file. """Returns SHA-256 hash digest of the specified file.
@ -182,6 +192,115 @@ def download(archive: ArchiveInfo, dryrun: bool = False) -> None:
) )
def llvm_extract_filter(
members: Iterator[tarfile.TarInfo],
) -> Iterator[tarfile.TarInfo]:
"""Custom extract filter for the LLVM Tar file.
This custom filter can be used to adjust directory structure and drop
unnecessary files/directories to save disk space.
Args:
members: an iterator of TarInfo from the Tar file.
Yields:
An iterator of TarInfo to be extracted.
"""
with ProgressPrinter() as printer:
for info in members:
paths = info.name.split('/')
if '..' in paths:
continue
if len(paths) < 1:
continue
skipping = True
if (
len(paths) == 3
and paths[1] == 'bin'
and paths[2] in ['clang-cl.exe', 'llvm-lib.exe', 'lld-link.exe']
):
skipping = False
elif len(paths) >= 2 and paths[1] in ['include', 'lib']:
skipping = False
if skipping:
printer.print_line('skipping ' + info.name)
continue
printer.print_line('extracting ' + info.name)
yield info
class StatefulLLVMExtractionFilter:
"""A stateful extraction filter for PEP 706.
See https://peps.python.org/pep-0706/ for details.
"""
def __enter__(self):
self.printer = ProgressPrinter().__enter__()
return self
def __exit__(self, *exc):
self.printer.__exit__(exc)
def __call__(
self,
member: tarfile.TarInfo,
dest_path: Union[str, pathlib.Path],
) -> Union[tarfile.TarInfo, None]:
data = tarfile.data_filter(member, dest_path)
if data is None:
return None
skipping = True
paths = member.name.split('/')
if (
len(paths) == 3
and paths[1] == 'bin'
and paths[2] in ['clang-cl.exe', 'llvm-lib.exe', 'lld-link.exe']
):
skipping = False
elif len(paths) >= 2 and paths[1] in ['include', 'lib']:
skipping = False
if skipping:
self.printer.print_line('skipping ' + member.name)
return None
self.printer.print_line('extracting ' + member.name)
return member
def extract_llvm(dryrun: bool = False) -> None:
"""Extract LLVM archive.
Args:
dryrun: True if this is a dry-run.
"""
if not is_windows():
return
archive = LLVM_WIN
src = CACHE_DIR.joinpath(archive.filename)
dest = ABS_THIRD_PARTY_DIR.joinpath('llvm').absolute()
if dest.exists():
if dryrun:
print(f"dryrun: shutil.rmtree(r'{dest}')")
else:
shutil.rmtree(dest)
if dryrun:
print(f'dryrun: Extracting {src} into {dest}')
else:
dest.mkdir(parents=True)
with tarfile.open(src, mode='r|xz') as f:
# tarfile.data_filter is available in Python 3.12+.
# See https://peps.python.org/pep-0706/ for details.
if getattr(tarfile, 'data_filter', None):
with StatefulLLVMExtractionFilter() as filter:
f.extractall(path=dest, filter=filter)
else:
f.extractall(path=dest, members=llvm_extract_filter(f))
def extract_ninja(dryrun: bool = False) -> None: def extract_ninja(dryrun: bool = False) -> None:
"""Extract ninja-win archive. """Extract ninja-win archive.
@ -329,6 +448,7 @@ def main():
parser.add_argument('--dryrun', action='store_true', default=False) parser.add_argument('--dryrun', action='store_true', default=False)
parser.add_argument('--noninja', action='store_true', default=False) parser.add_argument('--noninja', action='store_true', default=False)
parser.add_argument('--noqt', action='store_true', default=False) parser.add_argument('--noqt', action='store_true', default=False)
parser.add_argument('--nollvm', action='store_true', default=False)
parser.add_argument('--nowix', action='store_true', default=False) parser.add_argument('--nowix', action='store_true', default=False)
parser.add_argument('--nondk', action='store_true', default=False) parser.add_argument('--nondk', action='store_true', default=False)
parser.add_argument('--nosubmodules', action='store_true', default=False) parser.add_argument('--nosubmodules', action='store_true', default=False)
@ -349,6 +469,8 @@ def main():
archives.append(NDK_LINUX) archives.append(NDK_LINUX)
elif is_mac(): elif is_mac():
archives.append(NDK_MAC) archives.append(NDK_MAC)
if (not args.nollvm) and is_windows():
archives.append(LLVM_WIN)
for archive in archives: for archive in archives:
download(archive, args.dryrun) download(archive, args.dryrun)
@ -356,6 +478,9 @@ def main():
if args.cache_only: if args.cache_only:
return return
if LLVM_WIN in archives:
extract_llvm(args.dryrun)
if (not args.nowix) and is_windows(): if (not args.nowix) and is_windows():
restore_dotnet_tools(args.dryrun) restore_dotnet_tools(args.dryrun)