make-ring.py 15.1 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',
]

Simon Zeni's avatar
Simon Zeni committed
27 28 29 30
PACMAN_BASED_DISTROS = [
    'Arch Linux',
]

31 32 33 34
SUSE_BASED_DISTROS = [
    'openSUSE',
]

aviau's avatar
aviau committed
35 36 37 38 39
APT_INSTALL_SCRIPT = [
    'apt-get update',
    'apt-get install -y %(packages)s'
]

Alexandre Lision's avatar
Alexandre Lision committed
40 41 42 43 44
BREW_INSTALL_SCRIPT = [
    'brew update',
    'brew install -y %(packages)s',
    'brew link --force gettext'
]
Simon Zeni's avatar
Simon Zeni committed
45

Alexandre Lision's avatar
Alexandre Lision committed
46 47 48 49 50
RPM_INSTALL_SCRIPT = [
    'sudo dnf update',
    'sudo dnf install -y %(packages)s'
]

Simon Zeni's avatar
Simon Zeni committed
51 52 53 54 55
PACMAN_INSTALL_SCRIPT = [
    'sudo pacman -Sy',
    'sudo pacman -S %(packages)s'
]

56 57 58 59 60 61 62 63 64 65 66 67 68
ZYPPER_INSTALL_SCRIPT = [
    'sudo zypper update',
    'sudo zypper install -y %(packages)s'
]

OPENSUSE_DEPENDENCIES = [
# build system
    'autoconf', 'automake', 'cmake', 'patch', 'gcc-c++', 'libtool',
# daemon
    'speexdsp-devel', 'speex-devel', 'libdbus-c++-devel', 'jsoncpp-devel', 'yaml-cpp-devel',
    'libupnp-devel', 'boost-devel', 'yasm', 'libuuid-devel', 'libsamplerate-devel',
    'libnettle-devel', 'libopus-devel', 'libgnutls-devel', 'msgpack-devel', 'libavcodec-devel',
    'libavdevice-devel', 'pcre-devel', 'libogg-devel', 'libsndfile-devel', 'libvorbis-devel',
69
    'flac-devel', 'libgsm-devel', 'alsa-devel', 'libpulse-devel', 'libudev-devel', 'libva-devel',
70 71 72 73 74 75 76
# lrc
    'libQt5Core-devel', 'libQt5DBus-devel', 'libqt5-linguist-devel',
# gnome client
    'gtk3-devel', 'clutter-gtk-devel', 'qrencode-devel', 'evolution-data-server-devel',
    'gettext-tools', 'libnotify-devel', 'libappindicator3-devel',
]

77 78 79 80 81 82 83 84 85 86 87 88 89 90
MINGW64_FEDORA_DEPENDENCIES = [
    'mingw64-binutils', 'mingw64-gcc', 'mingw64-headers', 'mingw64-crt', 'mingw64-gcc-c++',
    'mingw64-pkg-config', 'yasm', 'gettext-devel', 'cmake', 'patch', 'libtool', 'automake',
    'make', 'xz', 'bzip2', 'which', 'mingw64-qt5-qtbase', 'mingw64-qt5-qttools',
    'mingw64-qt5-qtsvg', 'mingw64-qt5-qtwinextras'
]

MINGW32_FEDORA_DEPENDENCIES = [
    'mingw32-binutils', 'mingw32-gcc', 'mingw32-headers', 'mingw32-crt', 'mingw32-gcc-c++',
    'mingw32-pkg-config', 'yasm', 'gettext-devel', 'cmake', 'patch', 'libtool', 'automake',
    'make', 'xz', 'bzip2', 'which', 'mingw32-qt5-qtbase', 'mingw32-qt5-qttools',
    'mingw32-qt5-qtsvg', 'mingw32-qt5-qtwinextras'
]

Alexandre Lision's avatar
Alexandre Lision committed
91 92 93 94
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',
Stepan Salenikovich's avatar
Stepan Salenikovich committed
95 96
    'libsndfile-devel', 'libXext-devel', 'libXfixes-devel', 'yasm',
    'speex-devel', 'chrpath', 'check', 'astyle', 'uuid-c++-devel', 'gettext',
Alexandre Lision's avatar
Alexandre Lision committed
97 98
    'gcc-c++', 'which', 'alsa-lib-devel', 'systemd-devel', 'libuuid-devel',
    'uuid-devel', 'gnutls-devel', 'nettle-devel', 'opus-devel', 'speexdsp-devel',
Stepan Salenikovich's avatar
Stepan Salenikovich committed
99 100
    'yaml-cpp-devel', 'qt5-qtbase-devel', 'swig', 'qrencode-devel', 'jsoncpp-devel',
    'gtk3-devel', 'clutter-devel', 'clutter-gtk-devel', 'evolution-data-server-devel',
101
    'libnotify-devel', 'libappindicator-gtk3-devel', 'patch', 'libva-devel'
Alexandre Lision's avatar
Alexandre Lision committed
102
]
Alexandre Lision's avatar
Alexandre Lision committed
103

