mirror of
https://github.com/servo/servo.git
synced 2025-10-12 14:30:25 +01:00
This PR re-enables support for the gstreamer mediastack in macOS by consuming the official binary '.pkg' files from gstreamer.freedesktop.org To maintain symmetry with other platforms, the '.pkg' files are uploaded to servo-build-deps and fetched from there using the new script 'etc/install_macos_gstreamer.sh'. Unlike the Homebrew version, the official GStreamer is distributed as a 'relocatable' framework i.e the dylibs all have @rpath-relative install names and also link to other dylibs using @rpath relative path. To address this difference the 'servo' binary needs to be patched with 'install_name_tool' to add an LC_RPATH command that sets the relative paths that the dynamic linker should search when trying to satify dependencies. In Servo's case, this will be a path relative to the 'servo' binary itself i.e '@executable_path/lib/' The additional 'lib' is due to a flaw in the gstreamer packaging where the install names of some of the dylibs have the prefix '@rpath/lib' and some of them just have '@rpath'. This PR also fixes a couple of issues present in the `mach build` process on MacOS: 1. `mach build` process was not copying transitive dependencies of servo binary but only the first level dylibs 2. `mach build` process didn't patch the links to dylibs in servo binary (and dependencies). This meant though (some) dylibs were copied to local path, the binary still loaded the dylibs from system GStreamer installation i.e homebrew instead of the copieds dylibs The build and runtime dependencies in etc/homebrew/Brewfile and etc/homebrew/Brewfile-build have also been removed in This PR. Signed-off-by: Mukilan Thiyagarajan <me@mukilan.in>
314 lines
11 KiB
Python
314 lines
11 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
from __future__ import absolute_import, print_function
|
|
|
|
from distutils.spawn import find_executable
|
|
from distutils.version import LooseVersion
|
|
import os
|
|
import distro
|
|
import subprocess
|
|
import six
|
|
import urllib
|
|
|
|
from os import path
|
|
from subprocess import PIPE
|
|
from zipfile import BadZipfile
|
|
|
|
import servo.packages as packages
|
|
from servo.util import extract, download_file, host_triple
|
|
from servo.gstreamer import macos_gst_root
|
|
|
|
|
|
def check_macos_gstreamer_lib():
|
|
try:
|
|
env = os.environ.copy()
|
|
gst_root = macos_gst_root()
|
|
env["PATH"] = path.join(gst_root, "bin")
|
|
env["PKG_CONFIG_PATH"] = path.join(gst_root, "lib", "pkgconfig")
|
|
has_gst = subprocess.call(["pkg-config", "--atleast-version=1.21", "gstreamer-1.0"],
|
|
stdout=PIPE, stderr=PIPE, env=env) == 0
|
|
gst_lib_dir = subprocess.check_output(["pkg-config", "--variable=libdir", "gstreamer-1.0"],
|
|
env=env)
|
|
return has_gst and gst_lib_dir.startswith(bytes(gst_root, 'utf-8'))
|
|
except FileNotFoundError:
|
|
return False
|
|
|
|
|
|
def check_gstreamer_lib():
|
|
return subprocess.call(["pkg-config", "--atleast-version=1.16", "gstreamer-1.0"],
|
|
stdout=PIPE, stderr=PIPE) == 0
|
|
|
|
|
|
def run_as_root(command, force=False):
|
|
if os.geteuid() != 0:
|
|
command.insert(0, 'sudo')
|
|
if force:
|
|
command.append('-y')
|
|
return subprocess.call(command)
|
|
|
|
|
|
def install_linux_deps(context, pkgs_ubuntu, pkgs_fedora, pkgs_void, force):
|
|
install = False
|
|
pkgs = []
|
|
if context.distro in ['Ubuntu', 'Debian GNU/Linux']:
|
|
command = ['apt-get', 'install']
|
|
pkgs = pkgs_ubuntu
|
|
if subprocess.call(['dpkg', '-s'] + pkgs, stdout=PIPE, stderr=PIPE) != 0:
|
|
install = True
|
|
elif context.distro in ['CentOS', 'CentOS Linux', 'Fedora', 'Fedora Linux']:
|
|
installed_pkgs = str(subprocess.check_output(['rpm', '-qa'])).replace('\n', '|')
|
|
pkgs = pkgs_fedora
|
|
for p in pkgs:
|
|
command = ['dnf', 'install']
|
|
if "|{}".format(p) not in installed_pkgs:
|
|
install = True
|
|
break
|
|
elif context.distro == 'void':
|
|
installed_pkgs = str(subprocess.check_output(['xbps-query', '-l']))
|
|
pkgs = pkgs_void
|
|
for p in pkgs:
|
|
command = ['xbps-install', '-A']
|
|
if "ii {}-".format(p) not in installed_pkgs:
|
|
install = force = True
|
|
break
|
|
|
|
if not install:
|
|
return False
|
|
|
|
print("Installing missing dependencies...")
|
|
if run_as_root(command + pkgs, force) != 0:
|
|
raise Exception("Installation of dependencies failed.")
|
|
return True
|
|
|
|
|
|
def gstreamer(context, force=False):
|
|
cur = os.curdir
|
|
gstdir = os.path.join(cur, "support", "linux", "gstreamer")
|
|
if not os.path.isdir(os.path.join(gstdir, "gst", "lib")):
|
|
subprocess.check_call(["bash", "gstreamer.sh"], cwd=gstdir)
|
|
return True
|
|
return False
|
|
|
|
|
|
def bootstrap_gstreamer(context, force=False):
|
|
if not gstreamer(context, force):
|
|
print("gstreamer is already set up")
|
|
return 0
|
|
|
|
|
|
def linux(context, force=False):
|
|
# Please keep these in sync with the packages in README.md
|
|
pkgs_apt = ['git', 'curl', 'autoconf', 'libx11-dev', 'libfreetype6-dev',
|
|
'libgl1-mesa-dri', 'libglib2.0-dev', 'xorg-dev', 'gperf', 'g++',
|
|
'build-essential', 'cmake', 'libssl-dev',
|
|
'liblzma-dev', 'libxmu6', 'libxmu-dev',
|
|
"libxcb-render0-dev", "libxcb-shape0-dev", "libxcb-xfixes0-dev",
|
|
'libgles2-mesa-dev', 'libegl1-mesa-dev', 'libdbus-1-dev',
|
|
'libharfbuzz-dev', 'ccache', 'clang', 'libunwind-dev',
|
|
'libgstreamer1.0-dev', 'libgstreamer-plugins-base1.0-dev',
|
|
'libgstreamer-plugins-bad1.0-dev', 'autoconf2.13',
|
|
'libunwind-dev', 'llvm-dev']
|
|
pkgs_dnf = ['libtool', 'gcc-c++', 'libXi-devel', 'freetype-devel',
|
|
'libunwind-devel', 'mesa-libGL-devel', 'mesa-libEGL-devel',
|
|
'glib2-devel', 'libX11-devel', 'libXrandr-devel', 'gperf',
|
|
'fontconfig-devel', 'cabextract', 'ttmkfdir', 'expat-devel',
|
|
'rpm-build', 'openssl-devel', 'cmake',
|
|
'libXcursor-devel', 'libXmu-devel',
|
|
'dbus-devel', 'ncurses-devel', 'harfbuzz-devel', 'ccache',
|
|
'clang', 'clang-libs', 'llvm', 'autoconf213', 'python3-devel',
|
|
'gstreamer1-devel', 'gstreamer1-plugins-base-devel',
|
|
'gstreamer1-plugins-bad-free-devel', 'libjpeg-turbo-devel',
|
|
'zlib', 'libjpeg']
|
|
pkgs_xbps = ['libtool', 'gcc', 'libXi-devel', 'freetype-devel',
|
|
'libunwind-devel', 'MesaLib-devel', 'glib-devel', 'pkg-config',
|
|
'libX11-devel', 'libXrandr-devel', 'gperf', 'bzip2-devel',
|
|
'fontconfig-devel', 'cabextract', 'expat-devel', 'cmake',
|
|
'cmake', 'libXcursor-devel', 'libXmu-devel', 'dbus-devel',
|
|
'ncurses-devel', 'harfbuzz-devel', 'ccache', 'glu-devel',
|
|
'clang', 'gstreamer1-devel', 'autoconf213',
|
|
'gst-plugins-base1-devel', 'gst-plugins-bad1-devel']
|
|
|
|
installed_something = install_linux_deps(context, pkgs_apt, pkgs_dnf,
|
|
pkgs_xbps, force)
|
|
|
|
if not check_gstreamer_lib():
|
|
installed_something |= gstreamer(context, force)
|
|
|
|
if not installed_something:
|
|
print("Dependencies were already installed!")
|
|
|
|
return 0
|
|
|
|
|
|
def windows_msvc(context, force=False):
|
|
'''Bootstrapper for MSVC building on Windows.'''
|
|
|
|
deps_dir = os.path.join(context.sharedir, "msvc-dependencies")
|
|
deps_url = "https://github.com/servo/servo-build-deps/releases/download/msvc-deps/"
|
|
|
|
def version(package):
|
|
return packages.WINDOWS_MSVC[package]
|
|
|
|
def package_dir(package):
|
|
return os.path.join(deps_dir, package, version(package))
|
|
|
|
def check_cmake(version):
|
|
cmake_path = find_executable("cmake")
|
|
if cmake_path:
|
|
cmake = subprocess.Popen([cmake_path, "--version"], stdout=PIPE)
|
|
cmake_version_output = six.ensure_str(cmake.stdout.read()).splitlines()[0]
|
|
cmake_version = cmake_version_output.replace("cmake version ", "")
|
|
if LooseVersion(cmake_version) >= LooseVersion(version):
|
|
return True
|
|
return False
|
|
|
|
def prepare_file(zip_path, full_spec):
|
|
if not os.path.isfile(zip_path):
|
|
zip_url = "{}{}.zip".format(deps_url, urllib.parse.quote(full_spec))
|
|
download_file(full_spec, zip_url, zip_path)
|
|
|
|
print("Extracting {}...".format(full_spec), end='')
|
|
try:
|
|
extract(zip_path, deps_dir)
|
|
except BadZipfile:
|
|
print("\nError: %s.zip is not a valid zip file, redownload..." % full_spec)
|
|
os.remove(zip_path)
|
|
prepare_file(zip_path, full_spec)
|
|
else:
|
|
print("done")
|
|
|
|
to_install = {}
|
|
for package in packages.WINDOWS_MSVC:
|
|
# Don't install CMake if it already exists in PATH
|
|
if package == "cmake" and check_cmake(version("cmake")):
|
|
continue
|
|
|
|
if not os.path.isdir(package_dir(package)):
|
|
to_install[package] = version(package)
|
|
|
|
if not to_install:
|
|
return 0
|
|
|
|
print("Installing missing MSVC dependencies...")
|
|
for package in to_install:
|
|
full_spec = '{}-{}'.format(package, version(package))
|
|
|
|
parent_dir = os.path.dirname(package_dir(package))
|
|
if not os.path.isdir(parent_dir):
|
|
os.makedirs(parent_dir)
|
|
|
|
zip_path = package_dir(package) + ".zip"
|
|
prepare_file(zip_path, full_spec)
|
|
|
|
extracted_path = os.path.join(deps_dir, full_spec)
|
|
os.rename(extracted_path, package_dir(package))
|
|
|
|
return 0
|
|
|
|
|
|
LINUX_SPECIFIC_BOOTSTRAPPERS = {
|
|
"gstreamer": bootstrap_gstreamer,
|
|
}
|
|
|
|
|
|
def get_linux_distribution():
|
|
distrib = six.ensure_str(distro.name())
|
|
version = six.ensure_str(distro.version())
|
|
|
|
if distrib in ['LinuxMint', 'Linux Mint', 'KDE neon']:
|
|
if '.' in version:
|
|
major, _ = version.split('.', 1)
|
|
else:
|
|
major = version
|
|
|
|
if major == '22':
|
|
base_version = '22.04'
|
|
elif major == '21':
|
|
base_version = '21.04'
|
|
elif major == '20':
|
|
base_version = '20.04'
|
|
elif major == '19':
|
|
base_version = '18.04'
|
|
elif major == '18':
|
|
base_version = '16.04'
|
|
else:
|
|
raise Exception('unsupported version of %s: %s' % (distrib, version))
|
|
|
|
distrib, version = 'Ubuntu', base_version
|
|
elif distrib == 'Pop!_OS':
|
|
if '.' in version:
|
|
major, _ = version.split('.', 1)
|
|
else:
|
|
major = version
|
|
|
|
if major == '22':
|
|
base_version = '22.04'
|
|
elif major == '21':
|
|
base_version = '21.04'
|
|
elif major == '20':
|
|
base_version = '20.04'
|
|
elif major == '19':
|
|
base_version = '18.04'
|
|
elif major == '18':
|
|
base_version = '16.04'
|
|
else:
|
|
raise Exception('unsupported version of %s: %s' % (distrib, version))
|
|
|
|
distrib, version = 'Ubuntu', base_version
|
|
elif distrib.lower() == 'elementary':
|
|
if version == '5.0':
|
|
base_version = '18.04'
|
|
elif version[0:3] == '0.4':
|
|
base_version = '16.04'
|
|
else:
|
|
raise Exception('unsupported version of %s: %s' % (distrib, version))
|
|
distrib, version = 'Ubuntu', base_version
|
|
elif distrib.lower() == 'ubuntu':
|
|
if version > '22.04':
|
|
print('WARNING: unsupported version of %s: %s' % (distrib, version))
|
|
# Fixme: we should allow checked/supported versions only
|
|
elif distrib.lower() not in [
|
|
'centos',
|
|
'centos linux',
|
|
'debian gnu/linux',
|
|
'fedora',
|
|
'fedora linux',
|
|
'void',
|
|
'nixos',
|
|
'arch',
|
|
'arch linux',
|
|
]:
|
|
raise Exception('mach bootstrap does not support %s, please file a bug' % distrib)
|
|
|
|
return distrib, version
|
|
|
|
|
|
def bootstrap(context, force=False, specific=None):
|
|
'''Dispatches to the right bootstrapping function for the OS.'''
|
|
|
|
bootstrapper = None
|
|
if "windows-msvc" in host_triple():
|
|
bootstrapper = windows_msvc
|
|
elif "linux-gnu" in host_triple():
|
|
distrib, version = get_linux_distribution()
|
|
|
|
if distrib.lower() == 'nixos':
|
|
print('NixOS does not need bootstrap, it will automatically enter a nix-shell')
|
|
print('Just run ./mach build')
|
|
print('')
|
|
print('You will need to run a nix-shell if you are trying to run any of the built binaries')
|
|
print('To enter the nix-shell manually use:')
|
|
print(' $ nix-shell etc/shell.nix')
|
|
return
|
|
|
|
context.distro = distrib
|
|
context.distro_version = version
|
|
bootstrapper = LINUX_SPECIFIC_BOOTSTRAPPERS.get(specific, linux)
|
|
|
|
if bootstrapper is None:
|
|
print('Bootstrap support is not yet available for your OS.')
|
|
return 1
|
|
|
|
return bootstrapper(context, force=force)
|