blob: e5b996887dac603c637e2389cb630b13f6af3940 [file] [log] [blame]
# Copyright 2024 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utilities to create the first commit at the base of a new llvm branch.
Used primarily by new branch workflows and patch management tooling.
"""
from pathlib import Path
import re
import shutil
from cros_utils import git_utils
from llvm_tools import patch_utils
# This isn't a dict to prevent adding a tomli-w dependency.
PRESUBMIT_CFG_CONTENTS = """\
[Hook Overrides]
cros_license_check: True
long_line_check: True
[Hook Overrides Options]
cros_license_check: --exclude_regex=.*
long_line_check: --exclude_regex=.*
"""
CROS_DIR_README = """\
# CrOS Directory
This directory is used to store arbitrary changes for the ChromeOS Toolchain
team. Files in this directory are never meant to be upstreamed, and only
exist for local modification.
See src/third_party/toolchain-utils to see how this directory is configured.
"""
BASE_COMMIT_MESSAGE = """\
llvm-project: ChromeOS Base Commit
This is the LLVM ChromeOS Base Commit.
This commit marks the start of the ChromeOS patch branch. It introduces
the OWNERS file, and sets up the 'cros' directory for future use.
Functional patches for the ChromeOS LLVM Toolchain land after this
commit. This commit does not change how LLVM operates. The parent
commit to this change determines the LLVM synthetic revision.
BUG=None
TEST=CQ
"""
def make_base_commit(
toolchain_utils_dir: Path, llvm_src_dir: Path, chromiumos_overlay: Path
) -> None:
"""Create a commit which represents the base of a ChromeOS branch."""
toolchain_utils_copy_files = (
"OWNERS",
"OWNERS.toolchain",
)
for copy_file in toolchain_utils_copy_files:
shutil.copy(toolchain_utils_dir / copy_file, llvm_src_dir / copy_file)
(llvm_src_dir / "PRESUBMIT.cfg").write_text(PRESUBMIT_CFG_CONTENTS)
write_all_gentoo_cmake_hacks(llvm_src_dir, chromiumos_overlay)
set_up_cros_dir(llvm_src_dir)
git_utils.commit_all_changes(llvm_src_dir, BASE_COMMIT_MESSAGE)
def set_up_cros_dir(llvm_src_dir: Path) -> None:
"""Create and init the llvm-project/cros directory."""
cros_dir = llvm_src_dir / "cros"
cros_dir.mkdir()
readme = cros_dir / "README.md"
readme.write_text(CROS_DIR_README)
def write_gentoo_cmake_hack(llvm_src_dir: Path, ebuild_dir: Path) -> None:
"""Modifies cmake files in LLVM so cmake.eclass doesn't modify them.
Args:
llvm_src_dir: Path to llvm-project git root we want to modify.
ebuild_dir: Path to sys-devel/llvm Portage package directory.
"""
# Upstream's `cmake.eclass` will try to override "dangerous" configurations
# that override Gentoo settings. There's no way to skip this override, but
# it _does_ have logic to detect if it has already run & skips all
# modifications in that case. Since LLVM has no such "dangerous" settings,
# and the `9999` ebuild never "goes live," it's safe to skip these.
# The file to modify is the 'main' cmake file, which is determined based on
# `CMAKE_USE_DIR`. Parsing that out isn't _too_ painful, so try it.
ebuild_path = _find_ebuild_in_dir(ebuild_dir)
ebuild_contents = ebuild_path.read_text(encoding="utf-8")
cmake_use_dir_re = re.compile(
# Use string concatenation rather than re.VERBOSE, since this regex
# goes in an error message on failure, and that's _really_ hard to
# read.
r"^\s*"
# While these all use `export`, it's not strictly required by
# cmake.eclass.
r"(?:export\s+)?" r'CMAKE_USE_DIR="\$\{S\}/([^"]+)"',
re.MULTILINE,
)
cmake_use_dirs = cmake_use_dir_re.findall(ebuild_contents)
if len(cmake_use_dirs) != 1:
raise ValueError(
f"Expected to find 1 match of {cmake_use_dir_re} in "
f"{ebuild_path}; found {len(cmake_use_dirs)}"
)
cmake_file = llvm_src_dir / cmake_use_dirs[0] / "CMakeLists.txt"
special_marker = "<<< Gentoo configuration >>>"
if special_marker in cmake_file.read_text(encoding="utf-8"):
return
with cmake_file.open("a", encoding="utf-8") as f:
f.write(
f"\n# HACK from llvm_project_base_commit.py:\n# {special_marker}"
)
def write_all_gentoo_cmake_hacks(
llvm_src_dir: Path, chromiumos_overlay: Path
) -> None:
"""Writes gentoo cmake hacks for all known LLVM 9999 subprojects."""
for subproject in patch_utils.CHROMEOS_PATCHES_JSON_PACKAGES:
# N.B., this function will do nothing if the cmake file it aims to
# modify has the marker already. Hence, calling it unconditionally on
# all projects is fine.
write_gentoo_cmake_hack(llvm_src_dir, chromiumos_overlay / subproject)
def _find_ebuild_in_dir(ebuild_dir: Path) -> Path:
"""Returns the path to a 9999 ebuild in `ebuild_dir`; raises if none."""
candidates = list(ebuild_dir.glob("*-9999.ebuild"))
if len(candidates) != 1:
raise ValueError(
f"Expected exactly one 9999 ebuild in {ebuild_dir}; found "
f"{candidates}"
)
return candidates[0]