diff --git a/make-ring.py b/make-ring.py
index 4f9b3488490fed81581306f43b7351122008ebda..f6a29e833c45837349847eb438ae0a2eccc38233 100755
--- a/make-ring.py
+++ b/make-ring.py
@@ -13,6 +13,7 @@ import sys
 import time
 import platform
 import multiprocessing
+import shlex
 import shutil
 import signal
 
@@ -187,49 +188,50 @@ def run_dependencies(args):
             'Set-ExecutionPolicy Unrestricted; .\\scripts\\build-package-windows.ps1')
 
     elif args.distribution in APT_BASED_DISTROS:
-        execute_script(APT_INSTALL_SCRIPT,
-                       {"packages": ' '.join(APT_DEPENDENCIES)}
-                       )
+        execute_script(
+            APT_INSTALL_SCRIPT,
+            {"packages": ' '.join(map(shlex.quote, APT_DEPENDENCIES))}
+        )
 
     elif args.distribution in DNF_BASED_DISTROS:
         execute_script(
             RPM_INSTALL_SCRIPT,
-            {"packages": ' '.join(DNF_DEPENDENCIES)}
+            {"packages": ' '.join(map(shlex.quote, DNF_DEPENDENCIES))}
         )
 
     elif args.distribution in PACMAN_BASED_DISTROS:
         execute_script(
             PACMAN_INSTALL_SCRIPT,
-            {"packages": ' '.join(PACMAN_DEPENDENCIES)}
+            {"packages": ' '.join(map(shlex.quote, PACMAN_DEPENDENCIES))}
         )
 
     elif args.distribution in ZYPPER_BASED_DISTROS:
         execute_script(
             ZYPPER_INSTALL_SCRIPT,
-            {"packages": ' '.join(ZYPPER_DEPENDENCIES)}
+            {"packages": ' '.join(map(shlex.quote, ZYPPER_DEPENDENCIES))}
         )
 
     elif args.distribution == OSX_DISTRIBUTION_NAME:
         execute_script(
             BREW_UNLINK_SCRIPT,
-            {"packages": ' '.join(OSX_DEPENDENCIES_UNLINK)},
+            {"packages": ' '.join(map(shlex.quote, OSX_DEPENDENCIES_UNLINK))},
             False
         )
         execute_script(
             BREW_INSTALL_SCRIPT,
-            {"packages": ' '.join(OSX_DEPENDENCIES)},
+            {"packages": ' '.join(map(shlex.quote, OSX_DEPENDENCIES))},
             False
         )
 
     elif args.distribution == IOS_DISTRIBUTION_NAME:
         execute_script(
             BREW_UNLINK_SCRIPT,
-            {"packages": ' '.join(IOS_DEPENDENCIES_UNLINK)},
+            {"packages": ' '.join(map(shlex.quote, IOS_DEPENDENCIES_UNLINK))},
             False
         )
         execute_script(
             BREW_INSTALL_SCRIPT,
-            {"packages": ' '.join(IOS_DEPENDENCIES)},
+            {"packages": ' '.join(map(shlex.quote, IOS_DEPENDENCIES))},
             False
         )
 
@@ -255,8 +257,8 @@ def run_init():
             if line.startswith('[submodule "'):
                 module_names.append(line[line.find('"')+1:line.rfind('"')])
 
-    os.system("git submodule update --init")
-    os.system("git submodule foreach 'git checkout master && git pull'")
+    subprocess.run(["git", "submodule", "update", "--init"], check=True)
+    subprocess.run(["git", "submodule", "foreach", "git checkout master && git pull"], check=True)
     for name in module_names:
         copy_file("./scripts/commit-msg", ".git/modules/"+name+"/hooks")
 
@@ -274,33 +276,45 @@ def copy_file(src, dest):
 
 
 def run_install(args):
-    install_args = ' -p ' + str(multiprocessing.cpu_count())
+    # Platforms with special compilation scripts
+    if args.distribution == IOS_DISTRIBUTION_NAME:
+        return subprocess.run(["./compile-ios.sh"], cwd="./client-ios", check=True)
+    elif args.distribution == ANDROID_DISTRIBUTION_NAME:
+        return subprocess.run(["./compile.sh"], cwd="./client-android", check=True)
+    elif args.distribution == WIN32_DISTRIBUTION_NAME:
+        return subprocess.run([
+            sys.executable, os.path.join(os.getcwd(), "scripts/build-windows.py"),
+            "--toolset", args.toolset,
+            "--sdk", args.sdk
+        ], check=True)
+
+    # Unix-like platforms
+    environ = os.environ.copy()
+    
+    install_args = ['-p', str(multiprocessing.cpu_count())]
     if args.static:
-        install_args += ' -s'
+        install_args.append('-s')
     if args.global_install:
-        install_args += ' -g'
+        install_args.append('-g')
+
     if args.distribution == OSX_DISTRIBUTION_NAME:
-        proc = subprocess.Popen("brew --prefix qt5",
-                                shell=True, stdout=subprocess.PIPE)
-        qt5dir = proc.stdout.read()
-        os.environ['CMAKE_PREFIX_PATH'] = str(qt5dir.decode('ascii'))
-        install_args += " -c client-macosx"
-        execute_script(
-            ["CONFIGURE_FLAGS='--without-dbus' ./scripts/install.sh " + install_args])
-    elif args.distribution == IOS_DISTRIBUTION_NAME:
-        os.chdir("./client-ios")
-        execute_script(["./compile-ios.sh"])
-    elif args.distribution == ANDROID_DISTRIBUTION_NAME:
-        os.chdir("./client-android")
-        execute_script(["./compile.sh"])
-    elif args.distribution == WIN32_DISTRIBUTION_NAME:
-        subprocess.call('python ' + os.getcwd() + '/scripts/build-windows.py ' + '--toolset ' + args.toolset + ' --sdk ' + args.sdk)
+        # The `universal_newlines` parameter has been renamed to `text` in
+        # Python 3.7+ and triggering automatical binary to text conversion is
+        # what it actually does
+        proc = subprocess.run(["brew", "--prefix", "qt5"],
+                              stdout=subprocess.PIPE, check=True,
+                              universal_newlines=True)
+        
+        environ['CMAKE_PREFIX_PATH'] = proc.stdout.rstrip("\n")
+        environ['CONFIGURE_FLAGS']   = '--without-dbus'
+        install_args += ("-c", "client-macosx")
     else:
         if args.distribution in ZYPPER_BASED_DISTROS:
             # fix jsoncpp pkg-config bug, remove when jsoncpp package bumped
-            os.environ['JSONCPP_LIBS'] = "-ljsoncpp"
-        install_args += ' -c client-gnome'
-        execute_script(["./scripts/install.sh " + install_args])
+            environ['JSONCPP_LIBS'] = "-ljsoncpp"
+        install_args += ("-c", "client-gnome")
+
+    return subprocess.run(["./scripts/install.sh"] + install_args, env=environ, check=True)
 
 
 def run_uninstall(args):