subprojects: update glib-networking to 2.74.0 and switch to file wrap
[platform/upstream/gstreamer.git] / gst-env.py
index 2124c9c..fb266ad 100755 (executable)
@@ -1,21 +1,22 @@
 #!/usr/bin/env python3
 
 import argparse
-import contextlib
+import glob
 import json
 import os
 import platform
 import re
-import site
+import shlex
 import shutil
 import subprocess
-import sys
 import tempfile
 import pathlib
 import signal
+from functools import lru_cache
+from pathlib import PurePath, Path
+from sys import exit
 
-from distutils.sysconfig import get_python_lib
-from distutils.util import strtobool
+from typing import Any
 
 from scripts.common import get_meson
 from scripts.common import git
@@ -24,10 +25,12 @@ from scripts.common import get_wine_shortpath
 
 SCRIPTDIR = os.path.dirname(os.path.realpath(__file__))
 PREFIX_DIR = os.path.join(SCRIPTDIR, 'prefix')
-# Use '_build' as the builddir instead of 'build'
+# Look for the following build dirs: `build` `_build` `builddir`
 DEFAULT_BUILDDIR = os.path.join(SCRIPTDIR, 'build')
 if not os.path.exists(DEFAULT_BUILDDIR):
     DEFAULT_BUILDDIR = os.path.join(SCRIPTDIR, '_build')
+if not os.path.exists(DEFAULT_BUILDDIR):
+    DEFAULT_BUILDDIR = os.path.join(SCRIPTDIR, 'builddir')
 
 TYPELIB_REG = re.compile(r'.*\.typelib$')
 SHAREDLIB_REG = re.compile(r'\.so|\.dylib|\.dll')
@@ -37,6 +40,28 @@ SHAREDLIB_REG = re.compile(r'\.so|\.dylib|\.dll')
 GSTPLUGIN_FILEPATH_REG_TEMPLATE = r'.*/{libdir}/gstreamer-1.0/[^/]+$'
 GSTPLUGIN_FILEPATH_REG = None
 
+BC_RC = '''
+BASH_COMPLETION_SCRIPTS="{bash_completions}"
+BASH_COMPLETION_PATHS="{bash_completions_paths}"
+for p in $BASH_COMPLETION_PATHS; do
+for f in $BASH_COMPLETION_SCRIPTS; do
+  [ -f "$p/$f" ] && . "$p/$f"
+done
+done
+'''
+BASH_COMPLETION_PATHS = [SCRIPTDIR + '/subprojects/gstreamer/data/bash-completion/completions']
+BASH_COMPLETION_PATHS += [SCRIPTDIR + '/subprojects/gst-devtools/validate/data/bash-completion/completions']
+
+
+def str_to_bool(value: Any) -> bool:
+    """Return whether the provided string (or any value really) represents true. Otherwise false.
+    Just like plugin server stringToBoolean.
+    """
+    if not value:
+        return False
+    return str(value).lower() in ("y", "yes", "t", "true", "on", "1")
+
+
 def listify(o):
     if isinstance(o, str):
         return [o]
@@ -44,6 +69,7 @@ def listify(o):
         return o
     raise AssertionError('Object {!r} must be a string or a list'.format(o))
 
+
 def stringify(o):
     if isinstance(o, str):
         return o
@@ -53,7 +79,10 @@ def stringify(o):
         raise AssertionError('Did not expect object {!r} to have more than one element'.format(o))
     raise AssertionError('Object {!r} must be a string or a list'.format(o))
 
+
 def prepend_env_var(env, var, value, sysroot):
+    if var is None:
+        return
     if value.startswith(sysroot):
         value = value[len(sysroot):]
     # Try not to exceed maximum length limits for env vars on Windows
@@ -67,28 +96,76 @@ def prepend_env_var(env, var, value, sysroot):
     env[var] = val + env_val
     env[var] = env[var].replace(os.pathsep + os.pathsep, os.pathsep).strip(os.pathsep)
 
