523 lines
13 KiB
Python
523 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright 2023 The Chromium Authors
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
# Script to install everything needed to build chromium on Arch Linux
|
|
# including items requiring sudo privileges.
|
|
|
|
import argparse
|
|
import functools
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
logging.basicConfig(stream=sys.stderr,
|
|
level=logging.INFO,
|
|
format='%(name)s [%(levelname)s]: %(message)s')
|
|
logger = logging.getLogger(os.path.basename(sys.argv[0]))
|
|
|
|
|
|
@functools.lru_cache(maxsize=1)
|
|
def build_pacman_package_list():
|
|
logger.info("Building pacman package list.")
|
|
output = subprocess.check_output(["pacman", "-Q"]).decode()
|
|
return set(line.split()[0] for line in output.strip().splitlines())
|
|
|
|
|
|
def package_exists_in_repos(package_name: str) -> bool:
|
|
"""Check if package exists in repositories"""
|
|
result = subprocess.run(["pacman", "-Si", package_name],
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
return result.returncode == 0
|
|
|
|
|
|
def package_exists(package_name: str) -> bool:
|
|
# Check if package is installed
|
|
if package_name in build_pacman_package_list():
|
|
return True
|
|
# Check if package exists in repos
|
|
return package_exists_in_repos(package_name)
|
|
|
|
|
|
def parse_args(argv):
|
|
parser = argparse.ArgumentParser(
|
|
description="Install Chromium build dependencies on Arch Linux.")
|
|
parser.add_argument("--syms",
|
|
action="store_true",
|
|
help="Enable installation of debugging symbols")
|
|
parser.add_argument(
|
|
"--no-syms",
|
|
action="store_false",
|
|
dest="syms",
|
|
help="Disable installation of debugging symbols",
|
|
)
|
|
parser.add_argument(
|
|
"--lib32",
|
|
action="store_true",
|
|
help="Enable installation of 32-bit libraries, e.g. for V8 snapshot",
|
|
)
|
|
parser.add_argument("--arm",
|
|
action="store_true",
|
|
help="Enable installation of arm cross toolchain")
|
|
parser.add_argument(
|
|
"--no-arm",
|
|
action="store_false",
|
|
dest="arm",
|
|
help="Disable installation of arm cross toolchain",
|
|
)
|
|
parser.add_argument(
|
|
"--chromeos-fonts",
|
|
action="store_true",
|
|
help="Enable installation of Chrome OS fonts",
|
|
)
|
|
parser.add_argument(
|
|
"--no-chromeos-fonts",
|
|
action="store_false",
|
|
dest="chromeos_fonts",
|
|
help="Disable installation of Chrome OS fonts",
|
|
)
|
|
parser.add_argument("--no-prompt",
|
|
action="store_true",
|
|
help="Automatic yes to prompts")
|
|
parser.add_argument(
|
|
"--quick-check",
|
|
action="store_true",
|
|
help="Quickly try to determine if dependencies are installed",
|
|
)
|
|
parser.add_argument(
|
|
"--aur-helper",
|
|
default="yay",
|
|
help="AUR helper to use (default: yay)",
|
|
)
|
|
|
|
options = parser.parse_args(argv)
|
|
|
|
if options.arm:
|
|
options.lib32 = True
|
|
|
|
return options
|
|
|
|
|
|
def check_arch_linux():
|
|
try:
|
|
with open("/etc/os-release") as f:
|
|
content = f.read()
|
|
if "ID=arch" not in content and "ID_LIKE=arch" not in content:
|
|
logger.error("This script is designed for Arch Linux and derivatives")
|
|
sys.exit(1)
|
|
except FileNotFoundError:
|
|
logger.error("Cannot determine OS. This script is for Arch Linux.")
|
|
sys.exit(1)
|
|
|
|
|
|
def check_architecture():
|
|
architecture = subprocess.check_output(["uname", "-m"]).decode().strip()
|
|
if architecture not in ["i686", "x86_64", 'aarch64']:
|
|
logger.error("Only x86 and ARM64 architectures are currently supported")
|
|
sys.exit(1)
|
|
|
|
|
|
def check_root():
|
|
if os.geteuid() != 0:
|
|
logger.info("Running as non-root user.")
|
|
logger.info(
|
|
"You might have to enter your password one or more times for 'sudo'.\n")
|
|
|
|
|
|
def check_aur_helper(options):
|
|
if not shutil.which(options.aur_helper):
|
|
logger.error(f"AUR helper '{options.aur_helper}' not found.")
|
|
logger.error("Please install an AUR helper like 'yay' or 'paru'.")
|
|
logger.error("You can install yay with:")
|
|
logger.error(" git clone https://aur.archlinux.org/yay.git")
|
|
logger.error(" cd yay && makepkg -si")
|
|
sys.exit(1)
|
|
|
|
|
|
def pacman_update(options):
|
|
if options.lib32:
|
|
# Enable multilib repository
|
|
logger.info("Checking multilib repository...")
|
|
with open("/etc/pacman.conf", "r") as f:
|
|
config = f.read()
|
|
|
|
if "[multilib]" not in config or config.find("[multilib]") > config.find("#[multilib]"):
|
|
logger.info("Enabling multilib repository...")
|
|
subprocess.check_call([
|
|
"sudo", "sed", "-i",
|
|
"/^#\\[multilib\\]/,/^#Include = \\/etc\\/pacman.d\\/mirrorlist/ s/^#//",
|
|
"/etc/pacman.conf"
|
|
])
|
|
|
|
subprocess.check_call(["sudo", "pacman", "-Sy"])
|
|
|
|
|
|
# Core packages needed for Chromium development - verified Arch package names only
|
|
def get_core_packages():
|
|
"""Return core packages that definitely exist in Arch repos"""
|
|
return [
|
|
# Build essentials
|
|
"base-devel",
|
|
"git",
|
|
"python",
|
|
"curl",
|
|
"wget",
|
|
"unzip",
|
|
"zip",
|
|
"p7zip",
|
|
"bzip2",
|
|
"xz",
|
|
"patch",
|
|
"perl",
|
|
"ruby",
|
|
"nodejs",
|
|
"npm",
|
|
"bison",
|
|
"flex",
|
|
"gperf",
|
|
"pkgconf",
|
|
"nasm",
|
|
"yasm",
|
|
|
|
# Core libraries
|
|
"alsa-lib",
|
|
"at-spi2-core",
|
|
"cairo",
|
|
"cups",
|
|
"dbus",
|
|
"expat",
|
|
"fontconfig",
|
|
"freetype2",
|
|
"glib2",
|
|
"gtk3",
|
|
"krb5",
|
|
"libcap",
|
|
"libdrm",
|
|
"libelf",
|
|
"libevdev",
|
|
"libffi",
|
|
"mesa",
|
|
"libglvnd",
|
|
"libice",
|
|
"libinput",
|
|
"libjpeg-turbo",
|
|
"libpng",
|
|
"libpulse",
|
|
"libsm",
|
|
"libx11",
|
|
"libxau",
|
|
"libxcb",
|
|
"libxcomposite",
|
|
"libxcursor",
|
|
"libxdamage",
|
|
"libxdmcp",
|
|
"libxext",
|
|
"libxfixes",
|
|
"libxi",
|
|
"libxinerama",
|
|
"libxrandr",
|
|
"libxrender",
|
|
"libxshmfence",
|
|
"libxt",
|
|
"libxtst",
|
|
"nspr",
|
|
"nss",
|
|
"pam",
|
|
"pango",
|
|
"sqlite",
|
|
"systemd-libs",
|
|
"util-linux",
|
|
"vulkan-headers",
|
|
"vulkan-icd-loader",
|
|
"wayland",
|
|
"xorg-server",
|
|
"xorg-server-xvfb",
|
|
"zlib",
|
|
]
|
|
|
|
|
|
def get_optional_packages():
|
|
"""Return optional packages - only add if they exist"""
|
|
candidates = [
|
|
"bluez-libs",
|
|
"speech-dispatcher",
|
|
"libxss", # X11 screensaver
|
|
"pciutils", # PCI utilities
|
|
"xcompmgr", # Composite manager
|
|
"fd", # Find alternative
|
|
"ripgrep", # Fast grep
|
|
"lighttpd", # Web server
|
|
"openbox", # Window manager
|
|
]
|
|
|
|
packages = []
|
|
for pkg in candidates:
|
|
if package_exists_in_repos(pkg):
|
|
packages.append(pkg)
|
|
else:
|
|
logger.debug(f"Optional package '{pkg}' not found, skipping.")
|
|
|
|
return packages
|
|
|
|
|
|
def dev_list():
|
|
"""Get development packages"""
|
|
packages = get_core_packages() + get_optional_packages()
|
|
return packages
|
|
|
|
|
|
def lib_list():
|
|
"""Get runtime library packages"""
|
|
packages = [
|
|
"atk",
|
|
"at-spi2-atk",
|
|
"cairo",
|
|
"cups",
|
|
"dbus",
|
|
"expat",
|
|
"fontconfig",
|
|
"freetype2",
|
|
"glib2",
|
|
"gtk3",
|
|
"libdrm",
|
|
"libglvnd",
|
|
"libpulse",
|
|
"libx11",
|
|
"libxcb",
|
|
"libxcomposite",
|
|
"libxcursor",
|
|
"libxdamage",
|
|
"libxext",
|
|
"libxfixes",
|
|
"libxi",
|
|
"libxinerama",
|
|
"libxrandr",
|
|
"libxrender",
|
|
"libxtst",
|
|
"mesa",
|
|
"pango",
|
|
"wayland",
|
|
"zlib",
|
|
]
|
|
return packages
|
|
|
|
|
|
def lib32_list(options):
|
|
if not options.lib32:
|
|
logger.info("Skipping 32-bit libraries.")
|
|
return []
|
|
logger.info("Including 32-bit libraries.")
|
|
|
|
packages = [
|
|
"lib32-alsa-lib",
|
|
"lib32-atk",
|
|
"lib32-cairo",
|
|
"lib32-dbus",
|
|
"lib32-expat",
|
|
"lib32-fontconfig",
|
|
"lib32-freetype2",
|
|
"lib32-gcc-libs",
|
|
"lib32-glib2",
|
|
"lib32-glibc",
|
|
"lib32-gtk3",
|
|
"lib32-libdrm",
|
|
"lib32-libglvnd",
|
|
"lib32-libx11",
|
|
"lib32-libxcb",
|
|
"lib32-libxcomposite",
|
|
"lib32-libxcursor",
|
|
"lib32-libxdamage",
|
|
"lib32-libxext",
|
|
"lib32-libxfixes",
|
|
"lib32-libxi",
|
|
"lib32-libxinerama",
|
|
"lib32-libxrandr",
|
|
"lib32-libxrender",
|
|
"lib32-libxtst",
|
|
"lib32-mesa",
|
|
"lib32-nspr",
|
|
"lib32-nss",
|
|
"lib32-pango",
|
|
"lib32-zlib",
|
|
]
|
|
|
|
return packages
|
|
|
|
|
|
def arm_list(options):
|
|
if not options.arm:
|
|
logger.info("Skipping ARM cross toolchain.")
|
|
return []
|
|
logger.info("Including ARM cross toolchain.")
|
|
|
|
# ARM cross toolchain packages (these are AUR packages)
|
|
packages = [
|
|
"arm-linux-gnueabihf-gcc",
|
|
"arm-linux-gnueabihf-glibc",
|
|
"arm-linux-gnueabihf-binutils",
|
|
"arm-linux-gnueabihf-linux-api-headers",
|
|
]
|
|
|
|
return packages
|
|
|
|
|
|
def dbg_list(options):
|
|
if not options.syms:
|
|
logger.info("Skipping debugging symbols.")
|
|
return []
|
|
logger.info("Including debugging symbols.")
|
|
|
|
# Arch doesn't have separate debug packages like Debian/Ubuntu
|
|
logger.info("Note: Debug symbols in Arch are typically included in packages")
|
|
logger.info("or available through debug repositories. No additional packages needed.")
|
|
|
|
return []
|
|
|
|
|
|
def package_list(options):
|
|
packages = (dev_list() + lib_list() + dbg_list(options) +
|
|
lib32_list(options))
|
|
|
|
# ARM packages are from AUR, handle separately
|
|
arm_packages = arm_list(options) if options.arm else []
|
|
|
|
return list(set(packages)), arm_packages
|
|
|
|
|
|
def missing_packages(packages):
|
|
installed = build_pacman_package_list()
|
|
missing = []
|
|
for pkg in packages:
|
|
if pkg not in installed:
|
|
# Double-check that the package actually exists in repos
|
|
if package_exists_in_repos(pkg):
|
|
missing.append(pkg)
|
|
else:
|
|
logger.warning(f"Package '{pkg}' does not exist in repositories, skipping.")
|
|
return missing
|
|
|
|
|
|
def quick_check(options):
|
|
if not options.quick_check:
|
|
return
|
|
|
|
regular_packages, aur_packages = package_list(options)
|
|
missing_regular = missing_packages(regular_packages)
|
|
missing_aur = missing_packages(aur_packages)
|
|
|
|
if not missing_regular and not missing_aur:
|
|
sys.exit(0)
|
|
|
|
if missing_regular:
|
|
logger.warning("The following official packages are not installed:")
|
|
logger.warning(" ".join(missing_regular))
|
|
|
|
if missing_aur:
|
|
logger.warning("The following AUR packages are not installed:")
|
|
logger.warning(" ".join(missing_aur))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
def install_packages(options):
|
|
regular_packages, aur_packages = package_list(options)
|
|
|
|
# Install regular packages with pacman
|
|
missing_regular = missing_packages(regular_packages)
|
|
if missing_regular:
|
|
logger.info(f"Installing {len(missing_regular)} regular packages...")
|
|
cmd = ["sudo", "pacman", "-S", "--needed"]
|
|
if options.no_prompt:
|
|
cmd.append("--noconfirm")
|
|
subprocess.check_call(cmd + missing_regular)
|
|
else:
|
|
logger.info("All regular packages are already installed.")
|
|
|
|
# Install AUR packages
|
|
missing_aur = missing_packages(aur_packages)
|
|
if missing_aur:
|
|
logger.info(f"Installing {len(missing_aur)} AUR packages...")
|
|
cmd = [options.aur_helper, "-S", "--needed"]
|
|
if options.no_prompt:
|
|
cmd.append("--noconfirm")
|
|
subprocess.check_call(cmd + missing_aur)
|
|
else:
|
|
if aur_packages:
|
|
logger.info("All AUR packages are already installed.")
|
|
|
|
|
|
def install_chromeos_fonts(options):
|
|
if not options.chromeos_fonts:
|
|
logger.info("Skipping installation of Chrome OS fonts.")
|
|
return
|
|
|
|
logger.info("Installing Chrome OS fonts.")
|
|
|
|
# Check if we have the font installation script
|
|
script_dir = os.path.abspath(os.path.dirname(__file__))
|
|
font_script = os.path.join(script_dir, "linux", "install-chromeos-fonts.py")
|
|
|
|
if os.path.exists(font_script):
|
|
try:
|
|
subprocess.check_call(["sudo", font_script])
|
|
except subprocess.CalledProcessError:
|
|
logger.error("The installation of the Chrome OS default fonts failed.")
|
|
logger.info("You can skip this with --no-chromeos-fonts.")
|
|
else:
|
|
logger.warning("Chrome OS font installation script not found.")
|
|
logger.info("This is normal if you're not building from the full Chromium source tree.")
|
|
|
|
|
|
def install_locales():
|
|
logger.info("Installing locales.")
|
|
|
|
# Arch Linux locale setup is different from Debian/Ubuntu
|
|
CHROMIUM_LOCALES = [
|
|
"da_DK.UTF-8", "en_US.UTF-8", "fr_FR.UTF-8", "he_IL.UTF-8", "zh_TW.UTF-8"
|
|
]
|
|
|
|
LOCALE_GEN = "/etc/locale.gen"
|
|
if os.path.exists(LOCALE_GEN):
|
|
with open(LOCALE_GEN, 'r') as f:
|
|
current_config = f.read()
|
|
|
|
needs_update = False
|
|
for locale in CHROMIUM_LOCALES:
|
|
if f"#{locale}" in current_config and f"\n{locale}" not in current_config:
|
|
subprocess.check_call([
|
|
"sudo", "sed", "-i",
|
|
f"s/^#{locale}/{locale}/",
|
|
LOCALE_GEN
|
|
])
|
|
needs_update = True
|
|
|
|
if needs_update:
|
|
subprocess.check_call(["sudo", "locale-gen"])
|
|
else:
|
|
logger.info("Locales already up-to-date.")
|
|
else:
|
|
logger.warning("locale.gen not found. Locales may need manual configuration.")
|
|
|
|
|
|
def main():
|
|
options = parse_args(sys.argv[1:])
|
|
check_arch_linux()
|
|
check_architecture()
|
|
quick_check(options)
|
|
check_root()
|
|
check_aur_helper(options)
|
|
pacman_update(options)
|
|
install_packages(options)
|
|
install_chromeos_fonts(options)
|
|
install_locales()
|
|
logger.info("Chromium build dependencies installation complete!")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|