| # 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)) |