+
+def get_target_install_filename(target, filename):
+    '''
+    Checks whether this file is one of the files installed by the target
+    '''
+    basename = os.path.basename(filename)
+    for install_filename in listify(target['install_filename']):
+        if install_filename.endswith(basename):
+            return install_filename
+    return None
+
+
+def get_pkgconfig_variable_from_pcfile(pcfile, varname):
+    variables = {}
+    substre = re.compile('\$\{[^${}]+\}')
+    with pcfile.open('r', encoding='utf-8') as f:
+        for line in f:
+            if '=' not in line:
+                continue
+            key, value = line[:-1].split('=', 1)
+            subst = {}
+            for each in substre.findall(value):
+                substkey = each[2:-1]
+                subst[each] = variables.get(substkey, '')
+            for k, v in subst.items():
+                value = value.replace(k, v)
+            variables[key] = value
+    return variables.get(varname, '')
+
+
+@lru_cache()
+def get_pkgconfig_variable(builddir, pcname, varname):
+    '''
+    Parsing isn't perfect, but it's good enough.
+    '''
+    pcfile = Path(builddir) / 'meson-private' / (pcname + '.pc')
+    if pcfile.is_file():
+        return get_pkgconfig_variable_from_pcfile(pcfile, varname)
+    return subprocess.check_output(['pkg-config', pcname, '--variable=' + varname],
+                                   universal_newlines=True, encoding='utf-8')
+
+
+def is_gio_module(target, filename, builddir):
+    if target['type'] != 'shared module':
+        return False
+    install_filename = get_target_install_filename(target, filename)
+    if not install_filename:
+        return False
+    giomoduledir = PurePath(get_pkgconfig_variable(builddir, 'gio-2.0', 'giomoduledir'))
+    fpath = PurePath(install_filename)
+    if fpath.parent != giomoduledir:
+        return False
+    return True
+
+
 def is_library_target_and_not_plugin(target, filename):
     '''
     Don't add plugins to PATH/LD_LIBRARY_PATH because:
     1. We don't need to
     2. It causes us to exceed the PATH length limit on Windows and Wine
     '''
-    if not target['type'].startswith('shared'):
-        return False
-    if not target['installed']:
+    if target['type'] != 'shared library':
         return False
     # Check if this output of that target is a shared library
     if not SHAREDLIB_REG.search(filename):
         return False
     # Check if it's installed to the gstreamer plugin location
-    for install_filename in listify(target['install_filename']):
-        if install_filename.endswith(os.path.basename(filename)):
-            break
-    else:
-        # None of the installed files in the target correspond to the built
-        # filename, so skip
+    install_filename = get_target_install_filename(target, filename)
+    if not install_filename:
         return False
-
     global GSTPLUGIN_FILEPATH_REG
     if GSTPLUGIN_FILEPATH_REG is None:
         GSTPLUGIN_FILEPATH_REG = re.compile(GSTPLUGIN_FILEPATH_REG_TEMPLATE)
@@ -97,6 +174,19 @@ def is_library_target_and_not_plugin(target, filename):
     return True
 
 
+def is_binary_target_and_in_path(target, filename, bindir):
+    if target['type'] != 'executable':
+        return False
+    # Check if this file installed by this target is installed to bindir
+    install_filename = get_target_install_filename(target, filename)
+    if not install_filename:
+        return False
+    fpath = PurePath(install_filename)
+    if fpath.parent != bindir:
+        return False
+    return True
+
+
 def get_wine_subprocess_env(options, env):
     with open(os.path.join(options.builddir, 'meson-info', 'intro-buildoptions.json')) as f:
         buildoptions = json.load(f)
@@ -116,23 +206,79 @@ def get_wine_subprocess_env(options, env):
     return env
 
 
