pitivi-flatpak 27 KB
Newer Older
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3 4 5 6 7 8 9 10 11 12 13 14 15
# Pitivi video editor
# Copyright (c) 2016, Thibault Saunier <tsaunier@gnome.org>
#
# 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
16
# License along with this program; if not, see <http://www.gnu.org/licenses/>.
17
# pylint: disable=invalid-name
18 19 20 21
import argparse
import configparser
import json
import os
22
import re
23
import shlex
24 25 26 27
import shutil
import subprocess
import sys
import tempfile
28
from urllib.error import URLError
29 30 31
from urllib.parse import urlparse
from urllib.request import urlretrieve

32 33
# The default branch is master because the script is used most often
# for development.
34
PITIVI_BRANCH = "master"
35
MANIFEST_NAME = "org.pitivi.Pitivi.json"
Thibault Saunier's avatar
Thibault Saunier committed
36 37 38 39
FLATPAK_REQ = [
    ("flatpak", "0.10.0"),
    ("flatpak-builder", "0.10.0"),
]
40
FLATPAK_VERSION = {}
41
DEFAULT_GST_BRANCH = 'master'
42 43


Alexandru Băluț's avatar
Alexandru Băluț committed
44
class Colors:
45 46 47 48 49 50 51 52
    HEADER = "\033[95m"
    OKBLUE = "\033[94m"
    OKGREEN = "\033[92m"
    WARNING = "\033[93m"
    FAIL = "\033[91m"
    ENDC = "\033[0m"


Alexandru Băluț's avatar
Alexandru Băluț committed
53
class Console:
Alexandru Băluț's avatar
Alexandru Băluț committed
54 55 56 57 58 59 60 61 62 63 64 65

    quiet = False

    @classmethod
    def message(cls, str_format, *args):
        if cls.quiet:
            return

        if args:
            print(str_format % args)
        else:
            print(str_format)
66

Alexandru Băluț's avatar
Alexandru Băluț committed
67 68 69
        # Flush so that messages are printed at the right time
        # as we use many subprocesses.
        sys.stdout.flush()
70 71


Thibault Saunier's avatar
Thibault Saunier committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
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

        return match.group(1)  # captured quoted-string
    return regex.sub(_replacer, string)


def load_manifest(manifest_path):
    with open(manifest_path, "r") as mr:
        contents = mr.read()
        contents = remove_comments(contents)
        manifest = json.loads(contents)

    return manifest


def expand_manifest(manifest_path, outfile, basedir, gst_version, branchname):
98 99 100 101 102 103
    """Creates the manifest file."""
    try:
        os.remove(outfile)
    except FileNotFoundError:
        pass

Thibault Saunier's avatar
Thibault Saunier committed
104
    template = load_manifest(manifest_path)
105 106 107 108 109 110 111 112 113 114
    if branchname == "stable":
        try:
            del template["desktop-file-name-prefix"]
        except KeyError:
            pass
    elif branchname == "master":
        template["desktop-file-name-prefix"] = "(Rolling) "
    else:
        template["desktop-file-name-prefix"] = "(%s) " % branchname

Alexandru Băluț's avatar
Alexandru Băluț committed
115 116
    Console.message("-> Generating %s against GStreamer %s",
                    outfile, gst_version)
117 118

    for module in template["modules"]:
119 120 121 122
        if isinstance(module, str):
            # This is the name of a file to be included containing modules.
            continue

123 124 125 126 127
        if module["sources"][0]["type"] != "git":
            continue

        if module["name"].startswith("gst"):
            module["sources"][0]["branch"] = gst_version
128
            if gst_version != DEFAULT_GST_BRANCH:
129 130 131 132
                continue

        repo = os.path.join(basedir, module["name"])
        if not os.path.exists(os.path.join(repo, ".git")):
Alexandru Băluț's avatar
Alexandru Băluț committed
133 134
            Console.message("-> Module: %s using online repo: %s",
                            module["name"], module["sources"][0]["url"])
135 136 137 138 139 140 141 142
            continue

        branch = subprocess.check_output(
            r"git branch 2>&1 | grep \*", shell=True,
            cwd=repo).decode(
                "utf-8").split(" ")[1].strip("\n")

        repo = "file://" + repo
