| # -*- coding: utf-8 -*- |
| # 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 |
| import platform |
| |
| from webkitpy.common.system.logutils import configure_logging |
| from webkitcorepy import string_utils |
| import toml |
| import json |
| |
| 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 |
| |
| try: |
| from contextlib import nullcontext |
| except ImportError: |
| @contextmanager |
| def nullcontext(enter_result=None): |
| yield enter_result |
| |
| FLATPAK_REQUIRED_VERSION = "1.4.4" |
| |
| 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")) |
| DEFAULT_SCCACHE_SCHEDULER='https://sccache.igalia.com' |
| |
| # Our SDK branch matches with the FDO SDK branch. When updating the FDO SDK release branch |
| # in our SDK build definitions please don't forget to update the version here as well. |
| SDK_BRANCH = "21.08" |
| |
| 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 run_sanitized(command, gather_output=False, ignore_stderr=False, env=None): |
| """ Runs a command in a santized environment and optionally returns decoded output or raises |
| subprocess.CalledProcessError |
| """ |
| if env: |
| sanitized_env = env.copy() |
| else: |
| sanitized_env = os.environ.copy() |
| |
| # We need clean output free of debug messages |
| try: |
| del sanitized_env["G_MESSAGES_DEBUG"] |
| except KeyError: |
| pass |
| |
| _log.debug("Running %s", " ".join(command)) |
| keywords = dict(env=sanitized_env) |
| if gather_output: |
| if ignore_stderr: |
| with open(os.devnull, 'w') as devnull: |
| output = subprocess.check_output(command, stderr=devnull, **keywords) |
| else: |
| output = subprocess.check_output(command, **keywords) |
| return output.strip().decode('utf-8') |
| else: |
| keywords["stdout"] = sys.stdout |
| return subprocess.check_call(command, **keywords) |
| |
| |
| def check_flatpak(verbose=True): |
| # Flatpak is only supported on Linux. |
| if not sys.platform.startswith("linux"): |
| return () |
| |
| required_version = FLATPAK_REQUIRED_VERSION |
| try: |
| output = run_sanitized(["flatpak", "--version"], gather_output=True) |
| except (subprocess.CalledProcessError, OSError): |
| if verbose: |
| Console.error_message("You need to install flatpak >= %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" |
| " * https://flatpak.org/\n", required_version, |
| sys.argv[0]) |
| return () |
| |
| def comparable_version(version): |
| return tuple(map(int, (version.split(".")))) |
| |
| version = output.split(" ")[1].strip("\n") |
| current_version = comparable_version(version) |
| if current_version < comparable_version(required_version): |
| Console.error_message("flatpak %s required but %s found. Please update and try again\n", |
| required_version, version) |
| return () |
| |
| return current_version |
| |
| |
| class FlatpakObject: |
| |
| def __init__(self, user): |
| self.user = user |
| |
| def flatpak(self, command, *args, **kwargs): |
| comment = kwargs.pop("comment", None) |
| gather_output = kwargs.get("gather_output", False) |
| ignore_stderr = kwargs.get("ignore_stderr", False) |
| if comment: |
| Console.message(comment) |
| |
| command = ["flatpak", command] |
| help_output = run_sanitized(command + ["--help"], gather_output=True) |
| if self.user and "--user" in help_output: |
| command.append("--user") |
| if "--assumeyes" in help_output: |
| command.append("--assumeyes") |
| if "--noninteractive" in help_output and gather_output: |
| command.append("--noninteractive") |
| |
| command.extend(args) |
| |
| _log.debug("Executing %s" % ' '.join(command)) |
| return run_sanitized(command, gather_output=gather_output, ignore_stderr=ignore_stderr) |
| |
| def version(self, ref_id): |
| try: |
| output = self.flatpak("info", ref_id, gather_output=True, ignore_stderr=True) |
| except subprocess.CalledProcessError: |
| # ref is likely not installed |
| return "" |
| for line in output.splitlines(): |
| tokens = line.split(":") |
| if len(tokens) != 2: |
| continue |
| if tokens[0].strip().lower() == "version": |
| return tokens[1].strip() |
| return "" |
| |
| class FlatpakPackages(FlatpakObject): |
| |
| def __init__(self, repos, user=True): |
| FlatpakObject.__init__(self, user=user) |
| self.repos = repos |
| self.update() |
| |
| def update(self): |
| self.packages = [] |
| out = self.flatpak("list", "--columns=application,arch,branch,origin", "-a", gather_output=True) |
| 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) |
| self.packages.append(FlatpakPackage(name, branch, repo, arch)) |
| |
| def __iter__(self): |
| for package in self.packages: |
| yield package |
| |
| |
| class FlatpakRepos(FlatpakObject): |
| |
| def __init__(self, repos, user=True): |
| FlatpakObject.__init__(self, user=user) |
| self.update() |
| |
| updated = False |
| for repo in repos: |
| updated = self.add(repo, update=False) or updated |
| |
| # Only fetch the remote and package list again if we updated anything. |
| if updated: |
| self.update() |
| |
| def update(self): |
| self.repos = {} |
| out = self.flatpak("remote-list", "--columns=name,title,url", gather_output=True) |
| 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, update=True, override=True): |
| try: |
| repo.repos = self |
| |
| same_name = None |
| for name, tmprepo in self.repos.items(): |
| if repo.url == tmprepo.url: |
| return False |
| elif repo.name == name: |
| same_name = tmprepo |
| |
| if same_name: |
| if override and same_name.url != repo.url: |
| self.flatpak("remote-modify", repo.name, "--url=" + repo.url) |
| same_name.url = repo.url |
| return True |
| else: |
| return False |
| |
| args = ["remote-add", repo.name, "--if-not-exists"] |
| if repo.repo_file: |
| args.extend(["--from", repo.repo_file.name]) |
| else: |
| args.extend(["--no-gpg-verify", repo.url]) |
| self.flatpak(*args, comment="Adding repo %s" % repo.name) |
| return True |
| finally: |
| if update: |
| self.packages = FlatpakPackages(self) |
| |
| def is_package_installed(self, name, branch=None, arch=None): |
| for package in self.packages: |
| if name != package.name: |
| continue |
| if branch and branch != package.branch: |
| continue |
| if arch and arch != package.arch: |
| continue |
| return True |
| return False |
| |
| |
| 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 |
| |
| @property |
| def repo_file(self): |
| if self._repo_file: |
| return self._repo_file |
| |
| if not self.repo_file_name: |
| return None |
| |
| 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): |
| # Bundles installed from files do not have repositories. |
| if not self.repo: |
| return True |
| return self.repo.repos.is_package_installed(self.name, branch, self.arch) |
| |
| 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, comment=comment) |
| self.repo.repos.packages.update() |
| |
| 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, comment=comment) |
| |
| |
| @contextmanager |
| def disable_signals(signals=[signal.SIGINT, signal.SIGTERM, signal.SIGHUP]): |
| old_signal_handlers = [] |
| |
| for disabled_signal in signals: |
| handler = signal.getsignal(disabled_signal) |
| if handler: |
| old_signal_handlers.append((disabled_signal, handler)) |
| 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 messages') |
| general.add_argument('--version', action='store_true', help='Show SDK version', dest="show_version") |
| type_group = parser.add_mutually_exclusive_group() |
| type_group.add_argument("--debug", |
| help="Compile with Debug configuration, also installs Sdk debug symbols.", |
| dest='build_type', action="store_const", const="Debug") |
| type_group.add_argument("--release", help="Compile with Release configuration.", |
| dest='build_type', action="store_const", const="Release") |
| general.add_argument('--gtk', action='store_const', dest='platform', const='gtk', |
| help='Setup build directory for the GTK port') |
| general.add_argument('--wpe', action='store_const', dest='platform', const='wpe', |
| help=('Setup build directory for the WPE port')) |
| general.add_argument("-u", "--update", dest="update", |
| action="store_true", |
| help="Update the SDK") |
| 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("-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("--repo", help="Filesystem absolute path to the Flatpak repository to use", dest="user_repo") |
| |
| distributed_build_options = parser.add_argument_group("Distributed building") |
| distributed_build_options.add_argument("--use-icecream", dest="use_icecream", help="Use the distributed icecream (icecc) compiler.", action="store_true") |
| distributed_build_options.add_argument("-r", "--regenerate-toolchains", dest="regenerate_toolchains", action="store_true", |
| help="Regenerate IceCC/SCCache standalone toolchain archives") |
| distributed_build_options.add_argument("-t", "--sccache-token", dest="sccache_token", |
| help="sccache authentication token") |
| distributed_build_options.add_argument("-s", "--sccache-scheduler", dest="sccache_scheduler", |
| help="sccache scheduler URL (default: %s)" % DEFAULT_SCCACHE_SCHEDULER) |
| |
| 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("--cmakeargs", |
| help="One or more optional CMake flags (e.g. --cmakeargs=\"-DFOO=bar -DCMAKE_PREFIX_PATH=/usr/local\")") |
| |
| _, self.args = parser.parse_known_args(args=args, namespace=self) |
| |
| if not self.build_type: |
| self.build_type = "Release" |
| |
| if os.environ.get('CCACHE_PREFIX') == 'icecc': |
| self.use_icecream = True |
| |
| verbose = os.environ.get('WEBKIT_FLATPAK_SDK_VERBOSE') |
| if (not self.verbose) and (verbose is not None): |
| self.verbose = verbose != '0' |
| |
| configure_logging(logging.DEBUG if self.verbose else logging.INFO) |
| |
| if self.user_repo: |
| if not os.path.exists(self.user_repo): |
| _log.error('User repo at %s is not accessible\n' % self.user_repo) |
| sys.exit(1) |
| |
| return self |
| |
| def __init__(self): |
| self.sdk_repo = None |
| self.runtime = None |
| self.sdk = None |
| self.user_repo = None |
| |
| self.show_version = False |
| self.verbose = False |
| self.quiet = False |
| self.update = False |
| self.args = [] |
| self.gdb_stack_trace = False |
| |
| self.release = False |
| self.debug = False |
| 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.base_build_dir = 'WebKitBuild' |
| |
| self.build_gst = False |
| |
| self.platform = "gtk" |
| self.check_available = False |
| self.user_command = [] |
| |
| # debug options |
| self.gdb = None |
| self.coredumpctl_matches = "" |
| |
| # Extra build options |
| self.cmakeargs = "" |
| |
| self.use_icecream = False |
| self.icc_version = {} |
| self.regenerate_toolchains = False |
| self.sccache_token = "" |
| self.sccache_scheduler = DEFAULT_SCCACHE_SCHEDULER |
| |
| def execute_command(self, args, stdout=None, stderr=None, env=None, keep_signals=True): |
| if keep_signals: |
| ctx_manager = nullcontext() |
| else: |
| ctx_manager = disable_signals() |
| _log.debug('Running: %s\n' % ' '.join(string_utils.decode(arg) for arg in args)) |
| result = 0 |
| with ctx_manager: |
| try: |
| result = subprocess.check_call(args, stdout=stdout, stderr=stderr, env=env) |
| except subprocess.CalledProcessError as err: |
| if self.verbose: |
| cmd = ' '.join(string_utils.decode(arg) for arg in err.cmd) |
| message = "'%s' returned a non-zero exit code." % cmd |
| if stderr: |
| with open(stderr.name, 'r') as stderrf: |
| message += " Stderr: %s" % stderrf.read() |
| Console.error_message(message) |
| return err.returncode |
| return result |
| |
| def clean_args(self): |
| if self.user_repo: |
| os.environ["FLATPAK_USER_DIR"] = FLATPAK_USER_DIR_PATH + ".Local" |
| else: |
| os.environ["FLATPAK_USER_DIR"] = os.environ.get("WEBKIT_FLATPAK_USER_DIR", FLATPAK_USER_DIR_PATH) |
| self.flatpak_build_path = os.environ["FLATPAK_USER_DIR"] |
| try: |
| os.makedirs(self.flatpak_build_path) |
| except OSError as e: |
| pass |
| _log.debug("Using flatpak user dir: %s" % self.flatpak_build_path) |
| |
| self.platform = self.platform.upper() |
| |
| if self.gdb is None and '--gdb' in sys.argv: |
| self.gdb = True |
| |
| # This path doesn't take $WEBKIT_OUTPUTDIR in account because the standalone toolchains |
| # paths depend on it and those are also hard-coded in the generated sccache config. |
| self.default_build_root = os.path.join(self.source_root, self.base_build_dir) |
| |
| self.config_file = os.path.join(self.flatpak_build_path, 'webkit_flatpak_config.json') |
| self.sccache_config_file = os.path.join(self.flatpak_build_path, 'sccache.toml') |
| |
| build_root = os.environ.get("WEBKIT_OUTPUTDIR", self.source_root) |
| self.build_root = os.path.join(build_root, self.base_build_dir) |
| self.build_path = os.path.join(self.build_root, self.platform, self.build_type) |
| _log.debug("Building %s port in %s" % (self.platform, self.build_path)) |
| |
| self.toolchains_directory = os.path.join(self.flatpak_build_path, "Toolchains") |
| if not os.path.isdir(self.toolchains_directory): |
| os.makedirs(self.toolchains_directory) |
| |
| Console.quiet = self.quiet |
| self.flatpak_version = check_flatpak() |
| if not self.flatpak_version: |
| return False |
| |
| 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): |
| url = "https://software.igalia.com/webkit-sdk-repo/" |
| repo_file = "https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo" |
| if self.user_repo: |
| url = "file://%s" % self.user_repo |
| repo_file = None |
| |
| self.sdk_repo = FlatpakRepo("webkit-sdk", url=url, repo_file=repo_file) |
| self.flathub_repo = FlatpakRepo("flathub", url="https://dl.flathub.org/repo/", |
| repo_file="https://dl.flathub.org/repo/flathub.flatpakrepo") |
| self.repos = FlatpakRepos([self.sdk_repo, self. flathub_repo]) |
| |
| def setup_builddir(self): |
| if os.path.exists(os.path.join(self.flatpak_build_path, "metadata")): |
| return True |
| |
| if not self.check_installed_packages(): |
| return False |
| |
| self.sdk_repo.flatpak("build-init", |
| self.flatpak_build_path, |
| "org.webkit.Webkit", |
| str(self.sdk), |
| str(self.runtime), |
| self.sdk.branch) |
| |
| return True |
| |
| |
| def setup_gstbuild(self, building): |
| gst_dir = os.environ.get('GST_BUILD_PATH') |
| if not gst_dir: |
| if building: |
| _log.debug("$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.source_root, self.base_build_dir, 'gst-build') |
| if not os.path.exists(os.path.join(self.default_build_root, '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, start_sccache=False) |
| |
| if building: |
| Console.message("Building `gst-build` %s ", gst_dir) |
| if self.run_in_sandbox('ninja', '-C', gst_builddir, building_gst=True, start_sccache=False) != 0: |
| raise RuntimeError('Error while building gst-build.') |
| |
| command = [os.path.join(gst_dir, 'gst-env.py'), '--builddir', gst_builddir, '--srcdir', gst_dir, "--only-environment"] |
| gst_env = run_sanitized(command, gather_output=True) |
| allowlist = ("LD_LIBRARY_PATH", "PATH", "PKG_CONFIG_PATH") |
| nopathlist = ("GST_DEBUG", "GST_VERSION", "GST_ENV") |
| env = {} |
| for line in [line for line in gst_env.splitlines() if not line.startswith("export")]: |
| tokens = line.split("=") |
| var_name, contents = tokens[0], "=".join(tokens[1:]) |
| if not var_name.startswith("GST_") and var_name not in allowlist: |
| continue |
| if var_name not in nopathlist: |
| new_contents = ':'.join([self.host_path_to_sandbox_path(p) for p in contents.split(":")]) |
| else: |
| new_contents = contents.replace("'", "") |
| env[var_name] = new_contents |
| return env |
| |
| def is_branch_build(self): |
| try: |
| with open(os.devnull, 'w') as devnull: |
| rev_parse = subprocess.check_output(("git", "rev-parse", "--abbrev-ref", "HEAD"), stderr=devnull) |
| except subprocess.CalledProcessError: |
| # This is likely not a git checkout. |
| return False |
| git_branch_name = rev_parse.decode("utf-8").strip() |
| for option_name in ("branch.%s.webKitBranchBuild" % git_branch_name, |
| "webKitBranchBuild"): |
| try: |
| with open(os.devnull, 'w') as devnull: |
| output = subprocess.check_output(("git", "config", "--bool", option_name), stderr=devnull).strip() |
| except subprocess.CalledProcessError: |
| continue |
| |
| if output == "true": |
| return True |
| return False |
| |
| def is_build_webkit(self, command): |
| return command and "build-webkit" in os.path.basename(command) |
| |
| def is_build_jsc(self, command): |
| return command and "build-jsc" in os.path.basename(command) |
| |
| def host_path_to_sandbox_path(self, host_path): |
| # For now this supports only files in the WebKit path |
| return host_path.replace(self.source_root, self.sandbox_source_root) |
| |
| @staticmethod |
| def get_user_runtime_dir(): |
| return os.environ.get('XDG_RUNTIME_DIR', os.path.join('/run/user', str(os.getuid()))) |
| |
| def run_in_sandbox(self, *args, **kwargs): |
| if not self.setup_builddir(): |
| return 1 |
| cwd = kwargs.get("cwd", None) |
| extra_env_vars = kwargs.get("env", {}) |
| stdout = kwargs.get("stdout", sys.stdout) |
| extra_flatpak_args = kwargs.get("extra_flatpak_args", []) |
| start_sccache = kwargs.get("start_sccache", True) |
| skip_icc = kwargs.get("skip_icc", False) |
| building_gst = kwargs.get("building_gst", False) |
| gather_output = kwargs.get("gather_output", False) |
| |
| if gather_output: |
| start_sccache = False |
| skip_icc = True |
| building_gst = False |
| |
| if not isinstance(args, list): |
| args = list(args) |
| |
| sandbox_build_path = os.path.join(self.sandbox_source_root, self.base_build_dir, self.build_type) |
| sandbox_environment = { |
| "TEST_RUNNER_INJECTED_BUNDLE_FILENAME": os.path.join(sandbox_build_path, "lib/libTestRunnerInjectedBundle.so"), |
| "PATH": "/usr/lib/sdk/llvm12/bin:/usr/bin:/usr/lib/sdk/rust/bin/", |
| } |
| |
| if not args: |
| args.append("bash") |
| |
| if args: |
| if gather_output: |
| command = args[0] |
| elif 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] == "bash": |
| args.extend(['--noprofile', '--norc', '-i']) |
| sandbox_environment["PS1"] = "[📦🌐🐱 $FLATPAK_ID \\W]\\$ " |
| if gather_output: |
| building = False |
| else: |
| building = self.is_build_jsc(args[0]) or self.is_build_webkit(args[0]) |
| else: |
| building = False |
| |
| flatpak_command = ["flatpak", "run", |
| "--user", |
| "--die-with-parent", |
| "--filesystem=host", |
| "--allow=devel", |
| # FIXME: --session-bus is only a workaround for https://github.com/flatpak/flatpak/pull/4630 |
| "--session-bus", |
| "--no-a11y-bus", |
| "--talk-name=org.a11y.Bus", |
| "--talk-name=org.gtk.vfs", |
| "--talk-name=org.gtk.vfs.*"] |
| |
| if not gather_output and args and self.is_build_webkit(args[0]) and not self.is_branch_build(): |
| # Ensure self.build_path exists. |
| try: |
| os.makedirs(self.build_path) |
| except OSError as e: |
| if e.errno != errno.EEXIST: |
| raise e |
| |
| share_network_option = "--share=network" |
| |
| if self.platform == 'WPE': |
| flatpak_command.append(share_network_option) |
| |
| if not building: |
| flatpak_command.extend([ |
| "--device=all", |
| "--device=dri", |
| "--share=ipc", |
| "--share=network", |
| "--socket=pulseaudio", |
| "--socket=session-bus", |
| "--socket=system-bus", |
| "--socket=wayland", |
| "--socket=x11", |
| "--system-talk-name=org.a11y.Bus", |
| "--system-talk-name=org.freedesktop.GeoClue2", |
| "--talk-name=org.freedesktop.Flatpak", |
| "--talk-name=org.freedesktop.secrets" |
| ]) |
| |
| sandbox_environment.update({ |
| "TZ": "PST8PDT", |
| }) |
| |
| env_var_prefixes_to_keep = [ |
| "G", |
| "CCACHE", |
| "EGL", |
| "GIGACAGE", |
| "GTK", |
| "ICECC", |
| "JSC", |
| "MESA", |
| "LIBGL", |
| "PIPEWIRE", |
| "RUST", |
| "SCCACHE", |
| "SPA", |
| "WAYLAND", |
| "WEBKIT", |
| "WEBKIT2", |
| "WPE", |
| ] |
| |
| env_var_suffixes_to_keep = [ |
| "JSC_ARGS", |
| "PROCESS_CMD_PREFIX", |
| "WEBKIT_ARGS", |
| ] |
| |
| env_vars_to_keep = [ |
| "CC", |
| "CCACHE_PREFIX", |
| "CFLAGS", |
| "CXX", |
| "CXXFLAGS", |
| "DISPLAY", |
| "JavaScriptCoreUseJIT", |
| "LDFLAGS", |
| "MAX_CPU_LOAD", |
| "Malloc", |
| "NUMBER_OF_PROCESSORS", |
| "QML2_IMPORT_PATH", |
| "RESULTS_SERVER_API_KEY", |
| "SSLKEYLOGFILE", |
| "XR_RUNTIME_JSON", |
| ] |
| |
| def envvar_in_suffixes_to_keep(envvar): |
| for env_var in env_var_suffixes_to_keep: |
| if envvar.endswith(env_var): |
| return True |
| return False |
| |
| 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 envvar_in_suffixes_to_keep(envvar) or (not os.environ.get('GST_BUILD_PATH') and var_tokens[0] == "GST"): |
| sandbox_environment[envvar] = value |
| |
| remote_sccache_configs = set(["SCCACHE_REDIS", "SCCACHE_BUCKET", "SCCACHE_MEMCACHED", |
| "SCCACHE_GCS_BUCKET", "SCCACHE_AZURE_CONNECTION_STRING", |
| "WEBKIT_USE_SCCACHE"]) |
| if remote_sccache_configs.intersection(set(os.environ.keys())) and start_sccache and not building_gst: |
| _log.debug("Enabling network access for the remote sccache") |
| flatpak_command.append(share_network_option) |
| |
| sccache_environment = {} |
| if os.path.isfile(self.sccache_config_file) and not self.regenerate_toolchains and \ |
| "SCCACHE_CONF" not in os.environ.keys(): |
| sccache_environment["SCCACHE_CONF"] = self.host_path_to_sandbox_path(self.sccache_config_file) |
| |
| override_sccache_server_port = os.environ.get("WEBKIT_SCCACHE_SERVER_PORT") |
| if override_sccache_server_port: |
| _log.debug("Overriding sccache server port to %s" % override_sccache_server_port) |
| sccache_environment["SCCACHE_SERVER_PORT"] = override_sccache_server_port |
| |
| if building: |
| # Spawn the sccache server in background, and avoid recursing here, using a bool keyword. |
| _log.debug("Pre-starting the SCCache dist server") |
| self.run_in_sandbox("sccache", "--start-server", env=sccache_environment, building_gst=building_gst, |
| extra_flatpak_args=[share_network_option], start_sccache=False) |
| |
| # Forward sccache server env vars to sccache clients. |
| sandbox_environment.update(sccache_environment) |
| |
| if self.use_icecream and not skip_icc: |
| _log.debug('Enabling the icecream compiler') |
| flatpak_command.extend([share_network_option, "--filesystem=home"]) |
| |
| try: |
| n_cores = os.environ["NUMBER_OF_PROCESSORS"] |
| except KeyError: |
| n_cores = multiprocessing.cpu_count() * 3 |
| _log.debug('Following icecream recommendation for the number of cores to use: %d' % n_cores) |
| toolchain_name = os.environ.get("CC", "gcc") |
| default_toolchain = self.icc_version.get(toolchain_name) |
| |
| try: |
| toolchain_override = os.environ["ICECC_VERSION_OVERRIDE"] |
| except KeyError: |
| toolchain_path = default_toolchain |
| else: |
| if not os.path.isfile(toolchain_override): |
| Console.error_message("%s toolchain not found. ICECC_VERSION_OVERRIDE mis-configured?", toolchain_override) |
| return 1 |
| |
| toolchain_path = self.host_path_to_sandbox_path(toolchain_override) |
| |
| if not toolchain_path: |
| Console.error_message("Toolchains configuration not found. Please run webkit-flatpak -r or set ICECC_VERSION_OVERRIDE to a valid host path") |
| return 1 |
| |
| if "ICECC_VERSION_APPEND" in os.environ: |
| extra_toolchain = os.environ["ICECC_VERSION_APPEND"] |
| if not os.path.isfile(extra_toolchain): |
| Console.error_message("%s is not a valid IceCC toolchain. ICECC_VERSION_APPEND mis-configured?", extra_toolchain) |
| return 1 |
| toolchain_path += "," |
| toolchain_path += self.host_path_to_sandbox_path(extra_toolchain) |
| |
| sandbox_environment.update({ |
| "CCACHE_PREFIX": "icecc", |
| "ICECC_TEST_SOCKET": "/run/icecc/iceccd.socket", |
| "ICECC_VERSION": toolchain_path, |
| "NUMBER_OF_PROCESSORS": n_cores, |
| }) |
| |
| # Set PKG_CONFIG_PATH in sandbox so uninstalled WebKit.pc files can be used. |
| pkg_config_path = os.environ.get("PKG_CONFIG_PATH") |
| if pkg_config_path: |
| pkg_config_path = "%s:%s" % (self.build_path, pkg_config_path) |
| else: |
| pkg_config_path = self.build_path |
| sandbox_environment["PKG_CONFIG_PATH"] = pkg_config_path |
| |
| if not building_gst and args[0] != "sccache": |
| # Merge gst-build env vars in sandbox environment, without overriding previously set PATH values. |
| gst_env = self.setup_gstbuild(building) |
| for var_name in list(gst_env.keys()): |
| if var_name not in sandbox_environment: |
| sandbox_environment[var_name] = gst_env[var_name] |
| else: |
| contents = gst_env[var_name] |
| if var_name.endswith('PATH'): |
| sandbox_environment[var_name] = "%s:%s" % (sandbox_environment[var_name], contents) |
| |
| for envvar, value in sandbox_environment.items(): |
| flatpak_command.append("--env=%s=%s" % (envvar, value)) |
| |
| # $WEBKIT_OUTPUTDIR is not forwarded in the build sandbox because the host build path is |
| # always bind-mounted to /app in the sandbox. |
| env_vars_to_drop = ("WEBKIT_OUTPUTDIR", "LANGUAGE") |
| flatpak_env = os.environ.copy() |
| for envvar in list(flatpak_env.keys()): |
| if envvar.startswith("LC_") or envvar in env_vars_to_drop: |
| del flatpak_env[envvar] |
| if self.flatpak_version >= (1, 10, 0): |
| flatpak_command.append("--unset-env=%s" % envvar) |
| |
| # Avoid 'error: Invalid byte sequence in conversion input' after removing |
| # all `LANG` vars. |
| flatpak_env["LANG"] = "en_US.UTF-8" |
| |
| keep_signals = args[0] != "gdb" |
| if not keep_signals: |
| module_path = os.path.join(self.build_path, "lib", "libsigaction-disabler.so") |
| # Enable module in bwrap child processes. |
| extra_flatpak_args.append("--env=WEBKIT_FLATPAK_LD_PRELOAD=%s" % module_path) |
| # Enable module in `flatpak run`. |
| flatpak_env["LD_PRELOAD"] = module_path |
| |
| flatpak_command += extra_flatpak_args + ['--command=%s' % args[0], "org.webkit.Sdk"] + args[1:] |
| |
| flatpak_env.update({ |
| "FLATPAK_BWRAP": os.path.join(scriptdir, "webkit-bwrap"), |
| "WEBKIT_BUILD_DIR_BIND_MOUNT": "%s:%s" % (sandbox_build_path, self.build_path), |
| "WEBKIT_FLATPAK_USER_DIR": os.environ["FLATPAK_USER_DIR"], |
| }) |
| |
| display = os.environ.get("DISPLAY") |
| if display: |
| flatpak_env["WEBKIT_FLATPAK_DISPLAY"] = display |
| |
| # FIXME: Remove duplicate values from the flatpak command. |
| command = flatpak_command |
| |
| if gather_output: |
| return run_sanitized(command, gather_output=True, ignore_stderr=False, env=flatpak_env) |
| |
| try: |
| return self.execute_command(command, stdout=stdout, env=flatpak_env, keep_signals=keep_signals) |
| except KeyboardInterrupt: |
| return 0 |
| |
| return 0 |
| |
| def main(self): |
| if self.check_available: |
| return 0 |
| |
| if not self.clean_args(): |
| return 1 |
| |
| if self.show_version: |
| print(self.sdk_repo.version("org.webkit.Sdk")) |
| return 0 |
| |
| if self.update: |
| repo = self.sdk_repo |
| version_before_update = repo.version("org.webkit.Sdk") |
| repo.flatpak("update", comment="Updating Flatpak %s environment" % self.build_type) |
| regenerate_toolchains = (repo.version("org.webkit.Sdk") != version_before_update) or not self.check_toolchains_generated() |
| |
| # If we have an out-of-date package, simply remove the entire flatpak directory and start over. |
| for package in self._get_dependency_packages(): |
| if package.name.startswith("org.webkit") \ |
| and self.repos.is_package_installed(package.name) \ |
| and not self.repos.is_package_installed(package.name, branch=SDK_BRANCH): |
| |
| # Cache sccache auth token before removing UserFlatpak. |
| self.acquire_sccache_auth_token_from_config_file() |
| |
| Console.message("New SDK version available, removing local UserFlatpak directory before switching to new version") |
| shutil.rmtree(self.flatpak_build_path) |
| |
| Console.message("Forcing next WebKit build to re-run CMake") |
| for platform in ('GTK', 'WPE'): |
| for build_type in ('Release', 'Debug'): |
| cache_path = os.path.join(self.build_root, platform, build_type, 'CMakeCache.txt') |
| if os.path.isfile(cache_path): |
| Console.message("Removing %s", cache_path) |
| os.remove(cache_path) |
| |
| self._reset_repository() |
| break |
| |
| for package in self._get_dependency_packages(): |
| if not self.repos.is_package_installed(package.name): |
| package.install() |
| regenerate_toolchains = True |
| |
| print("SDK version: {}".format(self.sdk_repo.version("org.webkit.Sdk"))) |
| |
| else: |
| regenerate_toolchains = self.regenerate_toolchains |
| |
| result = self.setup_dev_env() |
| if regenerate_toolchains: |
| |
| # Toolchains used to be stored in WebKitBuild/Toolchains. Remove this path if found, to save |
| # up disk space. |
| old_toolchains_path = os.path.join(self.default_build_root, "Toolchains") |
| if os.path.isdir(old_toolchains_path): |
| Console.message("Purging obsolete toolchains") |
| shutil.rmtree(old_toolchains_path) |
| |
| if not os.path.isdir(self.toolchains_directory): |
| os.makedirs(self.toolchains_directory) |
| |
| Console.message("Updating icecc/sccache standalone toolchain archives") |
| self.icc_version = {} |
| gcc_archive, toolchains = self.pack_toolchain(("gcc", "g++"), {"/usr/bin/c++": "g++", |
| "/usr/bin/cc": "gcc"}) |
| clang_archive, clang_toolchains = self.pack_toolchain(("clang", "clang++"), {"/usr/bin/clang++": "clang++", |
| "/usr/bin/clang": "clang"}) |
| toolchains.extend(clang_toolchains) |
| if len(toolchains) > 1: |
| self.save_config(toolchains) |
| self.purge_unused_toolchains((gcc_archive, clang_archive)) |
| else: |
| Console.error_message("Error generating icecc/sccache standalone toolchain archives") |
| |
| return result |
| |
| 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 acquire_sccache_auth_token_from_config_file(self): |
| if os.path.isfile(self.sccache_config_file) and not self.sccache_token: |
| Console.message("Reusing sccache auth token from old configuration file") |
| with open(self.sccache_config_file) as config: |
| sccache_config = toml.load(config) |
| self.sccache_token = sccache_config['dist']['auth']['token'] |
| |
| def save_config(self, toolchains): |
| with open(self.config_file, 'w') as config: |
| json_config = {'icecc_version': self.icc_version} |
| json.dump(json_config, config) |
| |
| self.acquire_sccache_auth_token_from_config_file() |
| |
| if not self.sccache_token: |
| Console.message("No authentication token provided. Re-run this with the -t option if an sccache token was provided to you. Skipping sccache configuration for now.") |
| return |
| |
| with open(self.sccache_config_file, 'w') as config: |
| sccache_config = {'dist': {'scheduler_url': self.sccache_scheduler, |
| 'auth': {'type': 'token', |
| 'token': self.sccache_token}, |
| 'toolchains': toolchains}} |
| toml.dump(sccache_config, config) |
| Console.message("Created %s sccache config file. It will automatically be used when building WebKit", self.sccache_config_file) |
| |
| def purge_unused_toolchains(self, allow_list): |
| for filename in os.listdir(self.toolchains_directory): |
| if filename not in allow_list: |
| _log.debug("Removing unused toolchain: %s", filename) |
| os.remove(os.path.join(self.toolchains_directory, filename)) |
| |
| def check_toolchains_generated(self): |
| found_toolchains = 0 |
| if os.path.isfile(self.config_file): |
| with open(self.config_file, 'r') as config_fd: |
| config = json.load(config_fd) |
| if 'icecc_version' in config: |
| for compiler in config['icecc_version']: |
| if os.path.isfile(config['icecc_version'][compiler]): |
| found_toolchains += 1 |
| return found_toolchains > 1 |
| |
| def pack_toolchain(self, compilers, path_mapping): |
| compiler_mapping = {} |
| for compiler in compilers: |
| compiler_mapping[compiler] = self.run_in_sandbox("/usr/bin/which", compiler, gather_output=True) |
| |
| with tempfile.NamedTemporaryFile() as tmpfile: |
| command = ['icecc', '--build-native'] |
| command.extend(compiler_mapping.values()) |
| retcode = self.run_in_sandbox(*command, stdout=tmpfile, cwd=self.source_root, skip_icc=True) |
| if retcode != 0: |
| Console.error_message('Flatpak command "%s" failed with return code %s', " ".join(command), retcode) |
| return [] |
| tmpfile.flush() |
| tmpfile.seek(0) |
| icc_version_filename, = re.findall(br'.*creating (.*)', tmpfile.read()) |
| relative_filename = "webkit-sdk-{name}-{filename}".format(name=compilers[0], filename=icc_version_filename.decode()) |
| archive_filename = os.path.join(self.toolchains_directory, relative_filename) |
| os.rename(icc_version_filename, archive_filename) |
| archive_sandbox_path = self.host_path_to_sandbox_path(archive_filename) |
| self.icc_version[compilers[0]] = archive_sandbox_path |
| Console.message("Created %s self-contained toolchain archive", archive_filename) |
| |
| sccache_toolchains = [] |
| for (compiler_executable, archive_compiler_executable) in path_mapping.items(): |
| item = {'type': 'path_override', |
| 'compiler_executable': compiler_executable, |
| 'archive': archive_sandbox_path, |
| 'archive_compiler_executable': compiler_mapping[archive_compiler_executable]} |
| sccache_toolchains.append(item) |
| return (relative_filename, sccache_toolchains) |
| |
| def check_installed_packages(self): |
| for package in self._get_dependency_packages(): |
| if package.name.startswith("org.webkit") and not package.is_installed(SDK_BRANCH): |
| Console.error_message("Flatpak package %s not installed. Please update your SDK: Tools/Scripts/update-webkit-flatpak", package) |
| return False |
| else: |
| return True |
| |
| |
| def setup_dev_env(self): |
| if not os.path.exists(os.path.join(self.flatpak_build_path, "runtime", "org.webkit.Sdk")) or self.update: |
| self.install_all() |
| |
| if not self.update and not self.check_installed_packages(): |
| return 1 |
| |
| if self.gdb or self.gdb_stack_trace: |
| return self.run_gdb() |
| elif self.user_command: |
| program = self.user_command[0] |
| if self.is_build_webkit(program) and self.cmakeargs: |
| self.user_command.append("--cmakeargs=%s" % self.cmakeargs) |
| |
| return self.run_in_sandbox(*self.user_command) |
| elif not self.update and not self.build_gst and not self.regenerate_toolchains: |
| return self.run_in_sandbox() |
| |
| return 0 |
| |
| def _get_dependency_packages(self): |
| arch = platform.machine() |
| self.runtime = FlatpakPackage("org.webkit.Platform", SDK_BRANCH, |
| self.sdk_repo, arch) |
| self.sdk = FlatpakPackage("org.webkit.Sdk", SDK_BRANCH, |
| self.sdk_repo, arch) |
| packages = [self.runtime, self.sdk] |
| packages.append(FlatpakPackage('org.webkit.Sdk.Debug', SDK_BRANCH, |
| self.sdk_repo, arch)) |
| packages.append(FlatpakPackage("org.freedesktop.Sdk.Extension.llvm12", SDK_BRANCH, |
| self.flathub_repo, arch)) |
| packages.append(FlatpakPackage("org.freedesktop.Platform.GL.default", SDK_BRANCH, |
| self.flathub_repo, arch)) |
| 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_dependency_packages(): |
| if not package.is_installed(SDK_BRANCH): |
| package.install() |
| |
| def run_gdb(self): |
| with disable_signals(): |
| try: |
| subprocess.check_output(['which', 'coredumpctl']) |
| except subprocess.CalledProcessError as e: |
| Console.message("'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) |
| |
| result = self.execute_command(cmd, stdout=coredump, stderr=stderr) |
| if result != 0: |
| Console.error_message("coredumpctl failed") |
| with open(stderr.name, 'r') as stderrf: |
| stderr = stderrf.read() |
| Console.error_message(stderr) |
| return result |
| |
| 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 os.environ.get('WEBKIT_JHBUILD', '0') == '1': |
| return None |
| |
| os.environ["FLATPAK_USER_DIR"] = os.environ.get("WEBKIT_FLATPAK_USER_DIR", FLATPAK_USER_DIR_PATH) |
| if not os.path.isdir(os.environ["FLATPAK_USER_DIR"]): |
| return None |
| |
| if is_sandboxed(): |
| return None |
| |
| if not check_flatpak(verbose=False): |
| return None |
| |
| # Filter out flatpakutils args for the app. |
| runner_args = [] |
| app_args = [] |
| opt_prefix = "--flatpak-" |
| for arg in args: |
| if arg.startswith(opt_prefix): |
| runner_args.append("--%s" % arg[len(opt_prefix):]) |
| else: |
| runner_args.append(arg) |
| app_args.append(arg) |
| |
| flatpak_runner = WebkitFlatpak.load_from_args(runner_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(*app_args)) |