+def setup_gdb(options):
+    python_paths = set()
+
+    if not shutil.which('gdb'):
+        return python_paths
+
+    bdir = pathlib.Path(options.builddir).resolve()
+    for libpath, gdb_path in [
+            (os.path.join("subprojects", "gstreamer", "gst"),
+             os.path.join("subprojects", "gstreamer", "libs", "gst", "helpers")),
+            (os.path.join("subprojects", "glib", "gobject"), None),
+            (os.path.join("subprojects", "glib", "glib"), None)]:
+
+        if not gdb_path:
+            gdb_path = libpath
+
+        autoload_path = (pathlib.Path(bdir) / 'gdb-auto-load').joinpath(*bdir.parts[1:]) / libpath
+        autoload_path.mkdir(parents=True, exist_ok=True)
+        for gdb_helper in glob.glob(str(bdir / gdb_path / "*-gdb.py")):
+            python_paths.add(str(bdir / gdb_path))
+            python_paths.add(os.path.join(options.srcdir, gdb_path))
+            try:
+                if os.name == 'nt':
+                    shutil.copy(gdb_helper, str(autoload_path / os.path.basename(gdb_helper)))
+                else:
+                    os.symlink(gdb_helper, str(autoload_path / os.path.basename(gdb_helper)))
+            except (FileExistsError, shutil.SameFileError):
+                pass
+
+    gdbinit_line = 'add-auto-load-scripts-directory {}\n'.format(bdir / 'gdb-auto-load')
+    try:
+        with open(os.path.join(options.srcdir, '.gdbinit'), 'r') as f:
+            if gdbinit_line in f.readlines():
+                return python_paths
+    except FileNotFoundError:
+        pass
+
+    with open(os.path.join(options.srcdir, '.gdbinit'), 'a') as f:
+        f.write(gdbinit_line)
+
+    return python_paths
+
+
+def is_bash_completion_available(options):
+    return os.path.exists(os.path.join(options.builddir, 'subprojects/gstreamer/data/bash-completion/helpers/gst'))
+
+
 def get_subprocess_env(options, gst_version):
     env = os.environ.copy()
 
     env["CURRENT_GST"] = os.path.normpath(SCRIPTDIR)
     env["GST_VERSION"] = gst_version
-    env["GST_VALIDATE_SCENARIOS_PATH"] = os.path.normpath(
-        "%s/subprojects/gst-devtools/validate/data/scenarios" % SCRIPTDIR)
+    prepend_env_var(env, "GST_VALIDATE_SCENARIOS_PATH", os.path.normpath(
+        "%s/subprojects/gst-devtools/validate/data/scenarios" % SCRIPTDIR),
+        options.sysroot)
     env["GST_VALIDATE_PLUGIN_PATH"] = os.path.normpath(
         "%s/subprojects/gst-devtools/validate/plugins" % options.builddir)
-    env["GST_VALIDATE_APPS_DIR"] = os.path.normpath(
-        "%s/subprojects/gst-editing-services/tests/validate" % SCRIPTDIR)
-    env["GST_ENV"] = 'gst-' + gst_version
+    prepend_env_var(env, "GST_VALIDATE_APPS_DIR", os.path.normpath(
+        "%s/subprojects/gst-editing-services/tests/validate" % SCRIPTDIR),
+        options.sysroot)
+    env["GST_ENV"] = gst_version
     env["GST_REGISTRY"] = os.path.normpath(options.builddir + "/registry.dat")
     prepend_env_var(env, "PATH", os.path.normpath(
         "%s/subprojects/gst-devtools/validate/tools" % options.builddir),
         options.sysroot)
 
+    prepend_env_var(env, "GST_VALIDATE_SCENARIOS_PATH", os.path.normpath(
+        "%s/subprojects/gst-examples/webrtc/check/validate/scenarios" %
+        SCRIPTDIR), options.sysroot)
+    prepend_env_var(env, "GST_VALIDATE_APPS_DIR", os.path.normpath(
+        "%s/subprojects/gst-examples/webrtc/check/validate/apps" %
+        SCRIPTDIR), options.sysroot)
+
     if options.wine:
         return get_wine_subprocess_env(options, env)
 
@@ -148,7 +294,8 @@ def get_subprocess_env(options, gst_version):
     if os.name == 'nt':
         lib_path_envvar = 'PATH'
     elif platform.system() == 'Darwin':
-        lib_path_envvar = 'DYLD_LIBRARY_PATH'
+        # RPATH is sufficient on macOS, and DYLD_LIBRARY_PATH can cause issues with dynamic linker path priority
+        lib_path_envvar = None
     else:
         lib_path_envvar = 'LD_LIBRARY_PATH'
 