Alexandru Băluț's avatar
Alexandru Băluț committed
143 144
        Console.message("-> Module: %s repo: %s branch: %s",
                        module["name"], repo, branch)
145 146 147 148 149 150 151
        module["sources"][0]["url"] = repo
        module["sources"][0]["branch"] = branch

    with open(outfile, "w") as of:
        print(json.dumps(template, indent=4), file=of)


152 153 154 155 156 157 158 159
def check_flatpak(verbose=True):
    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"
luz.paz's avatar
luz.paz committed
160
                                "You can find some information about"
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
                                " 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


Alexandru Băluț's avatar
Alexandru Băluț committed
181
class FlatpakObject:
182 183 184 185 186 187

    def __init__(self, user):
        self.user = user

    def flatpak(self, command, *args, show_output=False, comment=None):
        if comment:
Alexandru Băluț's avatar
Alexandru Băluț committed
188
            Console.message(comment)
189 190 191 192 193 194 195 196

        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)

197 198
        if comment:
            Console.message(" ".join(command))
199 200 201 202 203 204
        if not show_output:
            return subprocess.check_output(command).decode("utf-8")

        return subprocess.check_call(command)


Alexandru Băluț's avatar
Alexandru Băluț committed
205
class FlatpakPackages(FlatpakObject):
206 207 208 209 210 211 212 213 214 215 216 217

    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 = []
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
        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")
235

236 237
                # If installed from a file, the package is in no repo
                repo = self.repos.repos.get(origin)
238

239
                packs.append(FlatpakPackage(name, branch, repo, arch))
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

        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)
258
        # The remote repositories, name -> FlatpakRepo
259 260 261 262 263
        self.repos = {}
        self.update()

    def update(self):
        self.repos = {}
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
        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:
287 288
                        break

289 290 291
                if not url:
                    Console.message("No valid URI found for: %s", repo)
                    continue
292

293 294 295 296 297 298 299 300 301 302
                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
303

304
                self.repos[name] = FlatpakRepo(name, url, title, repos=self)
305 306 307

        self.packages = FlatpakPackages(self)

308 309
    def add(self, name, flatpakrepo_url, override=True):
        flatpakrepo = tempfile.NamedTemporaryFile(mode="w")
310 311 312 313 314 315 316
        try:
            urlretrieve(flatpakrepo_url, flatpakrepo.name)
            repo = configparser.ConfigParser()
            repo.read(flatpakrepo.name)
            url = repo["Flatpak Repo"]["Url"]
        except URLError:
            url = None
317

318
        same_name = None
319
        for tmpname, tmprepo in self.repos.items():
320 321 322
            # If the URL is None (meaning we couldn't retrieve Repo infos)
            # just check if the repo names match.
            if url == tmprepo.url or url is None and name == tmpname:
323
                return tmprepo
324
            if name == tmpname:
325 326 327
                same_name = tmprepo

        if same_name:
328
            Console.message("Flatpak remote with the same name already exists: %s", same_name)
329
            if not override:
330 331
                Console.message("%sNote the URL is %s, not %s%s",
                                Colors.WARNING, same_name.url, url, Colors.ENDC)
332 333
                return None

334 335
            Console.message("The URL is different. Overriding.")
            self.flatpak("remote-modify", name, "--url=" + url,
336
                         comment="Setting repo %s URL from %s to %s"
337 338
                         % (name, same_name.url, url))
            same_name.url = url
339 340
            return same_name

341 342 343 344 345
        self.flatpak("remote-add", "--if-not-exists", name,
                     "--from", flatpakrepo_url,
                     comment="Adding repo %s" % name)
        self.update()
        return self.repos[name]
346 347


Alexandru Băluț's avatar
Alexandru Băluț committed
348
class FlatpakRepo(FlatpakObject):
349

Alexandru Băluț's avatar
Alexandru Băluț committed
350
    def __init__(self, name, url, desc=None, user=True, repos=None):
351 352 353
        FlatpakObject.__init__(self, user=user)

        self.name = name
354
        assert name
355 356
        self.desc = desc
        self.repos = repos
357
        self.url = url
358 359 360


class FlatpakPackage(FlatpakObject):
361
    """A flatpak app."""
