make-ring.py 10.7 KB
Newer Older
aviau's avatar
aviau committed
1 2 3 4 5 6 7 8 9 10 11 12 13
#!/usr/bin/env python3
#
# This is the Ring build helper, it can do these things:
#  - Build Ring
#  - Install Ring
#  - Run Ring
#

import argparse
import os
import subprocess
import sys
import time
Alexandre Lision's avatar
Alexandre Lision committed
14
import platform
Alexandre Lision's avatar
Alexandre Lision committed
15
import multiprocessing
Alexandre Lision's avatar
Alexandre Lision committed
16
import shutil
aviau's avatar
aviau committed
17 18 19 20 21 22

DEBIAN_BASED_DISTROS = [
    'Debian',
    'Ubuntu',
]

Alexandre Lision's avatar
Alexandre Lision committed
23 24 25 26
RPM_BASED_DISTROS = [
    'Fedora',
]

aviau's avatar
aviau committed
27 28 29 30 31
APT_INSTALL_SCRIPT = [
    'apt-get update',
    'apt-get install -y %(packages)s'
]

Alexandre Lision's avatar
Alexandre Lision committed
32 33 34 35 36
BREW_INSTALL_SCRIPT = [
    'brew update',
    'brew install -y %(packages)s',
    'brew link --force gettext'
]
Alexandre Lision's avatar
Alexandre Lision committed
37 38 39 40 41 42 43 44 45 46 47 48 49
RPM_INSTALL_SCRIPT = [
    'sudo dnf update',
    'sudo dnf install -y %(packages)s'
]

FEDORA_DEPENDENCIES = [
    'autoconf', 'automake', 'cmake', 'speexdsp-devel', 'pulseaudio-libs-devel',
    'libsamplerate-devel', 'libtool', 'dbus-devel', 'expat-devel', 'pcre-devel',
    'yaml-cpp-devel', 'boost-devel', 'dbus-c++-devel', 'dbus-devel',
    'libsndfile-devel', 'libsrtp-devel', 'libXext-devel', 'libXfixes-devel', 'yasm',
    'speex-devel', 'chrpath', 'check', 'astyle', 'uuid-c++-devel', 'gettext-devel',
    'gcc-c++', 'which', 'alsa-lib-devel', 'systemd-devel', 'libuuid-devel',
    'uuid-devel', 'gnutls-devel', 'nettle-devel', 'opus-devel', 'speexdsp-devel',
50
    'yaml-cpp-devel', 'qt5-qtbase-devel', 'swig',
Alexandre Lision's avatar
Alexandre Lision committed
51
]
Alexandre Lision's avatar
Alexandre Lision committed
52

aviau's avatar
aviau committed
53 54 55 56 57 58 59 60 61 62
UBUNTU_DEPENDENCIES = [
    'autoconf', 'autopoint', 'cmake', 'dbus', 'doxygen', 'g++', 'gettext',
    'gnome-icon-theme-symbolic', 'libasound2-dev', 'libavcodec-dev',
    'libavcodec-extra', 'libavdevice-dev', 'libavformat-dev', 'libboost-dev',
    'libclutter-gtk-1.0-dev', 'libcppunit-dev', 'libdbus-1-dev',
    'libdbus-c++-dev', 'libebook1.2-dev', 'libexpat1-dev', 'libgnutls-dev',
    'libgsm1-dev', 'libgtk-3-dev', 'libjack-dev', 'libnotify-dev',
    'libopus-dev', 'libpcre3-dev', 'libpulse-dev', 'libsamplerate0-dev',
    'libsndfile1-dev', 'libspeex-dev', 'libspeexdsp-dev', 'libsrtp-dev',
    'libswscale-dev', 'libtool', 'libudev-dev', 'libupnp-dev',
63
    'libyaml-cpp-dev', 'qtbase5-dev', 'sip-tester', 'swig',
aviau's avatar
aviau committed
64 65 66 67 68 69 70 71 72 73 74 75 76
    'uuid-dev', 'yasm'
]


