blob: 89ebc67310c1d5b84c90cf5407f940c01b05eee6 [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", "0.10.0"),
("flatpak-builder", "0.10.0"),
]
FLATPAK_VERSION = {}
WPE_MANIFEST_MAP = {
"qt": "org.webkit.WPEQT.yaml",
}
scriptdir = os.path.abspath(os.path.dirname(__file__))
_log = logging.getLogger(__name__)
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()
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.message("\n%sYou 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", Colors.FAIL,
app, required_version, sys.argv[0], Colors.ENDC)
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.message("\n%s%s %s required but %s found."
" Please update and try again%s\n", Colors.FAIL,
app, required_version, version, Colors.ENDC)
return False
return True
def remove_extension_points(array):
result_args = []
for arg in array:
if(not arg.startswith('--extension')):
result_args.append(arg)
return result_args
def remove_comments(string):
pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)"
# first group captures quoted strings (double or single)
# second group captures comments (//single-line or /* multi-line */)
regex = re.compile(pattern, re.MULTILINE | re.DOTALL)
def _replacer(match):
# if the 2nd group (capturing comments) is not None,
# it means we have captured a non-quoted (real) comment string.
if match.group(2) is not None:
return "" # so we will return empty to remove the comment
else: # otherwise, we will return the 1st group
return match.group(1) # captured quoted-string
return regex.sub(_replacer, string)
def load_manifest(manifest_path, port_name=None, command=None):
is_yaml = manifest_path.endswith('.yaml')
with open(manifest_path, "r") as mr:
contents = mr.read()
contents = contents % {"COMMAND": command, "PORTNAME": port_name}
if is_yaml:
import yaml
yaml_load = getattr(yaml, "safe_load", yaml.load)
manifest = yaml_load(contents)
else:
contents = remove_comments(contents)
manifest = json.loads(contents)
return manifest
def expand_submodules_recurse(modules, manifest_path, port_name, command):
all_modules = []
if type(modules) is str:
submanifest_path = os.path.join(os.path.dirname(manifest_path), modules)
submanifest = load_manifest(submanifest_path, port_name=port_name, command=command)
all_modules.extend(expand_submodules_recurse(submanifest, submanifest_path, port_name, command))
return all_modules
# The last recurse manifest expand iteration might lead to a single module.
if not isinstance(modules, list):
all_modules.append(modules)
return all_modules
for module in modules:
if type(module) is str:
submanifest_path = os.path.join(os.path.dirname(manifest_path), module)
submanifest = load_manifest(submanifest_path, port_name=port_name, command=command)
all_modules.extend(expand_submodules_recurse(submanifest, submanifest_path, port_name, command))
else:
all_modules.append(module)
return all_modules
def expand_manifest(manifest_path, outfile, port_name, source_root, command):
"""Creates the manifest file."""
try:
os.remove(outfile)
except OSError:
pass
manifest = load_manifest(manifest_path, port_name=port_name, command=command)
if not manifest:
return False
if "sdk-hash" in manifest:
del manifest["sdk-hash"]
if "runtime-hash" in manifest:
del manifest["runtime-hash"]
all_modules = []
overriden_modules = []
if "WEBKIT_EXTRA_MODULESETS" in os.environ:
overriden_modules = load_manifest(os.environ["WEBKIT_EXTRA_MODULESETS"])
if not overriden_modules:
overriden_modules = []
for modules in manifest["modules"]:
modules = expand_submodules_recurse(modules, manifest_path, port_name, command)
if not isinstance(modules, list):
modules = [modules]
for module in modules:
for overriden_module in overriden_modules:
if module['name'] == overriden_module['name']:
module = overriden_module
overriden_modules.remove(module)
break
all_modules.append(module)
# And add overriden modules right before the webkit port build def.
for overriden_module in overriden_modules:
all_modules.insert(-1, overriden_module)
manifest["modules"] = all_modules
for module in manifest["modules"]:
if not module.get("sources"):
continue
if module["sources"][0]["type"] == "git":
if port_name == module["name"]:
repo = "file://" + source_root
module["sources"][0]["url"] = repo
for source in module["sources"]:
if source["type"] == "patch" or (source["type"] == "file" and source.get('path')):
source["path"] = os.path.join(os.path.dirname(manifest_path), source["path"])
with open(outfile, "w") as of:
of.write(json.dumps(manifest, indent=4))
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]
if self.user:
res = subprocess.check_output(command + ["--help"]).decode("utf-8")
if "--user" in res:
command.append("--user")
command.extend(args)
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.runtimes = self.__detect_runtimes()
self.apps = self.__detect_apps()
self.packages = self.runtimes + self.apps
def __detect_packages(self, *args):
packs = []
if FLATPAK_VERSION["flatpak"] < (1, 1, 2):
out = self.flatpak("list", "-d", *args)
package_defs = [line for line in out.split("\n") if line]
for package_def in package_defs:
splited_packaged_def = package_def.split()
name, arch, branch = splited_packaged_def[0].split("/")
# If installed from a file, the package is in no repo
repo_name = splited_packaged_def[1]
repo = self.repos.repos.get(repo_name)
packs.append(FlatpakPackage(name, branch, repo, arch))
else:
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 __detect_apps(self):
return self.__detect_packages()
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 = {}
if FLATPAK_VERSION["flatpak"] < (1, 1, 2):
out = self.flatpak("remote-list", "-d")
remotes = [line for line in out.split("\n") if line]
for repo in remotes:
for components in [repo.split(" "), repo.split("\t")]:
if len(components) == 1:
components = repo.split("\t")
name = components[0]
desc = ""
url = None
for elem in components[1:]:
if not elem:
continue
parsed_url = urlparse(elem)
if parsed_url.scheme:
url = elem
break
if desc:
desc += " "
desc += elem
if url:
break
if not url:
Console.message("No valid URI found for: %s", repo)
continue
self.repos[name] = FlatpakRepo(name, url, desc, repos=self)
else:
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, url, 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)
self.url = repo["Flatpak Repo"]["Url"]
else:
assert url
@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
self.hash = hash
def __str__(self):
return "%s/%s/%s %s" % (self.name, self.arch, self.branch, self.repo.name)
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
args = ["install", self.repo.name, self.name, "--reinstall", self.branch, "--assumeyes"]
comment = "Installing from " + self.repo.name + " " + self.name + " " + self.arch + " " + self.branch
self.flatpak(*args, show_output=True, comment=comment)
if self.hash:
args = ["update", "--commit", self.hash, self.name]
comment = "Updating to %s" % self.hash
self.flatpak(*args, show_output=True, comment=comment)
def update(self):
if not self.is_installed(self.branch):
return self.install()
extra_args = []
comment = "Updating %s" % self.name
if self.hash:
extra_args = ["--commit", self.hash]
comment += " to %s" % self.hash
extra_args.append("--assumeyes")
self.flatpak("update", self.name, self.branch, show_output=True,
*extra_args, 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("-ba", "--build-all", dest="build_all",
action="store_true",
help="Force rebuilding the app and its dependencies.")
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", help="Use the distributed icecream (icecc) compiler.", action="store_true")
general.add_argument("--wpe-extension", action="store", dest="wpe_extension", help="WPE Extension to enable")
debugoptions = parser.add_argument_group("Debugging")
debugoptions.add_argument("--gdb", nargs="?", help="Activate gdb, passing extra args to it if wanted.")
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)
return self
def __init__(self):
self.sdk_repo = None
self.runtime = None
self.locale = None
self.sdk = None
self.sdk_debug = None
self.app = None
self.verbose = False
self.quiet = False
self.packs = []
self.update = False
self.args = []
self.finish_args = None
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_all = False
self.sdk_branch = None
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.app_module = None
self.flatpak_default_args = []
self.check_available = False
self.wpe_extension = None
# 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", os.path.realpath(os.path.join(scriptdir, "../../WebKitBuild", "UserFlatpak")))
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 = ""
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.name = "org.webkit.%s" % self.platform
if self.wpe_extension:
manifest_filename = WPE_MANIFEST_MAP[self.wpe_extension]
else:
manifest_filename = "org.webkit.WebKit.yaml"
self.manifest_path = os.path.abspath(os.path.join(scriptdir, '../flatpak/') + manifest_filename)
self.build_name = self.name + "-generated"
build_root = os.path.join(self.source_root, 'WebKitBuild')
self.flatpak_build_path = os.path.join(build_root, self.platform, "FlatpakTree" + self.build_type)
self.cache_path = os.path.join(build_root, "FlatpakCache")
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.build_path, 'webkit_flatpak_config.json')
Console.quiet = self.quiet
if not check_flatpak():
return False
repos = FlatpakRepos()
self.sdk_repo = repos.add(
FlatpakRepo("flathub",
url="https://dl.flathub.org/repo/",
repo_file="https://dl.flathub.org/repo/flathub.flatpakrepo"))
manifest = load_manifest(self.manifest_path, port_name=self.name)
if not manifest:
return False
self.app = manifest['app-id']
self.sdk_branch = manifest["runtime-version"]
self.finish_args = manifest.get("finish-args", [])
self.finish_args = remove_extension_points(self.finish_args)
self.runtime = FlatpakPackage(manifest['runtime'], self.sdk_branch,
self.sdk_repo, "x86_64",
hash=manifest.get("runtime-hash"))
self.locale = FlatpakPackage(manifest['runtime'] + '.Locale',
self.sdk_branch, self.sdk_repo, "x86_64")
self.sdk = FlatpakPackage(manifest['sdk'], self.sdk_branch,
self.sdk_repo, "x86_64",
hash=manifest.get("sdk-hash"))
self.packs = [self.runtime, self.locale, self.sdk]
if self.debug:
self.sdk_debug = FlatpakPackage(manifest['sdk'] + '.Debug', self.sdk_branch,
self.sdk_repo, "x86_64")
self.packs.append(self.sdk_debug)
self.manifest_generated_path = os.path.join(self.cache_path,
self.build_name + ".json")
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 run_in_sandbox(self, *args, **kwargs):
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")
sandbox_build_path = os.path.join(self.sandbox_source_root, "WebKitBuild", self.build_type)
with tempfile.NamedTemporaryFile(mode="w") as tmpscript:
flatpak_command = ["flatpak", "build", "--die-with-parent",
"--bind-mount=/run/shm=/dev/shm",
# Workaround for https://webkit.org/b/187384 to have our own perl modules usable inside the sandbox
# as setting the PERL5LIB envvar won't work inside apache (and for scripts using `perl -T``).
"--bind-mount=/run/host/%s=%s" % (tempfile.gettempdir(), tempfile.gettempdir()),
"--bind-mount=%s=%s" % (self.sandbox_source_root, self.source_root),
"--talk-name=org.a11y.Bus",
"--talk-name=org.gtk.vfs",
"--talk-name=org.gtk.vfs.*",
# 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": "/app/webkit/lib/libTestRunnerInjectedBundle.so",
"ICECC_VERSION": self.icc_version,
}
env_var_prefixes_to_keep = [
"GST",
"GTK",
"G",
"JSC",
"WEBKIT",
"WEBKIT2",
"WPE",
"GIGACAGE",
]
env_vars_to_keep = [
"JavaScriptCoreUseJIT",
"Malloc",
"WAYLAND_DISPLAY",
"WAYLAND_SOCKET",
"DISPLAY",
"LANG",
"NUMBER_OF_PROCESSORS",
"CCACHE_PREFIX",
"QML2_IMPORT_PATH",
]
if self.use_icecream:
_log.debug('Enabling the icecream compiler')
forwarded["CCACHE_PREFIX"] = "icecc"
if not os.environ.get('NUMBER_OF_PROCESSORS'):
n_cores = multiprocessing.cpu_count() * 3
_log.debug('Follow icecream recommendation for the number of cores to use: %d' % n_cores)
forwarded["NUMBER_OF_PROCESSORS"] = n_cores
env_vars = os.environ
env_vars.update(extra_env_vars)
for envvar, value in env_vars.items():
if envvar.split("_")[0] in env_var_prefixes_to_keep or envvar in env_vars_to_keep:
forwarded[envvar] = value
for envvar, value in forwarded.items():
flatpak_command.append("--env=%s=%s" % (envvar, value))
flatpak_command += self.finish_args + extra_flatpak_args + [self.flatpak_build_path]
shell_string = ""
if args:
if cwd:
shell_string = 'cd "%s" && "%s"' % (cwd, '" "'.join(args))
else:
shell_string = '"%s"' % ('" "'.join(args))
else:
shell_string = self.command
if self.args:
shell_string += ' "%s"' % '" "'.join(self.args)
tmpscript.write(shell_string)
tmpscript.flush()
_log.debug('Running in sandbox: "%s" %s\n' % ('" "'.join(flatpak_command), shell_string))
flatpak_command.extend(['sh', "/run/host/" + tmpscript.name])
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 run(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 environment for %s (%s)" % (
self.platform, self.build_type))
if not self.no_flatpak_update:
self.update_all()
return self.setup_dev_env()
def has_environment(self):
return os.path.exists(os.path.join(self.build_path, 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_ccache(self):
for compiler in ["c++", "cc", "clang", "clang++", "g++", "gcc"]:
self.run_in_sandbox("ln", "-s", "../../usr/bin/ccache", compiler, cwd="/app/bin")
def setup_icecc(self):
with tempfile.NamedTemporaryFile() as tmpfile:
self.run_in_sandbox('icecc', '--build-native', stdout=tmpfile, cwd=self.build_path)
tmpfile.flush()
tmpfile.seek(0)
icc_version_filename, = re.findall(r'.*creating (.*)', tmpfile.read())
self.icc_version = os.path.join(self.build_path, icc_version_filename)
def setup_dev_env(self):
if not os.path.exists(os.path.join(self.build_path, self.flatpak_build_path)) \
or self.update or self.build_all:
self.install_all()
Console.message("Building %s and dependencies in %s",
self.name, self.flatpak_build_path)
# Create environment dirs if necessary
try:
os.makedirs(os.path.dirname(self.manifest_generated_path))
except OSError as e:
if e.errno != errno.EEXIST:
raise e
if not expand_manifest(self.manifest_path, self.manifest_generated_path,
self.name, self.sandbox_source_root, self.command):
return 1
builder_args = ["flatpak-builder", "--disable-rofiles-fuse", "--state-dir",
self.cache_path, "--ccache", self.flatpak_build_path, "--force-clean",
self.manifest_generated_path]
builder_args.append("--build-only")
builder_args.append("--stop-at=%s" % self.app)
subprocess.check_call(builder_args)
self.setup_ccache()
self.setup_icecc()
self.save_config()
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
else:
Console.message("Using %s prefix in %s", self.name, self.flatpak_build_path)
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 is not None:
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:
return self.run_in_sandbox()
return 0
def install_all(self):
for package in self.packs:
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:
subprocess.check_call(["coredumpctl", "dump"] + shlex.split(self.coredumpctl_matches),
stdout=coredump, stderr=stderr)
with open(stderr.name, 'r') as stderrf:
stderr = stderrf.read()
executable, = re.findall(".*Executable: (.*)", stderr)
if not executable.startswith("/newroot"):
sys.stderr.write("Executable %s doesn't seem to be a flatpaked application.\n" % executable)
executable = executable.replace("/newroot", "")
args = ["gdb", executable, "/run/host/%s" % coredump.name] + shlex.split(self.gdb)
return self.run_in_sandbox(*args)
def update_all(self):
for m in [self.runtime, self.sdk, self.sdk_debug]:
if m:
m.update()
def is_sandboxed():
return os.path.exists("/.flatpak-info")
def run_in_sandbox_if_available(args):
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))