362

Alexandru Băluț's avatar
Alexandru Băluț committed
363
    def __init__(self, name, branch, repo, arch, user=True):
364 365 366 367 368 369 370 371 372 373 374
        FlatpakObject.__init__(self, user=user)

        self.name = name
        self.branch = branch
        self.repo = repo
        self.arch = arch

    def __str__(self):
        return "%s/%s/%s %s" % (self.name, self.arch, self.branch, self.repo.name)

    def is_installed(self):
375 376 377 378
        if not self.repo:
            # Bundle installed from file
            return True

379 380 381 382 383 384 385 386 387
        if len(self.name.split(".")) <= 3:
            self.repo.repos.update()
            for package in self.repo.repos.packages:
                if package.name == self.name and \
                        package.arch == self.arch and \
                        package.branch == self.branch:
                    return True
        else:
            try:
388
                self.flatpak("info", self.name, self.branch)
389
                return True
390
            except subprocess.CalledProcessError:
391
                pass
392 393 394 395

        return False

    def install(self):
396
        if not self.repo:
397
            return
398

399
        self.flatpak("install", self.repo.name, self.name,
400
                     self.branch, "--reinstall", "-y", show_output=True,
401 402 403 404
                     comment="Installing %s" % self.name)

    def update(self):
        if not self.is_installed():
405 406
            self.install()
            return
407

408
        self.flatpak("update", self.name, self.branch, "-y", show_output=True,
409 410
                     comment="Updating %s" % self.name)

411 412
    def run_app(self, *args):
        """Starts the app represented by this instance."""
413 414
        self.flatpak("run", "--branch=" + self.branch, self.name, *args,
                     show_output=True,
415
                     comment="Running %s (%s)" % (self.name, self.branch))
416 417


Alexandru Băluț's avatar
Alexandru Băluț committed
418
class PitiviFlatpak:
419 420 421 422 423 424 425 426 427

    def __init__(self):
        self.name = "Pitivi"
        self.sdk_repo = None
        self.runtime = None
        self.locale = None
        self.sdk = None

        self.packs = []
428
        self.init = False
429
        self.update = False
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
        self.json = None
        self.args = []
        self.build = False
        self.scriptdir = os.path.abspath(os.path.dirname(__file__))
        self.envpath = os.environ.get("FLATPAK_ENVPATH",
                                      os.path.expanduser("~/%s-flatpak" %
                                                         self.name.lower()))
        self.prefix = os.path.join(
            self.envpath, "%s-prefix" % self.name.lower())
        self.repodir = os.path.join(
            self.envpath, "flatpak-repos", self.name.lower())
        self.local_repos_path = os.path.abspath(os.path.join(
            self.scriptdir, os.pardir, os.pardir, os.pardir))
        self.topdir = os.path.abspath(os.path.join(
            self.scriptdir, os.pardir, os.pardir))

        self.build_name = self.name
        if os.path.exists(os.path.join(self.topdir, ".git")):
448
            devnull = open(os.devnull)
449
            try:
450 451 452
                branch = subprocess.check_output(
                    "git rev-parse --abbrev-ref HEAD".split(" "),
                    stderr=devnull,
453
                    cwd=self.topdir).decode("utf-8").strip("\n")
454
                self.build_name = self.name + "." + branch
455
                self.build_name = self.build_name.replace(os.path.sep, "_")
456 457 458 459 460
            except subprocess.CalledProcessError:
                pass
            finally:
                devnull.close()

461 462 463
        self.coredumpgdb = None
        self.coredumpctl_matches = ""

464
    def clean_args(self):
Alexandru Băluț's avatar
Alexandru Băluț committed
465 466
        Console.quiet = self.quiet

467
        if not check_flatpak():
Alexandru Băluț's avatar
Alexandru Băluț committed
468
            sys.exit(1)
469 470

        repos = FlatpakRepos()
471
        self.sdk_repo = repos.add("flathub",
472
                                  "https://dl.flathub.org/repo/flathub.flatpakrepo")
473

Thibault Saunier's avatar
Thibault Saunier committed
474 475 476 477 478 479
        manifest_path = os.path.join(self.scriptdir, MANIFEST_NAME)
        manifest = load_manifest(manifest_path)
        if not manifest:
            return

        sdk_branch = manifest["runtime-version"]
