blob: d18a7ed79062075d640ad8748c45b3467ad395a2 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2020 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 os
import re
import sys
import shlex
import tempfile
import time
import subprocess
import urllib.request
from html.parser import HTMLParser
REPO = "https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo"
SDK_BRANCH = "21.08"
USER_DIR = os.path.expanduser("~/.cache/wk-nightly")
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
# FIXME: Might be worth adding some JSON file listing builds on the servers.
class MyHTMLParser(HTMLParser):
builds = []
def handle_starttag(self, tag, attrs):
if tag != "a":
return
for (name, value) in attrs:
if name == "href" and (value.startswith("release") or value.startswith("debug")):
self.builds.append(value)
def flatpak(*args, env=None, stdout=None):
cmd_env = os.environ.copy()
if env:
cmd_env.update(env)
cmd_env["FLATPAK_USER_DIR"] = USER_DIR
return subprocess.call(("flatpak",) + args, env=cmd_env, stdout=stdout)
def ensure_sdk(build_type):
branch = SDK_BRANCH
if not os.path.isdir(USER_DIR):
flatpak("remote-add", "--user", "webkit-nightly", REPO)
flatpak("install", "--user", "-y", "webkit-nightly", f"org.webkit.Platform//{branch}")
else:
flatpak("update", "--user", "-y")
if (build_type == "Debug") and (flatpak("info", "org.webkit.Sdk.Debug", stdout=subprocess.DEVNULL) >= 1):
flatpak("install", "--user", "-y", "webkit-nightly", f"org.webkit.Sdk//{branch}")
flatpak("install", "--user", "-y", "webkit-nightly", f"org.webkit.Sdk.Debug//{branch}")
def get_build_path(platform, build_type, revision_number):
dirname = f"{platform}-{build_type}-r{revision_number}"
return os.path.join(tempfile.gettempdir(), dirname)
def get_revision_number(build_name):
match = re.match(r'.*_r([0-9]+)\.zip$', build_name)
if match:
return int(match.group(1))
def ensure_extracted_build(args):
build_type = args.build_type.lower()
if args.revision_number:
path = get_build_path(args.platform, build_type, args.revision_number)
if os.path.isdir(path):
return path
url = f"https://{args.platform}-{build_type}.igalia.com/built-products/"
with urllib.request.urlopen(url) as page_fd:
parser = MyHTMLParser()
parser.feed(page_fd.read().decode("utf-8"))
try:
latest = parser.builds[-1]
except IndexError:
print(f"No build found in {url}")
return ""
if args.revision_number:
try:
build = [b for b in parser.builds if b.endswith(f"r{args.revision_number}.zip")][0]
except IndexError:
print(f"Build {args.revision_number} not found. Falling back to latest: {latest}")
build = latest
else:
build = latest
print(f"Downloading build {build} from {url}")
with urllib.request.urlopen(f"{url}/{build}") as zip_fd:
with tempfile.NamedTemporaryFile() as zip_file:
data = zip_fd.read(8192)
spinner = spinning_cursor()
while data:
zip_file.write(data)
sys.stdout.write(next(spinner))
sys.stdout.flush()
data = zip_fd.read(8192)
sys.stdout.write('\b')
zip_file.flush()
revision_number = get_revision_number(build)
path = get_build_path(args.platform, build_type, revision_number)
print(f"Extracting build to {path}")
os.system(f"unzip -qq -o {zip_file.name} -d {path}")
return path
def run(path, build_type, args):
env = {
"FLATPAK_BWRAP": os.path.realpath(__file__),
"WEBKIT_BUILD_DIR_BIND_MOUNT": f"/app/webkit/WebKitBuild/{build_type}:{path}",
}
flatpak("run", "--die-with-parent", "--user",
f"--env=PATH=/app/webkit/WebKitBuild/{build_type}/bin:/usr/bin",
"--device=dri",
"--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",
"--talk-name=org.gtk.vfs",
"--talk-name=org.gtk.vfs.*",
f"--command={args[0]}", f"org.webkit.Platform//{SDK_BRANCH}", *args[1:], env=env)
def main(args):
parser = argparse.ArgumentParser()
parser.add_argument("--revision-number", type=int, help="Download a specific revision (default: latest)",
action='store', dest='revision_number')
parser.add_argument("-p", action="store", dest="path", help="Reuse previously downloaded build from given path")
type_group = parser.add_mutually_exclusive_group()
type_group.add_argument("--debug", help="Download a debug build, also installs Sdk debug symbols.",
dest='build_type', action="store_const", const="Debug")
type_group.add_argument("--release", help="Download a release build.",
dest='build_type', action="store_const", const="Release")
platform_group = parser.add_mutually_exclusive_group()
platform_group.add_argument('--gtk', action='store_const', dest='platform', const='webkitgtk',
help='Download and run GTK port build artefacts')
platform_group.add_argument('--wpe', action='store_const', dest='platform', const='wpewebkit',
help=('Download and run WPE port build artefacts'))
parser.add_argument("command", nargs=argparse.REMAINDER, help="Command to execute (example: MiniBrowser, jsc)")
if len(args) == 0:
parser.print_help(sys.stderr)
sys.exit(1)
parsed, _ = parser.parse_known_args(args=args)
if not parsed.platform:
parsed.platform = "webkitgtk"
if not parsed.build_type:
parsed.build_type = "Release"
ensure_sdk(parsed.build_type)
if not parsed.path:
parsed.path = ensure_extracted_build(parsed)
if parsed.path:
run(parsed.path, parsed.build_type, parsed.command)
def read_lines(fd: int):
new_fd = os.dup(fd)
pos = os.lseek(fd, 0, os.SEEK_CUR)
try:
with os.fdopen(new_fd, 'r') as handle:
data = handle.read()
lines = data.split('\x00')
for line in lines:
yield line
except Exception as e:
print("Error reading bwrap arguments", file=sys.stderr)
print(e, file=sys.stderr)
finally:
os.lseek(fd, pos, os.SEEK_SET)
def bwrap_main(args):
bind_mounts = {
"/run/shm": "/dev/shm",
}
args_idx = args.index('--args')
arg_fd = int(args[args_idx + 1])
lines = read_lines(arg_fd)
environ = os.environ.copy()
while True:
try:
arg = next(lines)
if arg == '--setenv':
key = next(lines)
value = next(lines)
environ[key] = value
except StopIteration:
break
build_path = environ.get("WEBKIT_BUILD_DIR_BIND_MOUNT")
if build_path:
dest, src = build_path.split(":")
bind_mounts[dest] = src
bwrap_args = ["bwrap", ]
for dst, src in bind_mounts.items():
bwrap_args.extend(["--bind", src, dst])
os.execvpe(bwrap_args[0], bwrap_args + args, os.environ)
if __name__ == "__main__":
ld_library_path = os.environ.get("LD_LIBRARY_PATH", "")
if '--args' in sys.argv:
bwrap_main(sys.argv[1:])
else:
main(sys.argv[1:])