DEBIAN_DEPENDENCIES = [
    'autoconf', 'autopoint', 'cmake', 'dbus', 'doxygen', 'g++', 'gettext',
    'gnome-icon-theme-symbolic', 'libasound2-dev', 'libavcodec-dev',
    'libavcodec-extra', 'libavdevice-dev', 'libavformat-dev', 'libboost-dev',
    'libclutter-gtk-1.0-dev', 'libcppunit-dev', 'libdbus-1-dev',
    'libdbus-c++-dev', 'libebook1.2-dev', 'libexpat1-dev', 'libgsm1-dev',
    'libgtk-3-dev', 'libjack-dev', 'libnotify-dev', 'libopus-dev',
    'libpcre3-dev', 'libpulse-dev', 'libsamplerate0-dev', 'libsndfile1-dev',
    'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
77
    'libudev-dev', 'libupnp-dev', 'libyaml-cpp-dev',
aviau's avatar
aviau committed
78 79 80
    'qtbase5-dev', 'sip-tester', 'swig',  'uuid-dev', 'yasm'
]

Alexandre Lision's avatar
Alexandre Lision committed
81
OSX_DEPENDENCIES = [
Romain Bertozzi's avatar
Romain Bertozzi committed
82
    'autoconf', 'cmake', 'gettext', 'pkg-config', 'homebrew/versions/qt55',
Alexandre Lision's avatar
Alexandre Lision committed
83 84 85
    'libtool', 'yasm', 'automake'
]

aviau's avatar
aviau committed
86 87 88 89 90 91
UNINSTALL_SCRIPT = [
    'make -C daemon uninstall',
    'xargs rm < lrc/build-global/install_manifest.txt',
    'xargs rm < client-gnome/build-global/install_manifest.txt',
]

Alexandre Lision's avatar
Alexandre Lision committed
92 93 94 95 96
OSX_UNINSTALL_SCRIPT = [
    'make -C daemon uninstall',
    'rm -rf install/client-macosx',
]

aviau's avatar
aviau committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
STOP_SCRIPT = [
    'xargs kill < daemon.pid',
    'xargs kill < gnome-ring.pid',
]


def run_dependencies(args):
    if args.distribution == "Ubuntu":
        execute_script(APT_INSTALL_SCRIPT,
            {"packages": ' '.join(UBUNTU_DEPENDENCIES)}
        )

    elif args.distribution == "Debian":
        execute_script(
            APT_INSTALL_SCRIPT,
            {"packages": ' '.join(DEBIAN_DEPENDENCIES)}
        )

Alexandre Lision's avatar
Alexandre Lision committed
115 116 117 118 119 120
    elif args.distribution == "Fedora":
        execute_script(
            RPM_INSTALL_SCRIPT,
            {"packages": ' '.join(FEDORA_DEPENDENCIES)}
        )

Alexandre Lision's avatar
Alexandre Lision committed
121 122 123 124 125 126
    elif args.distribution == "OSX":
        execute_script(
            BREW_INSTALL_SCRIPT,
            {"packages": ' '.join(OSX_DEPENDENCIES)}
        )

Romain Bertozzi's avatar
Romain Bertozzi committed
127 128 129 130
    elif args.distribution == "Android":
        print("The Android version does not need more dependencies.\nPlease continue with the --install instruction.")
        sys.exit(1)

aviau's avatar
aviau committed
131 132 133 134
    else:
        print("Not yet implemented for current distribution (%s)" % args.distribution)
        sys.exit(1)

Alexandre Lision's avatar
Alexandre Lision committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
def run_init():
    os.system("git submodule update --init")
    os.system("git submodule foreach 'git checkout master && git pull'")
    for project in ["daemon", "lrc", "client-macosx", "client-gnome", "client-android"]:
        copy_file("./scripts/commit-msg", ".git/modules/"+project+"/hooks")

def copy_file(src, dest):
    print("Copying:" + src + " to " + dest)
    try:
        shutil.copy2(src, dest)
    # eg. src and dest are the same file
    except shutil.Error as e:
        print('Error: %s' % e)
    # eg. source or destination doesn't exist
    except IOError as e:
        print('Error: %s' % e.strerror)