480
        self.runtime = FlatpakPackage(
481
            "org.gnome.Platform", sdk_branch, self.sdk_repo, "x86_64")
482
        self.locale = FlatpakPackage(
483
            "org.gnome.Platform.Locale", sdk_branch, self.sdk_repo, "x86_64")
484
        self.sdk = FlatpakPackage(
485
            "org.gnome.Sdk", sdk_branch, self.sdk_repo, "x86_64")
486
        self.packs = [self.runtime, self.locale, self.sdk]
487

488 489 490
        if self.coredumpgdb is None and "--coredumpgdb" in sys.argv or "-gdb" in sys.argv:
            self.coredumpgdb = ""

491 492 493 494 495
        if self.bundle:
            self.build = True

        self.json = os.path.join(self.scriptdir, self.build_name + ".json")

496 497
        if not self.args:
            self.args.append(os.path.join(self.scriptdir, "enter-env"))
498

499 500
    def run(self):
        if self.clean:
501
            Console.message("Removing prefix")
502 503 504 505
            try:
                shutil.rmtree(self.prefix)
            except FileNotFoundError:
                pass
506

507 508
        missing_prefix = not os.path.exists(self.prefix)
        if self.init or self.update:
509
            self.install_flatpak_runtimes()
510 511 512 513 514 515
            if missing_prefix or self.update:
                self.setup_sandbox()
        else:
            if missing_prefix:
                Console.message("%sPrefix missing, create it with: %s --init%s",
                                Colors.FAIL, __file__, Colors.ENDC)
Alexandru Băluț's avatar
Alexandru Băluț committed
516
                sys.exit(1)
517

518 519 520 521
        if self.coredumpgdb is not None:
            self.run_gdb()
            return

522
        if self.check:
523
            self.run_in_sandbox("gst-validate-launcher",
524 525
                                os.path.join(
                                    self.topdir, "tests/ptv_testsuite.py"),
526
                                "--xunit-file", "xunit.xml", exit_on_failure=True,
527 528 529 530 531
                                cwd=self.topdir)

        if self.bundle:
            self.update_bundle()

532
        if not self.check and not self.init and not self.update and not self.bundle:
533 534
            assert self.args
            self.run_in_sandbox(*self.args, exit_on_failure=True)
535 536 537 538 539 540 541 542 543 544 545 546 547 548

    def update_bundle(self):
        if not os.path.exists(self.repodir):
            os.mkdir(self.repodir)

        build_export_args = ["flatpak",
                             "build-export", self.repodir, self.prefix]
        if self.gpg_key:
            build_export_args.append("--gpg-sign=%s" % self.gpg_key)
        if self.commit_subject:
            build_export_args.append("--subject=%s" % self.commit_subject)
        if self.commit_body:
            build_export_args.append("--body=%s" % self.commit_body)

549 550
        build_export_args.append(self.branch)

Alexandru Băluț's avatar
Alexandru Băluț committed
551 552 553
        Console.message('-> Exporting repo %s %s (--body="%s" --subject="%s")',
                        self.repodir, self.branch, self.commit_body,
                        self.commit_subject)
554 555 556
        try:
            subprocess.check_call(build_export_args)
        except subprocess.CalledProcessError:
Alexandru Băluț's avatar
Alexandru Băluț committed
557
            sys.exit(1)
558 559 560 561 562 563 564 565

        update_repo_args = ["flatpak", "build-update-repo"]

        if self.generate_static_deltas:
            update_repo_args.append("--generate-static-deltas")

        update_repo_args.append(self.repodir)

566
        Console.message("Updating repo: '%s'", "' '".join(update_repo_args))
567 568 569
        try:
            subprocess.check_call(update_repo_args)
        except subprocess.CalledProcessError:
Alexandru Băluț's avatar
Alexandru Băluț committed
570
            sys.exit(1)
571

Alexandru Băluț's avatar
Alexandru Băluț committed
572
    def setup_sandbox(self):
573 574 575
        """Creates and updates the sandbox."""
        Console.message("Building Pitivi %s and dependencies in %s",
                        self.branch, self.prefix)
576