aviau's avatar
aviau committed
104 105 106 107 108 109 110 111
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',
Stepan Salenikovich's avatar
Stepan Salenikovich committed
112 113
    'libsndfile1-dev', 'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
    'libudev-dev', 'libupnp-dev', 'libyaml-cpp-dev', 'qtbase5-dev', 'sip-tester', 'swig',
114 115
    'uuid-dev', 'yasm', 'libqrencode-dev', 'libjsoncpp-dev', 'libappindicator3-dev',
    'libva-dev'
aviau's avatar
aviau committed
116 117 118 119 120 121 122
]

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',
Stepan Salenikovich's avatar
Stepan Salenikovich committed
123 124 125 126 127
    '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', 'libswscale-dev', 'libtool',
    'libudev-dev', 'libupnp-dev', 'libyaml-cpp-dev', 'qtbase5-dev', 'sip-tester', 'swig',
128 129
    'uuid-dev', 'yasm', 'libqrencode-dev', 'libjsoncpp-dev', 'libappindicator3-dev',
    'libva-dev'
aviau's avatar
aviau committed
130 131
]

Simon Zeni's avatar
Simon Zeni committed
132 133 134 135 136 137
ARCH_LINUX_DEPENDENCIES = [
    'autoconf', 'gettext', 'cmake', 'dbus', 'doxygen', 'gcc', 'gnome-icon-theme-symbolic',
    'ffmpeg', 'boost', 'clutter-gtk', 'cppunit', 'libdbus', 'dbus-c++', 'libe-book',
    'expat', 'gsm', 'gtk3', 'jack', 'libnotify', 'opus', 'pcre', 'libpulse', 'libsamplerate',
    'libsndfile', 'speex', 'speexdsp', 'libtool', 'libupnp', 'yaml-cpp', 'qt5-base',
    'swig', 'yasm', 'qrencode', 'evolution-data-server', 'make', 'patch', 'pkg-config',
138
    'automake', 'libva'
Simon Zeni's avatar
Simon Zeni committed
139 140
]

Alexandre Lision's avatar
Alexandre Lision committed
141
OSX_DEPENDENCIES = [
Romain Bertozzi's avatar
Romain Bertozzi committed
142
    'autoconf', 'cmake', 'gettext', 'pkg-config', 'homebrew/versions/qt55',
Alexandre Lision's avatar
Alexandre Lision committed
143 144 145
    'libtool', 'yasm', 'automake'
]

aviau's avatar
aviau committed
146 147 148 149 150 151
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
152 153 154 155 156
OSX_UNINSTALL_SCRIPT = [
    'make -C daemon uninstall',
    'rm -rf install/client-macosx',
]

aviau's avatar
aviau committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
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
175 176 177 178 179
    elif args.distribution == "Fedora":
        execute_script(
            RPM_INSTALL_SCRIPT,
            {"packages": ' '.join(FEDORA_DEPENDENCIES)}
        )
180 181 182 183 184 185 186 187 188 189
    elif args.distribution == "mingw32":
        execute_script(
            RPM_INSTALL_SCRIPT,
            {"packages": ' '.join(MINGW32_FEDORA_DEPENDENCIES)}
        )
    elif args.distribution == "mingw64":
        execute_script(
            RPM_INSTALL_SCRIPT,
            {"packages": ' '.join(MINGW64_FEDORA_DEPENDENCIES)}
        )
Simon Zeni's avatar
Simon Zeni committed
190 191 192 193 194 195
    elif args.distribution == "Arch Linux":
        execute_script(
            PACMAN_INSTALL_SCRIPT,
            {"packages": ' '.join(ARCH_LINUX_DEPENDENCIES)}
        )

196 197 198 199 200 201
    elif args.distribution == "openSUSE":
        execute_script(
            ZYPPER_INSTALL_SCRIPT,
            {"packages": ' '.join(OPENSUSE_DEPENDENCIES)}
        )

Alexandre Lision's avatar
Alexandre Lision committed
202 203 204 205 206 207
    elif args.distribution == "OSX":
        execute_script(
            BREW_INSTALL_SCRIPT,
            {"packages": ' '.join(OSX_DEPENDENCIES)}
        )

Romain Bertozzi's avatar
Romain Bertozzi committed
208 209 210 211
    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
212 213 214 215
    else:
        print("Not yet implemented for current distribution (%s)" % args.distribution)
        sys.exit(1)

Alexandre Lision's avatar
Alexandre Lision committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
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
232 233

def run_install(args):
Alexandre Lision's avatar
Alexandre Lision committed
234
    install_args = ' -p ' + str(multiprocessing.cpu_count())
aviau's avatar
aviau committed
235 236 237 238
    if args.static:
        install_args += ' -s'
    if args.global_install:
        install_args += ' -g'
Alexandre Lision's avatar
Alexandre Lision committed
239
    if args.distribution == "OSX":
Romain Bertozzi's avatar
Romain Bertozzi committed
240
        proc= subprocess.Popen("brew --prefix homebrew/versions/qt55", shell=True, stdout=subprocess.PIPE)
