blob: 39ecf8e8e4bbd84dd10e5acba30eafea6928fbc7 [file] [log] [blame]
# Copyright (C) 2017 Igalia S.L.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
import argparse
import logging
try:
import configparser
except ImportError:
import ConfigParser as configparser
from contextlib import contextmanager
import errno
import json
import multiprocessing
import os
import shlex
import shutil
import signal
import subprocess
import sys
import tempfile
import re
from webkitpy.common.system.systemhost import SystemHost
from webkitpy.port.factory import PortFactory
from webkitpy.common.system.logutils import configure_logging
try:
from urllib.parse import urlparse # pylint: disable=E0611
except ImportError:
from urlparse import urlparse
try:
from urllib.request import urlopen # pylint: disable=E0611
except ImportError:
from urllib2 import urlopen
FLATPAK_REQ = [
("flatpak", "1.4.4"),
]
FLATPAK_VERSION = {}
scriptdir = os.path.abspath(os.path.dirname(__file__))
_log = logging.getLogger(__name__)
FLATPAK_USER_DIR_PATH = os.path.realpath(os.path.join(scriptdir, "../../WebKitBuild", "UserFlatpak"))
is_colored_output_supported = False
try:
import curses
assert sys.stdout.isatty()
curses.setupterm()
assert curses.tigetnum("colors") > 0
except Exception:
is_colored_output_supported = False
else:
is_colored_output_supported = True
class Colors:
HEADER = "\033[95m"
OKBLUE = "\033[94m"
OKGREEN = "\033[92m"
WARNING = "\033[93m"
FAIL = "\033[91m"
ENDC = "\033[0m"
class Console:
quiet = False
@classmethod
def message(cls, str_format, *args):
if cls.quiet:
return
if args:
print(str_format % args)
else:
print(str_format)
# Flush so that messages are printed at the right time
# as we use many subprocesses.
sys.stdout.flush()
@classmethod
def colored_message_if_supported(cls, color, str_format, *args):
if args:
msg = str_format % args
else:
msg = str_format
if is_colored_output_supported:
cls.message("\n%s%s%s", color, msg, Colors.ENDC)
else:
cls.message(msg)
@classmethod
def error_message(cls, str_format, *args):
cls.colored_message_if_supported(Colors.FAIL, str_format, *args)
@classmethod
def warning_message(cls, str_format, *args):
cls.colored_message_if_supported(Colors.WARNING, str_format, *args)
def check_flatpak(verbose=True):
# Flatpak is only supported on Linux.
if not sys.platform.startswith("linux"):
return False
for app, required_version in FLATPAK_REQ:
try:
output = subprocess.check_output([app, "--version"])
except (subprocess.CalledProcessError, OSError):
if verbose:
Console.error_message("You need to install %s >= %s"
" to be able to use the '%s' script.\n\n"
"You can find some informations about"
" how to install it for your distribution at:\n"
" * http://flatpak.org/%s\n", app, required_version,
sys.argv[0])
return False
def comparable_version(version):
return tuple(map(int, (version.split("."))))
version = output.decode("utf-8").split(" ")[1].strip("\n")
current = comparable_version(version)
FLATPAK_VERSION[app] = current
if current < comparable_version(required_version):
Console.error_message("%s %s required but %s found. Please update and try again\n",
app, required_version, version)
return False
return True
class FlatpakObject:
def __init__(self, user):
self.user = user
def flatpak(self, command, *args, **kwargs):
show_output = kwargs.pop("show_output", False)
comment = kwargs.pop("comment", None)
if comment:
Console.message(comment)
command = ["flatpak", command]
res = subprocess.check_output(command + ["--help"]).decode("utf-8")
if self.user and "--user" in res:
command.append("--user")
if "--assumeyes" in res:
command.append("--assumeyes")
command.extend(args)
_log.debug("Executing %s" % ' '.join(command))
if not show_output:
return subprocess.check_output(command).decode("utf-8")
return subprocess.check_call(command)
class FlatpakPackages(FlatpakObject):
def __init__(self, repos, user=True):
FlatpakObject.__init__(self, user=user)
self.repos = repos
self.packages = self.__detect_runtimes()
def __detect_packages(self, *args):
packs = []
out = self.flatpak("list", "--columns=application,arch,branch,origin", *args)
package_defs = [line for line in out.split("\n") if line]
for package_def in package_defs:
name, arch, branch, origin = package_def.split("\t")
# If installed from a file, the package is in no repo
repo = self.repos.repos.get(origin)
packs.append(FlatpakPackage(name, branch, repo, arch))
return packs
def __detect_runtimes(self):
return self.__detect_packages("--runtime")
def __iter__(self):
for package in self.packages:
yield package
class FlatpakRepos(FlatpakObject):
def __init__(self, user=True):
FlatpakObject.__init__(self, user=user)
self.repos = {}
self.update()
def update(self):
self.repos = {}
out = self.flatpak("remote-list", "--columns=name,title,url")
remotes = [line for line in out.split("\n") if line]
for remote in remotes:
name, title, url = remote.split("\t")
parsed_url = urlparse(url)
if not parsed_url.scheme:
Console.message("No valid URI found for: %s", remote)
continue
self.repos[name] = FlatpakRepo(name=name, url=url, desc=title, repos=self)
self.packages = FlatpakPackages(self)
def add(self, repo, override=True):
same_name = None
for name, tmprepo in self.repos.items():
if repo.url == tmprepo.url:
return tmprepo
elif repo.name == name:
same_name = tmprepo
if same_name:
if override:
self.flatpak("remote-modify", repo.name, "--url=" + repo.url,
comment="Setting repo %s URL from %s to %s"
% (repo.name, same_name.url, repo.url))
same_name.url = repo.url
return same_name
else:
return None
else:
self.flatpak("remote-add", repo.name, "--from", repo.repo_file.name,
"--if-not-exists",
comment="Adding repo %s" % repo.name)
repo.repos = self
return repo
class FlatpakRepo(FlatpakObject):
def __init__(self, name, desc=None, url=None,
repo_file=None, user=True, repos=None):
FlatpakObject.__init__(self, user=user)
self.name = name
self.url = url
self.desc = desc
self.repo_file_name = repo_file
self._repo_file = None
self.repos = repos
assert name
if repo_file and not url:
repo = configparser.ConfigParser()
repo.read(self.repo_file.name)
try:
self.url = repo["Flatpak Repo"]["Url"]
except AttributeError:
self.url = repo.get("Flatpak Repo", "Url")
else:
assert url
self._app_registry = {}
output = self.flatpak("list", "--columns=application,branch,origin")
for line in output.splitlines():
name, branch, origin = line.split("\t")
if origin != self.name:
continue
self._app_registry[name] = branch
def is_app_installed(self, name, branch=None):
if branch:
try:
return self._app_registry[name] == branch
except KeyError:
return False
else:
return name in self._app_registry.keys()
@property
def repo_file(self):
if self._repo_file:
return self._repo_file
assert self.repo_file_name
self._repo_file = tempfile.NamedTemporaryFile(mode="wb")
self._repo_file.write(urlopen(self.repo_file_name).read())
self._repo_file.flush()
return self._repo_file
class FlatpakPackage(FlatpakObject):
"""A flatpak app."""
def __init__(self, name, branch, repo, arch, user=True, hash=None):
FlatpakObject.__init__(self, user=user)
self.name = name
self.branch = str(branch)
self.repo = repo
self.arch = arch
def __repr__(self):
return "%s/%s/%s %s" % (self.name, self.arch, self.branch, self.repo.name)
def __str__(self):
return "%s/%s/%s" % (self.name, self.arch, self.branch)
def is_installed(self, branch):
if not self.repo:
# Bundle installed from file
return True
self.repo.repos.update()
for package in self.repo.repos.packages:
if package.name == self.name and \
package.branch == branch and \
package.arch == self.arch:
return True
return False
def install(self):
if not self.repo:
return False
branch = self.branch
args = ("install", self.repo.name, self.name, "--reinstall", branch)
comment = "Installing from " + self.repo.name + " " + self.name + " " + self.arch + " " + branch
self.flatpak(*args, show_output=True, comment=comment)
def update(self):
if not self.is_installed(self.branch):
return self.install()
comment = "Updating %s" % self.name
self.flatpak("update", self.name, self.branch, show_output=True, comment=comment)
@contextmanager
def disable_signals(signals=[signal.SIGINT]):
old_signal_handlers = []
for disabled_signal in signals:
old_signal_handlers.append((disabled_signal, signal.getsignal(disabled_signal)))
signal.signal(disabled_signal, signal.SIG_IGN)
yield
for disabled_signal, previous_handler in old_signal_handlers:
signal.signal(disabled_signal, previous_handler)
class WebkitFlatpak:
@staticmethod
def load_from_args(args=None, add_help=True):
self = WebkitFlatpak()
parser = argparse.ArgumentParser(prog="webkit-flatpak", add_help=add_help)
general = parser.add_argument_group("General")
general.add_argument('--verbose', action='store_true',
help='Show debug message')
general.add_argument("--debug",
help="Compile with Debug configuration, also installs Sdk debug symboles.",
action="store_true")
general.add_argument("--release", help="Compile with Release configuration.", action="store_true")
general.add_argument('--platform', action='store', help='Platform to use (e.g., "mac-lion")'),
general.add_argument('--gtk', action='store_const', dest='platform', const='gtk',
help='Alias for --platform=gtk')
general.add_argument('--wpe', action='store_const', dest='platform', const='wpe',
help=('Alias for --platform=wpe'))
general.add_argument("-nf", "--no-flatpak-update", dest="no_flatpak_update",
action="store_true",
help="Do not update flaptak runtime/sdk")
general.add_argument("-u", "--update", dest="update",
action="store_true",
help="Update the runtime/sdk/app and rebuild the development environment if needed")
general.add_argument("-b", "--build-webkit", dest="build_webkit",
action="store_true",
help="Force rebuilding the app.")
general.add_argument("-bgst", "--build-gst", dest="build_gst",
action="store_true",
help="Force rebuilding gst-build, repository path is defined by the `GST_BUILD_PATH` environment variable.")
general.add_argument("-q", "--quiet", dest="quiet",
action="store_true",
help="Do not print anything")
general.add_argument("-t", "--tests", dest="run_tests",
nargs=argparse.REMAINDER,
help="Run LayoutTests")
general.add_argument("-c", "--command",
nargs=argparse.REMAINDER,
help="The command to run in the sandbox",
dest="user_command")
general.add_argument('--available', action='store_true', dest="check_available", help='Check if required dependencies are available.'),
general.add_argument("--use-icecream", dest="use_icecream", help="Use the distributed icecream (icecc) compiler.", action="store_true")
debugoptions = parser.add_argument_group("Debugging")
debugoptions.add_argument("--gdb", nargs="?", help="Activate gdb, passing extra args to it if wanted.")
debugoptions.add_argument("--gdb-stack-trace", dest="gdb_stack_trace", nargs="?",
help="Dump the stacktrace to stdout. The argument is a timestamp to be parsed by coredumpctl.")
debugoptions.add_argument("-m", "--coredumpctl-matches", default="", help='Arguments to pass to gdb.')
buildoptions = parser.add_argument_group("Extra build arguments")
buildoptions.add_argument("--makeargs", help="Optional Makefile flags")
buildoptions.add_argument("--cmakeargs",
help="One or more optional CMake flags (e.g. --cmakeargs=\"-DFOO=bar -DCMAKE_PREFIX_PATH=/usr/local\")")
general.add_argument("--clean", dest="clean", action="store_true",
help="Clean previous builds and restart from scratch")
_, self.args = parser.parse_known_args(args=args, namespace=self)
if os.environ.get('CCACHE_PREFIX') == 'icecc':
self.use_icecream = True
return self
def __init__(self):
self.sdk_repo = None
self.runtime = None
self.sdk = None
self.verbose = False
self.quiet = False
self.update = False
self.args = []
self.finish_args = None
self.gdb_stack_trace = False
self.no_flatpak_update = False
self.release = False
self.debug = False
self.clean = False
self.run_tests = None
self.source_root = os.path.normpath(os.path.abspath(os.path.join(scriptdir, '../../')))
# Where the source folder is mounted inside the sandbox.
self.sandbox_source_root = "/app/webkit"
self.build_webkit = False
self.build_gst = False
self.build_all = False
self.sdk_branch = "0.2"
self.platform = "gtk"
self.build_type = "Release"
self.manifest_path = None
self.name = None
self.build_name = None
self.flatpak_root_path = None
self.cache_path = None
self.flatpak_default_args = []
self.check_available = False
# Default application to run in the sandbox
self.command = None
self.user_command = []
# debug options
self.gdb = None
self.coredumpctl_matches = ""
# Extra build options
self.cmakeargs = ""
self.makeargs = ""
self.use_icecream = False
self.icc_version = None
def clean_args(self):
os.environ["FLATPAK_USER_DIR"] = os.environ.get("WEBKIT_FLATPAK_USER_DIR", FLATPAK_USER_DIR_PATH)
try:
os.makedirs(os.environ["FLATPAK_USER_DIR"])
except OSError as e:
pass
configure_logging(logging.DEBUG if self.verbose else logging.INFO)
_log.debug("Using flatpak user dir: %s" % os.environ["FLATPAK_USER_DIR"])
if not self.debug and not self.release:
factory = PortFactory(SystemHost())
port = factory.get(self.platform)
self.debug = port.default_configuration() == "Debug"
self.build_type = "Debug" if self.debug else "Release"
self.platform = self.platform.upper()
if self.gdb is None and '--gdb' in sys.argv:
self.gdb = True
self.command = "%s %s %s" % (os.path.join(self.sandbox_source_root,
"Tools/Scripts/run-minibrowser"),
"--" + self.platform.lower(),
" --debug" if self.debug else " --release")
self.flatpak_build_path = os.environ["FLATPAK_USER_DIR"]
build_root = os.path.join(self.source_root, 'WebKitBuild')
self.build_path = os.path.join(build_root, self.platform, self.build_type)
try:
os.makedirs(self.build_path)
except OSError as e:
if e.errno != errno.EEXIST:
raise e
self.config_file = os.path.join(self.flatpak_build_path, 'webkit_flatpak_config.json')
Console.quiet = self.quiet
if not check_flatpak():
return False
self.finish_args = []
self._reset_repository()
try:
with open(self.config_file) as config:
json_config = json.load(config)
self.icc_version = json_config['icecc_version']
except IOError as e:
pass
return True
def _reset_repository(self):
self.repos = FlatpakRepos()
self.sdk_repo = self.repos.add(
FlatpakRepo("webkit-sdk",
url="https://software.igalia.com/webkit-sdk-repo/",
repo_file="https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo")
)
def setup_builddir(self, **kwargs):
if os.path.exists(os.path.join(self.flatpak_build_path, "metadata")):
return
self.sdk_repo.flatpak("build-init",
self.flatpak_build_path,
"org.webkit.Webkit",
str(self.sdk),
str(self.runtime),
self.sdk.branch,
show_output="stdout" in kwargs.keys())
def setup_gstbuild(self, building):
gst_dir = os.environ.get('GST_BUILD_PATH')
if not gst_dir:
if building:
Console.warning_message("$GST_BUILD_PATH environment variable not set. Skipping gst-build\n")
return []
if not os.path.exists(os.path.join(gst_dir, 'gst-env.py')):
raise RuntimeError('GST_BUILD_PATH set to %s but it doesn\'t seem to be a valid `gst-build` checkout.' % gst_dir)
gst_builddir = os.path.join(self.sandbox_source_root, "WebKitBuild", 'gst-build')
if not os.path.exists(os.path.join(self.source_root, 'WebKitBuild', 'gst-build', 'build.ninja')):
if not building:
raise RuntimeError('Trying to enter gst-build env from %s but it is not built, make sure to rebuild webkit.' % gst_dir)
args = ['meson', ]
extra_args = os.environ.get('GST_BUILD_ARGS', '')
args.extend(shlex.split(extra_args) + [gst_dir, gst_builddir])
Console.message("Running %s ", ' '.join(args))
self.run_in_sandbox(*args, building_gst=True)
if not building:
return [os.path.join(gst_dir, 'gst-env.py'), '--builddir', gst_builddir, '--srcdir', gst_dir]
Console.message("Building `gst-build` %s ", gst_dir)
if self.run_in_sandbox('ninja', '-C', gst_builddir, building_gst=True) != 0:
raise RuntimeError('Error while building gst-build.')
return [os.path.join(gst_dir, 'gst-env.py'), '--builddir', gst_builddir, '--srcdir', gst_dir]
def run_in_sandbox(self, *args, **kwargs):
self.setup_builddir(stdout=kwargs.get("stdout", sys.stdout))
cwd = kwargs.pop("cwd", None)
extra_env_vars = kwargs.pop("env", {})
stdout = kwargs.pop("stdout", sys.stdout)
extra_flatpak_args = kwargs.pop("extra_flatpak_args", [])
if not isinstance(args, list):
args = list(args)
if args:
if os.path.exists(args[0]):
command = os.path.normpath(os.path.abspath(args[0]))
# Take into account the fact that the webkit source dir is remounted inside the sandbox.
args[0] = command.replace(self.source_root, self.sandbox_source_root)
if args[0].endswith("build-webkit"):
args.append("--prefix=/app")
building = os.path.basename(args[0]).startswith("build")
else:
building = False
sandbox_build_path = os.path.join(self.sandbox_source_root, "WebKitBuild", self.build_type)
# FIXME: Using the `run` flatpak command would be better, but it doesn't
# have a --bind-mount option.
flatpak_command = ["flatpak", "build",
"--die-with-parent",
"--talk-name=org.a11y.Bus",
"--talk-name=org.gtk.vfs",
"--talk-name=org.gtk.vfs.*",
"--bind-mount=/run/shm=/dev/shm",
# Access to /run/host is required by the crash log reporter.
"--bind-mount=/run/host/%s=%s" % (tempfile.gettempdir(), tempfile.gettempdir()),
# flatpak build doesn't expose a --socket option for
# white-listing the systemd journal socket. So
# white-list it in /run, hoping this is the right
# path.
"--bind-mount=/run/systemd/journal=/run/systemd/journal",
"--bind-mount=%s=%s" % (self.sandbox_source_root, self.source_root),
# We mount WebKitBuild/PORTNAME/BuildType to /app/webkit/WebKitBuild/BuildType
# so we can build WPE and GTK in a same source tree.
"--bind-mount=%s=%s" % (sandbox_build_path, self.build_path)]
forwarded = {
"WEBKIT_TOP_LEVEL": "/app/",
"TEST_RUNNER_INJECTED_BUNDLE_FILENAME": os.path.join(sandbox_build_path, "lib/libTestRunnerInjectedBundle.so"),
}
if not building:
flatpak_command.extend([
"--device=all",
"--device=dri",
"--filesystem=host",
"--share=ipc",
"--share=network",
"--socket=pulseaudio",
"--socket=system-bus",
"--socket=wayland",
"--socket=x11",
"--system-talk-name=org.a11y.Bus",
"--system-talk-name=org.freedesktop.GeoClue2",
"--talk-name=org.a11y.Bus",
"--talk-name=org.freedesktop.Flatpak"
])
forwarded.update({
"TZ": "PST8PDT",
"LANG": "en_US.UTF-8"
})
env_var_prefixes_to_keep = [
"G",
"GIGACAGE",
"GST",
"GTK",
"ICECC",
"JSC",
"WEBKIT",
"WEBKIT2",
"WPE",
]
env_var_suffixes_to_keep = [
"JSC_ARGS",
"WEBKIT_ARGS",
]
env_vars_to_keep = [
"CC",
"CCACHE_PREFIX",
"CFLAGS",
"CXX",
"CXXFLAGS",
"DISPLAY",
"JavaScriptCoreUseJIT",
"LANG",
"LDFLAGS",
"Malloc",
"GPU_PROCESS_CMD_PREFIX",
"NETWORK_PROCESS_CMD_PREFIX",
"NUMBER_OF_PROCESSORS",
"PLUGIN_PROCESS_CMD_PREFIX",
"QML2_IMPORT_PATH",
"WAYLAND_DISPLAY",
"WAYLAND_SOCKET",
"WEB_PROCESS_CMD_PREFIX"
]
env_vars = os.environ
env_vars.update(extra_env_vars)
for envvar, value in env_vars.items():
var_tokens = envvar.split("_")
if var_tokens[0] in env_var_prefixes_to_keep or envvar in env_vars_to_keep or var_tokens[-1] in env_var_suffixes_to_keep:
forwarded[envvar] = value
if self.use_icecream:
_log.debug('Enabling the icecream compiler')
flatpak_command.extend(["--share=network",
"--bind-mount=/var/run/icecc=/var/run/icecc"])
n_cores = multiprocessing.cpu_count() * 3
_log.debug('Following icecream recommendation for the number of cores to use: %d' % n_cores)
forwarded.update({
"CCACHE_PREFIX": "icecc",
"ICECC_VERSION": self.icc_version,
"NUMBER_OF_PROCESSORS": n_cores,
})
for envvar, value in forwarded.items():
flatpak_command.append("--env=%s=%s" % (envvar, value))
gst_env = []
if not kwargs.get('building_gst'):
gst_env = self.setup_gstbuild(building)
flatpak_command += self.finish_args + extra_flatpak_args + \
[self.flatpak_build_path] + gst_env + args
_log.debug('Running in sandbox: %s\n' % ' '.join(flatpak_command))
try:
subprocess.check_call(flatpak_command, stdout=stdout)
except subprocess.CalledProcessError as e:
sys.stderr.write(str(e) + "\n")
return e.returncode
except KeyboardInterrupt:
return 0
return 0
def main(self):
if self.check_available:
return 0
if not self.clean_args():
return 1
if self.clean:
if os.path.exists(self.flatpak_build_path):
shutil.rmtree(self.flatpak_build_path)
if os.path.exists(self.build_path):
shutil.rmtree(self.build_path)
if self.update:
Console.message("Updating Flatpak %s environment" % self.build_type)
if not self.no_flatpak_update:
repo = self.sdk_repo
repo.flatpak("update")
for package in self._get_packages():
if package.name.startswith("org.webkit") and repo.is_app_installed(package.name) \
and not repo.is_app_installed(package.name, branch=self.sdk_branch):
Console.message("New SDK version available, removing local UserFlatpak directory before switching to new version")
shutil.rmtree(self.flatpak_build_path)
self._reset_repository()
break
return self.setup_dev_env()
def run(self):
try:
return self.main()
except subprocess.CalledProcessError as error:
Console.error_message("The following command returned a non-zero exit status: %s\n"
"Output: %s", ' '.join(error.cmd), error.output)
return error.returncode
return 0
def has_environment(self):
return os.path.exists(self.flatpak_build_path)
def save_config(self):
with open(self.config_file, 'w') as config:
json_config = {'icecc_version': self.icc_version}
json.dump(json_config, config)
def setup_icecc(self):
with tempfile.NamedTemporaryFile() as tmpfile:
self.run_in_sandbox('icecc', '--build-native', stdout=tmpfile, cwd=self.source_root)
tmpfile.flush()
tmpfile.seek(0)
icc_version_filename, = re.findall(r'.*creating (.*)', tmpfile.read())
self.icc_version = os.path.join(self.source_root, icc_version_filename)
def setup_dev_env(self):
if not os.path.exists(os.path.join(self.flatpak_build_path, "runtime", "org.webkit.Sdk")) or self.update or self.build_all:
self.install_all()
self.setup_icecc()
self.save_config()
if not self.update:
for package in self._get_packages():
if package.name.startswith("org.webkit") and not package.is_installed(self.sdk_branch):
Console.error_message("Flatpak package %s not installed. Please update your SDK: Tools/Scripts/update-webkit-flatpak", package)
return 1
build_type = "--debug" if self.debug else "--release"
if self.build_webkit:
builder = [
os.path.join(self.sandbox_source_root, 'Tools/Scripts/build-webkit'),
build_type, '--' + self.platform.lower()]
if self.makeargs:
builder.append("--makeargs=%s" % self.makeargs)
if self.cmakeargs:
builder.append("--cmakeargs=%s" % self.cmakeargs)
Console.message("Building webkit")
res = self.run_in_sandbox(*builder)
if res:
return res
if self.run_tests is not None:
test_launcher = [os.path.join(self.sandbox_source_root, 'Tools/Scripts/run-webkit-tests'),
build_type, '--' + self.platform.lower()] + self.run_tests
return self.run_in_sandbox(*test_launcher)
elif self.gdb or self.gdb_stack_trace:
return self.run_gdb()
elif self.user_command:
return self.run_in_sandbox(*self.user_command)
elif not self.update and not self.build_webkit and not self.build_gst:
return self.run_in_sandbox()
return 0
def _get_packages(self):
self.runtime = FlatpakPackage("org.webkit.Platform", self.sdk_branch,
self.sdk_repo, "x86_64")
self.sdk = FlatpakPackage("org.webkit.Sdk", self.sdk_branch,
self.sdk_repo, "x86_64")
packages = [self.runtime, self.sdk]
# FIXME: For unknown reasons, the GL extension needs to be explicitely
# installed for Flatpak 1.2.x to be able to make use of it. Seems like
# it's not correctly inheriting it from the SDK.
self.flathub_repo = self.repos.add(
FlatpakRepo("flathub", repo_file="https://dl.flathub.org/repo/flathub.flatpakrepo")
)
gl_extension = FlatpakPackage("org.freedesktop.Platform.GL.default", "19.08",
self.flathub_repo, "x86_64")
packages.append(gl_extension)
if self.debug:
sdk_debug = FlatpakPackage('org.webkit.Sdk.Debug', self.sdk_branch,
self.sdk_repo, "x86_64")
packages.append(sdk_debug)
return packages
def install_all(self):
if os.path.exists(os.path.join(self.flatpak_build_path, "runtime", "org.webkit.Sdk")):
return
Console.message("Installing %s dependencies in %s", self.build_type, self.flatpak_build_path)
for package in self._get_packages():
if not package.is_installed(self.sdk_branch):
package.install()
def run_gdb(self):
with disable_signals():
try:
subprocess.check_output(['which', 'coredumpctl'])
except subprocess.CalledProcessError as e:
sys.stderr.write("'coredumpctl' not present on the system, can't run. (%s)\n" % e)
return e.returncode
# We need access to the host from the sandbox to run.
with tempfile.NamedTemporaryFile() as coredump:
with tempfile.NamedTemporaryFile() as stderr:
if self.gdb_stack_trace:
cmd = ["coredumpctl", "--since=%s" % self.gdb_stack_trace, "dump"]
else:
cmd = ["coredumpctl", "dump"] + shlex.split(self.coredumpctl_matches)
try:
subprocess.check_call(cmd, stdout=coredump, stderr=stderr)
except subprocess.CalledProcessError as err:
with open(stderr.name, 'r') as stderrf:
stderr = stderrf.read()
cmd = ' '.join(err.cmd)
sys.stderr.write("'%s' returned a non-zero exit code. Stderr: %s" % (cmd, stderr))
return err.returncode
with open(stderr.name, 'r') as stderrf:
stderr = stderrf.read()
executable, = re.findall(".*Executable: (.*)", stderr)
if self.gdb:
bargs = ["gdb", executable, "/run/host/%s" % coredump.name]
if type(self.gdb) != bool:
bargs.extend(shlex.split(self.gdb))
elif self.gdb_stack_trace:
bargs = ["gdb", '-ex', "thread apply all backtrace", '--batch', executable, "/run/host/%s" % coredump.name]
return self.run_in_sandbox(*bargs)
def is_sandboxed():
return os.path.exists("/.flatpak-info")
def run_in_sandbox_if_available(args):
if not os.path.isdir(FLATPAK_USER_DIR_PATH):
return None
if is_sandboxed():
return None
if not check_flatpak(verbose=False):
return None
flatpak_runner = WebkitFlatpak.load_from_args(args, add_help=False)
if not flatpak_runner.clean_args():
return None
if not flatpak_runner.has_environment():
return None
sys.exit(flatpak_runner.run_in_sandbox(*args))