aviau's avatar
aviau committed
151 152

def run_install(args):
Alexandre Lision's avatar
Alexandre Lision committed
153
    install_args = ' -p ' + str(multiprocessing.cpu_count())
aviau's avatar
aviau committed
154 155 156 157
    if args.static:
        install_args += ' -s'
    if args.global_install:
        install_args += ' -g'
Alexandre Lision's avatar
Alexandre Lision committed
158
    if args.distribution == "OSX":
Romain Bertozzi's avatar
Romain Bertozzi committed
159
        proc= subprocess.Popen("brew --prefix homebrew/versions/qt55", shell=True, stdout=subprocess.PIPE)
Alexandre Lision's avatar
Alexandre Lision committed
160 161
        qt5dir = proc.stdout.read()
        os.environ['CMAKE_PREFIX_PATH'] = str(qt5dir.decode('ascii'))
Alexandre Lision's avatar
Alexandre Lision committed
162 163
        install_args += " -c client-macosx"
        execute_script(["CONFIGURE_FLAGS='--without-dbus' ./scripts/install.sh " + install_args])
Romain Bertozzi's avatar
Romain Bertozzi committed
164 165 166
    elif args.distribution == "Android":
        os.chdir("./client-android")
        execute_script(["./compile.sh"])
Alexandre Lision's avatar
Alexandre Lision committed
167
    else:
168
        install_args += ' -c client-gnome'
Alexandre Lision's avatar
Alexandre Lision committed
169
        execute_script(["./scripts/install.sh " + install_args])
aviau's avatar
aviau committed
170 171 172


def run_uninstall(args):
Alexandre Lision's avatar
Alexandre Lision committed
173 174 175 176
    if args.distribution == "OSX":
        execute_script(OSX_UNINSTALL_SCRIPT)
    else:
        execute_script(UNINSTALL_SCRIPT)
aviau's avatar
aviau committed
177 178 179


def run_run(args):
Alexandre Lision's avatar
Alexandre Lision committed
180 181 182 183
    if args.distribution == "OSX":
        subprocess.Popen(["install/client-macosx/Ring.app/Contents/MacOS/Ring"])
        return True

aviau's avatar
aviau committed
184 185 186 187 188 189 190
    run_env = os.environ
    run_env['LD_LIBRARY_PATH'] = run_env.get('LD_LIBRARY_PATH', '') + ":install/lrc/lib"

    try:
        dring_log = open("daemon.log", 'a')
        dring_log.write('=== Starting daemon (%s) ===' % time.strftime("%d/%m/%Y %H:%M:%S"))
        dring_process = subprocess.Popen(
191
            ["./install/daemon/sbin/dring", "-c", "-d"],
aviau's avatar
aviau committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
            stdout=dring_log,
            stderr=dring_log
        )

        with open('daemon.pid', 'w') as f:
            f.write(str(dring_process.pid)+'\n')

        client_log = open("gnome-ring.log", 'a')
        client_log.write('=== Starting client (%s) ===' % time.strftime("%d/%m/%Y %H:%M:%S"))
        client_process = subprocess.Popen(
            ["./install/client-gnome/bin/gnome-ring", "-d"],
            stdout=client_log,
            stderr=client_log,
            env=run_env
        )

        with open('gnome-ring.pid', 'w') as f:
            f.write(str(client_process.pid)+'\n')

        if args.debug:
            subprocess.call(
213
                ['gdb','-x', 'gdb.gdb', './install/daemon/sbin/dring'],
aviau's avatar
aviau committed
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
            )

        if args.background == False:
            dring_process.wait()
            client_process.wait()

    except KeyboardInterrupt:
        print("\nCaught KeyboardInterrupt...")

    finally:
        if args.background == False:
            try:
                # Only kill the processes if they are running, as they could
                # have been closed by the user.
                print("Killing processes...")
                dring_log.close()
                if dring_process.poll() is None:
                    dring_process.kill()
                client_log.close()
                if client_process.poll() is None:
                    client_process.kill()
            except UnboundLocalError:
                # Its okay! We crashed before we could start a process or open a
                # file. All that matters is that we close files and kill processes
                # in the right order.
                pass