Thibault Saunier's avatar
Thibault Saunier committed
577 578 579 580
        manifest_path = os.path.join(self.scriptdir, MANIFEST_NAME)
        expand_manifest(manifest_path, self.json,
                        self.local_repos_path, self.gst_version,
                        self.branch)
581

582 583 584 585
        builder_args = ["flatpak-builder", "--force-clean",
                        "--ccache", self.prefix, self.json]
        if not self.bundle:
            builder_args.append("--build-only")
586

587 588 589 590 591
        try:
            subprocess.check_call(["flatpak-builder", "--version"])
        except FileNotFoundError:
            Console.message("\n%sYou need to install flatpak-builder%s\n",
                            Colors.FAIL, Colors.ENDC)
Alexandru Băluț's avatar
Alexandru Băluț committed
592
            sys.exit(1)
593 594 595 596 597 598 599 600
        subprocess.check_call(builder_args, cwd=self.scriptdir)

        if not os.path.isdir("mesonbuild/"):
            # Create the build directory.
            meson_args = ["meson", "mesonbuild/", "--prefix=/app",
                          "--libdir=lib", "-Ddisable_gtkdoc=true",
                          "-Ddisable_doc=true"]
            self.run_in_sandbox(*meson_args, exit_on_failure=True,
601 602
                                cwd=self.topdir)

603 604 605 606 607
        # Build the buildable parts of Pitivi.
        ninja_args = ["ninja", "-C", "mesonbuild/"]
        self.run_in_sandbox(*ninja_args, exit_on_failure=True,
                            cwd=self.topdir)

608 609 610 611 612 613 614 615 616
    def run_gdb(self):
        if not shutil.which("coredumpctl"):
            Console.message("%s'coredumpctl' not present on the system, can't run.%s"
                            % (Colors.WARNING, Colors.ENDC))
            sys.exit(1)

        # We need access to the host from the sandbox to run.
        with tempfile.NamedTemporaryFile() as coredump:
            with tempfile.NamedTemporaryFile() as stderr:
617 618
                command = ["coredumpctl", "dump"] + shlex.split(self.coredumpctl_matches)
                subprocess.check_call(command, stdout=coredump, stderr=stderr)
619 620 621 622 623 624 625 626 627 628 629 630 631

                with open(stderr.name, 'r') as stderrf:
                    stderr = stderrf.read()
                executable, = re.findall(".*Executable: (.*)", stderr)
                if not executable.startswith("/newroot"):
                    print("Executable %s doesn't seem to be a flatpaked application." % executable,
                          file=sys.stderr)
                executable = executable.replace("/newroot", "")
                args = ["gdb", executable, coredump.name] + shlex.split(self.coredumpgdb)

                self.run_in_sandbox(*args, mount_tmp=True)

    def run_in_sandbox(self, *args, exit_on_failure=False, cwd=None, mount_tmp=False):
632 633 634
        if not args:
            return

635 636
        flatpak_command = ["flatpak", "build", "--socket=x11", "--socket=wayland",
                           "--talk-name=org.freedesktop.Flatpak",
637 638
                           "--socket=session-bus", "--socket=pulseaudio",
                           "--share=network", "--env=PITIVI_DEVELOPMENT=1",
639
                           "--env=PYTHONUSERBASE=/app/",
640
                           "--env=CC=ccache gcc",
641
                           "--env=CXX=ccache g++", "--device=dri"]
642 643
        if mount_tmp:
            flatpak_command.append("--filesystem=/tmp/")
644 645 646 647

        # The forwarded environment variables.
        forwarded = {}
        for envvar, value in os.environ.items():
648
            if envvar.split("_")[0] in ("G", "GDK", "GST", "GTK", "LC", "PITIVI") or \
649
                    envvar in ["DISPLAY", "GIT_INDEX_FILE", "LANG", "PYTHONPATH"]:
650 651 652 653
                forwarded[envvar] = value

        prefixes = {
            "GST_ENCODING_TARGET_PATH":
654
                "/app/share/gstreamer-1.0/encoding-profiles/:/app/share/pitivi/encoding-profiles/",
655
            "GST_PLUGIN_SYSTEM_PATH": "/app/lib/gstreamer-1.0/",
656
            "FREI0R_PATH": "/app/lib/frei0r-1/",
657
            "GST_PRESET_PATH":
658 659
                "/app/share/gstreamer-1.0/presets/:/app/share/pitivi/gstpresets/",
        }