Alexandre Lision's avatar
Alexandre Lision committed
241 242
        qt5dir = proc.stdout.read()
        os.environ['CMAKE_PREFIX_PATH'] = str(qt5dir.decode('ascii'))
Alexandre Lision's avatar
Alexandre Lision committed
243 244
        install_args += " -c client-macosx"
        execute_script(["CONFIGURE_FLAGS='--without-dbus' ./scripts/install.sh " + install_args])
Romain Bertozzi's avatar
Romain Bertozzi committed
245 246 247
    elif args.distribution == "Android":
        os.chdir("./client-android")
        execute_script(["./compile.sh"])
248 249 250 251 252 253 254 255 256 257
    elif args.distribution == 'mingw32':
        os.environ['CMAKE_PREFIX_PATH'] = '/usr/i686-w64-mingw32/sys-root/mingw/lib/cmake'
        os.environ['QTDIR'] = '/usr/i686-w64-mingw32/sys-root/mingw/lib/qt5/'
        os.environ['PATH'] = '/usr/i686-w64-mingw32/bin/qt5/:' + os.environ['PATH']
        execute_script(["./scripts/win_compile.sh"])
    elif args.distribution == 'mingw64':
        os.environ['CMAKE_PREFIX_PATH'] = '/usr/x86_64-w64-mingw32/sys-root/mingw/lib/cmake'
        os.environ['QTDIR'] = '/usr/x86_64-w64-mingw32/sys-root/mingw/lib/qt5/'
        os.environ['PATH'] = '/usr/x86_64-w64-mingw32/bin/qt5/:' + os.environ['PATH']
        execute_script(["./scripts/win_compile.sh --arch=64"])
Alexandre Lision's avatar
Alexandre Lision committed
258
    else:
259 260
        if args.distribution == "openSUSE":
            os.environ['JSONCPP_LIBS'] = "-ljsoncpp" #fix jsoncpp pkg-config bug, remove when jsoncpp package bumped
261
        install_args += ' -c client-gnome'
Alexandre Lision's avatar
Alexandre Lision committed
262
        execute_script(["./scripts/install.sh " + install_args])
aviau's avatar
aviau committed
263 264 265


def run_uninstall(args):
Alexandre Lision's avatar
Alexandre Lision committed
266 267 268 269
    if args.distribution == "OSX":
        execute_script(OSX_UNINSTALL_SCRIPT)
    else:
        execute_script(UNINSTALL_SCRIPT)
aviau's avatar
aviau committed
270 271 272


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

aviau's avatar
aviau committed
277 278 279 280 281 282 283
    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(
284
            ["./install/daemon/lib/ring/dring", "-c", "-d"],
aviau's avatar
aviau committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
            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(
306
                ['gdb','-x', 'gdb.gdb', './install/daemon/lib/ring/dring'],
aviau's avatar
aviau committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
            )

        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
333
    return True
aviau's avatar
aviau committed
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356


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
357
    supported_distros = ['Android', 'Ubuntu', 'Debian', 'OSX', 'Fedora', 'Arch Linux', 'openSUSE', 'Automatic', 'mingw32', 'mingw64']
Simon Zeni's avatar
Simon Zeni committed
358

aviau's avatar
aviau committed
359 360 361 362 363 364 365 366 367 368
    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
369 370 371
    ga.add_argument(
        '--init', action='store_true',
        help='Init Ring repository')
aviau's avatar
aviau committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    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
388
    ap.add_argument('--distribution', default='Automatic')
aviau's avatar
aviau committed
389 390 391 392 393 394
    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
395 396 397 398

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

399 400 401 402 403
    if parsed_args.distribution in ['mingw32', 'mingw64']:
        if choose_distribution() != "Fedora":
            print('Windows version must be built on a Fedora distribution (>=23)')
            sys.exit(1)

aviau's avatar
aviau committed
404 405 406 407
    validate_args(parsed_args)

    return parsed_args

Alexandre Lision's avatar
Alexandre Lision committed
408 409 410
def choose_distribution():
    system = platform.system().lower()
    if system == "linux" or system == "linux2":
VietKen's avatar
VietKen committed
411 412
        if os.path.isfile("/etc/arch-release"):
            return "Arch Linux"
Alexandre Lision's avatar
Alexandre Lision committed
413 414 415 416
        with open("/etc/os-release") as f:
            for line in f:
                k,v = line.split("=")
                if k.strip() == 'NAME':
417
                    return v.strip().replace('"','')
Alexandre Lision's avatar
Alexandre Lision committed
418 419 420 421 422
    elif system == "darwin":
        return 'OSX'

    return 'Unknown'

aviau's avatar
aviau committed
423 424 425 426 427 428 429

def main():
    parsed_args = parse_args()

    if parsed_args.dependencies:
        run_dependencies(parsed_args)

Alexandre Lision's avatar
Alexandre Lision committed
430 431 432
    elif parsed_args.init:
        run_init()

aviau's avatar
aviau committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    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()