@@ -172,13 +319,17 @@ def get_subprocess_env(options, gst_version):
                     options.sysroot)
 
     # gst-indent
-    prepend_env_var(env, "PATH", os.path.join(SCRIPTDIR, 'gstreamer', 'tools'),
+    prepend_env_var(env, "PATH", os.path.join(SCRIPTDIR, 'scripts'),
                     options.sysroot)
 
     # tools: gst-launch-1.0, gst-inspect-1.0
     prepend_env_var(env, "PATH", os.path.join(options.builddir, 'subprojects',
                                               'gstreamer', 'tools'),
                     options.sysroot)
+    # plugin scanner and generator
+    prepend_env_var(env, "PATH", os.path.join(options.builddir, 'subprojects',
+                                              'gstreamer', 'docs'),
+                    options.sysroot)
     prepend_env_var(env, "PATH", os.path.join(options.builddir, 'subprojects',
                                               'gst-plugins-base', 'tools'),
                     options.sysroot)
@@ -206,13 +357,19 @@ def get_subprocess_env(options, gst_version):
     build_options_s = subprocess.check_output(meson + ['introspect', options.builddir, '--buildoptions'])
     build_options = json.loads(build_options_s.decode())
     libdir, = [o['value'] for o in build_options if o['name'] == 'libdir']
-    libdir = libdir.replace('\\', '/')
+    libdir = PurePath(libdir)
+    prefix, = [o['value'] for o in build_options if o['name'] == 'prefix']
+    bindir, = [o['value'] for o in build_options if o['name'] == 'bindir']
+    prefix = PurePath(prefix)
+    bindir = prefix / bindir
 
     global GSTPLUGIN_FILEPATH_REG_TEMPLATE
-    GSTPLUGIN_FILEPATH_REG_TEMPLATE = GSTPLUGIN_FILEPATH_REG_TEMPLATE.format(libdir=libdir)
+    GSTPLUGIN_FILEPATH_REG_TEMPLATE = GSTPLUGIN_FILEPATH_REG_TEMPLATE.format(libdir=libdir.as_posix())
 
     for target in targets:
         filenames = listify(target['filename'])
+        if not target['installed']:
+            continue
         for filename in filenames:
             root = os.path.dirname(filename)
             if srcdir_path / "subprojects/gst-devtools/validate/plugins" in (srcdir_path / root).parents:
@@ -227,30 +384,40 @@ def get_subprocess_env(options, gst_version):
                 prepend_env_var(env, lib_path_envvar,
                                 os.path.join(options.builddir, root),
                                 options.sysroot)
-            elif target['type'] == 'executable' and target['installed']:
+            elif is_binary_target_and_in_path(target, filename, bindir):
                 paths.add(os.path.join(options.builddir, root))
+            elif is_gio_module(target, filename, options.builddir):
+                prepend_env_var(env, 'GIO_EXTRA_MODULES',
+                                os.path.join(options.builddir, root),
+                                options.sysroot)
 
-    with open(os.path.join(options.builddir, 'GstPluginsPath.json')) as f:
-        for plugin_path in json.load(f):
-            prepend_env_var(env, 'GST_PLUGIN_PATH', plugin_path,
-                            options.sysroot)
+    # Search for the Plugin paths file either in the build directory root
+    # or check if gstreamer is a subproject of another project
+    for sub_directories in [[], ['subprojects', 'gstreamer']]:
+        plugin_paths = os.path.join(options.builddir, *sub_directories, 'GstPluginsPath.json')
+        if os.path.exists(plugin_paths):
+            with open(plugin_paths) as f:
+                for plugin_path in json.load(f):
+                    prepend_env_var(env, 'GST_PLUGIN_PATH', plugin_path,
+                                    options.sysroot)
+            break
 
-    for p in paths:
+    # Sort to iterate in a consistent order (`set`s and `hash`es are randomized)
+    for p in sorted(paths):
         prepend_env_var(env, 'PATH', p, options.sysroot)
 
     if os.name != 'nt':
-        for p in mono_paths:
+        for p in sorted(mono_paths):
             prepend_env_var(env, "MONO_PATH", p, options.sysroot)
 
     presets = set()
     encoding_targets = set()
-    pkg_dirs = set()
-    python_dirs = set(["%s/subprojects/gstreamer/libs/gst/helpers/" % options.srcdir])
+    python_dirs = setup_gdb(options)
+    overrides_dirs = set()
     if '--installed' in subprocess.check_output(meson + ['introspect', '-h']).decode():
         installed_s = subprocess.check_output(meson + ['introspect', options.builddir, '--installed'])
         for path, installpath in json.loads(installed_s.decode()).items():
             installpath_parts = pathlib.Path(installpath).parts
-            path_parts = pathlib.Path(path).parts
 
             # We want to add all python modules to the PYTHONPATH
             # in a manner consistent with the way they would be imported:
@@ -266,65 +433,77 @@ def get_subprocess_env(options, gst_version):
             if 'site-packages' in installpath_parts:
                 install_subpath = os.path.join(*installpath_parts[installpath_parts.index('site-packages') + 1:])
                 if path.endswith(install_subpath):
-                    python_dirs.add(path[:len (install_subpath) * -1])
+                    if os.path.commonprefix(["gi/overrides", install_subpath]):
+                        overrides_dirs.add(os.path.dirname(path))
+                    else:
+                        python_dirs.add(path[:len(install_subpath) * -1])
 
             if path.endswith('.prs'):
                 presets.add(os.path.dirname(path))
             elif path.endswith('.gep'):
                 encoding_targets.add(
                     os.path.abspath(os.path.join(os.path.dirname(path), '..')))
-            elif path.endswith('.pc'):
-                # Is there a -uninstalled pc file for this file?
-                uninstalled = "{0}-uninstalled.pc".format(path[:-3])
-                if os.path.exists(uninstalled):
-                    pkg_dirs.add(os.path.dirname(path))
 
             if path.endswith('gstomx.conf'):
                 prepend_env_var(env, 'GST_OMX_CONFIG_DIR', os.path.dirname(path),
                                 options.sysroot)
 
-        for p in presets:
+        for p in sorted(presets):
             prepend_env_var(env, 'GST_PRESET_PATH', p, options.sysroot)
 
-        for t in encoding_targets:
+        for t in sorted(encoding_targets):
             prepend_env_var(env, 'GST_ENCODING_TARGET_PATH', t, options.sysroot)
 
-        for pkg_dir in pkg_dirs:
-            prepend_env_var(env, "PKG_CONFIG_PATH", pkg_dir, options.sysroot)
-    prepend_env_var(env, "PKG_CONFIG_PATH", os.path.join(options.builddir,
-                                                         'subprojects',
-                                                         'gst-plugins-good',
-                                                         'pkgconfig'),
-                    options.sysroot)
+    # Check if meson has generated -uninstalled pkgconfig files
+    meson_uninstalled = pathlib.Path(options.builddir) / 'meson-uninstalled'
+    if meson_uninstalled.is_dir():
+        prepend_env_var(env, 'PKG_CONFIG_PATH', str(meson_uninstalled), options.sysroot)
 
-    for python_dir in python_dirs:
+    for python_dir in sorted(python_dirs):
         prepend_env_var(env, 'PYTHONPATH', python_dir, options.sysroot)
 
+    for python_dir in sorted(overrides_dirs):
+        prepend_env_var(env, '_GI_OVERRIDES_PATH', python_dir, options.sysroot)
+
     mesonpath = os.path.join(SCRIPTDIR, "meson")
     if os.path.join(mesonpath):
         # Add meson/ into PYTHONPATH if we are using a local meson
         prepend_env_var(env, 'PYTHONPATH', mesonpath, options.sysroot)
 
+    # Ensure that gst-python/gi is used first
+    prepend_env_var(env, "PYTHONPATH", os.path.join(SCRIPTDIR, 'subprojects', 'gst-python'),
+                    options.sysroot)
+
     # For devhelp books
-    if not 'XDG_DATA_DIRS' in env or not env['XDG_DATA_DIRS']:
+    if 'XDG_DATA_DIRS' not in env or not env['XDG_DATA_DIRS']:
         # Preserve default paths when empty
         prepend_env_var(env, 'XDG_DATA_DIRS', '/usr/local/share/:/usr/share/', '')
 
-    prepend_env_var (env, 'XDG_DATA_DIRS', os.path.join(options.builddir,
-                                                        'subprojects',
-                                                        'gst-docs',
-                                                        'GStreamer-doc'),
-                     options.sysroot)
+    prepend_env_var(env, 'XDG_DATA_DIRS', os.path.join(options.builddir,
+                                                       'subprojects',
+                                                       'gst-docs',
+                                                       'GStreamer-doc'),
+                    options.sysroot)
+
+    if 'XDG_CONFIG_DIRS' not in env or not env['XDG_CONFIG_DIRS']:
+        # Preserve default paths when empty
+        prepend_env_var(env, 'XDG_CONFIG_DIRS', '/etc/local/xdg:/etc/xdg', '')
+
+    prepend_env_var(env, "XDG_CONFIG_DIRS", os.path.join(PREFIX_DIR, 'etc', 'xdg'),
+                    options.sysroot)
 
     return env
 
+
 def get_windows_shell():
-    command = ['powershell.exe' ,'-noprofile', '-executionpolicy', 'bypass', '-file', 'cmd_or_ps.ps1']
+    command = ['powershell.exe', '-noprofile', '-executionpolicy', 'bypass', '-file',
+        os.path.join(SCRIPTDIR, 'data', 'misc', 'cmd_or_ps.ps1')]
     result = subprocess.check_output(command)
     return result.decode().strip()
 
+
 if __name__ == "__main__":
-    parser = argparse.ArgumentParser(prog="gstreamer-uninstalled")
+    parser = argparse.ArgumentParser(prog="gst-env")
 
     parser.add_argument("--builddir",
                         default=DEFAULT_BUILDDIR,
@@ -340,7 +519,11 @@ if __name__ == "__main__":
                         help="Build a wine env based on specified wine command")
     parser.add_argument("--winepath",
                         default='',
-                        help="Exra path to set to WINEPATH.")
+                        help="Extra path to set to WINEPATH.")
+    parser.add_argument("--only-environment",
+                        action='store_true',
+                        default=False,
+                        help="Do not start a shell, only print required environment.")
     options, args = parser.parse_known_args()
 
     if not os.path.exists(options.builddir):