660 661 662 663 664 665 666 667
        for envvar, path in prefixes.items():
            value = forwarded.get(envvar, "")
            forwarded[envvar] = "%s:%s" % (path, value)

        for envvar, value in forwarded.items():
            flatpak_command.append("--env=%s=%s" % (envvar, value))

        flatpak_command.append(self.prefix)
668

669
        flatpak_command.extend(args)
670

Alexandru Băluț's avatar
Alexandru Băluț committed
671
        Console.message("Running in sandbox: %s", ' '.join(args))
672
        Console.message(" ".join(flatpak_command))
673
        try:
674
            subprocess.check_call(flatpak_command, cwd=cwd)
675 676
        except subprocess.CalledProcessError as e:
            if exit_on_failure:
Alexandru Băluț's avatar
Alexandru Băluț committed
677
                sys.exit(e.returncode)
678

679 680 681 682 683
    def install_flatpak_runtimes(self):
        for runtime in self.packs:
            if not runtime.is_installed():
                runtime.install()
            else:
684 685 686
                # Update only if requested.
                if self.update:
                    runtime.update()
687

Alexandru Băluț's avatar
Alexandru Băluț committed
688

689
if __name__ == "__main__":
690 691
    app_flatpak = PitiviFlatpak()

692
    parser = argparse.ArgumentParser(prog="pitivi-flatpak")
693 694

    general = parser.add_argument_group("General")
695 696 697
    general.add_argument("--init", dest="init",
                         action="store_true",
                         help="Initialize the runtime/sdk/app and build the development environment if needed")
698 699
    general.add_argument("--update", dest="update",
                         action="store_true",
700
                         help="Update the runtime/sdk/app and rebuild the development environment")
Alexandru Băluț's avatar
Alexandru Băluț committed
701 702 703
    general.add_argument("-q", "--quiet", dest="quiet",
                         action="store_true",
                         help="Do not print anything")
704 705
    general.add_argument("args",
                         nargs=argparse.REMAINDER,
706
                         help="The command to run in the sandbox")
707

708 709 710 711 712 713
    devel = parser.add_argument_group("Development")
    devel.add_argument("--branch", dest="branch",
                       help="The flatpak branch to use (stable, master...)",
                       default="master")
    devel.add_argument("--gst-version", dest="gst_version",
                       help="The GStreamer version to build.",
714
                       default=DEFAULT_GST_BRANCH)
715 716 717 718 719 720 721
    devel.add_argument("--check", dest="check",
                       help="Run unit tests once the build is done.",
                       action="store_true")
    devel.add_argument("-c", "--clean", dest="clean",
                       action="store_true",
                       help="Clean previous builds and restart from scratch")

722 723 724 725 726 727
    debug_options = parser.add_argument_group("Debugging")
    debug_options.add_argument("-gdb", "--coredumpgdb", nargs="?",
                               help="Activate gdb, passing extra args to it if wanted.")
    debug_options.add_argument("-m", "--coredumpctl-matches", default="",
                               help="Arguments to pass to gdb.")

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
    bundling = parser.add_argument_group("Building bundle for distribution")
    bundling.add_argument("--bundle", dest="bundle",
                          action="store_true",
                          help="Create bundle repository, implies --build")
    bundling.add_argument(
        "--repo-commit-subject", dest="commit_subject", default=None,
        help="The commit subject to be used when updating the ostree repository")
    bundling.add_argument(
        "--repo-commit-body", dest="commit_body", default=None,
        help="The commit body to be used when updating the ostree repository")
    bundling.add_argument(
        "--gpg-sign", dest="gpg_key", default=None,
        help="The GPG key to sign the commit with (work only when --bundle is used)")
    bundling.add_argument(
        "--generate-static-deltas", dest="generate_static_deltas",
743
        action="store_true",
744
        help="Generate static deltas (check 'man flatpak-build-update-repo'"
745
        " for more information)")
746

747 748
    parser.parse_args(namespace=app_flatpak)
    app_flatpak.clean_args()
749
    app_flatpak.run()