Alexandre Lision's avatar
Alexandre Lision committed
240
    return True
aviau's avatar
aviau committed
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263


def run_stop(args):
    execute_script(STOP_SCRIPT)


def execute_script(script, settings=None):
    if settings == None:
        settings = {}
    for line in script:
        line = line % settings
        rv = os.system(line)
        if rv != 0:
            print('Error executing script! Exit code: %s' % rv,
                  file=sys.stderr)
            return False
    return True


def validate_args(parsed_args):
    """Validate the args values, exit if error is found"""

    # Check arg values
Romain Bertozzi's avatar
Romain Bertozzi committed
264
    supported_distros = ['Android', 'Ubuntu', 'Debian', 'OSX', 'Fedora', 'Automatic']
aviau's avatar
aviau committed
265 266 267 268 269 270 271 272 273 274
    if parsed_args.distribution not in supported_distros:
        print('Distribution not supported.\nChoose one of: %s' \
                  % ', '.join(supported_distros),
            file=sys.stderr)
        sys.exit(1)

def parse_args():
    ap = argparse.ArgumentParser(description="Ring build tool")

    ga = ap.add_mutually_exclusive_group(required=True)
Alexandre Lision's avatar
Alexandre Lision committed
275 276 277
    ga.add_argument(
        '--init', action='store_true',
        help='Init Ring repository')
aviau's avatar
aviau committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    ga.add_argument(
        '--dependencies', action='store_true',
        help='Install ring build dependencies')
    ga.add_argument(
        '--install', action='store_true',
        help='Build and install Ring')
    ga.add_argument(
        '--uninstall', action='store_true',
        help='Uninstall Ring')
    ga.add_argument(
        '--run', action='store_true',
         help='Run the Ring daemon and client')
    ga.add_argument(
        '--stop', action='store_true',
        help='Stop the Ring processes')

Alexandre Lision's avatar
Alexandre Lision committed
294
    ap.add_argument('--distribution', default='Automatic')
aviau's avatar
aviau committed
295 296 297 298 299 300
    ap.add_argument('--static', default=False, action='store_true')
    ap.add_argument('--global-install', default=False, action='store_true')
    ap.add_argument('--debug', default=False, action='store_true')
    ap.add_argument('--background', default=False, action='store_true')

    parsed_args = ap.parse_args()
Alexandre Lision's avatar
Alexandre Lision committed
301 302 303 304

    if parsed_args.distribution == 'Automatic':
        parsed_args.distribution = choose_distribution()

aviau's avatar
aviau committed
305 306 307 308
    validate_args(parsed_args)

    return parsed_args

Alexandre Lision's avatar
Alexandre Lision committed
309 310 311 312 313 314 315
def choose_distribution():
    system = platform.system().lower()
    if system == "linux" or system == "linux2":
        with open("/etc/os-release") as f:
            for line in f:
                k,v = line.split("=")
                if k.strip() == 'NAME':
316
                    return v.strip().replace('"','')
Alexandre Lision's avatar
Alexandre Lision committed
317 318 319 320 321
    elif system == "darwin":
        return 'OSX'

    return 'Unknown'

aviau's avatar
aviau committed
322 323 324 325 326 327 328

def main():
    parsed_args = parse_args()

    if parsed_args.dependencies:
        run_dependencies(parsed_args)

Alexandre Lision's avatar
Alexandre Lision committed
329 330 331
    elif parsed_args.init:
        run_init()

aviau's avatar
aviau committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
    elif parsed_args.install:
        run_install(parsed_args)

    elif parsed_args.uninstall:
        run_uninstall(parsed_args)

    elif parsed_args.run:
        run_run(parsed_args)

    elif parsed_args.stop:
        run_stop(parsed_args)


if __name__ == "__main__":
    main()