@@ -355,37 +538,56 @@ if __name__ == "__main__":
         exit(1)
 
     # The following incantation will retrieve the current branch name.
-    gst_version = git("rev-parse", "--symbolic-full-name", "--abbrev-ref", "HEAD",
-                      repository_path=options.srcdir).strip('\n')
+    try:
+        gst_version = git("rev-parse", "--symbolic-full-name", "--abbrev-ref", "HEAD",
+                          repository_path=options.srcdir).strip('\n')
+    except subprocess.CalledProcessError:
+        gst_version = "unknown"
 
     if options.wine:
         gst_version += '-' + os.path.basename(options.wine)
 
-    if not args:
-        if os.name == 'nt':
-            shell = get_windows_shell()
-            if shell == 'powershell.exe':
-                args = ['powershell.exe']
-                args += ['-NoLogo', '-NoExit']
-                prompt = 'function global:prompt {  "[gst-' + gst_version + '"+"] PS " + $PWD + "> "}'
-                args += ['-Command', prompt]
+    env = get_subprocess_env(options, gst_version)
+    if os.name == 'nt':
+        shell = get_windows_shell()
+        if shell in ['powershell.exe', 'pwsh.exe']:
+            new_args = [shell, '-NoLogo']
+            if not args:
+                prompt = 'function global:prompt {  "[' + gst_version + '"+"] PS " + $PWD + "> "}'
+                new_args += ['-NoExit', '-Command', prompt]
             else:
-                args = [os.environ.get("COMSPEC", r"C:\WINDOWS\system32\cmd.exe")]
-                args += ['/k', 'prompt [gst-{}] $P$G'.format(gst_version)]
+                new_args += ['-NonInteractive', '-Command'] + args
+            args = new_args
         else:
+            new_args = [os.environ.get("COMSPEC", r"C:\WINDOWS\system32\cmd.exe")]
+            if not args:
+                new_args += ['/k', 'prompt [{}] $P$G'.format(gst_version)]
+            else:
+                new_args += ['/c', 'start', '/b', '/wait'] + args
+            args = new_args
+    if not args:
+        if os.name != 'nt':
             args = [os.environ.get("SHELL", os.path.realpath("/bin/sh"))]
-        if args[0].endswith('bash') and not strtobool(os.environ.get("GST_BUILD_DISABLE_PS1_OVERRIDE", r"FALSE")):
+        if args[0].endswith('bash') and not str_to_bool(os.environ.get("GST_BUILD_DISABLE_PS1_OVERRIDE", r"FALSE")):
+            # Let the GC remove the tmp file
             tmprc = tempfile.NamedTemporaryFile(mode='w')
             bashrc = os.path.expanduser('~/.bashrc')
             if os.path.exists(bashrc):
                 with open(bashrc, 'r') as src:
                     shutil.copyfileobj(src, tmprc)
-            tmprc.write('\nexport PS1="[gst-%s] $PS1"' % gst_version)
+            tmprc.write('\nexport PS1="[%s] $PS1"' % gst_version)
             tmprc.flush()
-            # Let the GC remove the tmp file
+            if is_bash_completion_available(options):
+                bash_completions_files = []
+                for p in BASH_COMPLETION_PATHS:
+                    if os.path.exists(p):
+                        bash_completions_files += os.listdir(path=p)
+                bc_rc = BC_RC.format(bash_completions=' '.join(bash_completions_files), bash_completions_paths=' '.join(BASH_COMPLETION_PATHS))
+                tmprc.write(bc_rc)
+                tmprc.flush()
             args.append("--rcfile")
             args.append(tmprc.name)
-        if args[0].endswith('fish'):
+        elif args[0].endswith('fish'):
             # Ignore SIGINT while using fish as the shell to make it behave
             # like other shells such as bash and zsh.
             # See: https://gitlab.freedesktop.org/gstreamer/gst-build/issues/18
@@ -394,11 +596,31 @@ if __name__ == "__main__":
             args.append('--init-command')
             prompt_cmd = '''functions --copy fish_prompt original_fish_prompt
             function fish_prompt
-                echo -n '[gst-{}] '(original_fish_prompt)
+                echo -n '[{}] '(original_fish_prompt)
             end'''.format(gst_version)
             args.append(prompt_cmd)
+        elif args[0].endswith('zsh'):
+            tmpdir = tempfile.TemporaryDirectory()
+            # Let the GC remove the tmp file
+            tmprc = open(os.path.join(tmpdir.name, '.zshrc'), 'w')
+            zshrc = os.path.expanduser('~/.zshrc')
+            if os.path.exists(zshrc):
+                with open(zshrc, 'r') as src:
+                    shutil.copyfileobj(src, tmprc)
+            tmprc.write('\nexport PROMPT="[{}] $PROMPT"'.format(gst_version))
+            tmprc.flush()
+            env['ZDOTDIR'] = tmpdir.name
     try:
-        exit(subprocess.call(args, close_fds=False,
-                             env=get_subprocess_env(options, gst_version)))
+        if options.only_environment:
+            for name, value in env.items():
+                print('{}={}'.format(name, shlex.quote(value)))
+                print('export {}'.format(name))
+        else:
+            if os.environ.get("CI_PROJECT_NAME"):
+                print("Ignoring SIGINT when running on the CI,"
+                      " as we get spurious sigint in there for some reason.")
+                signal.signal(signal.SIGINT, signal.SIG_IGN)
+            exit(subprocess.call(args, close_fds=False, env=env))
+
     except subprocess.CalledProcessError as e:
         exit(e.returncode)